@tangle-network/agent-app 0.10.0 → 0.11.0
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.
- package/README.md +10 -0
- package/dist/agent-activity-C8ZG0F0M.d.ts +43 -0
- package/dist/chunk-5RT6KY4G.js +190 -0
- package/dist/chunk-5RT6KY4G.js.map +1 -0
- package/dist/chunk-AFDROJ64.js +218 -0
- package/dist/chunk-AFDROJ64.js.map +1 -0
- package/dist/{chunk-UIWB2F6N.js → chunk-UDXMR3AD.js} +80 -24
- package/dist/chunk-UDXMR3AD.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +31 -1
- package/dist/missions/index.d.ts +38 -5
- package/dist/missions/index.js +3 -1
- package/dist/trace/index.d.ts +137 -1
- package/dist/trace/index.js +24 -138
- package/dist/trace/index.js.map +1 -1
- package/dist/web-react/index.d.ts +79 -1
- package/dist/web-react/index.js +417 -116
- package/dist/web-react/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-UIWB2F6N.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/missions/service.ts","../src/missions/events.ts","../src/missions/engine.ts","../src/missions/plan-parse.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\nexport type MissionStepStatus = 'pending' | 'running' | 'waiting_approval' | 'done' | 'failed'\n\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\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 insert(record: MissionRecord): 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\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\nexport interface SetStepStatusPatch {\n sublabel?: string\n resultRef?: string\n error?: string\n}\n\nexport interface CompleteMissionInput {\n ok: boolean\n summary?: string\n}\n\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\nexport interface MissionServiceOptions {\n store: MissionStorePort\n /** Injectable clock (epoch ms). Default `Date.now`. */\n now?: () => number\n /** Row-id generator when `CreateMissionInput.id` is omitted.\n * Default `crypto.randomUUID`. */\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\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 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\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 // The plan write is guarded on the status, plan, AND metadata we read: a\n // concurrent stop request (a metadata write) or any plan mutation flips a\n // guard and the loser reports the race instead of clobbering it.\n const updated = await store.update(\n id,\n { status: mission.status, plan: mission.plan, metadata: mission.metadata },\n { plan: nextPlan },\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 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\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\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\nexport type MissionStreamStepStatus =\n | 'pending'\n | 'running'\n | 'done'\n | 'failed'\n | 'waiting_approval'\n\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 | {\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}\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 return {\n missionId,\n status: 'running',\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: prev ? maxMissionStatus(base.status, event.status ?? 'running') : 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 sublabel: event.sublabel,\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 }\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 })\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}\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\nexport interface SandboxDispatchInput {\n mission: MissionRecord\n step: MissionStep\n stepIndex: number\n}\n\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\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\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\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\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\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\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\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.\n {\n const deltaUsd = dispatched.cost?.deltaUsd ?? estimateStepCostUsd(step)\n if (deltaUsd > 0 || dispatched.cost?.ledgerDelta) {\n const cost = await recordCost(missionId, deltaUsd, {\n costUsd: deltaUsd,\n llmCalls: 1,\n ...(dispatched.cost?.ledgerDelta ?? {}),\n })\n if (!cost.succeeded) return rejectStep(cost)\n }\n }\n\n const done = await service.setStepStatus(missionId, stepId, 'done', {\n resultRef: dispatched.resultRef,\n ...(dispatched.sublabel === undefined ? {} : { sublabel: dispatched.sublabel }),\n })\n if (!done.succeeded) return rejectStep(done)\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 if (mission.spentUsd + estimatedCostUsd <= capUsd) return { kind: 'continue' }\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\nexport interface ParsedMissionStep {\n id: string\n kind: string\n intent: string\n}\n\nexport interface ParsedMission {\n title: string\n steps: ParsedMissionStep[]\n}\n\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"],"mappings":"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","names":[]}
|