//#region extensions/crypto/src/services/plan-compiler.ts
var PlanCompiler = class {
	idCounter = 0;
	/** Maps outputRef names to generated node IDs for step_output resolution. */
	outputRefToId = /* @__PURE__ */ new Map();
	/**
	* Compile an intent into a Plan IR.
	* Throws CompilationError on invalid intents.
	*/
	compile(intent, userId) {
		this.idCounter = 0;
		this.outputRefToId = /* @__PURE__ */ new Map();
		if (!intent.steps || intent.steps.length === 0) throw new CompilationError("Intent has no steps.");
		const trigger = intent.trigger ? this.compileTrigger(intent.trigger) : void 0;
		const rootSteps = intent.steps.map((step) => this.compileNode(step));
		const root = rootSteps.length === 1 ? rootSteps[0] : {
			id: this.nextId("seq"),
			label: "Main sequence",
			type: "sequence",
			steps: rootSteps
		};
		return {
			id: `plan_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
			name: intent.name ?? this.inferName(intent),
			userId,
			createdAt: Date.now(),
			status: trigger ? "draft" : "draft",
			trigger,
			root,
			tags: intent.tags,
			naturalLanguage: intent.naturalLanguage
		};
	}
	compileTrigger(t) {
		switch (t.type) {
			case "immediate": return { type: "immediate" };
			case "at_time": {
				if (!t.time) throw new CompilationError("at_time trigger requires a \"time\" field (ISO 8601).");
				const parsed = new Date(t.time);
				if (isNaN(parsed.getTime())) throw new CompilationError(`Invalid time: "${t.time}". Use ISO 8601 format.`);
				return {
					type: "time",
					at: parsed.toISOString()
				};
			}
			case "every": {
				if (!t.interval) throw new CompilationError("every trigger requires an \"interval\" field (e.g., \"4h\", \"30m\").");
				const ms = parseIntervalToMs(t.interval);
				if (ms < 3e4) throw new CompilationError("Interval must be at least 30 seconds.");
				return {
					type: "interval",
					everyMs: ms,
					maxRuns: t.maxRuns
				};
			}
			case "when_condition":
				if (t.token && t.op && t.value !== void 0) return {
					type: "condition",
					when: this.buildPriceCondition(t.token, t.op, t.value),
					pollIntervalMs: 6e4,
					expiresAfterMs: t.expires ? parseIntervalToMs(t.expires) : 10080 * 60 * 1e3
				};
				if (t.logic && t.conditions && t.conditions.length > 0) {
					const subConditions = t.conditions.map((sub) => {
						if (!sub.token || !sub.op || sub.value === void 0) throw new CompilationError("Each sub-condition needs token, op, and value.");
						return this.buildPriceCondition(sub.token, sub.op, sub.value);
					});
					return {
						type: "condition",
						when: {
							type: "logic",
							op: t.logic,
							conditions: subConditions
						},
						pollIntervalMs: 6e4,
						expiresAfterMs: t.expires ? parseIntervalToMs(t.expires) : 10080 * 60 * 1e3
					};
				}
				throw new CompilationError("when_condition trigger needs (token + op + value) or (logic + conditions[]).");
		}
	}
	buildPriceCondition(token, op, value) {
		return {
			type: "compare",
			left: {
				type: "runtime",
				fn: "price",
				args: [{
					type: "literal",
					value: token
				}]
			},
			op,
			right: {
				type: "literal",
				value
			},
			label: `${token} price ${opSymbol(op)} $${value}`
		};
	}
	compileNode(step) {
		switch (step.type ?? "action") {
			case "parallel": return this.compileParallel(step);
			case "wait": return this.compileWait(step);
			case "loop": return this.compileLoop(step);
			default: return this.compileAction(step);
		}
	}
	compileAction(step) {
		const action = step.action ?? "custom";
		const id = step.outputRef ?? this.nextId(action);
		const failurePolicy = this.compileFailurePolicy(step);
		if (step.outputRef) this.outputRefToId.set(step.outputRef, id);
		const extraParams = {};
		if (step.inputRefs) for (const [paramName, refStr] of Object.entries(step.inputRefs)) {
			const dotIdx = refStr.indexOf(".");
			if (dotIdx < 0) throw new CompilationError(`inputRef "${refStr}" must be "refName.path" (e.g., "swap1.amountOut").`);
			const refName = refStr.slice(0, dotIdx);
			const path = refStr.slice(dotIdx + 1);
			const stepId = this.outputRefToId.get(refName);
			if (!stepId) throw new CompilationError(`inputRef "${refStr}" references unknown outputRef "${refName}". Ensure the step with outputRef="${refName}" appears earlier.`);
			extraParams[paramName] = {
				type: "step_output",
				stepId,
				path
			};
		}
		let actionNode;
		switch (action) {
			case "swap":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Swap ${step.amountPct ? step.amountPct + "% " : ""}${step.tokenIn ?? "?"} → ${step.tokenOut ?? "?"}`,
					tool: "defi_swap",
					params: {
						action: "execute",
						...step.tokenIn && { token_in: step.tokenIn },
						...step.tokenOut && { token_out: step.tokenOut },
						...step.amount && { amount: step.amount },
						...step.amountPct !== void 0 && { amount_pct: step.amountPct },
						...step.slippageBps !== void 0 && { slippage_bps: step.slippageBps },
						...step.chainId !== void 0 && { chain_id: step.chainId },
						...extraParams
					},
					requireConfirmation: step.confirm ?? true,
					onFailure: failurePolicy
				};
				break;
			case "transfer":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Transfer ${step.amount ?? "?"} ${step.token ?? "ETH"} to ${step.to ? truncateAddr(step.to) : "?"}`,
					tool: "transfer",
					params: {
						action: "send",
						...step.token && { token: step.token },
						...step.amount && { amount: step.amount },
						...step.to && { to: step.to },
						...extraParams
					},
					requireConfirmation: step.confirm ?? true,
					onFailure: failurePolicy
				};
				break;
			case "bridge":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Bridge ${step.amount ?? "?"} ${step.token ?? "?"} to chain ${step.toChain ?? "?"}`,
					tool: "bridge",
					params: {
						action: "execute",
						...step.token && { token: step.token },
						...step.amount && { amount: step.amount },
						...step.toChain !== void 0 && { to_chain_id: step.toChain },
						...step.fromChain !== void 0 && { from_chain_id: step.fromChain },
						...extraParams
					},
					requireConfirmation: step.confirm ?? true,
					onFailure: failurePolicy
				};
				break;
			case "check_price":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Check ${step.token ?? "ETH"} price`,
					tool: "defi_price",
					params: {
						action: "lookup",
						...step.token && { token: step.token },
						...extraParams
					},
					requireConfirmation: false,
					onFailure: failurePolicy
				};
				break;
			case "check_balance":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Check ${step.token ?? "all"} balance`,
					tool: "defi_balance",
					params: {
						...step.token && { token: step.token },
						...extraParams
					},
					requireConfirmation: false,
					onFailure: failurePolicy
				};
				break;
			case "set_order":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Set ${step.orderType ?? "limit"} order for ${step.token ?? step.tokenOut ?? "?"}`,
					tool: "manage_orders",
					params: {
						action: "create",
						...step.orderType && { order_type: step.orderType },
						...step.token && { token: step.token },
						...step.tokenIn && { token_in: step.tokenIn },
						...step.tokenOut && { token_out: step.tokenOut },
						...step.amount && { amount: step.amount },
						...step.triggerPrice !== void 0 && { trigger_price: step.triggerPrice },
						...extraParams
					},
					requireConfirmation: step.confirm ?? false,
					onFailure: failurePolicy
				};
				break;
			case "approve":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Approve ${step.token ?? "?"}`,
					tool: "permit2",
					params: {
						action: "approve",
						...step.token && { token: step.token },
						...extraParams
					},
					requireConfirmation: step.confirm ?? true,
					onFailure: failurePolicy
				};
				break;
			case "launch":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? "Launch token",
					tool: "clawnch_launch",
					params: {
						...step.params ?? {},
						...extraParams
					},
					requireConfirmation: step.confirm ?? true,
					onFailure: failurePolicy
				};
				break;
			case "claim":
				actionNode = {
					id,
					type: "action",
					label: step.label ?? "Claim fees/rewards",
					tool: "clawnch_fees",
					params: {
						action: "claim",
						...step.token && { token: step.token },
						...extraParams
					},
					requireConfirmation: step.confirm ?? false,
					onFailure: failurePolicy
				};
				break;
			case "custom":
				if (!step.tool) throw new CompilationError("custom action requires a \"tool\" field.");
				actionNode = {
					id,
					type: "action",
					label: step.label ?? `Run ${step.tool}`,
					tool: step.tool,
					params: {
						...step.params ?? {},
						...extraParams
					},
					requireConfirmation: step.confirm ?? false,
					onFailure: failurePolicy
				};
				break;
			default: throw new CompilationError(`Unknown action: "${action}".`);
		}
		const onErrorNode = this.compileOnError(step);
		if (onErrorNode) actionNode.onError = onErrorNode;
		if (step.condition) {
			const cond = this.buildStepCondition(step.condition);
			return {
				id: this.nextId("if"),
				type: "if",
				label: `If ${step.condition.field ?? "price"}(${step.condition.token ?? "?"}) ${opSymbol(step.condition.op)} ${step.condition.value}`,
				condition: cond,
				then: actionNode
			};
		}
		return actionNode;
	}
	compileParallel(step) {
		if (!step.steps || step.steps.length === 0) throw new CompilationError("parallel step requires a non-empty \"steps\" array.");
		const children = step.steps.map((s) => this.compileNode(s));
		return {
			id: this.nextId("par"),
			type: "parallel",
			label: step.label ?? `Parallel (${children.length} steps)`,
			steps: children,
			allowPartialFailure: step.allowPartialFailure ?? false,
			onFailure: this.compileFailurePolicy(step)
		};
	}
	compileWait(step) {
		const node = {
			id: this.nextId("wait"),
			type: "wait",
			label: step.label ?? "Wait",
			onFailure: this.compileFailurePolicy(step)
		};
		if (step.duration) {
			node.durationMs = parseIntervalToMs(step.duration);
			node.label = step.label ?? `Wait ${step.duration}`;
		} else if (step.untilTime) {
			const parsed = new Date(step.untilTime);
			if (isNaN(parsed.getTime())) throw new CompilationError(`Invalid untilTime: "${step.untilTime}". Use ISO 8601.`);
			node.untilTime = parsed.toISOString();
			node.label = step.label ?? `Wait until ${step.untilTime}`;
		} else if (step.until) {
			node.until = this.buildStepCondition(step.until);
			node.pollIntervalMs = step.pollInterval ? parseIntervalToMs(step.pollInterval) : 6e4;
			node.maxWaitMs = step.maxWait ? parseIntervalToMs(step.maxWait) : 864e5;
			const field = step.until.field ?? "price";
			node.label = step.label ?? `Wait until ${step.until.token ?? "?"} ${field} ${opSymbol(step.until.op)} ${step.until.value}`;
		} else throw new CompilationError("wait step requires \"duration\", \"untilTime\", or \"until\" condition.");
		return node;
	}
	compileLoop(step) {
		if (!step.steps || step.steps.length === 0) throw new CompilationError("loop step requires a non-empty \"steps\" array.");
		const bodySteps = step.steps.map((s) => this.compileNode(s));
		const body = bodySteps.length === 1 ? bodySteps[0] : {
			id: this.nextId("seq"),
			type: "sequence",
			label: "Loop body",
			steps: bodySteps
		};
		const node = {
			id: this.nextId("loop"),
			type: "loop",
			label: step.label ?? `Loop (max ${step.maxIterations ?? 10} iterations)`,
			body,
			maxIterations: step.maxIterations ?? 10,
			onFailure: this.compileFailurePolicy(step)
		};
		if (step.exitWhen) node.exitWhen = this.buildStepCondition(step.exitWhen);
		if (step.delayBetween) node.delayMs = parseIntervalToMs(step.delayBetween);
		return node;
	}
	buildStepCondition(c) {
		const field = c.field ?? "price";
		const token = c.token ?? "ETH";
		let fn;
		if (field === "balance") fn = "balance";
		else if (field === "gas_price") fn = "gas_price";
		else fn = "price";
		return {
			type: "compare",
			left: {
				type: "runtime",
				fn,
				args: [{
					type: "literal",
					value: token
				}]
			},
			op: c.op,
			right: {
				type: "literal",
				value: c.value
			},
			label: `${token} ${field} ${opSymbol(c.op)} ${c.value}`
		};
	}
	compileFailurePolicy(step) {
		if (!step.onFailure || step.onFailure === "abort") return void 0;
		if (step.onFailure === "skip") return { strategy: "skip" };
		if (step.onFailure === "retry") return {
			strategy: "retry",
			maxAttempts: step.retryCount ?? 3,
			delayMs: step.retryDelayMs ?? 5e3,
			backoffMultiplier: step.backoffMultiplier ?? 2
		};
	}
	/** Compile onError fallback steps into a PlanNode sub-tree. */
	compileOnError(step) {
		if (!step.onError || step.onError.length === 0) return void 0;
		if (step.onError.length === 1) return this.compileNode(step.onError[0]);
		return {
			id: this.nextId("err_seq"),
			type: "sequence",
			label: "Error fallback",
			steps: step.onError.map((s) => this.compileNode(s))
		};
	}
	nextId(prefix) {
		return `${prefix}_${++this.idCounter}`;
	}
	inferName(intent) {
		if (intent.steps.length === 1) {
			const step = intent.steps[0];
			return step.label ?? `${step.action} ${step.tokenIn ?? step.token ?? ""}`.trim();
		}
		return `${intent.steps.map((s) => s.action).join(" → ")}${intent.trigger?.type === "at_time" ? ` at ${intent.trigger.time}` : intent.trigger?.type === "when_condition" ? ` when ${intent.trigger.token ?? ""} ${opSymbol(intent.trigger.op ?? "gte")} $${intent.trigger.value ?? "?"}` : ""}`;
	}
};
var CompilationError = class extends Error {
	constructor(message) {
		super(message);
		this.name = "CompilationError";
	}
};
function opSymbol(op) {
	switch (op) {
		case "gt": return ">";
		case "gte": return ">=";
		case "lt": return "<";
		case "lte": return "<=";
		case "eq": return "=";
		case "neq": return "!=";
		default: return op;
	}
}
function truncateAddr(addr) {
	if (addr.length <= 10) return addr;
	return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
}
/**
* Parse human-readable intervals to milliseconds.
* Supports: 30s, 5m, 4h, 1d, 2w
*/
function parseIntervalToMs(input) {
	const match = input.trim().match(/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day|w|wk)s?$/i);
	if (!match) throw new CompilationError(`Invalid interval format: "${input}". Use e.g., "30s", "5m", "4h", "1d", "2w".`);
	const ms = parseFloat(match[1]) * ({
		s: 1e3,
		sec: 1e3,
		m: 6e4,
		min: 6e4,
		h: 36e5,
		hr: 36e5,
		d: 864e5,
		day: 864e5,
		w: 6048e5,
		wk: 6048e5
	}[match[2].toLowerCase()] ?? 1e3);
	if (ms <= 0 || !isFinite(ms)) throw new CompilationError(`Invalid interval: "${input}".`);
	return Math.round(ms);
}
//#endregion
export { CompilationError, PlanCompiler, parseIntervalToMs };

//# sourceMappingURL=plan-compiler.mjs.map