{"version":3,"file":"confirmation-store.mjs","names":[],"sources":["../../../src/services/confirmation-store.ts"],"sourcesContent":["/**\n * Confirmation Store — manages pending step confirmations for the plan executor.\n *\n * When a plan step requires user confirmation (requireConfirmation: true),\n * the executor pauses and creates a pending confirmation. The user can\n * respond with /approve or /deny to resolve it.\n *\n * Confirmations time out after CONFIRMATION_TIMEOUT_MS (5 minutes).\n */\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport interface PendingConfirmation {\n  /** The plan execution ID. */\n  executionId: string;\n  /** Plan name for display. */\n  planName: string;\n  /** The step label (e.g., \"Swap 1 ETH → USDC\"). */\n  stepLabel: string;\n  /** The tool being called. */\n  tool: string;\n  /** Resolved params for display. */\n  params: Record<string, unknown>;\n  /** The userId who owns the plan. */\n  userId: string;\n  /** When this confirmation was created. */\n  createdAt: number;\n  /** Resolve the confirmation Promise. */\n  resolve: (approved: boolean) => void;\n}\n\n// ─── Store ──────────────────────────────────────────────────────────────\n\nconst CONFIRMATION_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/** Pending confirmations keyed by a compound key: `${userId}` (most recent wins). */\nconst pendingByUser = new Map<string, PendingConfirmation>();\n\n/** All pending confirmations keyed by executionId for cleanup. */\nconst pendingByExecution = new Map<string, PendingConfirmation>();\n\n/**\n * Create a pending confirmation for a step.\n * Returns a Promise<boolean> that resolves when the user responds.\n * Times out after 5 minutes (resolves false).\n */\nexport function createPendingConfirmation(opts: {\n  executionId: string;\n  planName: string;\n  stepLabel: string;\n  tool: string;\n  params: Record<string, unknown>;\n  userId: string;\n}): Promise<boolean> {\n  return new Promise<boolean>((resolve) => {\n    const pending: PendingConfirmation = {\n      ...opts,\n      createdAt: Date.now(),\n      resolve,\n    };\n\n    // Store by user (latest confirmation wins — older ones get auto-denied)\n    const existing = pendingByUser.get(opts.userId);\n    if (existing) {\n      existing.resolve(false); // Auto-deny the previous one\n      pendingByExecution.delete(existing.executionId);\n    }\n\n    pendingByUser.set(opts.userId, pending);\n    pendingByExecution.set(opts.executionId, pending);\n\n    // Auto-deny after timeout\n    setTimeout(() => {\n      const current = pendingByUser.get(opts.userId);\n      if (current === pending) {\n        pending.resolve(false);\n        pendingByUser.delete(opts.userId);\n        pendingByExecution.delete(opts.executionId);\n      }\n    }, CONFIRMATION_TIMEOUT_MS);\n  });\n}\n\n/**\n * Respond to the most recent pending confirmation for a user.\n * Returns the confirmation details if found, or null if no pending confirmation.\n */\nexport function respondToConfirmation(\n  userId: string,\n  approved: boolean,\n): PendingConfirmation | null {\n  const pending = pendingByUser.get(userId);\n  if (!pending) return null;\n\n  pending.resolve(approved);\n  pendingByUser.delete(userId);\n  pendingByExecution.delete(pending.executionId);\n  return pending;\n}\n\n/**\n * Get the pending confirmation for a user (for display purposes).\n */\nexport function getPendingConfirmation(userId: string): PendingConfirmation | null {\n  const pending = pendingByUser.get(userId);\n  if (!pending) return null;\n\n  // Check if it's expired\n  if (Date.now() - pending.createdAt > CONFIRMATION_TIMEOUT_MS) {\n    pending.resolve(false);\n    pendingByUser.delete(userId);\n    pendingByExecution.delete(pending.executionId);\n    return null;\n  }\n\n  return pending;\n}\n\n/**\n * Cancel all pending confirmations for an execution (e.g., when plan is cancelled).\n */\nexport function cancelExecutionConfirmations(executionId: string): void {\n  const pending = pendingByExecution.get(executionId);\n  if (pending) {\n    pending.resolve(false);\n    pendingByUser.delete(pending.userId);\n    pendingByExecution.delete(executionId);\n  }\n}\n\n/**\n * How many confirmations are pending.\n */\nexport function pendingCount(): number {\n  return pendingByUser.size;\n}\n"],"mappings":";AAiCA,MAAM,0BAA0B,MAAS;;AAGzC,MAAM,gCAAgB,IAAI,KAAkC;;AAG5D,MAAM,qCAAqB,IAAI,KAAkC;;;;;;AAOjE,SAAgB,0BAA0B,MAOrB;AACnB,QAAO,IAAI,SAAkB,YAAY;EACvC,MAAM,UAA+B;GACnC,GAAG;GACH,WAAW,KAAK,KAAK;GACrB;GACD;EAGD,MAAM,WAAW,cAAc,IAAI,KAAK,OAAO;AAC/C,MAAI,UAAU;AACZ,YAAS,QAAQ,MAAM;AACvB,sBAAmB,OAAO,SAAS,YAAY;;AAGjD,gBAAc,IAAI,KAAK,QAAQ,QAAQ;AACvC,qBAAmB,IAAI,KAAK,aAAa,QAAQ;AAGjD,mBAAiB;AAEf,OADgB,cAAc,IAAI,KAAK,OAAO,KAC9B,SAAS;AACvB,YAAQ,QAAQ,MAAM;AACtB,kBAAc,OAAO,KAAK,OAAO;AACjC,uBAAmB,OAAO,KAAK,YAAY;;KAE5C,wBAAwB;GAC3B;;;;;;AAOJ,SAAgB,sBACd,QACA,UAC4B;CAC5B,MAAM,UAAU,cAAc,IAAI,OAAO;AACzC,KAAI,CAAC,QAAS,QAAO;AAErB,SAAQ,QAAQ,SAAS;AACzB,eAAc,OAAO,OAAO;AAC5B,oBAAmB,OAAO,QAAQ,YAAY;AAC9C,QAAO;;;;;AAMT,SAAgB,uBAAuB,QAA4C;CACjF,MAAM,UAAU,cAAc,IAAI,OAAO;AACzC,KAAI,CAAC,QAAS,QAAO;AAGrB,KAAI,KAAK,KAAK,GAAG,QAAQ,YAAY,yBAAyB;AAC5D,UAAQ,QAAQ,MAAM;AACtB,gBAAc,OAAO,OAAO;AAC5B,qBAAmB,OAAO,QAAQ,YAAY;AAC9C,SAAO;;AAGT,QAAO;;;;;AAMT,SAAgB,6BAA6B,aAA2B;CACtE,MAAM,UAAU,mBAAmB,IAAI,YAAY;AACnD,KAAI,SAAS;AACX,UAAQ,QAAQ,MAAM;AACtB,gBAAc,OAAO,QAAQ,OAAO;AACpC,qBAAmB,OAAO,YAAY;;;;;;AAO1C,SAAgB,eAAuB;AACrC,QAAO,cAAc"}