@tangle-network/agent-app 0.43.76 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +1 -1
  2. package/dist/DesignCanvas-Z362I3R3.js +10 -0
  3. package/dist/{DesignCanvasEditor-FJIVYOA2.js → DesignCanvasEditor-ZFE2W4OJ.js} +4 -4
  4. package/dist/assets/index.d.ts +4 -4
  5. package/dist/assets/index.js +223 -11
  6. package/dist/assets/index.js.map +1 -1
  7. package/dist/assistant/index.d.ts +7 -7
  8. package/dist/assistant/index.js +3 -3
  9. package/dist/assistant/index.js.map +1 -1
  10. package/dist/{attachment-validation-Dv1A_Puy.d.ts → attachment-validation-DRuLEjAE.d.ts} +1 -1
  11. package/dist/{auth-_FU8w01b.d.ts → auth-anc7mv2W.d.ts} +2 -2
  12. package/dist/chat-routes/index.d.ts +14 -11
  13. package/dist/chat-routes/index.js +16 -17
  14. package/dist/chat-routes/index.js.map +1 -1
  15. package/dist/chat-store/index.d.ts +26 -7
  16. package/dist/chat-store/index.js +14 -5
  17. package/dist/chat-store/index.js.map +1 -1
  18. package/dist/{chunk-ROLFDOMJ.js → chunk-4TI7QWLW.js} +3 -3
  19. package/dist/{chunk-3JUTOVUH.js → chunk-6KMAM7BL.js} +1 -1
  20. package/dist/chunk-6KMAM7BL.js.map +1 -0
  21. package/dist/{chunk-WL7XHLDK.js → chunk-ICOHEZK6.js} +77 -5
  22. package/dist/chunk-ICOHEZK6.js.map +1 -0
  23. package/dist/{chunk-22GLESKE.js → chunk-O63WWYET.js} +2 -4
  24. package/dist/chunk-O63WWYET.js.map +1 -0
  25. package/dist/{chunk-E7N67FYI.js → chunk-Q3FONVAK.js} +5 -5
  26. package/dist/{chunk-E7N67FYI.js.map → chunk-Q3FONVAK.js.map} +1 -1
  27. package/dist/{chunk-RXOTWZ4G.js → chunk-TKRI463Z.js} +4 -4
  28. package/dist/chunk-TKRI463Z.js.map +1 -0
  29. package/dist/config/index.d.ts +1 -1
  30. package/dist/config/index.js +123 -4
  31. package/dist/config/index.js.map +1 -1
  32. package/dist/{contract-B3h7peV3.d.ts → contract-CEewO6DI.d.ts} +1 -1
  33. package/dist/design-canvas/index.d.ts +4 -4
  34. package/dist/design-canvas-react/index.js +7 -7
  35. package/dist/design-canvas-react/lazy.js +1 -1
  36. package/dist/eval/index.d.ts +1 -1
  37. package/dist/eval/index.js +22 -8
  38. package/dist/eval/index.js.map +1 -1
  39. package/dist/{flow-types-CqomVAUN.d.ts → flow-types-CJxEmaRy.d.ts} +1 -1
  40. package/dist/integrations/index.js +67 -5
  41. package/dist/integrations/index.js.map +1 -1
  42. package/dist/interactions/index.d.ts +6 -4
  43. package/dist/interactions/index.js +1 -2
  44. package/dist/knowledge/index.js +63 -4
  45. package/dist/knowledge/index.js.map +1 -1
  46. package/dist/knowledge-loop/index.d.ts +1 -1
  47. package/dist/knowledge-loop/index.js +94 -4
  48. package/dist/knowledge-loop/index.js.map +1 -1
  49. package/dist/{mcp-BjPqQ-3C.d.ts → mcp-D3qVzbE1.d.ts} +2 -2
  50. package/dist/{mcp-rpc-BfUOsalw.d.ts → mcp-rpc-CzU5LWWT.d.ts} +1 -1
  51. package/dist/missions/index.js +1143 -23
  52. package/dist/missions/index.js.map +1 -1
  53. package/dist/{model-DmdkIteM.d.ts → model-CdCDfBA9.d.ts} +1 -1
  54. package/dist/object-store/index.js +113 -8
  55. package/dist/object-store/index.js.map +1 -1
  56. package/dist/{parts-2ymE5cs-.d.ts → parts-Co_lFgLI.d.ts} +3 -3
  57. package/dist/platform/index.d.ts +1 -1
  58. package/dist/preflight/cli.d.ts +13 -0
  59. package/dist/preflight/cli.js +50 -0
  60. package/dist/preflight/cli.js.map +1 -0
  61. package/dist/preset-cloudflare/index.d.ts +1 -1
  62. package/dist/preset-cloudflare/index.js +284 -11
  63. package/dist/preset-cloudflare/index.js.map +1 -1
  64. package/dist/{queue-C24V13h9.d.ts → queue-CD5-_VTX.d.ts} +1 -1
  65. package/dist/redact/index.js +116 -8
  66. package/dist/redact/index.js.map +1 -1
  67. package/dist/runtime/index.d.ts +19 -111
  68. package/dist/runtime/index.js +234 -14
  69. package/dist/runtime/index.js.map +1 -1
  70. package/dist/sandbox/index.d.ts +3 -3
  71. package/dist/sandbox/index.js +2 -3
  72. package/dist/sequences/index.d.ts +4 -4
  73. package/dist/stream/index.d.ts +4 -4
  74. package/dist/{stream-normalizer-QYUl4vnl.d.ts → stream-normalizer-DnuqkZvw.d.ts} +2 -2
  75. package/dist/tangle/index.js +42 -4
  76. package/dist/tangle/index.js.map +1 -1
  77. package/dist/teams/index.js +5 -5
  78. package/dist/teams/invitations-api.js +4 -4
  79. package/dist/teams-react/index.js +3 -3
  80. package/dist/theme/index.js +0 -1
  81. package/dist/tools/index.d.ts +6 -6
  82. package/dist/tools/index.js +5 -7
  83. package/dist/trace/index.d.ts +1 -1
  84. package/dist/trace/index.js +179 -10
  85. package/dist/trace/index.js.map +1 -1
  86. package/dist/{turn-buffer-BaSgxVmK.d.ts → turn-buffer-BcPfhr1_.d.ts} +1 -1
  87. package/dist/turn-stream/index.d.ts +1 -1
  88. package/dist/{types-CBRyqijY.d.ts → types-BCxK0wyS.d.ts} +1 -1
  89. package/dist/{types-CEchbvgz.d.ts → types-XIZqIdX1.d.ts} +1 -1
  90. package/dist/web-react/index.d.ts +10 -10
  91. package/dist/web-react/index.js +5 -5
  92. package/dist/work-product/index.d.ts +4 -4
  93. package/dist/work-product/index.js +735 -15
  94. package/dist/work-product/index.js.map +1 -1
  95. package/dist/work-product-react/index.d.ts +1 -1
  96. package/dist/work-product-react/index.js.map +1 -1
  97. package/package.json +4 -19
  98. package/dist/DesignCanvas-UNL34LS2.js +0 -12
  99. package/dist/chunk-22GLESKE.js.map +0 -1
  100. package/dist/chunk-3JUTOVUH.js.map +0 -1
  101. package/dist/chunk-5EQCITY3.js +0 -20
  102. package/dist/chunk-5EQCITY3.js.map +0 -1
  103. package/dist/chunk-5SD6HT2V.js +0 -236
  104. package/dist/chunk-5SD6HT2V.js.map +0 -1
  105. package/dist/chunk-5TESA4SN.js +0 -74
  106. package/dist/chunk-5TESA4SN.js.map +0 -1
  107. package/dist/chunk-6GQ5DKFK.js +0 -920
  108. package/dist/chunk-6GQ5DKFK.js.map +0 -1
  109. package/dist/chunk-7ESQUSAC.js +0 -343
  110. package/dist/chunk-7ESQUSAC.js.map +0 -1
  111. package/dist/chunk-7LNGJDNA.js +0 -1
  112. package/dist/chunk-7LNGJDNA.js.map +0 -1
  113. package/dist/chunk-AP2UXKUK.js +0 -69
  114. package/dist/chunk-AP2UXKUK.js.map +0 -1
  115. package/dist/chunk-FA4XR66Y.js +0 -32
  116. package/dist/chunk-FA4XR66Y.js.map +0 -1
  117. package/dist/chunk-FKCB5FTD.js +0 -126
  118. package/dist/chunk-FKCB5FTD.js.map +0 -1
  119. package/dist/chunk-GPBRL2XR.js +0 -102
  120. package/dist/chunk-GPBRL2XR.js.map +0 -1
  121. package/dist/chunk-HRH7ASAG.js +0 -759
  122. package/dist/chunk-HRH7ASAG.js.map +0 -1
  123. package/dist/chunk-HYTIHYNV.js +0 -297
  124. package/dist/chunk-HYTIHYNV.js.map +0 -1
  125. package/dist/chunk-LGBO24FL.js +0 -190
  126. package/dist/chunk-LGBO24FL.js.map +0 -1
  127. package/dist/chunk-NLSSFLLS.js +0 -1168
  128. package/dist/chunk-NLSSFLLS.js.map +0 -1
  129. package/dist/chunk-OSMNYPOG.js +0 -129
  130. package/dist/chunk-OSMNYPOG.js.map +0 -1
  131. package/dist/chunk-QYOD3K56.js +0 -124
  132. package/dist/chunk-QYOD3K56.js.map +0 -1
  133. package/dist/chunk-RXOTWZ4G.js.map +0 -1
  134. package/dist/chunk-UAKFCMWK.js +0 -1
  135. package/dist/chunk-UAKFCMWK.js.map +0 -1
  136. package/dist/chunk-UKTYKG57.js +0 -48
  137. package/dist/chunk-UKTYKG57.js.map +0 -1
  138. package/dist/chunk-UOAYS72M.js +0 -80
  139. package/dist/chunk-UOAYS72M.js.map +0 -1
  140. package/dist/chunk-WL7XHLDK.js.map +0 -1
  141. package/dist/composer/index.d.ts +0 -2
  142. package/dist/composer/index.js +0 -22
  143. package/dist/composer/index.js.map +0 -1
  144. package/dist/core-7qIM7svy.d.ts +0 -21
  145. package/dist/durable-chat/index.d.ts +0 -464
  146. package/dist/durable-chat/index.js +0 -59
  147. package/dist/durable-chat/index.js.map +0 -1
  148. package/dist/index.d.ts +0 -51
  149. package/dist/index.js +0 -827
  150. package/dist/index.js.map +0 -1
  151. /package/dist/{DesignCanvas-UNL34LS2.js.map → DesignCanvas-Z362I3R3.js.map} +0 -0
  152. /package/dist/{DesignCanvasEditor-FJIVYOA2.js.map → DesignCanvasEditor-ZFE2W4OJ.js.map} +0 -0
  153. /package/dist/{chunk-ROLFDOMJ.js.map → chunk-4TI7QWLW.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/missions/service.ts","../src/missions/events.ts","../src/missions/engine.ts","../src/missions/plan-parse.ts","../src/missions/agent-activity.ts"],"sourcesContent":["/**\n * Durable mission state — the guarded status machine for a multi-step agent run.\n *\n * A mission is a persisted plan (ordered steps), a cursor (count of completed\n * steps), a cost ledger + budget, and a status machine. This module owns the\n * legal transitions and the guarded mutation surface; it does NOT execute steps\n * (the engine in `./engine` does) and it does NOT own persistence — products\n * implement {@link MissionStorePort} over their own tables. Every state change\n * appends a {@link MissionAuditEvent} so the run timeline is a single durable\n * audit trail.\n *\n * Concurrency contract: a mission MUST be driven by a single serialized owner\n * (a Durable Object, a Cloudflare Workflow, a queue consumer — one per\n * mission). The service is the typed guard layer, not a serializer: every\n * mutation re-reads the record and asks the store for a compare-and-set write\n * guarded on the values it read. When the guard misses, the row changed under\n * us and the caller gets `{ succeeded: false, conflict: true }` — never a\n * silent clobber, never a stale overwrite.\n */\n\nexport type MissionStatus =\n | 'scheduled'\n | 'running'\n | 'paused'\n | 'waiting_approval'\n | 'blocked'\n | 'succeeded'\n | 'failed'\n | 'aborted'\n | 'cancelled'\n\n/** Define possible statuses for a mission step during its execution lifecycle */\nexport type MissionStepStatus = 'pending' | 'running' | 'waiting_approval' | 'done' | 'failed'\n\n/** Define the structure and state details of a mission step within a workflow system */\nexport interface MissionStep {\n id: string\n /** What the step should accomplish — an intent, never an implementation. */\n intent: string\n /** Product-defined kind label. Labels intent for gating/UX; it never selects\n * a different execution path. */\n kind: string\n status: MissionStepStatus\n /** Count of genuine `* -> running` edges (retries inflate this; idempotent\n * re-asserts do not). */\n attempts: number\n /** One-line live status surfaced on the step row (\"7/15 refs\"). */\n sublabel?: string\n /** Small pointer at the produced artifact (vault path, asset id) — never the\n * full payload. */\n resultRef?: string\n}\n\n/** Define the structure for tracking mission token usage, cost, duration, and LLM call counts */\nexport interface MissionCostLedger {\n tokensIn: number\n tokensOut: number\n costUsd: number\n wallMs: number\n llmCalls: number\n}\n\n/** The durable mission row, shape-normalized. Timestamps are epoch ms. */\nexport interface MissionRecord {\n id: string\n workspaceId: string\n status: MissionStatus\n /** Product-defined origin label ('chat', 'manual', 'cron', …). */\n trigger: string\n summary: string | null\n plan: MissionStep[]\n /** Count of durably-completed steps; the next step to run is `plan[cursor]`. */\n cursor: number\n cost: MissionCostLedger | null\n budgetUsd: number | null\n spentUsd: number\n pauseReason: string | null\n /** The single owning engine's instance id, write-once (see `setEngineRef`). */\n engineRef: string | null\n scheduledAt: number | null\n startedAt: number\n completedAt: number | null\n metadata: Record<string, unknown> | null\n}\n\n/**\n * Discriminated outcome for guarded operations. Callers MUST inspect\n * `succeeded` before reading `value` — illegal transitions and missing rows\n * surface here, never as a throw-and-swallow or a silent no-op. `conflict`\n * distinguishes a lost guarded race (retryable — re-read and re-apply) from a\n * logic rejection (illegal edge, missing step — deterministic, never retried).\n */\nexport type MissionOutcome<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: string; conflict: boolean }\n\n/** Fields a guarded write compares against the values the caller read. A SQL\n * implementation compares JSON columns as serialized text\n * (`coalesce(col, 'null') = JSON.stringify(expected)`), matching how the\n * in-memory store compares. An absent field is unguarded. */\nexport interface MissionUpdateGuard {\n status?: MissionStatus\n cursor?: number\n plan?: MissionStep[]\n cost?: MissionCostLedger | null\n metadata?: Record<string, unknown> | null\n /** Guard that no engine has bound yet (the write-once bind). */\n engineRefIsNull?: true\n}\n\n/** Fields a guarded write sets when the guard holds. `null` values are real\n * writes (clear the column), not skips. */\nexport interface MissionUpdatePatch {\n status?: MissionStatus\n pauseReason?: string | null\n summary?: string\n completedAt?: number | null\n plan?: MissionStep[]\n cursor?: number\n cost?: MissionCostLedger\n spentUsd?: number\n metadata?: Record<string, unknown>\n engineRef?: string\n}\n\n/** One audit-trail row. Appended after every committed state change, so an\n * event always denotes a real transition (no phantom rows on rejected or\n * no-op calls). */\nexport interface MissionAuditEvent {\n missionId: string\n workspaceId: string\n level: 'info' | 'warn' | 'error'\n /** Machine-readable transition name ('mission.created', 'mission.step.done',\n * 'mission.cursor', 'mission.cost', 'mission.paused', …). */\n step: string\n message: string\n metadata: Record<string, unknown>\n at: number\n}\n\n/**\n * Persistence seam — the product implements this over its own tables. The\n * invariant the implementation MUST keep: `update` applies `patch` ONLY when\n * every guard field still equals the stored value, and returns `null` when the\n * guard misses. That null is how a concurrent write surfaces as a typed\n * failure instead of a clobber.\n */\nexport interface MissionStorePort {\n load(id: string): Promise<MissionRecord | null>\n /** `extras` are the opaque product-column values from\n * `CreateMissionInput.extras` — write them in the SAME statement as the\n * record (single-write creation) or ignore them if the table has no extra\n * columns. */\n insert(record: MissionRecord, extras?: Record<string, unknown>): Promise<MissionRecord>\n update(id: string, guard: MissionUpdateGuard, patch: MissionUpdatePatch): Promise<MissionRecord | null>\n appendEvent(event: MissionAuditEvent): Promise<void>\n}\n\nconst TERMINAL_STATUSES: ReadonlySet<MissionStatus> = new Set<MissionStatus>([\n 'succeeded',\n 'failed',\n 'aborted',\n 'cancelled',\n])\n\n/** Statuses a mission can never leave — the run is done. */\nexport function isMissionTerminal(status: MissionStatus): boolean {\n return TERMINAL_STATUSES.has(status)\n}\n\n/** The cooperative kill switch: a stop request rides metadata so it survives\n * any status and is honored by the engine before the next side effect. */\nexport function isMissionStopRequested(mission: MissionRecord): boolean {\n return (mission.metadata ?? {}).stopRequested === true\n}\n\n// Legal mission status transitions. A target absent from a source's set is\n// rejected by the guarded helpers. Terminal statuses have no outgoing edges.\nconst MISSION_TRANSITIONS: Record<MissionStatus, ReadonlySet<MissionStatus>> = {\n scheduled: new Set<MissionStatus>(['running', 'cancelled', 'aborted']),\n running: new Set<MissionStatus>([\n 'paused',\n 'waiting_approval',\n 'blocked',\n 'succeeded',\n 'failed',\n 'aborted',\n ]),\n paused: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n waiting_approval: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n blocked: new Set<MissionStatus>(['running', 'aborted', 'cancelled']),\n succeeded: new Set<MissionStatus>(),\n failed: new Set<MissionStatus>(),\n aborted: new Set<MissionStatus>(),\n cancelled: new Set<MissionStatus>(),\n}\n\n// Legal per-step transitions inside a plan.\nconst STEP_TRANSITIONS: Record<MissionStepStatus, ReadonlySet<MissionStepStatus>> = {\n pending: new Set<MissionStepStatus>(['running', 'waiting_approval', 'failed']),\n running: new Set<MissionStepStatus>(['done', 'failed', 'waiting_approval']),\n waiting_approval: new Set<MissionStepStatus>(['running', 'done', 'failed']),\n done: new Set<MissionStepStatus>([]),\n failed: new Set<MissionStepStatus>(['pending', 'running']),\n}\n\nconst ZERO_LEDGER: MissionCostLedger = {\n tokensIn: 0,\n tokensOut: 0,\n costUsd: 0,\n wallMs: 0,\n llmCalls: 0,\n}\n\n/** Define input parameters for creating a mission including optional deterministic id and unique plan steps */\nexport interface CreateMissionInput {\n /** Explicit row id. Omit to use the service's id generator. Pass a\n * DETERMINISTIC id (derived from the originating turn) when the caller may\n * re-create the same mission under at-least-once delivery — the duplicate\n * insert then trips the store's uniqueness instead of spawning a second run. */\n id?: string\n workspaceId: string\n /** Becomes the mission summary. */\n title: string\n /** Plan step ids MUST be unique: the owning workflow keys its durable step\n * cache by step id, so a collision would silently replay the wrong result.\n * A duplicate is rejected here (fail loud). */\n plan: MissionStep[]\n budgetUsd?: number | null\n /** Epoch ms. Present → the mission starts `scheduled` instead of `running`. */\n scheduledAt?: number | null\n trigger: string\n /** Caller-defined context stamped onto the record (thread ids, source turn,\n * model). Read back via `mission.metadata`; the engine only reads\n * `stopRequested` from it. */\n metadata?: Record<string, unknown> | null\n /**\n * Opaque product-column values handed VERBATIM to `MissionStorePort.insert`\n * (e.g. a `workflowId` FK on the product's mission table), so creation is a\n * single write — no post-insert stamp. The service never reads, validates,\n * or persists them itself; they exist only on the insert call.\n */\n extras?: Record<string, unknown>\n}\n\n/** Define a patch to update the status and optional metadata of a step in a process */\nexport interface SetStepStatusPatch {\n sublabel?: string\n resultRef?: string\n error?: string\n /**\n * Spend committed ATOMICALLY with the step transition. Folding the cost into\n * the SAME guarded write makes step completion the per-step idempotency key:\n * a same-status replay (the step is already `done`) short-circuits as a no-op\n * BEFORE the write, so the charge lands exactly once even when an engine\n * RESUME re-dispatches a step whose cost previously committed. `deltaUsd` is\n * the marginal spend; `ledgerDelta` carries the token/wall/llm breakdown.\n */\n cost?: {\n deltaUsd: number\n ledgerDelta?: Partial<MissionCostLedger>\n }\n}\n\n/** Define input parameters to complete a mission with status and optional summary */\nexport interface CompleteMissionInput {\n ok: boolean\n summary?: string\n}\n\n/** Define methods to create, retrieve, and update missions with controlled engine binding and metadata merging */\nexport interface MissionService {\n createMission(input: CreateMissionInput): Promise<MissionRecord>\n getMission(id: string): Promise<MissionRecord | null>\n /** Bind the executing engine's instance id, write-once from the single\n * owner: re-asserting the same ref is a no-op; binding a DIFFERENT ref over\n * an existing one is rejected so a second owner can never steal the run. */\n setEngineRef(id: string, engineRef: string): Promise<MissionOutcome<MissionRecord>>\n /** Shallow-merge keys into metadata. Guarded on the metadata read, so racing\n * merges surface as conflicts instead of silently dropping keys. */\n mergeMetadata(id: string, patch: Record<string, unknown>): Promise<MissionOutcome<MissionRecord>>\n /** Mutate one plan step's status (+ optional sublabel/resultRef) and append a\n * transition event. Rejects unknown steps and illegal step edges. Does NOT\n * move the cursor — call `advanceCursor` for that. */\n setStepStatus(\n id: string,\n stepId: string,\n status: MissionStepStatus,\n patch?: SetStepStatusPatch,\n ): Promise<MissionOutcome<MissionRecord>>\n /** Move the done-count cursor forward by one. Rejects advancing past the end\n * of the plan so the caller learns the mission has no further work. */\n advanceCursor(id: string): Promise<MissionOutcome<MissionRecord>>\n /** Increment spentUsd and merge a partial ledger into the cumulative ledger.\n * `deltaUsd` is the marginal spend; `ledgerDelta` carries the token/wall/\n * llm-call breakdown for the same unit of work. */\n addCost(\n id: string,\n deltaUsd: number,\n ledgerDelta?: Partial<MissionCostLedger>,\n ): Promise<MissionOutcome<MissionRecord>>\n pause(id: string, reason: string): Promise<MissionOutcome<MissionRecord>>\n resume(id: string): Promise<MissionOutcome<MissionRecord>>\n abort(id: string): Promise<MissionOutcome<MissionRecord>>\n /** Flip one step and the whole mission to waiting_approval together. The\n * mission transition is validated FIRST so an illegal source is rejected\n * without mutating the step — no half-applied state. */\n markWaitingApproval(id: string, stepId: string): Promise<MissionOutcome<MissionRecord>>\n complete(id: string, input: CompleteMissionInput): Promise<MissionOutcome<MissionRecord>>\n}\n\n/** Define options for configuring mission service behavior including storage, time, and ID generation */\nexport interface MissionServiceOptions {\n store: MissionStorePort\n /** Injectable clock (epoch ms). Default `Date.now`. */\n now?: () => number\n /**\n * Row-id generator when `CreateMissionInput.id` is omitted. Default\n * `crypto.randomUUID()` — a 36-char dashed UUID. Inject your own when your\n * mission table already has an id shape (e.g. 32-hex matching D1 row\n * defaults): the service stamps the generated id verbatim onto the inserted\n * record, so a mismatch with the rest of your schema surfaces only at the\n * product layer. Keep id shape parity here, not in the store.\n */\n generateId?: () => string\n}\n\nfunction rejected<T>(error: string): MissionOutcome<T> {\n return { succeeded: false, error, conflict: false }\n}\n\nfunction lostRace<T>(id: string): MissionOutcome<T> {\n return { succeeded: false, error: `Mission ${id} changed concurrently`, conflict: true }\n}\n\n/** Create a mission service that manages mission records and audit events with customizable options */\nexport function createMissionService(options: MissionServiceOptions): MissionService {\n const { store } = options\n const now = options.now ?? (() => Date.now())\n const generateId = options.generateId ?? (() => crypto.randomUUID())\n\n async function appendEvent(\n mission: MissionRecord,\n level: MissionAuditEvent['level'],\n step: string,\n message: string,\n metadata: Record<string, unknown> = {},\n ): Promise<void> {\n await store.appendEvent({\n missionId: mission.id,\n workspaceId: mission.workspaceId,\n level,\n step,\n message,\n metadata,\n at: now(),\n })\n }\n\n // Guarded status transition: load → validate against the transition table →\n // CAS-write guarded on the status we read → event. The loser of a racing\n // transition gets a conflict instead of silently violating the machine.\n async function transition(\n id: string,\n to: MissionStatus,\n patch: Omit<MissionUpdatePatch, 'status'> = {},\n eventMeta: Record<string, unknown> = {},\n ): Promise<MissionOutcome<MissionRecord>> {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const from = mission.status\n if (TERMINAL_STATUSES.has(from)) {\n return rejected(`Mission ${id} is terminal (${from}); cannot transition to ${to}`)\n }\n // Same-status calls are a true no-op (resume-while-running, idempotent\n // re-assert under at-least-once delivery): no patch re-apply, no phantom\n // event, so an event row always denotes a real state change.\n if (from === to) return { succeeded: true, value: mission }\n if (!MISSION_TRANSITIONS[from].has(to)) {\n return rejected(`Illegal mission transition ${from} -> ${to} for mission ${id}`)\n }\n\n const updated = await store.update(id, { status: from }, { status: to, ...patch })\n if (!updated) return lostRace(id)\n await appendEvent(updated, to === 'failed' ? 'error' : 'info', `mission.${to}`, `Mission ${from} -> ${to}`, {\n from,\n to,\n ...eventMeta,\n })\n return { succeeded: true, value: updated }\n }\n\n const createMission: MissionService['createMission'] = async (input) => {\n const seen = new Set<string>()\n for (const step of input.plan) {\n if (seen.has(step.id)) {\n throw new Error(`Duplicate plan step id \"${step.id}\" — mission plan step ids must be unique`)\n }\n seen.add(step.id)\n }\n\n const scheduledAt = input.scheduledAt ?? null\n const status: MissionStatus = scheduledAt !== null ? 'scheduled' : 'running'\n const plan: MissionStep[] = input.plan.map((step) => ({\n id: step.id,\n intent: step.intent,\n kind: step.kind,\n status: step.status,\n attempts: step.attempts,\n ...(step.sublabel === undefined ? {} : { sublabel: step.sublabel }),\n ...(step.resultRef === undefined ? {} : { resultRef: step.resultRef }),\n }))\n\n const record = await store.insert(\n {\n id: input.id ?? generateId(),\n workspaceId: input.workspaceId,\n status,\n trigger: input.trigger,\n summary: input.title,\n plan,\n cursor: 0,\n cost: { ...ZERO_LEDGER },\n budgetUsd: input.budgetUsd ?? null,\n spentUsd: 0,\n pauseReason: null,\n engineRef: null,\n scheduledAt,\n startedAt: now(),\n completedAt: null,\n metadata: input.metadata ?? null,\n },\n input.extras,\n )\n\n await appendEvent(record, 'info', 'mission.created', `Mission \"${input.title}\" ${status}`, {\n status,\n stepCount: plan.length,\n budgetUsd: input.budgetUsd ?? null,\n scheduledAt,\n })\n return record\n }\n\n const getMission: MissionService['getMission'] = (id) => store.load(id)\n\n const setEngineRef: MissionService['setEngineRef'] = async (id, engineRef) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (TERMINAL_STATUSES.has(mission.status) || isMissionStopRequested(mission)) {\n return rejected(`Mission ${id} is not writable in status ${mission.status}`)\n }\n if (mission.engineRef === engineRef) return { succeeded: true, value: mission }\n if (mission.engineRef !== null) {\n return rejected(`Mission ${id} is already bound to engine ${mission.engineRef}`)\n }\n\n // Guard on the null engineRef we read so two racing owners cannot both\n // bind: the second write misses the guard and reports the lost race —\n // distinct from the deterministic already-bound rejection above.\n const updated = await store.update(id, { engineRefIsNull: true }, { engineRef })\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.engine', `Engine bound: ${engineRef}`, { engineRef })\n return { succeeded: true, value: updated }\n }\n\n const mergeMetadata: MissionService['mergeMetadata'] = async (id, patch) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (TERMINAL_STATUSES.has(mission.status) || isMissionStopRequested(mission)) {\n return rejected(`Mission ${id} is not writable in status ${mission.status}`)\n }\n\n const updated = await store.update(\n id,\n { metadata: mission.metadata },\n { metadata: { ...(mission.metadata ?? {}), ...patch } },\n )\n if (!updated) return lostRace(id)\n return { succeeded: true, value: updated }\n }\n\n const setStepStatus: MissionService['setStepStatus'] = async (id, stepId, status, patch = {}) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const plan = mission.plan\n const index = plan.findIndex((step) => step.id === stepId)\n const current = index < 0 ? undefined : plan[index]\n if (!current) return rejected(`Step ${stepId} not found in mission ${id}`)\n\n const sameStatus = current.status === status\n if (!sameStatus && !STEP_TRANSITIONS[current.status].has(status)) {\n return rejected(`Illegal step transition ${current.status} -> ${status} for step ${stepId}`)\n }\n\n // True no-op: re-asserting the same status with no patch field change must\n // not bump attempts or write a duplicate event (at-least-once delivery,\n // retries, and reconnects re-assert state). A patch on the same status\n // still persists, but does not count a new attempt.\n const sublabelChanges = patch.sublabel !== undefined && patch.sublabel !== current.sublabel\n const resultRefChanges = patch.resultRef !== undefined && patch.resultRef !== current.resultRef\n if (sameStatus && !sublabelChanges && !resultRefChanges) {\n return { succeeded: true, value: mission }\n }\n\n // Only a genuine `* -> running` edge counts an attempt; a running->running\n // re-assert (handled as a no-op above) never inflates the counter.\n const nextStep: MissionStep = {\n ...current,\n status,\n attempts: status === 'running' && !sameStatus ? current.attempts + 1 : current.attempts,\n ...(patch.sublabel === undefined ? {} : { sublabel: patch.sublabel }),\n ...(patch.resultRef === undefined ? {} : { resultRef: patch.resultRef }),\n }\n const nextPlan = plan.slice()\n nextPlan[index] = nextStep\n\n // Spend folded into the SAME guarded write as the transition. Because the\n // same-status no-op above returns BEFORE this point, a step that is already\n // `done` never reaches here — so an engine RESUME re-dispatching a committed\n // step cannot charge a second time. The ledger CAS guard preserves the\n // over-spend protection `addCost` carried: an undercounted concurrent merge\n // surfaces as a lost race, never a clobber.\n const cost = patch.cost\n const ledgerDelta = cost?.ledgerDelta\n const base = mission.cost ?? { ...ZERO_LEDGER }\n const nextCost: MissionCostLedger | undefined =\n cost === undefined\n ? undefined\n : {\n tokensIn: base.tokensIn + (ledgerDelta?.tokensIn ?? 0),\n tokensOut: base.tokensOut + (ledgerDelta?.tokensOut ?? 0),\n costUsd: base.costUsd + (ledgerDelta?.costUsd ?? cost.deltaUsd),\n wallMs: base.wallMs + (ledgerDelta?.wallMs ?? 0),\n llmCalls: base.llmCalls + (ledgerDelta?.llmCalls ?? 0),\n }\n\n // The plan write is guarded on the status, plan, metadata, AND (when a cost\n // rides along) the ledger we read: a concurrent stop request (a metadata\n // write), any plan mutation, or a racing spend flips a guard and the loser\n // reports the race instead of clobbering it.\n const updated = await store.update(\n id,\n {\n status: mission.status,\n plan: mission.plan,\n metadata: mission.metadata,\n ...(cost === undefined ? {} : { cost: mission.cost }),\n },\n {\n plan: nextPlan,\n ...(cost === undefined ? {} : { cost: nextCost!, spentUsd: mission.spentUsd + cost.deltaUsd }),\n },\n )\n if (!updated) return lostRace(id)\n await appendEvent(\n updated,\n status === 'failed' ? 'error' : 'info',\n `mission.step.${status}`,\n patch.error ?? `Step ${stepId} (${current.intent}) -> ${status}`,\n {\n stepId,\n from: current.status,\n to: status,\n attempts: nextStep.attempts,\n ...(patch.resultRef ? { resultRef: patch.resultRef } : {}),\n },\n )\n if (cost !== undefined) {\n await appendEvent(updated, 'info', 'mission.cost', `Spent +$${cost.deltaUsd.toFixed(4)}`, {\n deltaUsd: cost.deltaUsd,\n spentUsd: updated.spentUsd,\n budgetUsd: updated.budgetUsd,\n })\n }\n return { succeeded: true, value: updated }\n }\n\n const advanceCursor: MissionService['advanceCursor'] = async (id) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const next = mission.cursor + 1\n if (next > mission.plan.length) {\n return rejected(`Cursor ${mission.cursor} is already at the end of mission ${id}`)\n }\n\n // Guarded on the cursor (and status) we read: a concurrent advance shifts\n // the cursor, the guard misses, and we report the lost race rather than\n // writing a stale value that drops the other increment.\n const updated = await store.update(\n id,\n { status: mission.status, cursor: mission.cursor },\n { cursor: next },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.cursor', `Cursor ${mission.cursor} -> ${updated.cursor}`, {\n from: mission.cursor,\n to: updated.cursor,\n })\n return { succeeded: true, value: updated }\n }\n\n const addCost: MissionService['addCost'] = async (id, deltaUsd, ledgerDelta) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n\n const base = mission.cost ?? { ...ZERO_LEDGER }\n const nextCost: MissionCostLedger = {\n tokensIn: base.tokensIn + (ledgerDelta?.tokensIn ?? 0),\n tokensOut: base.tokensOut + (ledgerDelta?.tokensOut ?? 0),\n costUsd: base.costUsd + (ledgerDelta?.costUsd ?? deltaUsd),\n wallMs: base.wallMs + (ledgerDelta?.wallMs ?? 0),\n llmCalls: base.llmCalls + (ledgerDelta?.llmCalls ?? 0),\n }\n\n // Guarded on the ledger we read: an undercounted spend is an over-spend\n // against the budget gate, so a concurrent merge must surface as a lost\n // race (retry re-reads and re-merges), never be clobbered.\n const updated = await store.update(\n id,\n { cost: mission.cost },\n { cost: nextCost, spentUsd: mission.spentUsd + deltaUsd },\n )\n if (!updated) return lostRace(id)\n await appendEvent(updated, 'info', 'mission.cost', `Spent +$${deltaUsd.toFixed(4)}`, {\n deltaUsd,\n spentUsd: updated.spentUsd,\n budgetUsd: updated.budgetUsd,\n })\n return { succeeded: true, value: updated }\n }\n\n const markWaitingApproval: MissionService['markWaitingApproval'] = async (id, stepId) => {\n const mission = await store.load(id)\n if (!mission) return rejected(`Mission ${id} not found`)\n if (!MISSION_TRANSITIONS[mission.status].has('waiting_approval')) {\n return rejected(`Illegal mission transition ${mission.status} -> waiting_approval for mission ${id}`)\n }\n\n const stepResult = await setStepStatus(id, stepId, 'waiting_approval')\n if (!stepResult.succeeded) return stepResult\n return transition(id, 'waiting_approval', {}, { stepId })\n }\n\n return {\n createMission,\n getMission,\n setEngineRef,\n mergeMetadata,\n setStepStatus,\n advanceCursor,\n addCost,\n markWaitingApproval,\n pause: (id, reason) => transition(id, 'paused', { pauseReason: reason }),\n resume: (id) => transition(id, 'running', { pauseReason: null }),\n abort: (id) => transition(id, 'aborted', { completedAt: now() }),\n complete: (id, input) =>\n transition(id, input.ok ? 'succeeded' : 'failed', {\n completedAt: now(),\n ...(input.summary === undefined ? {} : { summary: input.summary }),\n }),\n }\n}\n\n// ── in-memory store ──────────────────────────────────────────────────────────\n\n/** Define an in-memory mission store that tracks events and allows direct record writes */\nexport interface InMemoryMissionStore extends MissionStorePort {\n /** The full audit trail, append order. */\n events(): MissionAuditEvent[]\n /** Unguarded direct write — simulates a concurrent owner or a crash-shaped\n * state in tests. Production writers go through the guarded `update`. */\n put(record: MissionRecord): void\n}\n\n/**\n * In-memory {@link MissionStorePort} — the portable backend for tests and\n * sandbox/eval shells. Guard comparison uses JSON serialization of the read\n * value, the same contract a SQL implementation honors by comparing stored\n * JSON text. Records are deep-copied on every boundary so callers can never\n * mutate stored state around the guards.\n */\nexport function createInMemoryMissionStore(): InMemoryMissionStore {\n const rows = new Map<string, MissionRecord>()\n const events: MissionAuditEvent[] = []\n\n return {\n async load(id) {\n const record = rows.get(id)\n return record ? structuredClone(record) : null\n },\n async insert(record) {\n if (rows.has(record.id)) throw new Error(`Mission ${record.id} already exists`)\n rows.set(record.id, structuredClone(record))\n return structuredClone(record)\n },\n async update(id, guard, patch) {\n const current = rows.get(id)\n if (!current) return null\n if (guard.status !== undefined && current.status !== guard.status) return null\n if (guard.cursor !== undefined && current.cursor !== guard.cursor) return null\n if (guard.plan !== undefined && JSON.stringify(current.plan) !== JSON.stringify(guard.plan)) return null\n if (guard.cost !== undefined && JSON.stringify(current.cost) !== JSON.stringify(guard.cost)) return null\n if (\n guard.metadata !== undefined &&\n JSON.stringify(current.metadata) !== JSON.stringify(guard.metadata)\n ) {\n return null\n }\n if (guard.engineRefIsNull && current.engineRef !== null) return null\n\n const next: MissionRecord = { ...current }\n if (patch.status !== undefined) next.status = patch.status\n if (patch.pauseReason !== undefined) next.pauseReason = patch.pauseReason\n if (patch.summary !== undefined) next.summary = patch.summary\n if (patch.completedAt !== undefined) next.completedAt = patch.completedAt\n if (patch.plan !== undefined) next.plan = patch.plan\n if (patch.cursor !== undefined) next.cursor = patch.cursor\n if (patch.cost !== undefined) next.cost = patch.cost\n if (patch.spentUsd !== undefined) next.spentUsd = patch.spentUsd\n if (patch.metadata !== undefined) next.metadata = patch.metadata\n if (patch.engineRef !== undefined) next.engineRef = patch.engineRef\n rows.set(id, structuredClone(next))\n return structuredClone(next)\n },\n async appendEvent(event) {\n events.push(structuredClone(event))\n },\n events() {\n return events.map((event) => structuredClone(event))\n },\n put(record) {\n rows.set(record.id, structuredClone(record))\n },\n }\n}\n","/**\n * Shared mission realtime contract — the single source of truth for the typed\n * events the engine BROADCASTS over a live channel and the client REDUCES into\n * live mission state. Server emit and client reduce import the same module so\n * the wire shape can never drift between the two ends.\n *\n * This module is CLIENT-SAFE: no server imports, no platform globals, no DB\n * types. It is pure data + a pure reducer. Keep it that way — a server-only\n * import here would leak into the browser bundle.\n *\n * Sink contract — best-effort UI notification, never load-bearing:\n * - fire-and-forget: the engine never awaits `emit` and a sink failure can\n * never fail a step (the engine wraps every emit). The durable audit-event\n * row is the authoritative timeline; the socket is a convenience.\n * - replay-safe: the engine re-emits on a resume/replay. The reducer below is\n * idempotent + order-tolerant, so a re-sent or duplicated event converges.\n * The sink itself does no dedupe.\n */\n\nimport type { StepAgentActivity } from './agent-activity'\n\n/** Handle mission stream events by processing emitted MissionStreamEvent objects */\nexport interface MissionEventSink {\n emit(event: MissionStreamEvent): void\n}\n\n/** A sink that drops every event — the engine default when no live channel is\n * wired (and the unit-test default). */\nexport const noopEventSink: MissionEventSink = { emit() {} }\n\n/** Workspace-wide channel id missions broadcast on (alongside any per-thread\n * channel the product keys). */\nexport const MISSION_CONTROL_CHANNEL_ID = 'missions'\n\n/** One plan step as it appears on the wire — only what a live UI needs\n * (`sublabel` updates travel separately via `step.updated` so the snapshot\n * stays small). */\nexport interface MissionStreamStep {\n id: string\n intent: string\n kind: string\n status: MissionStreamStepStatus\n}\n\n/** Define possible status values for a mission stream step */\nexport type MissionStreamStepStatus =\n | 'pending'\n | 'running'\n | 'done'\n | 'failed'\n | 'waiting_approval'\n\n/** Define possible statuses representing the current state of a mission stream */\nexport type MissionStreamStatus =\n | 'scheduled'\n | 'running'\n | 'paused'\n | 'waiting_approval'\n | 'succeeded'\n | 'aborted'\n | 'cancelled'\n | 'failed'\n\n/**\n * Discriminated union of every live mission event. Every member carries\n * `missionId` (one channel may multiplex several missions) and a `type` the\n * client switches on. `at` is the emitter's wall-clock ms — used only for\n * display ordering; the reducer never trusts it for causality.\n */\nexport type MissionStreamEvent =\n | {\n type: 'mission.created'\n missionId: string\n at: number\n title: string\n status?: MissionStreamStatus\n steps: MissionStreamStep[]\n budgetUsd?: number | null\n }\n | { type: 'mission.started'; missionId: string; at: number }\n | { type: 'step.started'; missionId: string; at: number; stepId: string }\n | {\n type: 'step.updated'\n missionId: string\n at: number\n stepId: string\n sublabel?: string\n /**\n * Full CURRENT snapshot of the step's delegated runs — never a delta.\n * The reducer replaces the whole lane (latest snapshot wins by `at`), so\n * emitters re-send everything they know each time and at-least-once /\n * out-of-order delivery converges.\n */\n agentActivity?: StepAgentActivity[]\n }\n | {\n type: 'step.completed'\n missionId: string\n at: number\n stepId: string\n ok: boolean\n reason?: string\n durationMs?: number\n }\n | {\n type: 'cost.updated'\n missionId: string\n at: number\n spentUsd: number\n capUsd?: number | null\n }\n | { type: 'mission.paused'; missionId: string; at: number; reason?: string }\n | { type: 'mission.waiting_approval'; missionId: string; at: number; reason?: string }\n | { type: 'mission.resumed'; missionId: string; at: number }\n | {\n type: 'mission.plan.updated'\n missionId: string\n at: number\n title: string\n steps: MissionStreamStep[]\n budgetUsd?: number | null\n }\n | {\n type: 'mission.completed'\n missionId: string\n at: number\n ok: boolean\n status?: Extract<MissionStreamStatus, 'succeeded' | 'failed' | 'aborted' | 'cancelled'>\n summary?: string\n }\n\n// All mission event `type` discriminants. Used to cheaply reject the non-mission\n// events that share a channel without a full switch.\nconst MISSION_EVENT_TYPES: ReadonlySet<string> = new Set<MissionStreamEvent['type']>([\n 'mission.created',\n 'mission.started',\n 'step.started',\n 'step.updated',\n 'step.completed',\n 'cost.updated',\n 'mission.paused',\n 'mission.waiting_approval',\n 'mission.resumed',\n 'mission.plan.updated',\n 'mission.completed',\n])\n\n/**\n * Reconstruct the flat MissionStreamEvent from a broadcast envelope of shape\n * `{ type, data: { ...missionFields } }` (transports may also stamp routing\n * fields like workspaceId/threadId into `data`). The envelope `type` is the\n * AUTHORITATIVE discriminant set by the server, so it is spread LAST — a\n * payload that happens to carry a top-level `type` inside `data` cannot shadow\n * it and mis-render as a mission event. Non-mission envelopes and malformed\n * payloads return null and are simply skipped, so one channel can carry both\n * streams.\n */\nexport function parseSessionStreamEnvelope(raw: unknown): MissionStreamEvent | null {\n if (!raw || typeof raw !== 'object') return null\n const envelope = raw as { type?: unknown; data?: unknown }\n if (typeof envelope.type !== 'string') return null\n const data = envelope.data && typeof envelope.data === 'object' ? envelope.data : {}\n return asMissionStreamEvent({ ...(data as Record<string, unknown>), type: envelope.type })\n}\n\n/** Narrow an arbitrary channel payload to a MissionStreamEvent. Returns null\n * for non-mission events and anything malformed — the reducer skips those. */\nexport function asMissionStreamEvent(value: unknown): MissionStreamEvent | null {\n if (!value || typeof value !== 'object') return null\n const record = value as Record<string, unknown>\n const type = record.type\n if (typeof type !== 'string' || !MISSION_EVENT_TYPES.has(type)) return null\n if (typeof record.missionId !== 'string' || !record.missionId) return null\n // `at` is best-effort: this narrows the type but does not fill a default —\n // the reducer tolerates a missing `at` and treats it as 0.\n return value as MissionStreamEvent\n}\n\n/** Live per-step view the reducer maintains. `status` only ever moves FORWARD\n * (see STEP_RANK) so a duplicate/out-of-order event can never regress a step\n * from done back to running. */\nexport interface MissionStepState {\n id: string\n intent: string\n kind: string\n status: MissionStreamStepStatus\n sublabel?: string\n reason?: string\n durationMs?: number\n /** Latest delegated-run snapshot for the step (see `step.updated`). */\n agentActivity?: StepAgentActivity[]\n /** The `at` of the snapshot currently held — an older snapshot arriving\n * late never replaces a newer one. */\n agentActivityAt?: number\n}\n\n/** Live per-mission view the reducer folds events into. */\nexport interface MissionState {\n missionId: string\n title?: string\n status: MissionStreamStatus\n steps: MissionStepState[]\n spentUsd: number\n capUsd?: number | null\n pauseReason?: string\n summary?: string\n /** The largest `at` folded so far — purely for display; never gates folding. */\n lastEventAt: number\n /** The largest pause/resume control `at` folded — lets a newer resume beat an\n * older pause that arrives late. */\n lastControlAt?: number\n}\n\n// Monotonic rank for step status. A fold NEVER moves a step to a lower rank, so\n// a late/duplicate `step.started` arriving after `step.completed` is a no-op —\n// the reducer tolerates out-of-order and at-least-once delivery.\nconst STEP_RANK: Record<MissionStreamStepStatus, number> = {\n pending: 0,\n running: 1,\n waiting_approval: 2,\n // done and failed are both TERMINAL for a step; rank them equal-and-highest\n // so neither can be overwritten by the other or regressed to running.\n done: 3,\n failed: 3,\n}\n\n// Monotonic rank for mission status. Same forward-only guarantee: a stray\n// `mission.started` after `mission.completed` cannot un-finish a mission.\nconst MISSION_RANK: Record<MissionStreamStatus, number> = {\n scheduled: 0,\n running: 1,\n paused: 2,\n waiting_approval: 2,\n succeeded: 3,\n aborted: 3,\n cancelled: 3,\n failed: 3,\n}\n\nfunction maxStepStatus(\n current: MissionStreamStepStatus,\n next: MissionStreamStepStatus,\n): MissionStreamStepStatus {\n // Equal rank but different terminal kind (done vs failed): the FIRST terminal\n // wins — a step.completed is authoritative and a later contradicting one is a\n // duplicate-class artifact we ignore.\n if (STEP_RANK[next] <= STEP_RANK[current]) return current\n return next\n}\n\nfunction maxMissionStatus(\n current: MissionStreamStatus,\n next: MissionStreamStatus,\n): MissionStreamStatus {\n if (MISSION_RANK[next] <= MISSION_RANK[current]) return current\n return next\n}\n\nfunction emptyMission(missionId: string): MissionState {\n // Seed at the LOWEST status rank so a fold-in is purely monotonic. A\n // step.started that arrives before mission.created must not pin the mission\n // at `running`: were the create to carry `scheduled`, the reducer would then\n // diverge by arrival order (created-first → scheduled, started-first →\n // running). Seeding `scheduled` makes every status edge climb upward only, so\n // both orders converge to the same state.\n return {\n missionId,\n status: 'scheduled',\n steps: [],\n spentUsd: 0,\n lastEventAt: 0,\n lastControlAt: 0,\n }\n}\n\nfunction stepStateFrom(step: MissionStreamStep): MissionStepState {\n return { id: step.id, intent: step.intent, kind: step.kind, status: step.status }\n}\n\n/**\n * Fold one event into one mission's state. PURE: returns a new state, mutates\n * nothing. Idempotent + order-tolerant — every status move is clamped through\n * the monotonic ranks above, so duplicates and out-of-order delivery converge\n * to the same terminal state regardless of arrival order.\n */\nexport function applyMissionEvent(\n prev: MissionState | undefined,\n event: MissionStreamEvent,\n): MissionState {\n const at = typeof event.at === 'number' ? event.at : 0\n const base = prev ?? emptyMission(event.missionId)\n const lastEventAt = Math.max(base.lastEventAt, at)\n const lastControlAt = base.lastControlAt ?? 0\n\n switch (event.type) {\n case 'mission.created': {\n // The authoritative plan snapshot. Merge rather than overwrite so a\n // late-arriving create (e.g. after the client already saw a step.started\n // from a reconnect race) does not clobber forward step progress.\n const merged = event.steps.map((incoming) => {\n const existing = base.steps.find((s) => s.id === incoming.id)\n if (!existing) return stepStateFrom(incoming)\n return {\n ...existing,\n intent: incoming.intent,\n kind: incoming.kind,\n status: maxStepStatus(existing.status, incoming.status),\n }\n })\n // Keep any steps the client already knew that the snapshot omits (it\n // never should, but never drop known progress).\n for (const known of base.steps) {\n if (!merged.some((s) => s.id === known.id)) merged.push(known)\n }\n return {\n ...base,\n title: event.title || base.title,\n status: maxMissionStatus(base.status, event.status ?? base.status),\n capUsd: event.budgetUsd ?? base.capUsd,\n steps: merged,\n lastEventAt,\n }\n }\n\n case 'mission.started':\n return { ...base, status: maxMissionStatus(base.status, 'running'), lastEventAt }\n\n case 'step.started':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n status: maxStepStatus(step.status, 'running'),\n })),\n lastEventAt,\n }\n\n case 'step.updated':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n // A sublabel is a live counter (\"7/15\") — always take the latest; it\n // does not move status.\n ...(event.sublabel !== undefined ? { sublabel: event.sublabel } : {}),\n // agentActivity is a full snapshot, replaced wholesale. Guarded by\n // `at` so a stale snapshot delivered late never erases newer rows;\n // an equal-`at` replay rewrites identical content (idempotent).\n ...(event.agentActivity !== undefined && at >= (step.agentActivityAt ?? 0)\n ? { agentActivity: event.agentActivity, agentActivityAt: at }\n : {}),\n })),\n lastEventAt,\n }\n\n case 'step.completed':\n return {\n ...base,\n steps: upsertStep(base.steps, event.stepId, (step) => ({\n ...step,\n status: maxStepStatus(step.status, event.ok ? 'done' : 'failed'),\n ...(event.reason !== undefined ? { reason: event.reason } : {}),\n ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),\n })),\n lastEventAt,\n }\n\n case 'cost.updated':\n return {\n ...base,\n // spentUsd is cumulative and monotonically non-decreasing at the\n // source; clamp so an out-of-order older value never lowers the\n // displayed spend.\n spentUsd: Math.max(base.spentUsd, event.spentUsd),\n capUsd: event.capUsd ?? base.capUsd,\n lastEventAt,\n }\n\n case 'mission.paused':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: maxMissionStatus(base.status, 'paused'),\n ...(event.reason !== undefined ? { pauseReason: event.reason } : {}),\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.waiting_approval':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: maxMissionStatus(base.status, 'waiting_approval'),\n ...(event.reason !== undefined ? { pauseReason: event.reason } : {}),\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.resumed':\n if (at <= lastControlAt) return { ...base, lastEventAt }\n return {\n ...base,\n status: isTerminalStreamStatus(base.status) ? base.status : 'running',\n pauseReason: undefined,\n lastEventAt,\n lastControlAt: Math.max(lastControlAt, at),\n }\n\n case 'mission.plan.updated':\n return {\n ...base,\n title: event.title || base.title,\n capUsd: event.budgetUsd ?? base.capUsd,\n steps: event.steps.map((incoming) => {\n const existing = base.steps.find((s) => s.id === incoming.id)\n if (!existing) return stepStateFrom(incoming)\n return {\n ...stepStateFrom(incoming),\n status: maxStepStatus(existing.status, incoming.status),\n ...(existing.sublabel !== undefined ? { sublabel: existing.sublabel } : {}),\n ...(existing.reason !== undefined ? { reason: existing.reason } : {}),\n ...(existing.durationMs !== undefined ? { durationMs: existing.durationMs } : {}),\n ...(existing.agentActivity !== undefined ? { agentActivity: existing.agentActivity } : {}),\n ...(existing.agentActivityAt !== undefined ? { agentActivityAt: existing.agentActivityAt } : {}),\n }\n }),\n lastEventAt,\n }\n\n case 'mission.completed':\n return {\n ...base,\n status: maxMissionStatus(base.status, event.status ?? (event.ok ? 'succeeded' : 'failed')),\n ...(event.summary !== undefined ? { summary: event.summary } : {}),\n lastEventAt,\n }\n }\n}\n\nfunction isTerminalStreamStatus(status: MissionStreamStatus): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'aborted' || status === 'cancelled'\n}\n\n// Upsert one step by id, applying `patch`. A step unknown to the client (e.g. a\n// step.started arriving before the create snapshot on a reconnect) is created\n// as a minimal `pending` row so progress is never dropped; the create snapshot\n// fills in intent/kind when it arrives.\nfunction upsertStep(\n steps: MissionStepState[],\n stepId: string,\n patch: (step: MissionStepState) => MissionStepState,\n): MissionStepState[] {\n const index = steps.findIndex((s) => s.id === stepId)\n const existing = index < 0 ? undefined : steps[index]\n if (!existing) {\n const placeholder: MissionStepState = {\n id: stepId,\n intent: '',\n kind: '',\n status: 'pending',\n }\n return [...steps, patch(placeholder)]\n }\n const next = steps.slice()\n next[index] = patch(existing)\n return next\n}\n\n/**\n * Merge a loader SEED into the live state for one mission, advancing through\n * the SAME monotonic clamps the event reducer uses. The durable mission row is\n * the authoritative converged state: while the live channel is down the row\n * advances but the frozen live state does not, and nothing re-fires the gap to\n * a reconnecting client. Folding the seed THROUGH the clamps backfills that gap\n * on reconnect while never regressing a more-advanced live value:\n * - a stale seed for a more-advanced live mission is a no-op,\n * - an advanced seed after an outage fills the gap (status/steps/spend move\n * forward to the row's converged state).\n * `live === undefined` (mission unknown to the client) just adopts the seed.\n */\nexport function mergeMissionState(live: MissionState | undefined, seed: MissionState): MissionState {\n if (!live) return seed\n\n // The loader seed carries the authoritative current plan. Rebuild from it,\n // while preserving live progress fields for matching ids and retaining\n // live-only steps that have real progress evidence.\n const steps: MissionStepState[] = []\n for (const seededStep of seed.steps) {\n const current = live.steps.find((s) => s.id === seededStep.id)\n if (!current) {\n steps.push(seededStep)\n continue\n }\n steps.push({\n ...seededStep,\n intent: current.intent || seededStep.intent,\n kind: current.kind || seededStep.kind,\n status: maxStepStatus(current.status, seededStep.status),\n ...(current.sublabel !== undefined ? { sublabel: current.sublabel } : seededStep.sublabel !== undefined ? { sublabel: seededStep.sublabel } : {}),\n ...(current.reason !== undefined ? { reason: current.reason } : seededStep.reason !== undefined ? { reason: seededStep.reason } : {}),\n ...(current.durationMs !== undefined ? { durationMs: current.durationMs } : seededStep.durationMs !== undefined ? { durationMs: seededStep.durationMs } : {}),\n // The newer snapshot wins by its stamped `at`; an unstamped lane (loader\n // seed copied from the settled artifact) only fills an empty live lane.\n ...(mergeActivity(current, seededStep)),\n })\n }\n for (const current of live.steps) {\n if (seed.steps.some((step) => step.id === current.id)) continue\n if (hasStepProgressEvidence(current)) steps.push(current)\n }\n\n const status = mergeSeedMissionStatus(live.status, seed.status, live.lastControlAt ?? 0, seed.lastControlAt ?? 0)\n return {\n ...live,\n title: live.title ?? seed.title,\n status,\n steps,\n spentUsd: Math.max(live.spentUsd, seed.spentUsd),\n capUsd: live.capUsd ?? seed.capUsd,\n pauseReason: status === 'running' || status === 'scheduled' ? undefined : seed.pauseReason ?? live.pauseReason,\n summary: live.summary ?? seed.summary,\n lastEventAt: Math.max(live.lastEventAt, seed.lastEventAt),\n lastControlAt: Math.max(live.lastControlAt ?? 0, seed.lastControlAt ?? 0),\n }\n}\n\nfunction hasStepProgressEvidence(step: MissionStepState): boolean {\n return step.status !== 'pending' ||\n step.sublabel !== undefined ||\n step.reason !== undefined ||\n step.durationMs !== undefined ||\n step.agentActivity !== undefined\n}\n\n// Pick the activity lane to keep when merging a loader seed into live state:\n// the snapshot with the larger `at` wins; ties keep the live side.\nfunction mergeActivity(\n live: MissionStepState,\n seeded: MissionStepState,\n): Pick<MissionStepState, 'agentActivity' | 'agentActivityAt'> {\n const winner = (seeded.agentActivityAt ?? 0) > (live.agentActivityAt ?? 0) ? seeded : live\n if (winner.agentActivity === undefined) {\n const fallback = winner === live ? seeded : live\n if (fallback.agentActivity === undefined) return {}\n return {\n agentActivity: fallback.agentActivity,\n ...(fallback.agentActivityAt !== undefined ? { agentActivityAt: fallback.agentActivityAt } : {}),\n }\n }\n return {\n agentActivity: winner.agentActivity,\n ...(winner.agentActivityAt !== undefined ? { agentActivityAt: winner.agentActivityAt } : {}),\n }\n}\n\nfunction mergeSeedMissionStatus(\n liveStatus: MissionStreamStatus,\n seedStatus: MissionStreamStatus,\n liveControlAt: number,\n seedControlAt: number,\n): MissionStreamStatus {\n if (isTerminalStreamStatus(liveStatus)) return liveStatus\n if (isTerminalStreamStatus(seedStatus)) return seedStatus\n if ((seedStatus === 'paused' || seedStatus === 'waiting_approval') && liveControlAt > seedControlAt) {\n return liveStatus\n }\n if (seedStatus === 'running') return 'running'\n if (seedStatus === 'scheduled' && liveStatus !== 'scheduled') return liveStatus\n return maxMissionStatus(liveStatus, seedStatus)\n}\n\n/**\n * Fold a whole event sequence into a Map<missionId, MissionState>. PURE and\n * order-tolerant: feeding the same events in any order (with duplicates)\n * converges to the same map. `seed` lets a reload start from loader-rehydrated\n * state before live events arrive.\n */\nexport function reduceMissionEvents(\n events: MissionStreamEvent[],\n seed?: Map<string, MissionState>,\n): Map<string, MissionState> {\n const next = new Map<string, MissionState>(seed ?? [])\n for (const event of events) {\n next.set(event.missionId, applyMissionEvent(next.get(event.missionId), event))\n }\n return next\n}\n","/**\n * Mission execution engine — drives one mission's plan to completion under a\n * SINGLE serialized owner (a Cloudflare Workflow, a Durable Object alarm, a\n * queue consumer — one per mission). The owner wraps each `runStep` call in its\n * durable-step primitive (e.g. Workflows `step.do(step.id, …)`) so a completed\n * step's result is persisted and replayed instead of re-run after a mid-run\n * restart. This module holds the logic that must be correct independent of any\n * runtime, so it is injectable and unit-testable with the dispatch mocked.\n *\n * Idempotency is layered, belt-and-suspenders:\n * 1. The owner's durable-step cache replays a completed step's result.\n * 2. `runStep` re-reads the mission first; a step already `done` (with a\n * resultRef) returns the cached pointer WITHOUT re-dispatching — this\n * closes the at-least-once window where a callback re-runs after the\n * side effect committed but before the owner durably recorded it.\n * 3. The cursor advances only after a step is `done`, so a fresh run resumes\n * from `mission.cursor` and never re-touches earlier steps.\n *\n * Seams (the product supplies domain; the engine owns mechanism):\n * - {@link SandboxDispatch} — how a step actually executes.\n * - {@link MissionEngineOptions.estimateStepCostUsd} — per-step USD estimate.\n * - {@link MissionGateOptions.classifyStep} — which steps need approval.\n * - {@link MissionApprovalsPort} — where gate proposals live and how they\n * resolve.\n */\n\nimport type {\n MissionCostLedger,\n MissionOutcome,\n MissionRecord,\n MissionService,\n MissionStatus,\n MissionStep,\n} from './service'\nimport { isMissionStopRequested, isMissionTerminal } from './service'\nimport { noopEventSink, type MissionEventSink, type MissionStreamEvent } from './events'\n\n/**\n * A side-effecting unit of per-step work. The owner supplies the real\n * implementation (e.g. a detached sandbox-session dispatcher); tests supply a\n * mock. MUST return a SMALL pointer — large output is written to the product's\n * storage and only the resultRef is returned.\n */\nexport type SandboxDispatch = (input: SandboxDispatchInput) => Promise<SandboxDispatchResult>\n\n/** Define input parameters for dispatching a mission step in the sandbox environment */\nexport interface SandboxDispatchInput {\n mission: MissionRecord\n step: MissionStep\n stepIndex: number\n}\n\n/** Define the result of a completed sandbox dispatch including artifact reference and optional cost details */\nexport interface SandboxDispatchDoneResult {\n kind?: 'done'\n /** Small pointer at the produced artifact/output (vault path, asset id, exec\n * digest). Stored on the step as `resultRef`; never the full payload. */\n resultRef: string\n /** Optional one-line status surfaced on the step row. */\n sublabel?: string\n /** Optional marginal spend for this step. `ledgerDelta` carries platform-\n * reported truth (real token counts, wall time); `deltaUsd` is set ONLY when\n * a provider-authored price is known. Omit fields rather than synthesizing\n * zeros — the engine substitutes its injected per-step estimate for a\n * missing deltaUsd and records that estimate in the ledger. */\n cost?: {\n deltaUsd?: number\n ledgerDelta?: Partial<MissionCostLedger>\n }\n}\n\n/** The dispatched step's detached session is still executing on the platform.\n * The owner sleeps `pollAfterMs` and re-invokes the step; the dispatch is\n * idempotent on the session ref, so the re-invocation settles the same session\n * rather than starting a second run. */\nexport interface SandboxDispatchInProgressResult {\n kind: 'in_progress'\n sessionRef: string\n pollAfterMs: number\n sublabel?: string\n}\n\n/** Resolve the result of a sandbox dispatch as done or in progress */\nexport type SandboxDispatchResult = SandboxDispatchDoneResult | SandboxDispatchInProgressResult\n\n/** Outcome of running a single step. `cached` distinguishes a replay/skip\n * (step was already done) from a fresh execution so the engine and its tests\n * can assert the dispatch was NOT re-invoked. */\nexport type StepOutcome =\n | { kind: 'done'; resultRef: string; cached: boolean }\n | { kind: 'in_progress'; sessionRef: string; pollAfterMs: number; sublabel?: string }\n | { kind: 'skipped-cursor'; reason: string }\n | { kind: 'failed'; error: string; fatal: boolean }\n\n/** Outcome of running the whole plan from the cursor to the end. */\nexport type PlanOutcome =\n | { kind: 'completed'; summary: string }\n | { kind: 'in_progress'; stepId: string; sessionRef: string; pollAfterMs: number; sublabel?: string }\n | { kind: 'failed'; failedStepId: string; error: string }\n | { kind: 'halted'; status: MissionStatus; reason?: string | null }\n | { kind: 'terminal'; status: MissionStatus }\n | { kind: 'not-found' }\n\ntype StepGateOutcome =\n | { kind: 'continue' }\n | { kind: 'halted'; status: MissionStatus; reason: string }\n\n/** Define options to control mission plan execution with optional pre-step veto logic */\nexport interface MissionPlanRunOptions {\n /** Pre-step veto (kill switch, schedule window). A non-null return pauses the\n * mission with that reason before the step's side effect starts. */\n beforeStep?: (mission: MissionRecord, step: MissionStep) => Promise<string | null>\n}\n\n/** Thrown to make the owner's durable-step wrapper retry. The single-owner\n * invariant makes a genuine concurrent change rare (it means another writer\n * touched the row), so retrying — rather than corrupting state by forcing a\n * stale write — is the correct response. Distinct from a task failure, which\n * is recorded on the step. */\nexport class MissionConcurrencyError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MissionConcurrencyError'\n }\n}\n\n/** Thrown by a {@link SandboxDispatch} for a TRANSIENT failure (platform blip,\n * exec-time network fault) that should be re-attempted. `runStep` RE-THROWS it\n * so the owner engages its bounded retry+backoff; the step is left `running`\n * and the re-dispatch is made idempotent by the cached-done guard. A\n * deterministic failure must be a plain Error instead — that is recorded as a\n * fatal `failed` step and is never retried (no money-burning loop on a\n * deterministic error). */\nexport class RetryableStepError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RetryableStepError'\n }\n}\n\n/** Resolution states a gate proposal can be in. `approved`/`executed` unblock\n * the gated step; everything else keeps the mission parked. */\nexport type MissionProposalResolution = 'pending' | 'approved' | 'rejected' | 'executed' | 'ignored'\n\n/** Define mission gate categories as step, budget, or volume */\nexport type MissionGateKind = 'step' | 'budget' | 'volume'\n\n/** Product classification of one step. Returned by\n * {@link MissionGateOptions.classifyStep}; the matching rules (regexes, intent\n * vocabularies, path allowlists) are product domain and never live here. */\nexport interface StepGateClassification {\n /** Product approval-type label persisted on the proposal ('generate',\n * 'integration_invoke', …). */\n type: string\n /** Counted against the per-mission external-action volume cap. */\n externalAction?: boolean\n estCostUsd?: number | null\n}\n\n/** A gate proposal the engine asks the product to persist. The id is\n * deterministic per (gate, mission, step) — see the `*ProposalId` helpers —\n * so a replay re-finds the same proposal instead of duplicating it. The\n * product composes its own title/description from the structured fields. */\nexport interface MissionGateProposal {\n id: string\n missionId: string\n stepId: string\n gate: MissionGateKind\n mission: MissionRecord\n step: MissionStep\n /** Present for `gate: 'step'` — the classification that triggered the gate. */\n classification?: StepGateClassification\n /** Present for `gate: 'budget'`. */\n budget?: { spentUsd: number; budgetUsd: number; estimatedCostUsd: number }\n /** Present for `gate: 'volume'`. */\n volume?: { externalActionCount: number; cap: number }\n}\n\n/** Approval persistence seam — the product implements this over its own\n * proposal table and resolution flow. */\nexport interface MissionApprovalsPort {\n /** Resolution of the proposal with this id, or null when none exists. */\n findResolution(proposalId: string): Promise<MissionProposalResolution | null>\n /** Persist a new gate proposal (id is deterministic; called at most once per\n * (gate, mission, step) absent a resolution). */\n createProposal(proposal: MissionGateProposal): Promise<void>\n /** Count of this mission's `gate: 'step'` proposals whose classification was\n * `externalAction: true` — the denominator of the volume cap. */\n countExternalActionProposals(missionId: string): Promise<number>\n}\n\n/** Define configuration options for mission gating including approvals, step classification, and action limits */\nexport interface MissionGateOptions {\n approvals: MissionApprovalsPort\n /** Which steps need human approval, and as what. Return null for an ungated\n * step. The rules are product domain (intent regexes, kind tables). */\n classifyStep: (step: MissionStep) => StepGateClassification | null\n /** Max external-action approvals per mission before an approved override is\n * required to request another. Default 5. */\n externalActionCap?: number\n}\n\n/** Define configuration options for initializing and controlling the mission engine behavior */\nexport interface MissionEngineOptions {\n service: MissionService\n /** Per-step USD estimate. Load-bearing twice: the budget gate parks on it\n * BEFORE a step runs, and the engine records it as the step's spend when the\n * dispatch reports no provider-authored price — using one estimator keeps\n * spend and gate consistent. */\n estimateStepCostUsd: (step: MissionStep) => number\n /** Best-effort live notifier. Fired AFTER each guarded write commits, so a\n * broadcast always reflects persisted state; re-fired on idempotent replays\n * so a reconnecting client converges. Never awaited; a throwing sink can\n * never fail a step. Default: drop everything. */\n sink?: MissionEventSink\n /** Approval gating. Omitted → no classification/volume gates, and a budget\n * overrun pauses the mission (fail closed) instead of parking it\n * waiting_approval behind an override proposal. */\n gates?: MissionGateOptions\n /** Step kinds whose failure does NOT abort the whole mission — enrichment\n * steps the plan can complete without. Every other kind is fatal-on-failure.\n * Default `['optional', 'best-effort']`. */\n nonFatalStepKinds?: readonly string[]\n}\n\n/** Resolve mission plan steps with concurrency control and durable state management */\nexport interface MissionEngine {\n /** Run exactly one plan step. Idempotent: re-invoking for a step already\n * `done` returns the cached pointer without re-dispatching. A lost guarded\n * race throws {@link MissionConcurrencyError} so the owner's durable-step\n * wrapper retries instead of writing a stale value. */\n runStep(missionId: string, stepId: string, dispatch: SandboxDispatch): Promise<StepOutcome>\n /** Walk the plan from the durable cursor to the end, re-reading the mission\n * between steps so a pause/stop control that lands while a step is running\n * is honored before the next side effect. `runStep` is the owner's boundary:\n * in production `(step) => durableStep.do(step.id, () => engine.runStep(…))`;\n * in tests `engine.runStep` directly. */\n runPlan(\n missionId: string,\n runStep: (step: MissionStep, stepIndex: number) => Promise<StepOutcome>,\n options?: MissionPlanRunOptions,\n ): Promise<PlanOutcome>\n /** Record spend durable-first, live second: the guarded ledger write commits,\n * then the sink sees the new total. A guarded failure returns unchanged. */\n recordCost(\n missionId: string,\n deltaUsd: number,\n ledgerDelta?: Partial<MissionCostLedger>,\n ): Promise<MissionOutcome<MissionRecord>>\n /** Pause durable-first, live second (the paused event fires only on a real\n * edge, not an idempotent re-pause). */\n pauseMission(missionId: string, reason: string): Promise<MissionOutcome<MissionRecord>>\n}\n\nconst DEFAULT_EXTERNAL_ACTION_CAP = 5\nconst DEFAULT_NON_FATAL_STEP_KINDS: readonly string[] = ['optional', 'best-effort']\n\n/** Deterministic proposal id for a step-classification gate. */\nexport function stepGateProposalId(missionId: string, stepId: string): string {\n return `mission-step-gate:${missionId}:${stepId}`\n}\n\n/** Deterministic proposal id for a budget-overrun override. */\nexport function budgetGateProposalId(missionId: string, stepId: string): string {\n return `mission-budget-gate:${missionId}:${stepId}`\n}\n\n/** Deterministic proposal id for an external-action volume-cap override. */\nexport function volumeGateProposalId(missionId: string, stepId: string): string {\n return `mission-volume-gate:${missionId}:${stepId}`\n}\n\n// Emit a live event without letting the sink's failure touch the step path.\n// The engine owns the fire-and-forget guarantee at this boundary: a sink that\n// throws synchronously can NEVER fail a step. The durable audit row is the\n// authoritative timeline; the live channel is a convenience.\nfunction safeEmit(sink: MissionEventSink, event: MissionStreamEvent): void {\n try {\n sink.emit(event)\n } catch {\n // Best-effort UI notification — a broadcast fault never fails the step.\n }\n}\n\nfunction unblocked(resolution: MissionProposalResolution | null): boolean {\n return resolution === 'approved' || resolution === 'executed'\n}\n\n// Map a durable terminal status onto the live `mission.completed` event. Only\n// `succeeded` is ok=true; aborted/cancelled/failed fold to ok=false with the\n// terminal status preserved so the reducer keeps user stops distinct from\n// failures.\nfunction terminalMissionEvent(\n missionId: string,\n status: MissionStatus,\n): Extract<MissionStreamEvent, { type: 'mission.completed' }> {\n const terminal = status === 'succeeded' || status === 'failed' || status === 'aborted' || status === 'cancelled'\n ? status\n : 'failed'\n return {\n type: 'mission.completed',\n missionId,\n at: Date.now(),\n ok: terminal === 'succeeded',\n status: terminal,\n ...(terminal === 'succeeded' ? {} : { summary: `Mission ${status}` }),\n }\n}\n\n/** Create a mission engine configured with options to manage mission execution and error handling */\nexport function createMissionEngine(options: MissionEngineOptions): MissionEngine {\n const { service, estimateStepCostUsd, gates } = options\n const sink = options.sink ?? noopEventSink\n const nonFatalStepKinds = new Set(options.nonFatalStepKinds ?? DEFAULT_NON_FATAL_STEP_KINDS)\n const externalActionCap = gates?.externalActionCap ?? DEFAULT_EXTERNAL_ACTION_CAP\n\n function isFatalStepKind(kind: string): boolean {\n return !nonFatalStepKinds.has(kind)\n }\n\n // A guarded service rejection is one of two things:\n // - a lost race (conflict: true) — THROW so the owner's durable-step\n // wrapper retries; the single owner makes this rare and a retry re-reads\n // fresh state rather than forcing a stale write.\n // - a logic rejection (illegal transition, missing step) — that step is\n // structurally broken; return a fatal `failed` outcome instead of\n // looping a retry forever (no money-burning retry on a deterministic\n // error).\n function rejectStep(failure: { error: string; conflict: boolean }): StepOutcome {\n if (failure.conflict) throw new MissionConcurrencyError(failure.error)\n return { kind: 'failed', error: failure.error, fatal: true }\n }\n\n function isStepCurrentOrFuture(mission: MissionRecord, stepId: string): boolean {\n const index = mission.plan.findIndex((candidate) => candidate.id === stepId)\n return index >= mission.cursor\n }\n\n const recordCost: MissionEngine['recordCost'] = async (missionId, deltaUsd, ledgerDelta) => {\n const recorded = await service.addCost(missionId, deltaUsd, ledgerDelta)\n if (!recorded.succeeded) return recorded\n safeEmit(sink, {\n type: 'cost.updated',\n missionId,\n at: Date.now(),\n spentUsd: recorded.value.spentUsd,\n capUsd: recorded.value.budgetUsd,\n })\n return recorded\n }\n\n const pauseMission: MissionEngine['pauseMission'] = async (missionId, reason) => {\n const before = await service.getMission(missionId)\n const paused = await service.pause(missionId, reason)\n if (!paused.succeeded) return paused\n if (before?.status !== 'paused') {\n safeEmit(sink, {\n type: 'mission.paused',\n missionId,\n at: Date.now(),\n reason: paused.value.pauseReason ?? reason,\n })\n }\n return paused\n }\n\n const runStep: MissionEngine['runStep'] = async (missionId, stepId, dispatch) => {\n const mission = await service.getMission(missionId)\n if (!mission) throw new MissionConcurrencyError(`Mission ${missionId} not found mid-run`)\n\n const stepIndex = mission.plan.findIndex((candidate) => candidate.id === stepId)\n const step = stepIndex < 0 ? undefined : mission.plan[stepIndex]\n if (!step) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer in mission plan` }\n }\n if (isMissionStopRequested(mission)) {\n return { kind: 'failed', error: mission.pauseReason ?? 'Mission stop requested', fatal: true }\n }\n if (mission.status !== 'running') {\n return { kind: 'skipped-cursor', reason: mission.pauseReason ?? `Mission is ${mission.status}` }\n }\n\n // (2) Idempotent short-circuit: a step that already reached `done` keeps\n // its resultRef. Return it WITHOUT re-running the side effect. Reconcile\n // the cursor first: a crash in the window between setStepStatus('done')\n // and advanceCursor leaves the cursor one slot behind this done step; a\n // fresh resume would otherwise end with cursor = plan.length - 1. Advance\n // it here (guarded — a lost race throws for the owner to retry) so the\n // cursor stays a true done-count.\n if (step.status === 'done' && step.resultRef) {\n if (stepIndex === mission.cursor) {\n const reconciled = await service.advanceCursor(missionId)\n if (!reconciled.succeeded) return rejectStep(reconciled)\n }\n // Re-emit the terminal event on replay so a client that connected after\n // the step finished still folds it (idempotent at the reducer).\n safeEmit(sink, { type: 'step.completed', missionId, at: Date.now(), stepId, ok: true })\n return { kind: 'done', resultRef: step.resultRef, cached: true }\n }\n\n // (3) The cursor has already moved past this step but it is not `done` —\n // the owner is replaying a step the engine no longer considers active.\n // Skip it rather than re-run; the cursor is the source of truth.\n if (stepIndex < mission.cursor) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is behind cursor ${mission.cursor}` }\n }\n\n const startedAt = Date.now()\n if (step.status !== 'running') {\n const running = await service.setStepStatus(missionId, stepId, 'running')\n if (!running.succeeded) return rejectStep(running)\n }\n safeEmit(sink, { type: 'step.started', missionId, at: startedAt, stepId })\n\n let dispatched: SandboxDispatchResult\n try {\n dispatched = await dispatch({ mission, step, stepIndex })\n } catch (error) {\n // A TRANSIENT failure re-throws so the owner engages its bounded\n // retry+backoff: leave the step `running` (the re-dispatch is idempotent\n // via the cached-done guard) and let the owner re-attempt up to its limit.\n if (error instanceof RetryableStepError) throw error\n const latest = await service.getMission(missionId)\n if (\n latest &&\n !isMissionTerminal(latest.status) &&\n !isMissionStopRequested(latest) &&\n !isStepCurrentOrFuture(latest, stepId)\n ) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer active` }\n }\n // A deterministic failure is recorded as a fatal `failed` step and is\n // NOT retried — looping a retry on a deterministic error burns money.\n const message = error instanceof Error ? error.message : 'Sandbox dispatch failed'\n const failed = await service.setStepStatus(missionId, stepId, 'failed', { error: message })\n if (!failed.succeeded) return rejectStep(failed)\n safeEmit(sink, {\n type: 'step.completed',\n missionId,\n at: Date.now(),\n stepId,\n ok: false,\n reason: message,\n durationMs: Date.now() - startedAt,\n })\n return { kind: 'failed', error: message, fatal: isFatalStepKind(step.kind) }\n }\n\n const afterDispatch = await service.getMission(missionId)\n if (!afterDispatch) throw new MissionConcurrencyError(`Mission ${missionId} not found after dispatch`)\n if (isMissionTerminal(afterDispatch.status) || isMissionStopRequested(afterDispatch)) {\n return { kind: 'failed', error: afterDispatch.pauseReason ?? 'Mission stop requested', fatal: true }\n }\n if (afterDispatch.status !== 'running') {\n return { kind: 'skipped-cursor', reason: afterDispatch.pauseReason ?? `Mission is ${afterDispatch.status}` }\n }\n if (!isStepCurrentOrFuture(afterDispatch, stepId)) {\n return { kind: 'skipped-cursor', reason: `Step ${stepId} is no longer active` }\n }\n\n // The detached session is still running: surface the elapsed sublabel and\n // hand the poll cadence to the owner. The step row stays `running`.\n if (dispatched.kind === 'in_progress') {\n if (dispatched.sublabel !== undefined) {\n safeEmit(sink, { type: 'step.updated', missionId, at: Date.now(), stepId, sublabel: dispatched.sublabel })\n }\n return {\n kind: 'in_progress',\n sessionRef: dispatched.sessionRef,\n pollAfterMs: dispatched.pollAfterMs,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n }\n }\n\n // A live counter (\"7/15\") the dispatch surfaced — push it before the\n // terminal event so a long step shows progress, then settles to done.\n if (dispatched.sublabel !== undefined) {\n safeEmit(sink, { type: 'step.updated', missionId, at: Date.now(), stepId, sublabel: dispatched.sublabel })\n }\n\n // USD: provider-authored price when the dispatch reports one, the injected\n // estimate otherwise (the budget gate runs on the same estimate, so spend\n // and gate stay consistent). Token counts/wall time come from the\n // dispatch's ledgerDelta when the platform reported them. The spend is\n // folded into the SAME guarded pending->done write below so step completion\n // is the per-step idempotency key: a RESUME re-dispatching an already-done\n // step short-circuits as a no-op and never charges twice.\n const deltaUsd = dispatched.cost?.deltaUsd ?? estimateStepCostUsd(step)\n const chargeable = deltaUsd > 0 || Boolean(dispatched.cost?.ledgerDelta)\n const spentBefore = afterDispatch.spentUsd\n\n const done = await service.setStepStatus(missionId, stepId, 'done', {\n resultRef: dispatched.resultRef,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n ...(chargeable\n ? {\n cost: {\n deltaUsd,\n ledgerDelta: {\n costUsd: deltaUsd,\n llmCalls: 1,\n ...(dispatched.cost?.ledgerDelta ?? {}),\n },\n },\n }\n : {}),\n })\n if (!done.succeeded) return rejectStep(done)\n // Emit cost.updated only when the spend actually committed (a real\n // transition, not a replayed no-op that re-asserted an already-done step).\n if (done.value.spentUsd !== spentBefore) {\n safeEmit(sink, {\n type: 'cost.updated',\n missionId,\n at: Date.now(),\n spentUsd: done.value.spentUsd,\n capUsd: done.value.budgetUsd,\n })\n }\n safeEmit(sink, {\n type: 'step.completed',\n missionId,\n at: Date.now(),\n stepId,\n ok: true,\n durationMs: Date.now() - startedAt,\n })\n\n // Advance the cursor only after the step is durably `done`. A racing\n // advance (single owner ⇒ should not happen) surfaces as a retry.\n const advanced = await service.advanceCursor(missionId)\n if (!advanced.succeeded) return rejectStep(advanced)\n\n return { kind: 'done', resultRef: dispatched.resultRef, cached: false }\n }\n\n async function parkForApproval(\n mission: MissionRecord,\n step: MissionStep,\n reason: string,\n ): Promise<StepGateOutcome> {\n const waiting = await service.markWaitingApproval(mission.id, step.id)\n if (!waiting.succeeded) {\n if (waiting.conflict) throw new MissionConcurrencyError(waiting.error)\n return { kind: 'halted', status: mission.status, reason: waiting.error }\n }\n safeEmit(sink, {\n type: 'mission.waiting_approval',\n missionId: mission.id,\n at: Date.now(),\n reason,\n })\n safeEmit(sink, {\n type: 'mission.plan.updated',\n missionId: mission.id,\n at: Date.now(),\n title: waiting.value.summary ?? 'Mission',\n steps: waiting.value.plan.map((candidate) => ({\n id: candidate.id,\n intent: candidate.intent,\n kind: candidate.kind,\n status: candidate.status,\n })),\n budgetUsd: waiting.value.budgetUsd,\n })\n return { kind: 'halted', status: 'waiting_approval', reason }\n }\n\n // A budgeted mission never starts a step whose estimate would push spend\n // past the cap. With an approvals port the overrun parks behind a\n // deterministic override proposal; without one it pauses (fail closed) for a\n // manual budget raise + resume.\n async function enforceBudget(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n const capUsd = mission.budgetUsd\n if (capUsd === null) return { kind: 'continue' }\n const estimatedCostUsd = estimateStepCostUsd(step)\n if (estimatedCostUsd <= 0) return { kind: 'continue' }\n // Compare in integer cents: USD amounts accumulate binary-float error (e.g.\n // 0.1 + 0.2 > 0.3), so a raw float compare can spuriously trip the gate at\n // the exact cap. Cent-rounding makes the boundary deterministic.\n if (Math.round((mission.spentUsd + estimatedCostUsd) * 100) <= Math.round(capUsd * 100)) {\n return { kind: 'continue' }\n }\n\n if (!gates) {\n const reason = `Budget cap reached before step ${step.id}: $${mission.spentUsd.toFixed(2)} spent of $${capUsd.toFixed(2)}, next step estimated $${estimatedCostUsd.toFixed(2)}`\n const paused = await pauseMission(mission.id, reason)\n if (!paused.succeeded) {\n if (paused.conflict) throw new MissionConcurrencyError(paused.error)\n return { kind: 'halted', status: mission.status, reason: paused.error }\n }\n return { kind: 'halted', status: paused.value.status, reason }\n }\n\n const proposalId = budgetGateProposalId(mission.id, step.id)\n const resolution = await gates.approvals.findResolution(proposalId)\n if (unblocked(resolution)) return { kind: 'continue' }\n if (resolution === null) {\n await gates.approvals.createProposal({\n id: proposalId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'budget',\n mission,\n step,\n budget: { spentUsd: mission.spentUsd, budgetUsd: capUsd, estimatedCostUsd },\n })\n }\n return parkForApproval(mission, step, `Budget approval required for step ${step.id}`)\n }\n\n async function enforceVolumeCap(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n if (!gates) return { kind: 'continue' }\n const overrideId = volumeGateProposalId(mission.id, step.id)\n const override = await gates.approvals.findResolution(overrideId)\n if (unblocked(override)) return { kind: 'continue' }\n\n const externalCount = await gates.approvals.countExternalActionProposals(mission.id)\n if (externalCount < externalActionCap) return { kind: 'continue' }\n\n if (override === null) {\n await gates.approvals.createProposal({\n id: overrideId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'volume',\n mission,\n step,\n volume: { externalActionCount: externalCount, cap: externalActionCap },\n })\n }\n return parkForApproval(mission, step, `External action cap approval required for step ${step.id}`)\n }\n\n async function enforceStepGate(mission: MissionRecord, step: MissionStep): Promise<StepGateOutcome> {\n if (!gates) return { kind: 'continue' }\n const classification = gates.classifyStep(step)\n if (!classification) return { kind: 'continue' }\n\n if (classification.externalAction) {\n const volume = await enforceVolumeCap(mission, step)\n if (volume.kind === 'halted') return volume\n }\n\n const proposalId = stepGateProposalId(mission.id, step.id)\n const resolution = await gates.approvals.findResolution(proposalId)\n if (unblocked(resolution)) return { kind: 'continue' }\n\n if (resolution === null) {\n await gates.approvals.createProposal({\n id: proposalId,\n missionId: mission.id,\n stepId: step.id,\n gate: 'step',\n mission,\n step,\n classification,\n })\n }\n return parkForApproval(mission, step, `Approval required for step ${step.id}`)\n }\n\n const runPlan: MissionEngine['runPlan'] = async (missionId, runStepFn, planOptions = {}) => {\n const mission = await service.getMission(missionId)\n if (!mission) return { kind: 'not-found' }\n if (isMissionTerminal(mission.status)) {\n // Already terminal on entry (a replay after the row finished, or a\n // resume of an aborted/cancelled run). Re-emit the terminal event so a\n // client that connected during an outage still converges off this run.\n // Idempotent at the reducer.\n safeEmit(sink, terminalMissionEvent(missionId, mission.status))\n return { kind: 'terminal', status: mission.status }\n }\n\n // Resume from the durable cursor. Re-read the mission between steps so a\n // pause/stop control that lands while a step is running is honored before\n // the next side effect starts.\n while (true) {\n const currentMission = await service.getMission(missionId)\n if (!currentMission) return { kind: 'not-found' }\n if (isMissionTerminal(currentMission.status)) {\n safeEmit(sink, terminalMissionEvent(missionId, currentMission.status))\n return { kind: 'terminal', status: currentMission.status }\n }\n if (isMissionStopRequested(currentMission)) {\n return {\n kind: 'halted',\n status: currentMission.status,\n reason: currentMission.pauseReason ?? 'Mission stop requested',\n }\n }\n if (currentMission.status !== 'running') {\n return {\n kind: 'halted',\n status: currentMission.status,\n reason: currentMission.pauseReason,\n }\n }\n\n const index = currentMission.cursor\n if (index >= currentMission.plan.length) break\n const step = currentMission.plan[index]\n if (!step) break\n\n const haltReason = await planOptions.beforeStep?.(currentMission, step)\n if (haltReason) {\n const paused = await pauseMission(missionId, haltReason)\n if (!paused.succeeded) throw new MissionConcurrencyError(paused.error)\n return { kind: 'halted', status: paused.value.status, reason: paused.value.pauseReason ?? haltReason }\n }\n\n if (step.status !== 'done') {\n const budget = await enforceBudget(currentMission, step)\n if (budget.kind === 'halted') {\n return { kind: 'halted', status: budget.status, reason: budget.reason }\n }\n const gate = await enforceStepGate(currentMission, step)\n if (gate.kind === 'halted') {\n return { kind: 'halted', status: gate.status, reason: gate.reason }\n }\n }\n\n const outcome = await runStepFn(step, index)\n if (outcome.kind === 'failed' && outcome.fatal) {\n // The owner flips the mission row to `failed` on this outcome; emit the\n // live terminal event here so a watcher sees the failure without\n // waiting for the next loader poll. Idempotent at the reducer.\n safeEmit(sink, {\n type: 'mission.completed',\n missionId,\n at: Date.now(),\n ok: false,\n summary: `Step ${step.id} failed: ${outcome.error}`,\n })\n return { kind: 'failed', failedStepId: step.id, error: outcome.error }\n }\n if (outcome.kind === 'failed' && !outcome.fatal) {\n const advanced = await service.advanceCursor(missionId)\n if (!advanced.succeeded) throw new MissionConcurrencyError(advanced.error)\n }\n if (outcome.kind === 'in_progress') {\n return {\n kind: 'in_progress',\n stepId: step.id,\n sessionRef: outcome.sessionRef,\n pollAfterMs: outcome.pollAfterMs,\n ...(outcome.sublabel === undefined ? {} : { sublabel: outcome.sublabel }),\n }\n }\n // Cursor-skips fall through: another pass already moved past it. The next\n // loop re-reads the authoritative cursor and plan.\n }\n\n const finalMission = await service.getMission(missionId)\n const planLength = finalMission?.plan.length ?? 0\n const summary = `Completed ${planLength} step${planLength === 1 ? '' : 's'}`\n const completed = await service.complete(missionId, { ok: true, summary })\n if (!completed.succeeded) {\n // complete() is guarded; a concurrent terminal transition (abort/cancel)\n // wins the race. Re-read to report the real terminal status rather than\n // claim a completion that did not land.\n const after = await service.getMission(missionId)\n if (after && isMissionTerminal(after.status)) {\n safeEmit(sink, terminalMissionEvent(missionId, after.status))\n return { kind: 'terminal', status: after.status }\n }\n throw new MissionConcurrencyError(completed.error)\n }\n safeEmit(sink, { type: 'mission.completed', missionId, at: Date.now(), ok: true, summary })\n return { kind: 'completed', summary }\n }\n\n return { runStep, runPlan, recordCost, pauseMission }\n}\n","/**\n * Parsing for the agent-authored `:::mission` block — the bridge from a chat\n * prompt contract to the engine's MissionStep[] shape. The block format:\n *\n * :::mission\n * title: <mission title>\n * <id>: <kind> | <intent>\n * :::\n *\n * The allowed kind vocabulary is a PARAMETER — products pass their own list to\n * match their prompt directive; {@link DEFAULT_MISSION_STEP_KINDS} is the\n * default. Kinds label intent for gating and UX; they never select a different\n * execution path.\n */\n\nimport type { MissionStep } from './service'\n\n/** Default step-kind vocabulary. `best-effort` matches the engine's default\n * non-fatal kind (a failure does not abort the mission); the rest are\n * fatal-on-failure agent sub-tasks. */\nexport const DEFAULT_MISSION_STEP_KINDS: readonly string[] = [\n 'research',\n 'generate',\n 'analyze',\n 'write',\n 'best-effort',\n]\n\n/** Define the structure representing a parsed mission step with id, kind, and intent fields */\nexport interface ParsedMissionStep {\n id: string\n kind: string\n intent: string\n}\n\n/** Describe a mission with a title and an ordered list of parsed steps */\nexport interface ParsedMission {\n title: string\n steps: ParsedMissionStep[]\n}\n\n/** Define options to specify allowed lowercase step kinds for parsing mission blocks */\nexport interface ParseMissionBlocksOptions {\n /** Allowed step kinds (lowercase). Default {@link DEFAULT_MISSION_STEP_KINDS}. */\n kinds?: readonly string[]\n}\n\n/**\n * Parse every well-formed `:::mission` block. A block without a title or\n * without at least one valid step yields nothing (it is malformed — never\n * guess a plan from loose prose). Unknown kinds and malformed step lines are\n * dropped; an empty result lets the caller skip the block rather than start an\n * empty mission.\n */\nexport function parseMissionBlocks(\n fullContent: string,\n options: ParseMissionBlocksOptions = {},\n): ParsedMission[] {\n const kinds = new Set(options.kinds ?? DEFAULT_MISSION_STEP_KINDS)\n const missionRegex = /:::mission\\s*\\n([\\s\\S]*?)\\n\\s*:::/g\n const missions: ParsedMission[] = []\n let match\n while ((match = missionRegex.exec(fullContent)) !== null) {\n const body = match[1]\n if (body === undefined) continue\n const parsed = parseMissionBody(body, kinds)\n if (parsed) missions.push(parsed)\n }\n return missions\n}\n\nfunction parseMissionBody(body: string, kinds: ReadonlySet<string>): ParsedMission | null {\n let title: string | null = null\n const steps: ParsedMissionStep[] = []\n for (const rawLine of body.split('\\n')) {\n const line = rawLine.trim()\n if (!line) continue\n const titleMatch = /^title\\s*:\\s*(.+)$/i.exec(line)\n if (titleMatch?.[1] !== undefined) {\n if (title === null) title = titleMatch[1].trim()\n continue\n }\n // `<id>: <kind> | <intent>`\n const stepMatch = /^([A-Za-z0-9][A-Za-z0-9_-]*)\\s*:\\s*([A-Za-z-]+)\\s*\\|\\s*(.+)$/.exec(line)\n if (!stepMatch) continue\n const id = stepMatch[1]?.trim()\n const kind = stepMatch[2]?.trim().toLowerCase()\n const intent = stepMatch[3]?.trim()\n if (!id || !kind || !intent) continue\n if (!kinds.has(kind)) continue\n steps.push({ id, kind, intent })\n }\n if (!title || steps.length === 0) return null\n return { title, steps }\n}\n\n/**\n * Materialize parsed steps into the engine's MissionStep[] shape. Rejects a\n * duplicate step id (fail loud — the owner keys its durable step cache by\n * step id and `createMission` rejects duplicates anyway; catching it here\n * gives a clearer diagnostic). Every step starts `pending` with zero attempts.\n */\nexport function buildAgentMissionPlan(steps: ParsedMissionStep[]): MissionStep[] {\n if (steps.length === 0) throw new Error('mission plan must have at least one step')\n const seen = new Set<string>()\n for (const step of steps) {\n if (seen.has(step.id)) {\n throw new Error(`duplicate mission step id \"${step.id}\" — step ids must be unique`)\n }\n seen.add(step.id)\n }\n return steps.map((step) => ({\n id: step.id,\n intent: step.intent,\n kind: step.kind,\n status: 'pending' as const,\n attempts: 0,\n }))\n}\n","/**\n * Canonical shape for the per-step agent-activity lane — the delegated runs\n * (delegation MCP registry entries) a mission step spawned, journaled onto the\n * step so a live UI can render \"what the agent is actually doing\" under the\n * step row.\n *\n * CLIENT-SAFE, like everything in `./events`: pure data + a validator. The\n * server attaches `StepAgentActivity[]` when a step settles (and live via\n * `step.updated`); the client re-validates with {@link stepAgentActivity}\n * because the value rides loader/JSON boundaries as untyped metadata.\n */\n\nexport interface StepAgentActivity {\n taskId: string\n /** Delegation profile: coder | researcher | ui-auditor | product-defined. */\n tool: string\n status: string\n /** Title-ish excerpt of the delegation's args (goal / question / audit root). */\n detail: string\n costUsd?: number\n durationMs?: number\n /** ISO timestamp the delegation started. */\n startedAt: string\n /** Live loop progress (DelegationProgress.iteration) while the run is in flight. */\n iteration?: number\n /** Live loop progress (DelegationProgress.phase) while the run is in flight. */\n phase?: string\n /** 32-hex trace id when the delegation joined a mission trace (see `/trace`). */\n traceId?: string\n /** 16-hex span id of the delegation's span inside that trace. */\n spanId?: string\n}\n\n/** Step state extended with the activity lane a loader/seed route attaches. */\nexport type WithAgentActivity<Step> = Step & { agentActivity?: StepAgentActivity[] }\n\n/**\n * Re-validate an `agentActivity` lane that crossed a JSON boundary. Entries\n * missing any required field are dropped (a torn row must not crash the lane);\n * optional fields are kept only when well-typed.\n */\nexport function stepAgentActivity(step: object): StepAgentActivity[] {\n const value = (step as { agentActivity?: unknown }).agentActivity\n if (!Array.isArray(value)) return []\n const items: StepAgentActivity[] = []\n for (const entry of value) {\n if (!entry || typeof entry !== 'object') continue\n const record = entry as Record<string, unknown>\n if (\n typeof record.taskId !== 'string' ||\n typeof record.tool !== 'string' ||\n typeof record.status !== 'string' ||\n typeof record.detail !== 'string' ||\n typeof record.startedAt !== 'string'\n ) {\n continue\n }\n items.push({\n taskId: record.taskId,\n tool: record.tool,\n status: record.status,\n detail: record.detail,\n startedAt: record.startedAt,\n ...(typeof record.costUsd === 'number' && Number.isFinite(record.costUsd) ? { costUsd: record.costUsd } : {}),\n ...(typeof record.durationMs === 'number' && Number.isFinite(record.durationMs) ? { durationMs: record.durationMs } : {}),\n ...(typeof record.iteration === 'number' && Number.isFinite(record.iteration) ? { iteration: record.iteration } : {}),\n ...(typeof record.phase === 'string' ? { phase: record.phase } : {}),\n ...(typeof record.traceId === 'string' ? { traceId: record.traceId } : {}),\n ...(typeof record.spanId === 'string' ? { spanId: record.spanId } : {}),\n })\n }\n return items\n}\n"],"mappings":"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","names":[]}
@@ -1,129 +0,0 @@
1
- // src/config/index.ts
2
- function defineAgentApp(config) {
3
- return config;
4
- }
5
- var agentAppConfigJsonSchema = {
6
- $schema: "https://json-schema.org/draft/2020-12/schema",
7
- $id: "https://tangle.tools/schemas/agent-app-config.json",
8
- title: "AgentAppConfig",
9
- description: "The declarative domain surface of a Tangle agent product.",
10
- type: "object",
11
- additionalProperties: false,
12
- required: ["identity", "taxonomy", "knowledge", "integrations"],
13
- properties: {
14
- identity: {
15
- type: "object",
16
- additionalProperties: false,
17
- required: ["name", "persona"],
18
- properties: {
19
- name: { type: "string", description: "Product/agent name." },
20
- persona: { type: "string", description: "One-paragraph persona \u2014 the system-prompt spine." },
21
- systemPromptFragments: {
22
- type: "array",
23
- items: { type: "string" },
24
- description: "Verbatim system-prompt fragments appended after the persona, in order."
25
- },
26
- disclaimers: {
27
- type: "object",
28
- additionalProperties: { type: "string" },
29
- description: "Named disclaimers keyed by stable id; values are literal text."
30
- }
31
- }
32
- },
33
- taxonomy: {
34
- type: "object",
35
- additionalProperties: false,
36
- required: ["proposalTypes", "regulatedTypes"],
37
- properties: {
38
- proposalTypes: {
39
- type: "array",
40
- items: { type: "string" },
41
- description: "Closed allow-list of proposal types the product can emit."
42
- },
43
- regulatedTypes: {
44
- type: "array",
45
- items: { type: "string" },
46
- description: "Subset of proposalTypes that is approval-gated (certified-human required)."
47
- }
48
- }
49
- },
50
- knowledge: {
51
- type: "object",
52
- additionalProperties: false,
53
- required: ["sources", "requirements"],
54
- properties: {
55
- sources: {
56
- type: "array",
57
- description: "Sources the acquisition loop may draw on.",
58
- items: {
59
- type: "object",
60
- additionalProperties: false,
61
- required: ["uri"],
62
- properties: {
63
- uri: { type: "string", description: "Where the source lives (opaque to agent-app)." },
64
- kind: { type: "string", description: "Optional source classifier (web/vault/regulation/\u2026)." }
65
- }
66
- }
67
- },
68
- requirements: {
69
- type: "array",
70
- description: "Declarative KnowledgeRequirementSpec[] (../knowledge) that gate the loop.",
71
- items: { type: "object", additionalProperties: true }
72
- },
73
- loop: {
74
- type: "object",
75
- additionalProperties: false,
76
- description: "Acquisition-loop config.",
77
- properties: {
78
- goal: { type: "string", description: "Acquisition goal in natural language." },
79
- minConfidence: {
80
- type: "number",
81
- minimum: 0,
82
- maximum: 1,
83
- description: "Minimum aggregate confidence the loop must reach."
84
- },
85
- freshness: { type: "string", description: "How fresh acquired knowledge must be." }
86
- }
87
- }
88
- }
89
- },
90
- integrations: {
91
- type: "object",
92
- additionalProperties: false,
93
- required: ["enabled"],
94
- properties: {
95
- enabled: {
96
- type: "array",
97
- items: { type: "string" },
98
- description: "Enabled agent-integrations catalog kinds."
99
- }
100
- }
101
- },
102
- ui: {
103
- type: "object",
104
- additionalProperties: false,
105
- description: "UI capability flags.",
106
- properties: {
107
- generatedUi: { type: "boolean", description: "Whether the agent may emit generated UI." }
108
- }
109
- },
110
- model: {
111
- type: "object",
112
- additionalProperties: false,
113
- description: "Resolved TangleModelConfig (../runtime). Omit to resolve from env.",
114
- required: ["provider", "model", "apiKey", "baseUrl"],
115
- properties: {
116
- provider: { type: "string", enum: ["openai-compat", "anthropic"] },
117
- model: { type: "string" },
118
- apiKey: { type: "string" },
119
- baseUrl: { type: "string" }
120
- }
121
- }
122
- }
123
- };
124
-
125
- export {
126
- defineAgentApp,
127
- agentAppConfigJsonSchema
128
- };
129
- //# sourceMappingURL=chunk-OSMNYPOG.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/index.ts"],"sourcesContent":["/**\n * `@tangle-network/agent-app/config` — the typed `AgentAppConfig` contract.\n *\n * This is the single declarative DATA surface a coding agent fills to stand up a\n * new agent product. Everything an agent product otherwise hand-rolls as\n * imperative wiring — who the agent is, which proposal types exist and which are\n * approval-gated, what knowledge the control loop requires, which integrations\n * are connectable, how the model resolves — is expressed here as plain data the\n * rest of agent-app's modules consume through their typed seams:\n *\n * - `identity` → the system-prompt + disclaimer surface the chat pipeline reads.\n * - `taxonomy` → which `submit_proposal` types the `./tools` side channel\n * accepts, and which the approval queue treats as regulated\n * (certified-human-gated).\n * - `knowledge` → the `./knowledge` requirement specs that gate the loop, plus\n * the acquisition-loop goal/threshold the researcher pursues.\n * - `integrations` → which `@tangle-network/agent-integrations` catalog kinds the\n * product enables.\n * - `ui` → whether the agent may emit generated UI (`render_ui`).\n * - `model` → the `./runtime` `TangleModelConfig` (or null to resolve from env).\n *\n * Layering: this module is the CONTRACT only — pure types + an identity helper.\n * It introduces no behavior and no engine dependency; it REUSES the existing\n * `KnowledgeRequirementSpec`/`SatisfiedByRule` (from `../knowledge`) and\n * `TangleModelConfig` (from `../runtime`) rather than redefining them, so the\n * config a product authors is the exact shape those modules already consume.\n * Steps that build the composer/loader read this file as the schema floor.\n */\n\nimport type {\n KnowledgeRequirementSpec,\n SatisfiedByRule,\n} from '../knowledge/index'\nimport type { TangleModelConfig } from '../runtime/index'\n\n// Re-export the borrowed types so a config author has a single import surface\n// (`@tangle-network/agent-app/config`) and never has to reach into `../knowledge`\n// or `../runtime` to spell out a field's type. These are the SAME types the\n// knowledge gate and runtime model resolver consume — not parallel copies.\nexport type { KnowledgeRequirementSpec, SatisfiedByRule, TangleModelConfig }\n\n/**\n * Who the agent is, as data. Composed into the chat pipeline's system prompt;\n * never baked into agent-app (a domain value, per the layering contract).\n */\nexport interface AgentIdentityConfig {\n /** Product/agent name, e.g. `\"Acme Support Copilot\"`. Shown to the user and\n * available to the system-prompt builder. */\n name: string\n /** One-paragraph persona statement — the agent's role, voice, and remit. The\n * spine of the system prompt. */\n persona: string\n /** Additional system-prompt fragments appended verbatim after the persona\n * (standing workflows, hard rules, tone). Order is preserved. Optional. */\n systemPromptFragments?: string[]\n /** Named disclaimers the product surfaces (e.g. a regulatory human-in-the-loop\n * notice). Keyed by a stable id (`compliance`, `not-advice`, …) so the chat\n * pipeline / UI can select one by name. Values are the literal text. Optional. */\n disclaimers?: Record<string, string>\n}\n\n/**\n * The proposal taxonomy, as data. `proposalTypes` is the closed set of\n * `submit_proposal` types the `./tools` side channel accepts; `regulatedTypes`\n * is the subset that is approval-gated — the executor refuses to run one without\n * a certified human approver. `regulatedTypes` MUST be a subset of\n * `proposalTypes` (validated by the loader step; not enforced at the type level).\n */\nexport interface AgentTaxonomyConfig {\n /** Every proposal type this product can emit, e.g.\n * `['recommend', 'contact', 'escalate']`. The closed allow-list\n * the tool layer validates a `submit_proposal` call against. */\n proposalTypes: string[]\n /** The subset of `proposalTypes` that is regulated → cannot execute without a\n * certified-human approver. The approval executor reads this to decide which\n * proposals are fail-loud certified-gated. */\n regulatedTypes: string[]\n}\n\n/** A knowledge source the acquisition loop / researcher may draw on. */\nexport interface KnowledgeSourceSpec {\n /** Where the source lives — a URL, a `vault://` path, an integration ref, etc.\n * Opaque to agent-app; the consumer's loader resolves it. */\n uri: string\n /** Optional source classifier the researcher uses to pick a fetch strategy,\n * e.g. `'web'`, `'vault'`, `'regulation'`, `'integration'`. Free-form. */\n kind?: string\n}\n\n/**\n * The knowledge-acquisition loop config — the goal the researcher pursues and\n * the gate it must clear before the loop is considered satisfied. All optional;\n * a product with only static requirement specs omits it.\n */\nexport interface KnowledgeLoopConfig {\n /** The acquisition goal in natural language, e.g.\n * `\"ground every quoted premium against a real policy record\"`. */\n goal?: string\n /** The minimum aggregate confidence [0, 1] the loop must reach to pass its\n * gate. The runtime control loop blocks below this. Default decided by the\n * consumer's loop wiring when omitted. */\n minConfidence?: number\n /** How fresh acquired knowledge must be, e.g. `'static'`, `'7d'`, `'session'`.\n * Free-form; the consumer's loop interprets it. */\n freshness?: string\n}\n\n/**\n * The knowledge surface, as data. `sources` are what the researcher may read;\n * `requirements` are the declarative `./knowledge` specs that gate the control\n * loop (reused verbatim — the same `KnowledgeRequirementSpec` the gate scores);\n * `loop` configures the acquisition pass.\n */\nexport interface AgentKnowledgeConfig {\n /** Sources the acquisition loop / researcher may draw on. */\n sources: KnowledgeSourceSpec[]\n /** The declarative requirement specs that gate the loop. Reuses\n * `KnowledgeRequirementSpec` from `../knowledge` — `buildKnowledgeRequirements`\n * + `deriveSignals` consume these directly. */\n requirements: KnowledgeRequirementSpec[]\n /** Optional acquisition-loop config (goal + confidence gate + freshness). */\n loop?: KnowledgeLoopConfig\n}\n\n/**\n * Which integrations the product enables, as data. `enabled` lists\n * `@tangle-network/agent-integrations` catalog kinds the product connects\n * (e.g. `['salesforce', 'gmail', 'whatsapp']`); the integration hub resolves\n * each to a connector. Strings, not connector objects — agent-app bakes no\n * catalog value.\n */\nexport interface AgentIntegrationsConfig {\n /** Catalog kinds this product enables. */\n enabled: string[]\n}\n\n/** UI capability flags, as data. */\nexport interface AgentUiConfig {\n /** Whether the agent may emit generated UI (the `render_ui` / OpenUI side\n * channel). When false/omitted, the tool layer can omit/refuse the tool.\n * Default behavior decided by the consumer; omit to leave unset. */\n generatedUi?: boolean\n}\n\n/**\n * The declarative domain surface of a Tangle agent product.\n *\n * A coding agent fills THIS object and nothing else to define a product's\n * identity, proposal taxonomy, knowledge gate, integrations, UI capability, and\n * model. Every field is DATA consumed by an agent-app module through its typed\n * seam — no field is behavior. Author it through {@link defineAgentApp} for\n * autocomplete and a single import.\n */\nexport interface AgentAppConfig {\n /** Who the agent is — name, persona, system-prompt fragments, disclaimers. */\n identity: AgentIdentityConfig\n /** Proposal types + which are regulated/approval-gated. */\n taxonomy: AgentTaxonomyConfig\n /** Knowledge sources, requirement specs (the loop gate), and loop config. */\n knowledge: AgentKnowledgeConfig\n /** Enabled integration catalog kinds. */\n integrations: AgentIntegrationsConfig\n /** UI capability flags. Optional. */\n ui?: AgentUiConfig\n /** The resolved model config (`../runtime`'s `TangleModelConfig`). Omit to\n * resolve from env at boot via `resolveTangleModelConfig`. */\n model?: TangleModelConfig\n}\n\n/**\n * Identity helper: returns its argument unchanged, but anchors inference so a\n * coding agent authoring a config gets full autocomplete + type-checking from a\n * single import. The canonical way to declare a product config:\n *\n * ```ts\n * import { defineAgentApp } from '@tangle-network/agent-app/config'\n *\n * export const config = defineAgentApp({\n * identity: { name: 'Acme Ops', persona: '…' },\n * taxonomy: { proposalTypes: ['recommend', 'contact'], regulatedTypes: ['recommend'] },\n * knowledge: { sources: [], requirements: [] },\n * integrations: { enabled: ['salesforce'] },\n * })\n * ```\n */\nexport function defineAgentApp<const T extends AgentAppConfig>(config: T): T {\n return config\n}\n\n/**\n * Machine-readable JSON Schema (draft 2020-12) for {@link AgentAppConfig}.\n *\n * The schema FLOOR a non-TypeScript coding agent (or a config validator/UI) reads\n * to know the shape without parsing the `.d.ts`. Kept in lockstep with the\n * interfaces above by the config test, which asserts the documented fields are\n * present. `KnowledgeRequirementSpec`'s full sub-shape is intentionally left\n * `additionalProperties: true` here — its authoritative definition lives in\n * `../knowledge`; this floor pins only the fields the config contract owns.\n */\nexport const agentAppConfigJsonSchema = {\n $schema: 'https://json-schema.org/draft/2020-12/schema',\n $id: 'https://tangle.tools/schemas/agent-app-config.json',\n title: 'AgentAppConfig',\n description: 'The declarative domain surface of a Tangle agent product.',\n type: 'object',\n additionalProperties: false,\n required: ['identity', 'taxonomy', 'knowledge', 'integrations'],\n properties: {\n identity: {\n type: 'object',\n additionalProperties: false,\n required: ['name', 'persona'],\n properties: {\n name: { type: 'string', description: 'Product/agent name.' },\n persona: { type: 'string', description: 'One-paragraph persona — the system-prompt spine.' },\n systemPromptFragments: {\n type: 'array',\n items: { type: 'string' },\n description: 'Verbatim system-prompt fragments appended after the persona, in order.',\n },\n disclaimers: {\n type: 'object',\n additionalProperties: { type: 'string' },\n description: 'Named disclaimers keyed by stable id; values are literal text.',\n },\n },\n },\n taxonomy: {\n type: 'object',\n additionalProperties: false,\n required: ['proposalTypes', 'regulatedTypes'],\n properties: {\n proposalTypes: {\n type: 'array',\n items: { type: 'string' },\n description: 'Closed allow-list of proposal types the product can emit.',\n },\n regulatedTypes: {\n type: 'array',\n items: { type: 'string' },\n description: 'Subset of proposalTypes that is approval-gated (certified-human required).',\n },\n },\n },\n knowledge: {\n type: 'object',\n additionalProperties: false,\n required: ['sources', 'requirements'],\n properties: {\n sources: {\n type: 'array',\n description: 'Sources the acquisition loop may draw on.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['uri'],\n properties: {\n uri: { type: 'string', description: 'Where the source lives (opaque to agent-app).' },\n kind: { type: 'string', description: 'Optional source classifier (web/vault/regulation/…).' },\n },\n },\n },\n requirements: {\n type: 'array',\n description: \"Declarative KnowledgeRequirementSpec[] (../knowledge) that gate the loop.\",\n items: { type: 'object', additionalProperties: true },\n },\n loop: {\n type: 'object',\n additionalProperties: false,\n description: 'Acquisition-loop config.',\n properties: {\n goal: { type: 'string', description: 'Acquisition goal in natural language.' },\n minConfidence: {\n type: 'number',\n minimum: 0,\n maximum: 1,\n description: 'Minimum aggregate confidence the loop must reach.',\n },\n freshness: { type: 'string', description: 'How fresh acquired knowledge must be.' },\n },\n },\n },\n },\n integrations: {\n type: 'object',\n additionalProperties: false,\n required: ['enabled'],\n properties: {\n enabled: {\n type: 'array',\n items: { type: 'string' },\n description: 'Enabled agent-integrations catalog kinds.',\n },\n },\n },\n ui: {\n type: 'object',\n additionalProperties: false,\n description: 'UI capability flags.',\n properties: {\n generatedUi: { type: 'boolean', description: 'Whether the agent may emit generated UI.' },\n },\n },\n model: {\n type: 'object',\n additionalProperties: false,\n description: \"Resolved TangleModelConfig (../runtime). Omit to resolve from env.\",\n required: ['provider', 'model', 'apiKey', 'baseUrl'],\n properties: {\n provider: { type: 'string', enum: ['openai-compat', 'anthropic'] },\n model: { type: 'string' },\n apiKey: { type: 'string' },\n baseUrl: { type: 'string' },\n },\n },\n },\n} as const\n"],"mappings":"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","names":[]}
@@ -1,124 +0,0 @@
1
- import {
2
- constantTimeEqual,
3
- hmacSha256Base64Url
4
- } from "./chunk-S5SRJJQG.js";
5
-
6
- // src/object-store/index.ts
7
- function createR2ObjectStore({ bucket }) {
8
- return {
9
- async put(key, body, opts) {
10
- const options = opts?.contentType ? { httpMetadata: { contentType: opts.contentType } } : void 0;
11
- await bucket.put(key, body, options);
12
- },
13
- async get(key) {
14
- const obj = await bucket.get(key);
15
- if (!obj) return null;
16
- return {
17
- stream: () => obj.body,
18
- size: obj.size,
19
- contentType: obj.httpMetadata?.contentType
20
- };
21
- },
22
- async head(key) {
23
- const obj = await bucket.head(key);
24
- if (!obj) return null;
25
- return { size: obj.size, contentType: obj.httpMetadata?.contentType };
26
- },
27
- async delete(key) {
28
- await bucket.delete(key);
29
- }
30
- };
31
- }
32
- function assertSafeKeySegment(s) {
33
- if (s.length === 0) throw new Error("object-store: empty key segment");
34
- if (s.includes("..")) throw new Error(`object-store: unsafe key segment (contains "..") \u2014 ${s}`);
35
- if (s.includes("/")) throw new Error(`object-store: unsafe key segment (contains "/") \u2014 ${s}`);
36
- if (s.includes("\\")) throw new Error(`object-store: unsafe key segment (backslash) \u2014 ${s}`);
37
- return s;
38
- }
39
- function sanitizeFilename(filename) {
40
- const leaf = filename.split(/[/\\]/).pop() ?? "";
41
- const cleaned = leaf.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^\.+/, "");
42
- return cleaned.length > 0 ? cleaned : "file";
43
- }
44
- function objectKey({ operatorId, customerId, uploadId, filename }) {
45
- const operator = assertSafeKeySegment(operatorId);
46
- const customer = customerId == null ? "_unattributed" : assertSafeKeySegment(customerId);
47
- const upload = assertSafeKeySegment(uploadId);
48
- return `${operator}/${customer}/${upload}-${sanitizeFilename(filename)}`;
49
- }
50
- function canonicalizeObjectKey(raw) {
51
- let decoded;
52
- try {
53
- decoded = decodeURIComponent(raw);
54
- } catch {
55
- throw new Error("object-store: malformed key encoding");
56
- }
57
- for (const segment of decoded.split("/")) assertSafeKeySegment(segment);
58
- return decoded;
59
- }
60
- function signingMessage(canonicalKey, exp) {
61
- return JSON.stringify({ v: 1, exp, key: canonicalKey });
62
- }
63
- async function signObjectUrl({ key, exp, secret }) {
64
- if (!secret) throw new Error("object-store: signObjectUrl requires a non-empty secret (fail-closed)");
65
- const canonical = canonicalizeObjectKey(key);
66
- const sig = await hmacSha256Base64Url(signingMessage(canonical, exp), secret);
67
- const params = new URLSearchParams({ key: canonical, exp: String(exp), sig });
68
- return `?${params.toString()}`;
69
- }
70
- async function verifyObjectUrl(request, { secret }) {
71
- if (!secret) return { ok: false };
72
- const url = new URL(request.url);
73
- const rawKey = url.searchParams.get("key");
74
- const expRaw = url.searchParams.get("exp");
75
- const sig = url.searchParams.get("sig");
76
- if (!rawKey || !expRaw || !sig) return { ok: false };
77
- let key;
78
- try {
79
- key = canonicalizeObjectKey(rawKey);
80
- } catch {
81
- return { ok: false };
82
- }
83
- const exp = Number(expRaw);
84
- if (!Number.isFinite(exp)) return { ok: false };
85
- const expected = await hmacSha256Base64Url(signingMessage(key, exp), secret);
86
- if (!constantTimeEqual(expected, sig)) return { ok: false };
87
- if (Date.now() > exp) return { ok: false };
88
- return { ok: true, key };
89
- }
90
- function createProxiedArtifactRoute({
91
- store,
92
- secret
93
- }) {
94
- return async (request) => {
95
- const rawKey = new URL(request.url).searchParams.get("key");
96
- if (!rawKey) return new Response("Missing key", { status: 400 });
97
- try {
98
- canonicalizeObjectKey(rawKey);
99
- } catch {
100
- return new Response("Malformed key", { status: 400 });
101
- }
102
- const verified = await verifyObjectUrl(request, { secret });
103
- if (!verified.ok) return new Response("Forbidden", { status: 403 });
104
- const obj = await store.get(verified.key);
105
- if (!obj) return new Response("Not found", { status: 404 });
106
- const headers = {
107
- "Content-Type": "application/octet-stream",
108
- "Content-Disposition": "attachment",
109
- "X-Content-Type-Options": "nosniff"
110
- };
111
- if (Number.isFinite(obj.size) && obj.size >= 0) headers["Content-Length"] = String(obj.size);
112
- return new Response(obj.stream(), { status: 200, headers });
113
- };
114
- }
115
-
116
- export {
117
- createR2ObjectStore,
118
- assertSafeKeySegment,
119
- objectKey,
120
- signObjectUrl,
121
- verifyObjectUrl,
122
- createProxiedArtifactRoute
123
- };
124
- //# sourceMappingURL=chunk-QYOD3K56.js.map