{"version":3,"file":"plan-executor.mjs","names":[],"sources":["../../../src/services/plan-executor.ts"],"sourcesContent":["/**\n * Plan Executor — walks a Plan IR tree and executes actions via tool dispatch.\n *\n * The executor is the bridge between the scheduler (which determines WHEN) and\n * the actual tool calls (which do the WORK). It:\n *\n * 1. Walks the node tree respecting sequence, parallel, if, wait, and loop semantics.\n * 2. Resolves ValueRefs (step outputs, runtime values, literals) at execution time.\n * 3. Dispatches action nodes to tool functions (provided via ToolDispatcher interface).\n * 4. Tracks per-step execution state for observability.\n * 5. Handles failure policies (abort, skip, retry).\n * 6. Supports cancellation mid-execution.\n *\n * The executor does NOT call tools directly — it receives a ToolDispatcher that the\n * plugin wires up to the real tool registry. This keeps the executor testable.\n *\n * ─── Rollback Awareness ─────────────────────────────────────────────────\n *\n * DeFi operations are NOT atomically rollbackable. A completed swap cannot be \"undone\"\n * — you'd have to do a reverse swap at whatever the current price is. The executor does\n * NOT attempt automatic rollback. Instead, it:\n *\n * - Stops on failure (abort policy) and reports exactly which step failed\n * - Records all completed steps so the user knows what already happened\n * - For skip policy, continues but logs the failure\n * - For retry policy, retries with delay before giving up\n *\n * The LLM reads the execution report and can suggest corrective actions.\n */\n\nimport type {\n  Plan,\n  PlanNode,\n  ActionNode,\n  SequenceNode,\n  ParallelNode,\n  IfNode,\n  WaitNode,\n  LoopNode,\n  PlanExecution,\n  StepExecution,\n  StepStatus,\n  ValueRef,\n  Condition,\n  CompareOp,\n  FailurePolicy,\n  DeadLetterEntry,\n  ExecutionCheckpoint,\n} from './plan-types.js';\nimport type { PlanScheduler } from './plan-scheduler.js';\n\n// ─── Tool Dispatcher Interface ──────────────────────────────────────────\n// The executor doesn't know about specific tools. It dispatches via this interface.\n\nexport interface ToolDispatcher {\n  /**\n   * Call a tool by name with the given params.\n   * Returns the tool result (the `details` field from jsonResult/errorResult).\n   * Throws on tool-level errors.\n   * @param userId — passed through so tools can enforce per-user gates (readonly, evolution).\n   */\n  call(toolName: string, params: Record<string, unknown>, userId?: string): Promise<unknown>;\n\n  /**\n   * Check if a tool exists.\n   */\n  exists(toolName: string): boolean;\n}\n\n// ─── Confirmation Callback ──────────────────────────────────────────────\n// For steps that require user confirmation before execution.\n\nexport type ConfirmationCallback = (\n  step: ActionNode,\n  resolvedParams: Record<string, unknown>,\n  userId: string,\n) => Promise<boolean>;\n\n// ─── Execution Context ──────────────────────────────────────────────────\n\ninterface ExecutionContext {\n  planId: string;\n  executionId: string;\n  /** The user who owns this plan — passed to tools for per-user gates. */\n  userId: string;\n  /** Results from completed steps, keyed by node ID. */\n  stepResults: Map<string, unknown>;\n  /** Execution records for each step. */\n  steps: StepExecution[];\n  /** Set to true to abort execution. */\n  cancelled: boolean;\n  /** Start time. */\n  startedAt: number;\n}\n\n// ─── Executor ───────────────────────────────────────────────────────────\n\nexport class PlanExecutor {\n  private dispatcher: ToolDispatcher;\n  private scheduler: PlanScheduler;\n  private confirmCallback?: ConfirmationCallback;\n  private deadLetterCallback?: (entry: DeadLetterEntry) => void;\n  private activeContexts = new Map<string, ExecutionContext>();\n\n  constructor(opts: {\n    dispatcher: ToolDispatcher;\n    scheduler: PlanScheduler;\n    onConfirmRequired?: ConfirmationCallback;\n    onDeadLetter?: (entry: DeadLetterEntry) => void;\n  }) {\n    this.dispatcher = opts.dispatcher;\n    this.scheduler = opts.scheduler;\n    this.confirmCallback = opts.onConfirmRequired;\n    this.deadLetterCallback = opts.onDeadLetter;\n  }\n\n  /**\n   * Execute a plan. Returns the execution record.\n   * For scheduled plans, this is called when the scheduler fires a trigger.\n   */\n  async execute(plan: Plan, executionId: string): Promise<PlanExecution> {\n    const ctx: ExecutionContext = {\n      planId: plan.id,\n      executionId,\n      userId: plan.userId,\n      stepResults: new Map(),\n      steps: [],\n      cancelled: false,\n      startedAt: Date.now(),\n    };\n\n    this.activeContexts.set(executionId, ctx);\n\n    try {\n      await this.executeNode(plan.root, ctx);\n\n      const execution: PlanExecution = {\n        planId: plan.id,\n        executionId,\n        status: ctx.cancelled ? 'cancelled' : 'completed',\n        startedAt: ctx.startedAt,\n        completedAt: Date.now(),\n        steps: ctx.steps,\n      };\n\n      this.scheduler.markCompleted(plan.id, execution);\n      return execution;\n\n    } catch (err: any) {\n      const isCancelled = ctx.cancelled || err instanceof ExecutionCancelledError;\n      const execution: PlanExecution = {\n        planId: plan.id,\n        executionId,\n        status: isCancelled ? 'cancelled' : 'failed',\n        startedAt: ctx.startedAt,\n        completedAt: Date.now(),\n        steps: ctx.steps,\n      };\n\n      if (isCancelled) {\n        this.scheduler.markCompleted(plan.id, execution);\n      } else {\n        this.scheduler.markFailed(plan.id, execution);\n      }\n      return execution;\n\n    } finally {\n      this.activeContexts.delete(executionId);\n      this.deleteCheckpoint(executionId);\n    }\n  }\n\n  /**\n   * Cancel a running execution.\n   */\n  cancel(executionId: string): boolean {\n    const ctx = this.activeContexts.get(executionId);\n    if (!ctx) return false;\n    ctx.cancelled = true;\n    return true;\n  }\n\n  /**\n   * Get the number of currently running executions.\n   */\n  get activeCount(): number {\n    return this.activeContexts.size;\n  }\n\n  // ─── Checkpointing ──────────────────────────────────────────────────\n\n  private writeCheckpoint(nodeId: string, ctx: ExecutionContext): void {\n    try {\n      const cp: ExecutionCheckpoint = {\n        executionId: ctx.executionId,\n        planId: ctx.planId,\n        userId: ctx.userId,\n        currentNodeId: nodeId,\n        stepResults: Array.from(ctx.stepResults.entries()),\n        steps: ctx.steps,\n        status: 'running',\n        startedAt: ctx.startedAt,\n        updatedAt: Date.now(),\n      };\n      this.scheduler.saveCheckpoint(cp);\n    } catch { /* never let checkpointing break execution */ }\n  }\n\n  private deleteCheckpoint(executionId: string): void {\n    try {\n      this.scheduler.deleteCheckpoint(executionId);\n    } catch { /* ignore */ }\n  }\n\n  /**\n   * Resume an execution from a persisted checkpoint.\n   * Returns the execution record, or null if checkpoint not found.\n   */\n  async resumeFromCheckpoint(plan: Plan, executionId: string): Promise<PlanExecution | null> {\n    const cp = this.scheduler.loadCheckpoint(executionId);\n    if (!cp || cp.planId !== plan.id) return null;\n\n    // Rebuild context from checkpoint\n    const ctx: ExecutionContext = {\n      planId: cp.planId,\n      executionId: cp.executionId,\n      userId: cp.userId,\n      stepResults: new Map(cp.stepResults),\n      steps: cp.steps,\n      cancelled: false,\n      startedAt: cp.startedAt,\n    };\n\n    this.activeContexts.set(executionId, ctx);\n\n    // Find the node to resume from and execute the remaining tree\n    const resumeNode = this.findNode(plan.root, cp.currentNodeId);\n\n    try {\n      if (resumeNode) {\n        await this.executeNode(resumeNode, ctx);\n      }\n\n      // After resuming, execute any remaining siblings if we were mid-sequence\n      // The checkpoint only captures which node was active — parent sequence\n      // continuation is handled by the fact that completed steps are tracked\n      // and the executor skips already-completed action nodes.\n\n      const execution: PlanExecution = {\n        planId: plan.id,\n        executionId,\n        status: ctx.cancelled ? 'cancelled' : 'completed',\n        startedAt: ctx.startedAt,\n        completedAt: Date.now(),\n        steps: ctx.steps,\n      };\n\n      this.scheduler.markCompleted(plan.id, execution);\n      return execution;\n\n    } catch (err: any) {\n      const isCancelled = ctx.cancelled || err instanceof ExecutionCancelledError;\n      const execution: PlanExecution = {\n        planId: plan.id,\n        executionId,\n        status: isCancelled ? 'cancelled' : 'failed',\n        startedAt: ctx.startedAt,\n        completedAt: Date.now(),\n        steps: ctx.steps,\n      };\n\n      if (isCancelled) {\n        this.scheduler.markCompleted(plan.id, execution);\n      } else {\n        this.scheduler.markFailed(plan.id, execution);\n      }\n      return execution;\n\n    } finally {\n      this.activeContexts.delete(executionId);\n      this.deleteCheckpoint(executionId);\n    }\n  }\n\n  /**\n   * Get all pending checkpoints (for resume-on-startup).\n   */\n  getPendingCheckpoints(): ExecutionCheckpoint[] {\n    return this.scheduler.loadAllCheckpoints();\n  }\n\n  /** Find a node by ID in the plan tree. */\n  private findNode(node: PlanNode, targetId: string): PlanNode | null {\n    if (node.id === targetId) return node;\n    switch (node.type) {\n      case 'sequence':\n        for (const child of node.steps) {\n          const found = this.findNode(child, targetId);\n          if (found) return found;\n        }\n        return null;\n      case 'parallel':\n        for (const child of node.steps) {\n          const found = this.findNode(child, targetId);\n          if (found) return found;\n        }\n        return null;\n      case 'if':\n        if (node.then) {\n          const found = this.findNode(node.then, targetId);\n          if (found) return found;\n        }\n        if (node.else) {\n          const found = this.findNode(node.else, targetId);\n          if (found) return found;\n        }\n        return null;\n      case 'loop': {\n        const found = this.findNode(node.body, targetId);\n        if (found) return found;\n        return null;\n      }\n      case 'action':\n        if (node.onError) {\n          const found = this.findNode(node.onError, targetId);\n          if (found) return found;\n        }\n        return null;\n      default:\n        return null;\n    }\n  }\n\n  // ─── Node Execution ─────────────────────────────────────────────────\n\n  private async executeNode(node: PlanNode, ctx: ExecutionContext): Promise<void> {\n    if (ctx.cancelled) throw new ExecutionCancelledError();\n\n    // Write checkpoint before executing each node\n    this.writeCheckpoint(node.id, ctx);\n\n    switch (node.type) {\n      case 'action':    return this.executeAction(node, ctx);\n      case 'sequence':  return this.executeSequence(node, ctx);\n      case 'parallel':  return this.executeParallel(node, ctx);\n      case 'if':        return this.executeIf(node, ctx);\n      case 'wait':      return this.executeWait(node, ctx);\n      case 'loop':      return this.executeLoop(node, ctx);\n    }\n  }\n\n  // ─── Action ─────────────────────────────────────────────────────────\n\n  private async executeAction(node: ActionNode, ctx: ExecutionContext): Promise<void> {\n    const step: StepExecution = {\n      nodeId: node.id,\n      status: 'running',\n      startedAt: Date.now(),\n      retryCount: 0,\n    };\n    ctx.steps.push(step);\n\n    // Resolve all parameter ValueRefs to concrete values\n    const resolvedParams = await this.resolveParams(node.params, ctx);\n\n    // Confirmation check\n    if (node.requireConfirmation && this.confirmCallback) {\n      const confirmed = await this.confirmCallback(node, resolvedParams, ctx.userId);\n      if (!confirmed) {\n        step.status = 'skipped';\n        step.completedAt = Date.now();\n        step.error = 'User declined confirmation';\n        return;\n      }\n    }\n\n    // Check tool exists\n    if (!this.dispatcher.exists(node.tool)) {\n      step.status = 'failed';\n      step.completedAt = Date.now();\n      step.error = `Tool \"${node.tool}\" not found`;\n      await this.handleFailure(node, step, ctx);\n      return;\n    }\n\n    // Execute with retry logic (exponential backoff)\n    const policy = node.onFailure ?? { strategy: 'abort' as const };\n    const maxAttempts = policy.strategy === 'retry' ? policy.maxAttempts : 1;\n    const baseDelay = policy.strategy === 'retry' ? policy.delayMs : 0;\n    const multiplier = policy.strategy === 'retry' ? (policy.backoffMultiplier ?? 2) : 1;\n\n    for (let attempt = 0; attempt < maxAttempts; attempt++) {\n      if (ctx.cancelled) throw new ExecutionCancelledError();\n\n      try {\n        // Apply timeout if configured; pass userId for per-user gates (readonly, evolution)\n        const result = node.timeoutMs\n          ? await withTimeout(this.dispatcher.call(node.tool, resolvedParams, ctx.userId), node.timeoutMs)\n          : await this.dispatcher.call(node.tool, resolvedParams, ctx.userId);\n\n        // Success\n        step.status = 'completed';\n        step.completedAt = Date.now();\n        step.result = result;\n        step.retryCount = attempt;\n        ctx.stepResults.set(node.id, result);\n        return;\n\n      } catch (err: any) {\n        step.retryCount = attempt + 1;\n        step.error = err.message ?? String(err);\n\n        if (attempt < maxAttempts - 1) {\n          // Exponential backoff: baseDelay * multiplier^attempt\n          const delay = baseDelay * Math.pow(multiplier, attempt);\n          await sleep(delay);\n          continue;\n        }\n\n        // Final attempt failed — try onError fallback before propagating\n        step.status = 'failed';\n        step.completedAt = Date.now();\n\n        if (node.onError) {\n          // Execute fallback branch instead of propagating failure\n          try {\n            await this.executeNode(node.onError, ctx);\n            // Fallback succeeded — don't propagate the original failure\n            return;\n          } catch (fallbackErr: any) {\n            // Fallback also failed — record and propagate\n            step.error = `${step.error} | fallback failed: ${fallbackErr.message ?? String(fallbackErr)}`;\n          }\n        }\n\n        // Log to dead-letter before propagating\n        this.writeDeadLetter(node, step, resolvedParams, ctx);\n        await this.handleFailure(node, step, ctx);\n        return;\n      }\n    }\n  }\n\n  // ─── Sequence ───────────────────────────────────────────────────────\n\n  private async executeSequence(node: SequenceNode, ctx: ExecutionContext): Promise<void> {\n    for (const child of node.steps) {\n      if (ctx.cancelled) throw new ExecutionCancelledError();\n      await this.executeNode(child, ctx);\n    }\n  }\n\n  // ─── Parallel ───────────────────────────────────────────────────────\n\n  private async executeParallel(node: ParallelNode, ctx: ExecutionContext): Promise<void> {\n    const results = await Promise.allSettled(\n      node.steps.map(child => this.executeNode(child, ctx)),\n    );\n\n    if (!node.allowPartialFailure) {\n      const failed = results.filter(r => r.status === 'rejected');\n      if (failed.length > 0) {\n        const firstError = (failed[0] as PromiseRejectedResult).reason;\n        throw firstError;\n      }\n    }\n  }\n\n  // ─── If ─────────────────────────────────────────────────────────────\n\n  private async executeIf(node: IfNode, ctx: ExecutionContext): Promise<void> {\n    const conditionMet = await this.evaluateCondition(node.condition, ctx);\n    if (conditionMet) {\n      await this.executeNode(node.then, ctx);\n    } else if (node.else) {\n      await this.executeNode(node.else, ctx);\n    }\n  }\n\n  // ─── Wait ───────────────────────────────────────────────────────────\n\n  private async executeWait(node: WaitNode, ctx: ExecutionContext): Promise<void> {\n    const step: StepExecution = {\n      nodeId: node.id,\n      status: 'waiting',\n      startedAt: Date.now(),\n    };\n    ctx.steps.push(step);\n\n    try {\n      // Fixed duration wait\n      if (node.durationMs && !node.until && !node.untilTime) {\n        await this.waitDuration(node.durationMs, ctx);\n        step.status = 'completed';\n        step.completedAt = Date.now();\n        return;\n      }\n\n      // Wait until specific time\n      if (node.untilTime && !node.until) {\n        const target = new Date(node.untilTime).getTime();\n        const remaining = target - Date.now();\n        if (remaining > 0) {\n          await this.waitDuration(remaining, ctx);\n        }\n        step.status = 'completed';\n        step.completedAt = Date.now();\n        return;\n      }\n\n      // Wait until condition\n      if (node.until) {\n        const pollMs = node.pollIntervalMs ?? 60_000;\n        const maxWait = node.maxWaitMs ?? 86_400_000; // 24h default\n        const deadline = Date.now() + maxWait;\n\n        while (Date.now() < deadline) {\n          if (ctx.cancelled) throw new ExecutionCancelledError();\n\n          const met = await this.evaluateCondition(node.until, ctx);\n          if (met) {\n            step.status = 'completed';\n            step.completedAt = Date.now();\n            return;\n          }\n\n          await this.waitDuration(Math.min(pollMs, deadline - Date.now()), ctx);\n        }\n\n        // Timed out waiting\n        step.status = 'failed';\n        step.completedAt = Date.now();\n        step.error = `Wait timed out after ${maxWait}ms`;\n        await this.handleFailure(\n          { ...node, type: 'wait' as const, id: node.id, label: node.label },\n          step,\n          ctx,\n        );\n      }\n    } catch (err: any) {\n      if (err instanceof ExecutionCancelledError) throw err;\n      step.status = 'failed';\n      step.completedAt = Date.now();\n      step.error = err.message ?? String(err);\n    }\n  }\n\n  // ─── Loop ───────────────────────────────────────────────────────────\n\n  private async executeLoop(node: LoopNode, ctx: ExecutionContext): Promise<void> {\n    for (let i = 0; i < node.maxIterations; i++) {\n      if (ctx.cancelled) throw new ExecutionCancelledError();\n\n      // Check exit condition\n      if (node.exitWhen) {\n        const shouldExit = await this.evaluateCondition(node.exitWhen, ctx);\n        if (shouldExit) return;\n      }\n\n      await this.executeNode(node.body, ctx);\n\n      // Delay between iterations\n      if (node.delayMs && i < node.maxIterations - 1) {\n        await this.waitDuration(node.delayMs, ctx);\n      }\n    }\n  }\n\n  // ─── Condition Evaluation ────────────────────────────────────────\n  // The executor evaluates conditions using ctx.stepResults for step_output refs,\n  // falling back to the scheduler's resolver for runtime refs (price, balance, etc.).\n  // This fixes the bug where the scheduler's evaluateCondition returns 0 for step_output.\n\n  private async evaluateCondition(cond: Condition, ctx: ExecutionContext): Promise<boolean> {\n    if (cond.type === 'compare') {\n      const left = await this.resolveConditionValue(cond.left, ctx);\n      const right = await this.resolveConditionValue(cond.right, ctx);\n      return this.compare(left, cond.op, right);\n    } else if (cond.type === 'logic') {\n      switch (cond.op) {\n        case 'and': {\n          for (const sub of cond.conditions) {\n            if (!await this.evaluateCondition(sub, ctx)) return false;\n          }\n          return true;\n        }\n        case 'or': {\n          for (const sub of cond.conditions) {\n            if (await this.evaluateCondition(sub, ctx)) return true;\n          }\n          return false;\n        }\n        case 'not': {\n          return !await this.evaluateCondition(cond.conditions[0]!, ctx);\n        }\n      }\n    }\n    return false;\n  }\n\n  private async resolveConditionValue(ref: ValueRef, ctx: ExecutionContext): Promise<number> {\n    switch (ref.type) {\n      case 'literal':\n        return typeof ref.value === 'number' ? ref.value : parseFloat(String(ref.value)) || 0;\n      case 'step_output': {\n        // Resolve from executor's step results (the fix for the WaitNode bug)\n        const result = ctx.stepResults.get(ref.stepId);\n        if (result === undefined) return 0;\n        const val = getNestedValue(result, ref.path);\n        return typeof val === 'number' ? val : parseFloat(String(val)) || 0;\n      }\n      case 'env': {\n        const v = process.env[ref.key];\n        return v ? parseFloat(v) || 0 : 0;\n      }\n      case 'runtime':\n        // Runtime refs need the scheduler's RuntimeResolver\n        return this.scheduler.resolveValue(ref);\n    }\n  }\n\n  private compare(left: number, op: CompareOp, right: number): boolean {\n    switch (op) {\n      case 'gt': return left > right;\n      case 'gte': return left >= right;\n      case 'lt': return left < right;\n      case 'lte': return left <= right;\n      case 'eq': return Math.abs(left - right) < Number.EPSILON;\n      case 'neq': return Math.abs(left - right) >= Number.EPSILON;\n      default: return false;\n    }\n  }\n\n  // ─── Value Resolution ─────────────────────────────────────────────\n\n  private async resolveParams(\n    params: Record<string, ValueRef | string | number | boolean>,\n    ctx: ExecutionContext,\n  ): Promise<Record<string, unknown>> {\n    const resolved: Record<string, unknown> = {};\n\n    for (const [key, val] of Object.entries(params)) {\n      resolved[key] = await this.resolveValue(val, ctx);\n    }\n\n    return resolved;\n  }\n\n  private async resolveValue(\n    ref: ValueRef | string | number | boolean,\n    ctx: ExecutionContext,\n  ): Promise<unknown> {\n    // Literal shorthand (raw string, number, or boolean passed directly)\n    if (typeof ref !== 'object' || ref === null) return ref;\n\n    switch (ref.type) {\n      case 'literal':\n        return ref.value;\n\n      case 'step_output': {\n        const result = ctx.stepResults.get(ref.stepId);\n        if (result === undefined) {\n          throw new Error(`Step \"${ref.stepId}\" has no result yet (referenced by path \"${ref.path}\").`);\n        }\n        return getNestedValue(result, ref.path);\n      }\n\n      case 'env': {\n        // Security: only allow reading env vars from an explicit allowlist.\n        // Without this, an LLM-generated plan could exfiltrate secrets like\n        // CLAWNCHER_PRIVATE_KEY by referencing them as env ValueRefs.\n        const ALLOWED_ENV_KEYS = new Set([\n          'NODE_ENV',\n          'CLAWNCHER_NETWORK',\n          'CLAWNCHER_API_URL',\n          'CHAIN_ID',\n          'DEFAULT_SLIPPAGE_BPS',\n          'LOG_LEVEL',\n        ]);\n        if (!ALLOWED_ENV_KEYS.has(ref.key)) {\n          throw new Error(`Plan env ref \"${ref.key}\" is not in the allowed env var list.`);\n        }\n        return process.env[ref.key] ?? undefined;\n      }\n\n      case 'runtime':\n        return this.scheduler.resolveValue(ref);\n    }\n  }\n\n  // ─── Dead-Letter Logging ──────────────────────────────────────────\n\n  private writeDeadLetter(\n    node: ActionNode,\n    step: StepExecution,\n    resolvedParams: Record<string, unknown>,\n    ctx: ExecutionContext,\n  ): void {\n    if (!this.deadLetterCallback) return;\n    try {\n      this.deadLetterCallback({\n        planId: ctx.planId,\n        nodeId: node.id,\n        executionId: ctx.executionId,\n        userId: ctx.userId,\n        error: step.error ?? 'Unknown error',\n        retryCount: step.retryCount ?? 0,\n        tool: node.tool,\n        params: resolvedParams,\n        timestamp: Date.now(),\n      });\n    } catch { /* never let dead-letter logging break execution */ }\n  }\n\n  // ─── Failure Handling ─────────────────────────────────────────────\n\n  private async handleFailure(node: PlanNode, step: StepExecution, ctx: ExecutionContext): Promise<void> {\n    const policy: FailurePolicy = node.onFailure ?? { strategy: 'abort' };\n\n    switch (policy.strategy) {\n      case 'abort':\n        throw new StepFailedError(node.id, step.error ?? 'Unknown error');\n\n      case 'skip':\n        step.status = 'skipped';\n        // Continue execution — don't throw\n        return;\n\n      case 'retry':\n        // Retry is handled in executeAction's loop — if we're here, all retries exhausted\n        throw new StepFailedError(node.id, `All ${policy.maxAttempts} retry attempts exhausted: ${step.error}`);\n    }\n  }\n\n  // ─── Helpers ──────────────────────────────────────────────────────\n\n  /** Wait for a duration, checking for cancellation periodically. */\n  private async waitDuration(ms: number, ctx: ExecutionContext): Promise<void> {\n    const checkInterval = 1000; // Check cancellation every 1s\n    let remaining = ms;\n\n    while (remaining > 0) {\n      if (ctx.cancelled) throw new ExecutionCancelledError();\n      const wait = Math.min(remaining, checkInterval);\n      await sleep(wait);\n      remaining -= wait;\n    }\n  }\n}\n\n// ─── Errors ─────────────────────────────────────────────────────────────\n\nexport class ExecutionCancelledError extends Error {\n  constructor() {\n    super('Plan execution was cancelled');\n    this.name = 'ExecutionCancelledError';\n  }\n}\n\nexport class StepFailedError extends Error {\n  public readonly stepId: string;\n\n  constructor(stepId: string, message: string) {\n    super(`Step \"${stepId}\" failed: ${message}`);\n    this.name = 'StepFailedError';\n    this.stepId = stepId;\n  }\n}\n\n// ─── Utility Functions ──────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n  return new Promise<T>((resolve, reject) => {\n    const timer = setTimeout(() => reject(new Error(`Timed out after ${ms}ms`)), ms);\n    promise.then(\n      val => { clearTimeout(timer); resolve(val); },\n      err => { clearTimeout(timer); reject(err); },\n    );\n  });\n}\n\n/**\n * Access a nested value by dot-path. E.g., \"result.amountOut\" on { result: { amountOut: 100 } }\n * returns 100.\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n  const parts = path.split('.');\n  let current: unknown = obj;\n  for (const part of parts) {\n    if (current === null || current === undefined) return undefined;\n    if (typeof current === 'object') {\n      current = (current as Record<string, unknown>)[part];\n    } else {\n      return undefined;\n    }\n  }\n  return current;\n}\n\n// ─── Execution Summary Formatter ────────────────────────────────────────\n// Produces a human-readable summary for Telegram output.\n\nexport function formatExecutionSummary(exec: PlanExecution, plan: Plan): string {\n  const statusIcon: Record<string, string> = {\n    completed: 'Done',\n    failed: 'FAILED',\n    cancelled: 'Cancelled',\n    running: 'Running',\n  };\n\n  const lines: string[] = [\n    `**Plan: ${plan.name}**`,\n    `Status: ${statusIcon[exec.status] ?? exec.status}`,\n    `Duration: ${((exec.completedAt ?? Date.now()) - exec.startedAt) / 1000}s`,\n    '',\n  ];\n\n  for (const step of exec.steps) {\n    const stepStatusIcon: Record<string, string> = {\n      completed: '[OK]',\n      failed: '[FAIL]',\n      skipped: '[SKIP]',\n      waiting: '[WAIT]',\n      running: '[...]',\n      pending: '[ ]',\n    };\n\n    const icon = stepStatusIcon[step.status] ?? '[ ]';\n    const duration = step.startedAt && step.completedAt\n      ? ` (${((step.completedAt - step.startedAt) / 1000).toFixed(1)}s)`\n      : '';\n    const error = step.error ? ` — ${step.error}` : '';\n    const retries = step.retryCount && step.retryCount > 0 ? ` (${step.retryCount} retries)` : '';\n\n    lines.push(`${icon} ${step.nodeId}${duration}${retries}${error}`);\n  }\n\n  return lines.join('\\n');\n}\n"],"mappings":"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"}