zidane 5.10.13 → 5.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 +26 -0
- package/dist/{agent-BHkvYIH9.d.ts → agent-B7ilLoh8.d.ts} +63 -4
- package/dist/agent-B7ilLoh8.d.ts.map +1 -0
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +6 -6
- package/dist/chat.js +2 -2
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +53 -14
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +168 -0
- package/dist/contexts/e2b.d.ts.map +1 -0
- package/dist/contexts/e2b.js +261 -0
- package/dist/contexts/e2b.js.map +1 -0
- package/dist/{contexts-BJVgG0LY.js → contexts-DglWSzmR.js} +59 -9
- package/dist/contexts-DglWSzmR.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +4 -4
- package/dist/{headless-CPaunZsU.js → headless-CYHU4ZB-.js} +6 -6
- package/dist/{headless-CPaunZsU.js.map → headless-CYHU4ZB-.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-C4aT2kO_.d.ts → index-C-zEbeFI.d.ts} +13 -101
- package/dist/index-C-zEbeFI.d.ts.map +1 -0
- package/dist/{index-C_t8tW_X.d.ts → index-CrMb8jCE.d.ts} +2 -2
- package/dist/{index-C_t8tW_X.d.ts.map → index-CrMb8jCE.d.ts.map} +1 -1
- package/dist/{index-BIo67xLV.d.ts → index-CsRdmSh6.d.ts} +10 -3
- package/dist/index-CsRdmSh6.d.ts.map +1 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/{interpolate-Dy7Lunvg.js → interpolate-CTfr0GdR.js} +19 -1
- package/dist/{interpolate-Dy7Lunvg.js.map → interpolate-CTfr0GdR.js.map} +1 -1
- package/dist/logger-Bd4Wn9Hc.d.ts +102 -0
- package/dist/logger-Bd4Wn9Hc.d.ts.map +1 -0
- package/dist/logger-Ktm-lj1s.js +300 -0
- package/dist/logger-Ktm-lj1s.js.map +1 -0
- package/dist/{login-0jP1pnSJ.js → login-DVLJzf-S.js} +4 -301
- package/dist/login-DVLJzf-S.js.map +1 -0
- package/dist/{mcp-tevNihk_.js → mcp-DPneQbw2.js} +21 -8
- package/dist/mcp-DPneQbw2.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-C_1AmSpk.js → messages-DJ7xdoUg.js} +5 -1
- package/dist/messages-DJ7xdoUg.js.map +1 -0
- package/dist/output/stream-json.d.ts +2 -2
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +2 -2
- package/dist/output/terminal.js +1 -0
- package/dist/output/terminal.js.map +1 -1
- package/dist/{presets-Cm2BPJaU.js → presets-Q0yUDRA6.js} +2 -2
- package/dist/{presets-Cm2BPJaU.js.map → presets-Q0yUDRA6.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-BGBB18zz.js → providers-BPdi7cUU.js} +73 -11
- package/dist/providers-BPdi7cUU.js.map +1 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/restate.d.ts +2 -2
- package/dist/restate.js +4 -1
- package/dist/restate.js.map +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +36 -4
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-CtAWwwkn.js → session-D4GckETs.js} +69 -21
- package/dist/session-D4GckETs.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +3 -3
- package/dist/shell-quote-BmnhZmdM.js +33 -0
- package/dist/shell-quote-BmnhZmdM.js.map +1 -0
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +1 -1
- package/dist/skills.js.map +1 -1
- package/dist/{tool-formatters-D_fX6FGl.d.ts → tool-formatters-DXO8rGut.d.ts} +2 -2
- package/dist/{tool-formatters-D_fX6FGl.d.ts.map → tool-formatters-DXO8rGut.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/{tools-NxnEmzYg.js → tools-DqNkGwfd.js} +80 -73
- package/dist/tools-DqNkGwfd.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-B_c7gWot.js → transcript-anchors-C3tVPR5n.js} +8 -8
- package/dist/{transcript-anchors-B_c7gWot.js.map → transcript-anchors-C3tVPR5n.js.map} +1 -1
- package/dist/{transcript-anchors-DA6XawEU.d.ts → transcript-anchors-p-ZsBSEF.d.ts} +10 -4
- package/dist/transcript-anchors-p-ZsBSEF.d.ts.map +1 -0
- package/dist/tui.d.ts +3 -3
- package/dist/tui.js +151 -41
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CCl7rpbT.d.ts → turn-operations-DD1D5VFx.d.ts} +3 -3
- package/dist/{turn-operations-CCl7rpbT.d.ts.map → turn-operations-DD1D5VFx.d.ts.map} +1 -1
- package/dist/{types-BibzMDjX.d.ts → types-B39tBba1.d.ts} +69 -2
- package/dist/types-B39tBba1.d.ts.map +1 -0
- package/dist/types.d.ts +5 -5
- package/docs/EXECUTION_CONTEXT.md +257 -0
- package/docs/RUN_IN_BACKGROUND.md +8 -0
- package/package.json +57 -24
- package/dist/agent-BHkvYIH9.d.ts.map +0 -1
- package/dist/contexts-BJVgG0LY.js.map +0 -1
- package/dist/index-BIo67xLV.d.ts.map +0 -1
- package/dist/index-C4aT2kO_.d.ts.map +0 -1
- package/dist/login-0jP1pnSJ.js.map +0 -1
- package/dist/mcp-tevNihk_.js.map +0 -1
- package/dist/messages-C_1AmSpk.js.map +0 -1
- package/dist/providers-BGBB18zz.js.map +0 -1
- package/dist/session-CtAWwwkn.js.map +0 -1
- package/dist/tools-NxnEmzYg.js.map +0 -1
- package/dist/transcript-anchors-DA6XawEU.d.ts.map +0 -1
- package/dist/types-BibzMDjX.d.ts.map +0 -1
package/dist/restate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restate.js","names":[],"sources":["../src/restate/awakeable.ts","../src/restate/awakeable-tool.ts","../src/restate/errors.ts","../src/restate/behavior.ts","../src/restate/clock.ts","../src/restate/parallel.ts","../src/restate/provider.ts","../src/restate/replay.ts","../src/restate/session.ts","../src/restate/tool.ts"],"sourcesContent":["/**\n * Awakeable helpers — durable waits with deadlines.\n *\n * `awakeableWithTimeout` packages the canonical \"park until a resumer\n * fires, but not forever\" shape: an awakeable raced against a durable\n * `ctx.sleep(timeoutMs)` timer. The race itself survives process death —\n * on replay both branches resume from the journal and the same side\n * wins. This is the three-line building block for\n * `ExecutionContext.waitBackground` implementations on durable hosts\n * (runner fires task-exit → `resolveAwakeable(id, info)`; the timeout\n * branch maps to the seam's `null` return).\n */\n\nimport type { RestateAwakeable, RestateContextLike } from './types'\n\n/**\n * Structural subset of the SDK's `RestatePromise` static surface used by\n * {@link awakeableWithTimeout} — mirrors `RestatePromiseAllLike` from\n * `./parallel`. Pass `RestatePromise` from `@restatedev/restate-sdk` so\n * the race goes through the journaled combinator; native `Promise.race`\n * over two durable promises leaves the winner to the runtime's replay\n * ordering instead of an explicit journal entry.\n */\nexport interface RestatePromiseRaceLike<TPromise extends Promise<unknown> = Promise<unknown>> {\n race: (values: readonly TPromise[]) => Promise<unknown>\n}\n\nexport interface AwakeableWithTimeoutOptions<TPromise extends Promise<unknown> = Promise<unknown>> {\n /**\n * Durable deadline in milliseconds. Unset / non-positive = no timeout\n * (the returned awakeable is the plain `ctx.awakeable()` passthrough).\n */\n timeoutMs?: number\n /**\n * Durable race combinator — pass `RestatePromise` from the SDK. When\n * omitted, native `Promise.race` is used: correct at runtime and\n * replay-safe for THIS pattern (both branches are journaled\n * primitives), but the SDK combinator is the recommended belt-and-\n * braces choice for deterministic replay.\n */\n promises?: RestatePromiseRaceLike<TPromise>\n}\n\n/**\n * Mint an awakeable that resolves with the resumer's payload, or `null`\n * when `timeoutMs` elapses first. The id is journal-stable (send it\n * out-of-band as usual); the timeout is a durable `ctx.sleep`, so a\n * parked invocation suspends instead of holding a worker.\n *\n * Timeout surfaces as `null` — a resumer that resolves `undefined` is\n * indistinguishable from a timeout, so resolve a concrete payload.\n *\n * ```ts\n * // waitBackground for a durable host, in three lines:\n * const { id, promise } = awakeableWithTimeout<TaskExitInfo>(ctx, { timeoutMs, promises: RestatePromise })\n * await registerExitResumer(taskId, id) // runner → ctx.resolveAwakeable(id, info)\n * return await promise // TaskExitInfo, or null on timeout\n * ```\n */\nexport function awakeableWithTimeout<T, TPromise extends Promise<unknown> = Promise<unknown>>(\n ctx: RestateContextLike,\n options: AwakeableWithTimeoutOptions<TPromise> = {},\n): RestateAwakeable<T | null> {\n const awakeable = ctx.awakeable<T>()\n const timeoutMs = options.timeoutMs\n if (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs) || timeoutMs <= 0)\n return awakeable\n\n const timer = ctx.sleep(timeoutMs)\n const raced = options.promises\n ? options.promises.race([awakeable.promise, timer] as readonly unknown[] as readonly TPromise[])\n : Promise.race<unknown>([awakeable.promise, timer])\n return {\n id: awakeable.id,\n // The sleep branch resolves `undefined`; normalize to the seam's\n // `null` so callers get one documented timeout sentinel.\n promise: raced.then(value => (value === undefined ? null : value as T)),\n }\n}\n","/**\n * Awakeable-backed tool — durable human/external-in-the-loop waits.\n *\n * Builds a `ToolDef` whose `execute` parks on a Restate awakeable: the\n * tool mints a journal-stable awakeable id, broadcasts it out-of-band\n * (webhook, queue, collab stream — the `notify` callback), and suspends\n * until a resumer calls `ctx.resolveAwakeable(id, value)` on the SDK\n * side. The classic shape for `ask_user` / `request_review` /\n * `wait_for_approval` tools inside durable handlers.\n *\n * Replay contract\n * ---------------\n * - **Broadcast-once-across-replays**: `notify` runs inside `ctx.run`,\n * so a crash + replay returns the journaled marker without re-sending\n * the notification. The user is pinged exactly once per tool call, no\n * matter how many times the worker restarts.\n * - **Marker invariant**: the journaled marker records the awakeable id\n * it was broadcast for. On replay the freshly-minted awakeable id must\n * match (the SDK derives awakeable ids deterministically from the\n * journal position); a mismatch means the journal and the code path\n * drifted — the tool throws instead of parking on an id nobody will\n * ever resolve.\n */\n\nimport type { ToolContext, ToolDef } from '../tools/types'\nimport type { ToolResultContent } from '../types'\nimport type { RestateContextLike, RestateRunOptions } from './types'\n\n/** Journaled broadcast marker — see \"Marker invariant\" in the module doc. */\ninterface AwakeableNotifyMarker {\n notified: true\n awakeableId: string\n}\n\nexport interface RestateAwakeableToolOptions<T = unknown> {\n /** Canonical tool name (what the model calls). */\n name: string\n /** Model-facing tool description. */\n description: string\n /** JSON schema for the tool input. */\n inputSchema: Record<string, unknown>\n /** The Restate context the awakeable + journal entries bind to. */\n ctx: RestateContextLike\n /**\n * Send the awakeable id (plus the call's input) out-of-band so a\n * resumer can later call `ctx.resolveAwakeable(awakeableId, value)`.\n * Runs inside `ctx.run` — fires exactly once across replays.\n */\n notify: (info: {\n awakeableId: string\n input: Record<string, unknown>\n toolCtx: ToolContext\n }) => void | Promise<void>\n /**\n * Map the resolved awakeable payload to the tool result. Default:\n * strings pass through, everything else is JSON-stringified.\n */\n toResult?: (value: T, input: Record<string, unknown>) => string | ToolResultContent[]\n /**\n * `ctx.run` options for the notify entry. Defaults to\n * `{ maxRetryAttempts: 1 }`, matching `restateTool`'s rationale.\n */\n runOptions?: RestateRunOptions\n /**\n * Concurrency-safety flag passed through to the `ToolDef`. Defaults to\n * `false` — a parked wait holding a scheduler slot should barrier.\n */\n isConcurrencySafe?: boolean\n}\n\n/**\n * Build a `ToolDef` that parks on a Restate awakeable until an external\n * resumer resolves it. See the module doc for the replay contract.\n *\n * Do NOT additionally wrap the returned tool in `restateTool` — the\n * journal discipline is already internal, and an outer `ctx.run` would\n * illegally nest the suspension point inside a journaled closure.\n */\nexport function restateAwakeableTool<T = unknown>(\n options: RestateAwakeableToolOptions<T>,\n): ToolDef {\n const { name, description, inputSchema, ctx, notify, toResult } = options\n const runOpts: RestateRunOptions = options.runOptions ?? { maxRetryAttempts: 1 }\n let seq = 0\n\n const execute = async (input: Record<string, unknown>, toolCtx: ToolContext): Promise<string | ToolResultContent[]> => {\n seq += 1\n // Mint BEFORE the journaled broadcast: the awakeable's journal\n // position precedes the notify entry, so on replay both come back\n // in the same order with the same id.\n const awakeable = ctx.awakeable<T>()\n\n const marker = await ctx.run<AwakeableNotifyMarker>(\n `awakeable-${name}-${seq}-notify`,\n async () => {\n await notify({ awakeableId: awakeable.id, input, toolCtx })\n return { notified: true, awakeableId: awakeable.id }\n },\n runOpts,\n )\n\n // Marker invariant — a drifted journal would otherwise park this\n // call on an id that was never broadcast (nobody will resolve it).\n if (marker.awakeableId !== awakeable.id) {\n throw new Error(\n `restateAwakeableTool(${name}): journal drift — broadcast marker carries awakeable \"${marker.awakeableId}\" but this replay minted \"${awakeable.id}\". The journal and the code path no longer line up; refusing to park on an unreachable awakeable.`,\n )\n }\n\n const value = await awakeable.promise\n if (toResult)\n return toResult(value, input)\n return typeof value === 'string' ? value : JSON.stringify(value)\n }\n\n return {\n spec: { name, description, inputSchema },\n ...(options.isConcurrencySafe !== undefined ? { isConcurrencySafe: options.isConcurrencySafe } : {}),\n execute,\n // The awakeable park IS the durable body — the loop's durable path\n // can use it directly without an extra wrapper.\n durableExecute: execute,\n }\n}\n","/**\n * Restate control-flow errors must escape the agent loop. If they are turned\n * into model-visible tool results, the runtime can retry/suspend incorrectly.\n *\n * Kept structural so `zidane/restate` does not need to import the SDK at\n * runtime just to recognize the common error shapes.\n */\nexport function shouldRethrowRestateControlError(error: unknown): boolean {\n if (!(error instanceof Error))\n return false\n\n const codeValue = Reflect.get(error, 'code')\n const code = typeof codeValue === 'number'\n ? codeValue\n : undefined\n if (code === 599)\n return true\n\n const name = error.name\n if (\n name === 'TerminalError'\n || name === 'TimeoutError'\n || name === 'CancelledError'\n || name === 'SuspendedError'\n ) {\n return true\n }\n\n const ctorName = error.constructor?.name\n const serializedCtorNameValue = Reflect.get(error, 'constructorName')\n const serializedCtorName = typeof serializedCtorNameValue === 'string'\n ? serializedCtorNameValue\n : undefined\n if (\n ctorName === 'TerminalError'\n || ctorName === 'TimeoutError'\n || ctorName === 'CancelledError'\n || ctorName === 'SuspendedError'\n || serializedCtorName === 'TerminalError'\n || serializedCtorName === 'TimeoutError'\n || serializedCtorName === 'CancelledError'\n || serializedCtorName === 'SuspendedError'\n ) {\n return true\n }\n\n return typeof name === 'string' && name.toLowerCase().includes('suspend')\n}\n","import type { AgentBehavior } from '../types'\nimport { shouldRethrowRestateControlError } from './errors'\n\n/**\n * Conservative behavior defaults for agents running inside Restate.\n *\n * The helper is opt-in and returns a plain AgentBehavior object, so callers can\n * spread or override it without changing non-Restate harness defaults.\n *\n * `disableBackgroundTasks: 'non-durable'` is capability-aware: background\n * tasks stay enabled for execution contexts whose\n * `capabilities.detachedTasks` is `'durable'` (tasks live on a remote\n * runner and survive worker crashes) and are disabled for everything\n * else (host-local processes don't survive process death). Hosts on\n * plain `ProcessContext` see the same effective behavior as the old\n * blanket `true`.\n */\nexport function restateBehavior(overrides: AgentBehavior = {}): AgentBehavior {\n return {\n maxConcurrentTools: 1,\n disableBackgroundTasks: 'non-durable',\n cache: true,\n shouldRethrowToolError: shouldRethrowRestateControlError,\n ...overrides,\n }\n}\n","/**\n * Restate-backed {@link AgentClock} — journals `now()` + `randomUUID()` so\n * `SessionTurn.createdAt`, `runId`, and fallback / synthetic turn ids stay\n * byte-identical across replays of the same invocation.\n *\n * Pass to `agent.run({ clock: restateClock(ctx) })` or bake it into\n * `createAgent({ clock: restateClock(ctx) })` for the duration of a\n * handler invocation. The clock holds a reference to `ctx`; do not reuse\n * a clock instance across invocations.\n */\n\nimport type { AgentClock } from '../types'\nimport type { RestateContextLike } from './types'\n\n/**\n * Wrap a Restate context's `date.now()` + `rand.uuidv4()` into the\n * {@link AgentClock} shape Zidane consumes for journaled metadata.\n *\n * The returned clock is a thin live view onto `ctx` — each call hits\n * the SDK afresh. No caching, no closure-captured values. Restate's\n * journal handles determinism on its end.\n */\nexport function restateClock(ctx: RestateContextLike): AgentClock {\n return {\n now: () => ctx.date.now(),\n randomUUID: () => ctx.rand.uuidv4(),\n }\n}\n","import type { ToolResult } from '../providers'\nimport type { ToolBatchExecutionContext, ToolBatchExecutor } from '../types'\n\nexport interface RestatePromiseAllLike<TPromise extends Promise<unknown> = RestatePromiseLike<unknown>> {\n all: (values: readonly TPromise[]) => Promise<readonly unknown[]>\n}\n\nexport type RestatePromiseLike<T> = Promise<T> & {\n map: <U>(mapper: (value?: T, failure?: unknown) => U) => Promise<U>\n}\n\nexport interface RestateToolBatchExecutorOptions<TPromise extends Promise<unknown> = RestatePromiseLike<unknown>> {\n /**\n * Wait for a safe fleet through Restate's durable combinator. Pass\n * `RestatePromise` from `@restatedev/restate-sdk` in real deployments.\n */\n promises: RestatePromiseAllLike<TPromise>\n}\n\n/**\n * Tool batch scheduler for Restate-backed agents.\n *\n * It preserves unsafe-tool barriers and emits results in model submission\n * order. Safe fleets are started in submission order, capped by\n * `maxConcurrentTools`, and joined via the supplied durable `all()`.\n */\nexport function restateToolBatchExecutor<TPromise extends Promise<unknown> = RestatePromiseLike<unknown>>(\n options: RestateToolBatchExecutorOptions<TPromise>,\n): ToolBatchExecutor {\n return async (ctx: ToolBatchExecutionContext): Promise<ToolResult[]> => {\n const results: ToolResult[] = []\n const { toolCalls } = ctx\n\n const fillRemaining = (from: number, factory: (index: number) => ToolResult): ToolResult[] => {\n for (let i = from; i < toolCalls.length; i++)\n results[i] = factory(i)\n return results\n }\n\n for (let i = 0; i < toolCalls.length;) {\n if (ctx.signal.aborted)\n return fillRemaining(i, ctx.interruptedResult)\n if (ctx.steeringQueue.length > 0)\n return fillRemaining(i, ctx.skippedResult)\n\n if (!ctx.isConcurrencySafe(i)) {\n results[i] = await ctx.execute(i)\n i += 1\n continue\n }\n\n const start = i\n const fleet: number[] = []\n while (\n i < toolCalls.length\n && ctx.isConcurrencySafe(i)\n && fleet.length < ctx.maxConcurrentTools\n ) {\n fleet.push(i)\n i += 1\n }\n\n const fleetResults = fleet.every(index => ctx.canExecuteDurably(index))\n ? await ctx.executeDurable(\n fleet,\n async <T>(values: readonly Promise<T>[]): Promise<readonly T[]> => {\n const results = await options.promises.all(values as readonly TPromise[])\n return results as readonly T[]\n },\n )\n : await executeSequential(ctx, fleet)\n for (let offset = 0; offset < fleetResults.length; offset++)\n results[start + offset] = fleetResults[offset]\n }\n\n return results\n }\n}\n\nasync function executeSequential(\n ctx: ToolBatchExecutionContext,\n indices: readonly number[],\n): Promise<ToolResult[]> {\n const results: ToolResult[] = []\n for (const index of indices) {\n if (ctx.signal.aborted) {\n results.push(ctx.interruptedResult(index))\n continue\n }\n if (ctx.steeringQueue.length > 0) {\n results.push(ctx.skippedResult(index))\n continue\n }\n results.push(await ctx.execute(index))\n }\n return results\n}\n","/**\n * Restate-backed Provider wrapper.\n *\n * Wraps `Provider.stream` so every assistant turn (the LLM call as a\n * whole) is journaled. The model is invoked once per turn — the live\n * stream still fires `onText` / `onThinking` callbacks for UX during the\n * original execution. On replay, `ctx.run` returns the journaled\n * `TurnResult` immediately and the inner provider is NEVER re-invoked →\n * no duplicate billed tokens, no duplicate stream chunks.\n *\n * Replay caveat: stream callbacks don't re-fire on replay. Consumers\n * that paint live UI from `stream:text` should reconstruct transcripts\n * from `session.turns` via `eventsFromTurns` (zidane/chat) instead of\n * relying on hook re-emission. The chat layer already does this.\n *\n * Journal-entry naming: a monotonic per-wrapper counter (`llm-call-1`,\n * `llm-call-2`, …). One wrapper instance per Restate handler invocation\n * resets the counter; Restate's journal is invocation-scoped so names\n * don't need to be globally unique. The counter ticks the same way on\n * replay because the wrapper is constructed fresh each attempt.\n */\n\nimport type { Provider, StreamCallbacks, StreamOptions, TurnResult } from '../providers'\nimport type { ReplayTracker } from './replay'\nimport type { RestateContextLike, RestateRunOptions } from './types'\n\nexport interface RestateProviderOptions {\n /**\n * `ctx.run` options forwarded on every wrapped stream call.\n *\n * Default depends on {@link streamCallbacks}:\n *\n * - `'suppress'` → `{ maxRetryAttempts: 3 }` — provider transients\n * (rate limits, 5xx, connection resets) get retried inside the\n * journal entry; nothing streams to the host, so a retry is\n * invisible.\n * - `'live'` (the default) → `{ maxRetryAttempts: 1 }`. A live stream\n * that fails AFTER emitting deltas would be re-run inside the same\n * journal entry, re-firing `onText` into the loop's accumulating\n * text buffer and producing duplicated partial1+full2 output. The\n * loop's own retry (which only re-issues on a clean slate) is the\n * correct retry layer in live mode.\n *\n * Once the cap is reached the failure becomes terminal and surfaces\n * through the loop's normal `AgentProviderError` path. Set this\n * explicitly to override either default — but with live callbacks,\n * `maxRetryAttempts > 1` re-introduces the duplicate-delta hazard.\n */\n runOptions?: RestateRunOptions\n /**\n * Override the journal-entry name. Receives the 1-indexed sequence\n * number for this wrapper instance and the `StreamOptions` of the\n * call. Default: `llm-call-<n>`. Override to inject extra structure\n * (e.g. `${agentName}/llm-call-${n}`) when multiple agents share a\n * service.\n */\n entryName?: (seq: number, opts: StreamOptions) => string\n /**\n * Whether live stream callbacks should fire during the original execution.\n * Use `'suppress'` for strict durable mode: the model call still returns a\n * full TurnResult, and UIs reconstruct from persisted session turns.\n *\n * Default: `'live'`.\n */\n streamCallbacks?: 'live' | 'suppress'\n /**\n * Per-invocation replay tracker (see `createReplayTracker`). When set,\n * the wrapper marks it live from inside the journaled closure — i.e.\n * the first time the inner provider is ACTUALLY invoked rather than\n * replayed from the journal. Share one tracker across the provider\n * and tool wrappers of the same handler invocation.\n */\n replayTracker?: ReplayTracker\n}\n\n/**\n * Wrap a Zidane `Provider` so its `stream()` method journals each turn's\n * response inside `ctx.run`. All other Provider methods (`formatTools`,\n * `userMessage`, `toolResultsMessage`, `classifyError`, …) pass through\n * unchanged — they're pure functions over already-deterministic inputs.\n */\nexport function restateProvider(\n inner: Provider,\n ctx: RestateContextLike,\n options: RestateProviderOptions = {},\n): Provider {\n // Live callbacks must not retry inside the journal entry — a stream that\n // fails after emitting deltas would re-fire `onText` on the retry and\n // duplicate text in the loop's accumulator. See the `runOptions` doc.\n const runOpts: RestateRunOptions = options.runOptions\n ?? (options.streamCallbacks === 'suppress' ? { maxRetryAttempts: 3 } : { maxRetryAttempts: 1 })\n const nameFor = options.entryName ?? ((seq: number) => `llm-call-${seq}`)\n let seq = 0\n\n return {\n ...inner,\n stream(streamOpts: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n seq += 1\n const durableCallbacks = options.streamCallbacks === 'suppress'\n ? suppressStreamingCallbacks(callbacks)\n : callbacks\n return ctx.run<TurnResult>(\n nameFor(seq, streamOpts),\n () => {\n // Reached only when the journal cursor is past this entry —\n // a replayed call returns the journaled TurnResult without\n // executing this closure.\n options.replayTracker?.markLive()\n return inner.stream(streamOpts, durableCallbacks)\n },\n runOpts,\n )\n },\n }\n}\n\nfunction suppressStreamingCallbacks(callbacks: StreamCallbacks): StreamCallbacks {\n return {\n ...callbacks,\n onText: () => {},\n onThinking: undefined,\n onServerToolUse: undefined,\n onServerToolResult: undefined,\n }\n}\n","/**\n * Replay-state tracking + replay-aware abort for Restate-backed agents.\n *\n * ## The problem\n *\n * `agent.abort()` during a journal replay is undefined behavior: the\n * loop throws `AgentAbortedError` at whatever point the replay cursor\n * happens to be, the host's catch arm typically journals something\n * (`report-fail`, a status write) mid-journal, and the invocation dies\n * with `[570 Journal mismatch]` — permanently, because every retry\n * replays into the same mismatch. Hosts have been inferring liveness\n * from side channels (e.g. counting live provider stream calls), which\n * works only because `restateProvider` replays `llm-call-N` without\n * touching the live provider — incidental coupling, not API.\n *\n * ## The seam\n *\n * The wrappers already know the answer: on replay, `ctx.run` returns\n * the journaled value WITHOUT invoking the wrapped closure; the first\n * time a closure actually executes, the journal cursor is past the\n * replayed prefix and the invocation is live. {@link createReplayTracker}\n * packages that signal:\n *\n * - `restateProvider(inner, ctx, { replayTracker })` and\n * `restateTool(tool, ctx, { replayTracker })` mark the tracker live\n * from inside their journaled closures.\n * - `tracker.isReplaying()` is the public liveness probe.\n * - `tracker.onLive(cb)` fires once at the replay→live transition\n * (immediately when already live).\n * - {@link replayAwareAbort} wraps `agent.abort` so external aborts\n * queue during replay and apply at the first live boundary — the\n * earliest point where the abort's side effects (loop throw, host\n * catch-arm journaling) append to the journal instead of forking it.\n *\n * ## Wiring\n *\n * ```ts\n * const tracker = createReplayTracker({ hooks: agent.hooks })\n * const provider = restateProvider(inner, ctx, { replayTracker: tracker })\n * const tools = wrapAgentTools(agentTools, ctx, { replayTracker: tracker })\n * const abort = replayAwareAbort(tracker, () => agent.abort())\n * // external cancel signal → abort() instead of agent.abort()\n * ```\n *\n * One tracker per handler invocation — like the wrappers themselves, it\n * is constructed fresh each attempt and must not be reused across\n * invocations.\n *\n * ## Semantics fine print\n *\n * \"Replaying\" is defined conservatively: `isReplaying()` returns `true`\n * until the first live closure executes. A fresh (non-resumed)\n * invocation therefore reports \"replaying\" for the short window before\n * its first journaled call — harmless for the abort use case (the abort\n * applies moments later, at that first live execution) and the honest\n * answer for journal safety: before the first `ctx.run` settles you\n * cannot know whether the SDK is about to feed you journal entries.\n *\n * A replay that completes without ever going live (the journaled run\n * already finished; the handler exits during replay) never fires\n * `onLive` — queued aborts are dropped with the invocation, which is\n * correct: there is nothing left to abort.\n */\n\n/** Minimal structural slice of the agent's Hookable for `replay:done`. */\nexport interface ReplayDoneHooks {\n callHook: (name: 'replay:done', ctx: Record<string, never>) => unknown\n}\n\nexport interface ReplayTrackerOptions {\n /**\n * Agent hooks to notify at the replay→live transition. When set, the\n * tracker fires `replay:done` exactly once, right after the `onLive`\n * callbacks. Rejections are swallowed (surfaced under `ZIDANE_DEBUG`)\n * — a misbehaving listener must not break the live execution that\n * triggered the transition.\n */\n hooks?: ReplayDoneHooks\n}\n\nexport interface ReplayTracker {\n /**\n * `true` until the first live (non-journaled) execution of a wrapped\n * provider stream / tool body. See the module doc for the fresh-\n * invocation caveat.\n */\n isReplaying: () => boolean\n /**\n * Register a callback for the replay→live transition. Fires once.\n * When the tracker is already live, the callback runs synchronously\n * before `onLive` returns. Returns an unregister function (no-op\n * after firing).\n */\n onLive: (cb: () => void) => () => void\n /**\n * Mark the invocation live. Called by the wrappers from inside their\n * journaled closures — a closure only executes when the journal\n * cursor is past it. Idempotent; only the first call transitions.\n */\n markLive: () => void\n}\n\n/**\n * Create a per-invocation replay tracker. Pass it to `restateProvider`\n * / `restateTool` / `wrapAgentTools` via their `replayTracker` option;\n * consume it via `isReplaying()` / `onLive()` / {@link replayAwareAbort}.\n */\nexport function createReplayTracker(options: ReplayTrackerOptions = {}): ReplayTracker {\n let live = false\n let callbacks: (() => void)[] = []\n\n return {\n isReplaying: () => !live,\n markLive: () => {\n if (live)\n return\n live = true\n const pending = callbacks\n callbacks = []\n for (const cb of pending) {\n try {\n cb()\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/restate] onLive callback threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }\n if (options.hooks) {\n Promise.resolve(options.hooks.callHook('replay:done', {})).catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/restate] replay:done hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n }\n },\n onLive: (cb) => {\n if (live) {\n cb()\n return () => {}\n }\n callbacks.push(cb)\n return () => {\n const idx = callbacks.indexOf(cb)\n if (idx >= 0)\n callbacks.splice(idx, 1)\n }\n },\n }\n}\n\n/**\n * Wrap an abort function so it defers during journal replay.\n *\n * Live invocation → `abort()` runs immediately, exactly like calling\n * `agent.abort()` directly. Replaying invocation → the abort is queued\n * and applied at the first live boundary (the start of the first\n * non-journaled provider stream or tool body), where the resulting\n * `AgentAbortedError` + host catch-arm journaling append to the journal\n * instead of forking it mid-replay. Multiple calls during replay\n * collapse into one queued abort.\n */\nexport function replayAwareAbort(tracker: ReplayTracker, abort: () => void): () => void {\n let queued = false\n return () => {\n if (!tracker.isReplaying()) {\n abort()\n return\n }\n if (queued)\n return\n queued = true\n tracker.onLive(abort)\n }\n}\n","/**\n * Restate-backed `SessionStore` — turn / run state lives in the bound\n * virtual object's K/V state instead of SQLite / file / remote HTTP.\n *\n * Trade-offs vs. journal-only replay\n * ----------------------------------\n * If you only care about durable execution within ONE invocation,\n * skip this — the journaled `ctx.run` entries (LLM calls, tool\n * executions) are enough to replay the loop to byte-identical state\n * after a crash. The in-memory `createMemoryStore()` is the right\n * choice; turns rematerialize from the journal each replay.\n *\n * Pick this store when EXTERNAL consumers (TUI, web GUI, support\n * tooling) need to read a session's history WITHOUT re-entering the\n * agent handler. Virtual-object state is exposed via the SDK's read\n * APIs (`ctx.get` from another handler, `restate-cli`, the Restate\n * web UI) — same data, different access path. The journal is\n * invocation-local; this state is the externally-visible projection\n * of it.\n *\n * Storage layout\n * --------------\n * One virtual-object key per session, three K/V slots:\n *\n * `session-data` → `SessionData` minus the `turns` array (small header)\n * `session-turns` → `SessionTurn[]` (the conversation history)\n * `session-runs` → `SessionRun[]` (run lifecycle records)\n *\n * Splitting `turns` / `runs` out keeps the header light for `load()` —\n * a handler that only needs metadata doesn't pull the full history.\n *\n * Listing / discovery\n * -------------------\n * `list()` returns `[]` because virtual objects don't expose cross-key\n * enumeration. `delete()` clears the current object's session keys only.\n * Use a separate index handler if you need discovery (one virtual object\n * per `projectRoot` that maintains an indexed list of session ids; this\n * store doesn't own that concern).\n * `generateSessionId` is omitted so the agent falls back to its\n * default UUID generator — for a session bound 1:1 to a virtual-\n * object key, pass `id: ctx.key` to `createSession()` instead and\n * skip ID generation entirely.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '../session'\nimport type { SessionTurn } from '../types'\nimport type { RestateObjectContextLike } from './types'\n\nconst KEY_DATA = 'session-data'\nconst KEY_TURNS = 'session-turns'\nconst KEY_RUNS = 'session-runs'\n\n/**\n * Construct a `SessionStore` backed by the bound virtual-object's K/V\n * state. The store is single-tenant — every method ignores the\n * `sessionId` argument because the virtual object is already keyed by\n * `ctx.key`. Calling `appendTurns('other-id', …)` writes to the\n * CURRENT object's state, not the one named in the argument. Bind one\n * virtual-object key per session and the contract holds.\n */\nexport function restateSessionStore(ctx: RestateObjectContextLike): SessionStore {\n let nextTurnSeq: number | undefined\n\n async function generateTurnId(): Promise<string> {\n if (nextTurnSeq === undefined) {\n const existing = (await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? []\n nextTurnSeq = existing.length\n }\n nextTurnSeq += 1\n return `turn_${nextTurnSeq}`\n }\n\n return {\n /**\n * Turn ids are deterministic from the persisted turn count. Random ids\n * are replay-stable only when the whole handler replays from the same\n * point; restarting from an arbitrary journal step can shift the RNG\n * sequence before a state write.\n */\n generateTurnId,\n\n async load(_sessionId: string): Promise<SessionData | null> {\n const header = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n const turns = sanitizeTurnsForState((await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? [])\n const runs = sanitizeRunsForState((await ctx.get<SessionRun[]>(KEY_RUNS)) ?? [])\n if (!header) {\n if (turns.length === 0 && runs.length === 0)\n return null\n return {\n id: _sessionId,\n turns,\n runs,\n status: inferStatus(runs),\n metadata: {},\n createdAt: inferCreatedAt(turns, runs),\n updatedAt: inferUpdatedAt(turns, runs),\n }\n }\n return { ...header, turns, runs }\n },\n\n async save(session: SessionData): Promise<void> {\n const { turns, runs, ...header } = session\n // Journal-stable header timestamps. The in-memory `Session` stamps\n // `createdAt` / `updatedAt` with raw `Date.now()` (its `touch()`\n // helper is synchronous and can't await a journaled clock), so the\n // values arriving here differ on every replay — and a replayed\n // `ctx.set` with drifting values is exactly the non-determinism\n // Restate exists to prevent. Source both from the journal instead:\n // `createdAt` sticks to the first persisted value, `updatedAt`\n // comes from `ctx.date.now()` (journaled — replay returns the\n // recorded reading).\n const prior = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n const now = await ctx.date.now()\n ctx.set(KEY_DATA, {\n ...header,\n createdAt: prior?.createdAt ?? now,\n updatedAt: now,\n })\n ctx.set(KEY_TURNS, sanitizeTurnsForState(turns))\n ctx.set(KEY_RUNS, sanitizeRunsForState(runs))\n },\n\n async delete(_sessionId: string): Promise<void> {\n ctx.clear(KEY_DATA)\n ctx.clear(KEY_TURNS)\n ctx.clear(KEY_RUNS)\n },\n\n /**\n * Always empty — virtual objects don't support cross-key\n * enumeration. Maintain a separate index handler if discovery\n * matters.\n */\n async list(): Promise<string[]> {\n return []\n },\n\n async appendTurns(_sessionId: string, turns: SessionTurn[]): Promise<void> {\n const existing = sanitizeTurnsForState((await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? [])\n ctx.set(KEY_TURNS, [...existing, ...sanitizeTurnsForState(turns)])\n },\n\n async getTurns(_sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n const turns = sanitizeTurnsForState((await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? [])\n return turns.slice(from, limit !== undefined ? from + limit : undefined)\n },\n\n async updateRun(_sessionId: string, run: SessionRun): Promise<void> {\n const runs = sanitizeRunsForState((await ctx.get<SessionRun[]>(KEY_RUNS)) ?? [])\n const idx = runs.findIndex(r => r.id === run.id)\n const next = [...runs]\n if (idx >= 0)\n next[idx] = sanitizeRunForState(run)\n else\n next.push(sanitizeRunForState(run))\n ctx.set(KEY_RUNS, next)\n },\n\n async updateStatus(_sessionId: string, status: SessionData['status']): Promise<void> {\n const header = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n const now = await ctx.date.now()\n if (header) {\n ctx.set(KEY_DATA, { ...header, status, updatedAt: now })\n }\n else {\n ctx.set(KEY_DATA, {\n id: _sessionId,\n status,\n metadata: {},\n createdAt: now,\n updatedAt: now,\n })\n }\n },\n }\n}\n\nfunction sanitizeTurnsForState(turns: readonly SessionTurn[]): SessionTurn[] {\n return turns.map(sanitizeTurnForState)\n}\n\nfunction sanitizeTurnForState(turn: SessionTurn): SessionTurn {\n if (!turn.usage || turn.usage.timeToFirstTokenMs === undefined)\n return turn\n\n const { timeToFirstTokenMs: _timeToFirstTokenMs, ...usage } = turn.usage\n return { ...turn, usage }\n}\n\nfunction sanitizeRunsForState(runs: readonly SessionRun[]): SessionRun[] {\n return runs.map(sanitizeRunForState)\n}\n\nfunction sanitizeRunForState(run: SessionRun): SessionRun {\n if (!run.turnUsage?.some(usage => usage.timeToFirstTokenMs !== undefined))\n return run\n\n return {\n ...run,\n turnUsage: run.turnUsage.map((usage) => {\n if (usage.timeToFirstTokenMs === undefined)\n return usage\n const { timeToFirstTokenMs: _timeToFirstTokenMs, ...stableUsage } = usage\n return stableUsage\n }),\n }\n}\n\nfunction inferStatus(runs: readonly SessionRun[]): SessionData['status'] {\n if (runs.some(run => run.status === 'running'))\n return 'running'\n if (runs.some(run => run.status === 'error'))\n return 'error'\n if (runs.some(run => run.status === 'completed'))\n return 'completed'\n return 'idle'\n}\n\nfunction inferCreatedAt(turns: readonly SessionTurn[], runs: readonly SessionRun[]): number {\n return minDefined([\n ...turns.map(turn => turn.createdAt),\n ...runs.map(run => run.startedAt),\n ]) ?? 0\n}\n\nfunction inferUpdatedAt(turns: readonly SessionTurn[], runs: readonly SessionRun[]): number {\n return maxDefined([\n ...turns.map(turn => turn.createdAt),\n ...runs.map(run => run.endedAt ?? run.startedAt),\n ]) ?? inferCreatedAt(turns, runs)\n}\n\nfunction minDefined(values: readonly number[]): number | undefined {\n let min: number | undefined\n for (const value of values) {\n if (min === undefined || value < min)\n min = value\n }\n return min\n}\n\nfunction maxDefined(values: readonly number[]): number | undefined {\n let max: number | undefined\n for (const value of values) {\n if (max === undefined || value > max)\n max = value\n }\n return max\n}\n","/**\n * Restate-backed tool wrappers.\n *\n * Wraps `ToolDef.execute` so every tool invocation is journaled. The\n * tool body runs on the original execution; after Restate persists the\n * journal entry, replay returns the recorded result without re-executing.\n * If the process dies before the entry is durable, the latest in-flight\n * tool can run again, so non-idempotent tools still need idempotency keys.\n *\n * `tool:gate`, `tool:transform`, `tool:after` hooks fire OUTSIDE the\n * journaled boundary, so they re-execute on replay. Built-in handlers\n * are deterministic given the journaled input + output (truncation,\n * image-stripping, `<edit-outcomes>` merge — all pure). Consumer hooks\n * that hit external services should themselves wrap side effects in\n * `ctx.run` so the substitution / annotation is replay-stable.\n *\n * `isConcurrencySafe` is preserved — Restate's per-key serialization\n * doesn't preclude in-handler parallelism. The loop's scheduler still\n * fans out safe tools up to `behavior.maxConcurrentTools`, each call\n * lands in its own journal entry. Order-of-journal-completion does NOT\n * affect replay correctness: Restate journals by call-site name, not\n * by completion order.\n */\n\nimport type { DetachedTasksCapability } from '../contexts'\nimport type { Session } from '../session'\nimport type { ToolContext, ToolDef } from '../tools/types'\nimport type { ToolResultContent } from '../types'\nimport type { RestatePromiseRaceLike } from './awakeable'\nimport type { ReplayTracker } from './replay'\nimport type { RestateContextLike, RestateRunOptions } from './types'\nimport { hydrateReadStateFromSessionAsync } from '../tools/read-state'\nimport { awakeableWithTimeout } from './awakeable'\n\n/**\n * Opt-in two-phase journaling for long-running, re-attachable tools.\n *\n * The default `restateTool` wrapping journals only the COMPLETED result —\n * a crash mid-execute re-runs the tool on replay, which is unacceptable\n * for builds / migrations / deploys. `durableStart` splits the execution:\n *\n * 1. `start` runs inside its own `ctx.run` entry, journaled BEFORE the\n * body — it kicks off the side effect and returns a re-attach token\n * (a background task id, a job handle, an idempotency key).\n * 2. the wait — either `attach` (one `ctx.run` holding the wait in the\n * result entry; only for short, bounded waits) or `wait` (awakeable\n * park: register a resumer in a journaled entry, suspend outside\n * the journal, synthesize the result in a final entry). On replay\n * both receive the SAME token and re-attach to the already-running\n * work instead of starting it again.\n *\n * The wrapped tool's own `execute` is bypassed; the start + wait pair IS\n * the execution.\n */\nexport interface RestateDurableStart<Token = unknown, Resumed = unknown> {\n /** Kick off the side effect; journaled before `attach`. Must be cheap + idempotent-safe up to journal persistence. */\n start: (input: Record<string, unknown>, toolCtx: ToolContext) => Promise<Token> | Token\n /**\n * Await / re-attach to the work identified by `token` and produce the\n * tool result. Runs inside ONE `ctx.run` entry, so the body must\n * settle within the runtime's attempt lifecycle — a long busy-poll in\n * here starves the journal, trips the server's inactivity/abort\n * timeouts, and each retry restarts the wait from scratch. Use this\n * only for waits bounded well below the inactivity timeout (or as the\n * degraded mode on hosts without a push seam); prefer {@link wait}\n * for anything long-running.\n *\n * Required unless {@link wait} is provided; ignored when both are set.\n */\n attach?: (token: Token, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<string | ToolResultContent[]>\n /**\n * Awakeable-based completion wait — the durable alternative to\n * {@link attach} for long-running work on hosts with a push seam\n * (e.g. a runner that fires `task:exit` → resumer → `ctx.resolveAwakeable`).\n * `executeTwoPhase` orchestrates three phases with the same journal\n * discipline as `restateAwakeableTool`:\n *\n * 1. `register` — a short, journaled `ctx.run`: hand the awakeable\n * id to the host as the exit resumer for `token`. Fires exactly\n * once across replays (marker invariant included).\n * 2. park — `await awakeable.promise`, OUTSIDE any `ctx.run`. The\n * invocation suspends cleanly: no held worker, immune to\n * inactivity/abort timeouts. With `timeoutMs`, the park is a\n * durable race against `ctx.sleep` (replay-stable — no\n * `Date.now()` deadline that resets per attempt).\n * 3. `result` — a final journaled `ctx.run`: synthesize the tool\n * result from the resumer payload (`null` = timeout; kill the\n * job and shape the timeout result here).\n */\n wait?: RestateDurableWait<Token, Resumed>\n /**\n * Best-effort cleanup when the per-call abort signal fires before the\n * wait settles — `attach` or the awakeable park alike (run abort,\n * `agent.cancelTool`, sibling-cascade\n * cancel, `agent.destroy()` mid-run). Without it, the journaled token\n * identifies a remote job that keeps running with nobody attached —\n * a leak for hosts whose jobs outlive the session.\n *\n * Contract:\n * - Fires at most once per call, outside the journal (an aborting\n * invocation can't open new `ctx.run` entries reliably) — journal\n * your own cleanup on the host side if it must be durable.\n * - May run concurrently with a still-unwinding `attach`; make it\n * idempotent against \"job already gone\".\n * - NOT called on process death — that's not termination: the\n * journaled token re-attaches on replay instead. This seam is for\n * deliberate aborts, where no replay is coming.\n * - Errors are swallowed (surfaced under `ZIDANE_DEBUG`) — cleanup\n * failure must not mask the abort itself.\n */\n onAbort?: (token: Token, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<void> | void\n}\n\n/**\n * Awakeable-based completion wait for {@link RestateDurableStart.wait}.\n *\n * Replay contract (mirrors `restateAwakeableTool`):\n * - The awakeable is minted BEFORE the journaled `register` entry, so on\n * replay both come back in the same order with the same id — a replayed\n * call re-parks on the SAME awakeable without re-registering.\n * - The journaled marker records the awakeable id it was registered for;\n * a mismatch on replay (journal drift) throws instead of parking on an\n * id nobody will ever resolve.\n */\nexport interface RestateDurableWait<Token = unknown, Resumed = unknown> {\n /**\n * Register `awakeableId` as the exit resumer for the work identified\n * by `token` — the host must call `ctx.resolveAwakeable(awakeableId,\n * payload)` when the work completes. Runs inside `ctx.run`; must be\n * short and idempotent-safe up to journal persistence.\n */\n register: (token: Token, awakeableId: string, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<void> | void\n /**\n * Synthesize the tool result once the park settles. `resumed` is the\n * resumer's payload, or `null` when `timeoutMs` elapsed first — kill\n * the still-running job and shape the timeout result in that branch.\n * Runs inside the final journaled `ctx.run` (the result entry).\n */\n result: (token: Token, resumed: Resumed | null, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<string | ToolResultContent[]> | string | ToolResultContent[]\n /**\n * Durable park deadline in milliseconds — becomes the\n * `awakeableWithTimeout` duration (a `ctx.sleep` raced against the\n * awakeable, replay-stable). A function receives the call's input so\n * the model's own `timeout` parameter can drive it. Unset /\n * non-positive = park indefinitely.\n */\n timeoutMs?: number | ((input: Record<string, unknown>, toolCtx: ToolContext) => number | undefined)\n /**\n * Durable race combinator for the timeout race — pass `RestatePromise`\n * from the SDK. See `awakeableWithTimeout`'s rationale; native\n * `Promise.race` is the fallback.\n */\n promises?: RestatePromiseRaceLike\n /**\n * Per-call fast path: when this returns `true` for the journaled\n * `token`, the awakeable park is SKIPPED entirely — no awakeable is\n * minted, no `-register` entry is journaled, and the invocation never\n * suspends. `result` is invoked directly with `resumed = null` inside\n * the final result entry.\n *\n * This is for `start` phases that sometimes have nothing to wait on\n * (e.g. a shell tool that only spawns a real background task above a\n * duration threshold and otherwise runs the command inline in\n * `result`). Parking such calls pays a full suspend → resolve →\n * re-invoke → replay round-trip for nothing.\n *\n * MUST be a pure function of the journaled `token` (+ input) so the\n * decision is identical across replays — a token-derived branch keeps\n * the journal entry shape deterministic. When omitted, every call\n * parks (the original behaviour).\n */\n inline?: (token: Token, input: Record<string, unknown>, toolCtx: ToolContext) => boolean\n}\n\n/** Journaled registration marker — see the marker invariant in {@link RestateDurableWait}. */\ninterface DurableWaitRegisterMarker {\n registered: true\n awakeableId: string\n}\n\nexport interface RestateToolOptions<Token = unknown, Resumed = unknown> {\n /**\n * `ctx.run` options forwarded on every wrapped tool call. Defaults to\n * `{ maxRetryAttempts: 1 }` — tool errors are typically deterministic\n * (validation failures, missing files) so blind retries waste budget.\n * Override per-tool for network-heavy custom tools that benefit from\n * SDK-level retries.\n */\n runOptions?: RestateRunOptions\n /**\n * Override the journal-entry name. Receives the canonical tool name\n * and the call's input. Default: `tool-<name>-<seq>` where `<seq>` is\n * a per-wrapper monotonic counter. Override to inject the model's\n * `callId` (read it off `ToolContext.callId` inside `execute` if you\n * need an LLM-visible key — but the default counter is sufficient\n * for journal correctness).\n */\n entryName?: (seq: number, name: string, input: Record<string, unknown>) => string\n /**\n * Hide and reject the built-in shell tool's background mode.\n *\n * Default: derived from {@link RestateToolOptions.detachedTasks} —\n * disabled unless the execution context declares `'durable'` detached\n * tasks. An explicit boolean always wins.\n */\n disableShellBackground?: boolean\n /**\n * `detachedTasks` capability of the execution context the agent will\n * run with (see `ContextCapabilities.detachedTasks` /\n * `resolveDetachedTasksCapability`). Drives the\n * `disableShellBackground` default: a `'durable'` context keeps its\n * background tasks across worker crashes, so the wrapper leaves the\n * shell tool's background mode intact.\n *\n * Default: `undefined`, treated as non-durable (background disabled).\n */\n detachedTasks?: DetachedTasksCapability\n /**\n * Two-phase start/attach journaling for crash-safe long tools — see\n * {@link RestateDurableStart}. Per-tool by nature; not accepted by\n * `wrapAgentTools` (a single start/attach pair can't fit a whole map).\n */\n durableStart?: RestateDurableStart<Token, Resumed>\n /**\n * Per-invocation replay tracker (see `createReplayTracker`). When set,\n * the wrapper marks it live from inside its journaled closures — i.e.\n * the first time a tool body (or a `durableStart.start` phase)\n * ACTUALLY executes rather than replaying from the journal. Share one\n * tracker across the provider and tool wrappers of the same handler\n * invocation.\n */\n replayTracker?: ReplayTracker\n}\n\nconst SHELL_BACKGROUND_DISABLED_MESSAGE = 'shell error: background mode is disabled for Restate-backed agents. Fall back to foreground (drop `run_in_background`).'\nconst HYDRATED_READ_STATE_SESSIONS = new WeakSet<Session>()\n\n/**\n * Wrap a single `ToolDef` so its `execute` runs inside `ctx.run`. The\n * tool's `spec` and `isConcurrencySafe` pass through unchanged.\n */\nexport function restateTool<Token = unknown, Resumed = unknown>(\n inner: ToolDef,\n ctx: RestateContextLike,\n options: RestateToolOptions<Token, Resumed> = {},\n): ToolDef {\n const runOpts: RestateRunOptions = options.runOptions ?? { maxRetryAttempts: 1 }\n const nameFor = options.entryName ?? ((seq: number, name: string) => `tool-${name}-${seq}`)\n const canonicalName = inner.spec.name\n const disableShellBackground = options.disableShellBackground ?? (options.detachedTasks !== 'durable')\n const shellBackgroundDisabled = disableShellBackground && canonicalName === 'shell'\n const durableStart = options.durableStart\n if (durableStart && !durableStart.attach && !durableStart.wait)\n throw new Error(`restateTool(${canonicalName}): durableStart needs an \\`attach\\` (in-journal wait) or a \\`wait\\` (awakeable park) phase`)\n let seq = 0\n const hydrateOnce = async (toolCtx: Parameters<ToolDef['execute']>[1]) => {\n if (toolCtx.session && !HYDRATED_READ_STATE_SESSIONS.has(toolCtx.session)) {\n HYDRATED_READ_STATE_SESSIONS.add(toolCtx.session)\n await hydrateReadStateFromSessionAsync(toolCtx.session, toolCtx.handle.cwd, {\n defaultLineNumbers: toolCtx.behavior?.readLineNumbers ?? true,\n readFileForHash: async (path) => {\n try {\n return await toolCtx.execution.readFile(toolCtx.handle, path)\n }\n catch {\n return null\n }\n },\n })\n }\n }\n // IMPORTANT: NOT async, and the `ctx.run` promise is returned without\n // native combinators — `durableExecute` consumers (the Restate batch\n // scheduler) need the runtime's own promise shape (`.map`) intact.\n const executeDurably = (input: Record<string, unknown>, toolCtx: Parameters<ToolDef['execute']>[1]) => {\n seq += 1\n return ctx.run(\n nameFor(seq, canonicalName, input),\n async () => {\n // Closure executes only when live — replays return the journaled\n // result without entering here.\n options.replayTracker?.markLive()\n await hydrateOnce(toolCtx)\n if (shellBackgroundDisabled && input.run_in_background === true)\n return SHELL_BACKGROUND_DISABLED_MESSAGE\n return inner.execute(input, toolCtx)\n },\n runOpts,\n )\n }\n\n // Two-phase mode: journal the start token BEFORE the body so a crash\n // mid-execution re-attaches on replay instead of re-running. The await\n // between the two entries makes this a native-async composition, so the\n // tool deliberately does NOT advertise `durableExecute` — the batch\n // scheduler runs it sequentially via plain `execute`, which is the\n // right shape for long re-attachable work anyway.\n const executeTwoPhase = async (input: Record<string, unknown>, toolCtx: Parameters<ToolDef['execute']>[1]) => {\n const start = durableStart!\n seq += 1\n const entry = nameFor(seq, canonicalName, input)\n const token = await ctx.run(\n `${entry}-start`,\n async () => {\n options.replayTracker?.markLive()\n await hydrateOnce(toolCtx)\n return start.start(input, toolCtx)\n },\n runOpts,\n )\n\n // Abort seam — see RestateDurableStart.onAbort. At-most-once via the\n // `fired` latch; armed only while the wait is in flight.\n const onAbort = start.onAbort\n const signal = toolCtx.signal as AbortSignal | undefined\n let fired = false\n const fireAbortCleanup = (): void => {\n if (fired || !onAbort)\n return\n fired = true\n Promise.resolve(onAbort(token, input, toolCtx)).catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/restate] durableStart onAbort threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n }\n\n const wait = start.wait\n if (wait) {\n // Per-call fast path: nothing to wait on for this token (decided\n // purely from the journaled token, so replay-stable). Skip minting\n // the awakeable, the `-register` entry, and the suspend/resume\n // round-trip entirely — run the result inline. A short command that\n // executes in the result entry shouldn't pay a park it never uses.\n if (wait.inline?.(token, input, toolCtx)) {\n if (!onAbort || !signal) {\n return await ctx.run(entry, () => {\n options.replayTracker?.markLive()\n return wait.result(token, null, input, toolCtx)\n }, runOpts)\n }\n if (signal.aborted)\n fireAbortCleanup()\n else\n signal.addEventListener('abort', fireAbortCleanup, { once: true })\n try {\n return await ctx.run(entry, () => {\n options.replayTracker?.markLive()\n return wait.result(token, null, input, toolCtx)\n }, runOpts)\n }\n finally {\n signal.removeEventListener('abort', fireAbortCleanup)\n }\n }\n // Awakeable path — register / park / result. The park lives\n // OUTSIDE any ctx.run, so the invocation suspends cleanly instead\n // of busy-holding an attempt until the inactivity timeout fires.\n //\n // Pre-aborted: nobody will consume the result — clean up the\n // journaled start and bail without registering a resumer.\n if (signal?.aborted) {\n fireAbortCleanup()\n throw new Error(`${canonicalName}: aborted before durable wait registration`)\n }\n signal?.addEventListener('abort', fireAbortCleanup, { once: true })\n try {\n const timeoutMs = typeof wait.timeoutMs === 'function' ? wait.timeoutMs(input, toolCtx) : wait.timeoutMs\n // Mint BEFORE the journaled registration (same ordering contract\n // as restateAwakeableTool) — replay returns the same id.\n const awakeable = awakeableWithTimeout<Resumed>(ctx, {\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n ...(wait.promises ? { promises: wait.promises } : {}),\n })\n const marker = await ctx.run<DurableWaitRegisterMarker>(\n `${entry}-register`,\n async () => {\n options.replayTracker?.markLive()\n await wait.register(token, awakeable.id, input, toolCtx)\n return { registered: true, awakeableId: awakeable.id }\n },\n runOpts,\n )\n if (marker.awakeableId !== awakeable.id) {\n throw new Error(\n `restateTool(${canonicalName}): journal drift — registration marker carries awakeable \"${marker.awakeableId}\" but this replay minted \"${awakeable.id}\". Refusing to park on an unreachable awakeable.`,\n )\n }\n const resumed = await parkWithSignal(awakeable.promise, signal, canonicalName)\n return await ctx.run(\n entry,\n () => {\n options.replayTracker?.markLive()\n return wait.result(token, resumed, input, toolCtx)\n },\n runOpts,\n )\n }\n finally {\n signal?.removeEventListener('abort', fireAbortCleanup)\n }\n }\n\n const attach = () => ctx.run(\n entry,\n () => {\n options.replayTracker?.markLive()\n return start.attach!(token, input, toolCtx)\n },\n runOpts,\n )\n\n if (!onAbort || !signal)\n return attach()\n\n // Pre-aborted: the journaled start already launched the job — clean\n // it up, then still run attach so the call surfaces the host's own\n // cancellation shape (it should fail fast on an aborted signal).\n if (signal.aborted) {\n fireAbortCleanup()\n return attach()\n }\n signal.addEventListener('abort', fireAbortCleanup, { once: true })\n try {\n return await attach()\n }\n finally {\n signal.removeEventListener('abort', fireAbortCleanup)\n }\n }\n\n if (durableStart) {\n // Strip any pre-existing `durableExecute` — see the executeTwoPhase\n // comment; advertising one here would hand the batch scheduler a\n // native promise it can't `.map`.\n const { durableExecute: _dropped, ...rest } = inner\n return {\n ...rest,\n spec: shellBackgroundDisabled ? withoutShellBackgroundInput(inner.spec) : inner.spec,\n execute: executeTwoPhase,\n }\n }\n\n return {\n ...inner,\n spec: shellBackgroundDisabled ? withoutShellBackgroundInput(inner.spec) : inner.spec,\n execute: executeDurably,\n durableExecute: executeDurably,\n }\n}\n\n/**\n * Park on `promise`, but stop waiting when the per-call abort signal\n * fires (run abort, `cancelTool`, sibling-cascade cancel) — a deliberate\n * abort means nobody will consume the result, and like `onAbort` it\n * implies no replay is coming, so the native (non-journaled) race is\n * replay-safe. The underlying awakeable stays registered; a late\n * resolution lands on a completed invocation and is dropped by the\n * runtime.\n */\nfunction parkWithSignal<T>(promise: Promise<T>, signal: AbortSignal | undefined, name: string): Promise<T> {\n if (!signal)\n return promise\n if (signal.aborted)\n return Promise.reject(new Error(`${name}: aborted while parked on durable wait`))\n return new Promise<T>((resolve, reject) => {\n const onAbortEvt = (): void => reject(new Error(`${name}: aborted while parked on durable wait`))\n signal.addEventListener('abort', onAbortEvt, { once: true })\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbortEvt)\n resolve(value)\n },\n (err: unknown) => {\n signal.removeEventListener('abort', onAbortEvt)\n reject(err instanceof Error ? err : new Error(String(err)))\n },\n )\n })\n}\n\nfunction withoutShellBackgroundInput(spec: ToolDef['spec']): ToolDef['spec'] {\n const inputSchema = spec.inputSchema\n const properties = isRecord(inputSchema.properties) ? inputSchema.properties : undefined\n if (!properties || !Object.hasOwn(properties, 'run_in_background'))\n return spec\n\n const { run_in_background: _runInBackground, ...nextProperties } = properties\n const required = Array.isArray(inputSchema.required)\n ? inputSchema.required.filter(name => name !== 'run_in_background')\n : inputSchema.required\n return {\n ...spec,\n description: stripShellBackgroundDescription(spec.description),\n inputSchema: {\n ...inputSchema,\n properties: nextProperties,\n ...(required !== inputSchema.required ? { required } : {}),\n },\n }\n}\n\nfunction stripShellBackgroundDescription(description: string): string {\n return description\n .split('\\n')\n .filter(line =>\n !line.includes('run_in_background')\n && !line.includes('<task-notification>')\n && !line.includes('background task'),\n )\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Wrap an entire tool map at once — convenience for the common case of\n * \"journal every tool the agent has\". Pass-through for tools whose\n * names are listed in `opts.exclude` (e.g. fully-deterministic local\n * helpers where journaling is pure overhead).\n *\n * The returned map is a fresh object; the input is untouched.\n */\nexport function wrapAgentTools(\n tools: Record<string, ToolDef>,\n ctx: RestateContextLike,\n opts: Omit<RestateToolOptions, 'durableStart'> & { exclude?: readonly string[] } = {},\n): Record<string, ToolDef> {\n const excludeSet = new Set(opts.exclude ?? [])\n // `detachedTasks: 'durable'` implies the host's `waitBackground` parks\n // on an awakeable (that's the durable wait pattern) — and a Restate\n // suspension point must NOT nest inside a journaled `ctx.run` closure.\n // Auto-exclude the built-in `wait_task` from wrapping so its park runs\n // outside the journal; the wait result is replay-safe anyway (a replayed\n // wait re-attaches to an already-terminated task and resolves from the\n // registry). Hosts with a journal-safe wait (e.g. a bridge RPC poll)\n // can re-wrap explicitly via `restateTool(waitTask, ctx, …)`.\n const skipWaitTask = opts.detachedTasks === 'durable'\n const wrapped: Record<string, ToolDef> = {}\n for (const [name, def] of Object.entries(tools)) {\n const passThrough = excludeSet.has(name) || (skipWaitTask && def.spec.name === 'wait_task')\n wrapped[name] = passThrough ? def : restateTool(def, ctx, opts)\n }\n return wrapped\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"restate.js","names":[],"sources":["../src/restate/awakeable.ts","../src/restate/awakeable-tool.ts","../src/restate/errors.ts","../src/restate/behavior.ts","../src/restate/clock.ts","../src/restate/parallel.ts","../src/restate/provider.ts","../src/restate/replay.ts","../src/restate/session.ts","../src/restate/tool.ts"],"sourcesContent":["/**\n * Awakeable helpers — durable waits with deadlines.\n *\n * `awakeableWithTimeout` packages the canonical \"park until a resumer\n * fires, but not forever\" shape: an awakeable raced against a durable\n * `ctx.sleep(timeoutMs)` timer. The race itself survives process death —\n * on replay both branches resume from the journal and the same side\n * wins. This is the three-line building block for\n * `ExecutionContext.waitBackground` implementations on durable hosts\n * (runner fires task-exit → `resolveAwakeable(id, info)`; the timeout\n * branch maps to the seam's `null` return).\n */\n\nimport type { RestateAwakeable, RestateContextLike } from './types'\n\n/**\n * Structural subset of the SDK's `RestatePromise` static surface used by\n * {@link awakeableWithTimeout} — mirrors `RestatePromiseAllLike` from\n * `./parallel`. Pass `RestatePromise` from `@restatedev/restate-sdk` so\n * the race goes through the journaled combinator; native `Promise.race`\n * over two durable promises leaves the winner to the runtime's replay\n * ordering instead of an explicit journal entry.\n */\nexport interface RestatePromiseRaceLike<TPromise extends Promise<unknown> = Promise<unknown>> {\n race: (values: readonly TPromise[]) => Promise<unknown>\n}\n\nexport interface AwakeableWithTimeoutOptions<TPromise extends Promise<unknown> = Promise<unknown>> {\n /**\n * Durable deadline in milliseconds. Unset / non-positive = no timeout\n * (the returned awakeable is the plain `ctx.awakeable()` passthrough).\n */\n timeoutMs?: number\n /**\n * Durable race combinator — pass `RestatePromise` from the SDK. When\n * omitted, native `Promise.race` is used: correct at runtime and\n * replay-safe for THIS pattern (both branches are journaled\n * primitives), but the SDK combinator is the recommended belt-and-\n * braces choice for deterministic replay.\n */\n promises?: RestatePromiseRaceLike<TPromise>\n}\n\n/**\n * Mint an awakeable that resolves with the resumer's payload, or `null`\n * when `timeoutMs` elapses first. The id is journal-stable (send it\n * out-of-band as usual); the timeout is a durable `ctx.sleep`, so a\n * parked invocation suspends instead of holding a worker.\n *\n * Timeout surfaces as `null` — a resumer that resolves `undefined` is\n * indistinguishable from a timeout, so resolve a concrete payload.\n *\n * ```ts\n * // waitBackground for a durable host, in three lines:\n * const { id, promise } = awakeableWithTimeout<TaskExitInfo>(ctx, { timeoutMs, promises: RestatePromise })\n * await registerExitResumer(taskId, id) // runner → ctx.resolveAwakeable(id, info)\n * return await promise // TaskExitInfo, or null on timeout\n * ```\n */\nexport function awakeableWithTimeout<T, TPromise extends Promise<unknown> = Promise<unknown>>(\n ctx: RestateContextLike,\n options: AwakeableWithTimeoutOptions<TPromise> = {},\n): RestateAwakeable<T | null> {\n const awakeable = ctx.awakeable<T>()\n const timeoutMs = options.timeoutMs\n if (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs) || timeoutMs <= 0)\n return awakeable\n\n const timer = ctx.sleep(timeoutMs)\n const raced = options.promises\n ? options.promises.race([awakeable.promise, timer] as readonly unknown[] as readonly TPromise[])\n : Promise.race<unknown>([awakeable.promise, timer])\n return {\n id: awakeable.id,\n // The sleep branch resolves `undefined`; normalize to the seam's\n // `null` so callers get one documented timeout sentinel.\n promise: raced.then(value => (value === undefined ? null : value as T)),\n }\n}\n","/**\n * Awakeable-backed tool — durable human/external-in-the-loop waits.\n *\n * Builds a `ToolDef` whose `execute` parks on a Restate awakeable: the\n * tool mints a journal-stable awakeable id, broadcasts it out-of-band\n * (webhook, queue, collab stream — the `notify` callback), and suspends\n * until a resumer calls `ctx.resolveAwakeable(id, value)` on the SDK\n * side. The classic shape for `ask_user` / `request_review` /\n * `wait_for_approval` tools inside durable handlers.\n *\n * Replay contract\n * ---------------\n * - **Broadcast-once-across-replays**: `notify` runs inside `ctx.run`,\n * so a crash + replay returns the journaled marker without re-sending\n * the notification. The user is pinged exactly once per tool call, no\n * matter how many times the worker restarts.\n * - **Marker invariant**: the journaled marker records the awakeable id\n * it was broadcast for. On replay the freshly-minted awakeable id must\n * match (the SDK derives awakeable ids deterministically from the\n * journal position); a mismatch means the journal and the code path\n * drifted — the tool throws instead of parking on an id nobody will\n * ever resolve.\n */\n\nimport type { ToolContext, ToolDef } from '../tools/types'\nimport type { ToolResultContent } from '../types'\nimport type { RestateContextLike, RestateRunOptions } from './types'\n\n/** Journaled broadcast marker — see \"Marker invariant\" in the module doc. */\ninterface AwakeableNotifyMarker {\n notified: true\n awakeableId: string\n}\n\nexport interface RestateAwakeableToolOptions<T = unknown> {\n /** Canonical tool name (what the model calls). */\n name: string\n /** Model-facing tool description. */\n description: string\n /** JSON schema for the tool input. */\n inputSchema: Record<string, unknown>\n /** The Restate context the awakeable + journal entries bind to. */\n ctx: RestateContextLike\n /**\n * Send the awakeable id (plus the call's input) out-of-band so a\n * resumer can later call `ctx.resolveAwakeable(awakeableId, value)`.\n * Runs inside `ctx.run` — fires exactly once across replays.\n */\n notify: (info: {\n awakeableId: string\n input: Record<string, unknown>\n toolCtx: ToolContext\n }) => void | Promise<void>\n /**\n * Map the resolved awakeable payload to the tool result. Default:\n * strings pass through, everything else is JSON-stringified.\n */\n toResult?: (value: T, input: Record<string, unknown>) => string | ToolResultContent[]\n /**\n * `ctx.run` options for the notify entry. Defaults to\n * `{ maxRetryAttempts: 1 }`, matching `restateTool`'s rationale.\n */\n runOptions?: RestateRunOptions\n /**\n * Concurrency-safety flag passed through to the `ToolDef`. Defaults to\n * `false` — a parked wait holding a scheduler slot should barrier.\n */\n isConcurrencySafe?: boolean\n}\n\n/**\n * Build a `ToolDef` that parks on a Restate awakeable until an external\n * resumer resolves it. See the module doc for the replay contract.\n *\n * Do NOT additionally wrap the returned tool in `restateTool` — the\n * journal discipline is already internal, and an outer `ctx.run` would\n * illegally nest the suspension point inside a journaled closure.\n */\nexport function restateAwakeableTool<T = unknown>(\n options: RestateAwakeableToolOptions<T>,\n): ToolDef {\n const { name, description, inputSchema, ctx, notify, toResult } = options\n const runOpts: RestateRunOptions = options.runOptions ?? { maxRetryAttempts: 1 }\n let seq = 0\n\n const execute = async (input: Record<string, unknown>, toolCtx: ToolContext): Promise<string | ToolResultContent[]> => {\n seq += 1\n // Mint BEFORE the journaled broadcast: the awakeable's journal\n // position precedes the notify entry, so on replay both come back\n // in the same order with the same id.\n const awakeable = ctx.awakeable<T>()\n\n const marker = await ctx.run<AwakeableNotifyMarker>(\n `awakeable-${name}-${seq}-notify`,\n async () => {\n await notify({ awakeableId: awakeable.id, input, toolCtx })\n return { notified: true, awakeableId: awakeable.id }\n },\n runOpts,\n )\n\n // Marker invariant — a drifted journal would otherwise park this\n // call on an id that was never broadcast (nobody will resolve it).\n if (marker.awakeableId !== awakeable.id) {\n throw new Error(\n `restateAwakeableTool(${name}): journal drift — broadcast marker carries awakeable \"${marker.awakeableId}\" but this replay minted \"${awakeable.id}\". The journal and the code path no longer line up; refusing to park on an unreachable awakeable.`,\n )\n }\n\n const value = await awakeable.promise\n if (toResult)\n return toResult(value, input)\n return typeof value === 'string' ? value : JSON.stringify(value)\n }\n\n return {\n spec: { name, description, inputSchema },\n ...(options.isConcurrencySafe !== undefined ? { isConcurrencySafe: options.isConcurrencySafe } : {}),\n execute,\n // The awakeable park IS the durable body — the loop's durable path\n // can use it directly without an extra wrapper.\n durableExecute: execute,\n }\n}\n","/**\n * Restate control-flow errors must escape the agent loop. If they are turned\n * into model-visible tool results, the runtime can retry/suspend incorrectly.\n *\n * Kept structural so `zidane/restate` does not need to import the SDK at\n * runtime just to recognize the common error shapes.\n */\nexport function shouldRethrowRestateControlError(error: unknown): boolean {\n if (!(error instanceof Error))\n return false\n\n const codeValue = Reflect.get(error, 'code')\n const code = typeof codeValue === 'number'\n ? codeValue\n : undefined\n if (code === 599)\n return true\n\n const name = error.name\n if (\n name === 'TerminalError'\n || name === 'TimeoutError'\n || name === 'CancelledError'\n || name === 'SuspendedError'\n ) {\n return true\n }\n\n const ctorName = error.constructor?.name\n const serializedCtorNameValue = Reflect.get(error, 'constructorName')\n const serializedCtorName = typeof serializedCtorNameValue === 'string'\n ? serializedCtorNameValue\n : undefined\n if (\n ctorName === 'TerminalError'\n || ctorName === 'TimeoutError'\n || ctorName === 'CancelledError'\n || ctorName === 'SuspendedError'\n || serializedCtorName === 'TerminalError'\n || serializedCtorName === 'TimeoutError'\n || serializedCtorName === 'CancelledError'\n || serializedCtorName === 'SuspendedError'\n ) {\n return true\n }\n\n return typeof name === 'string' && name.toLowerCase().includes('suspend')\n}\n","import type { AgentBehavior } from '../types'\nimport { shouldRethrowRestateControlError } from './errors'\n\n/**\n * Conservative behavior defaults for agents running inside Restate.\n *\n * The helper is opt-in and returns a plain AgentBehavior object, so callers can\n * spread or override it without changing non-Restate harness defaults.\n *\n * `disableBackgroundTasks: 'non-durable'` is capability-aware: background\n * tasks stay enabled for execution contexts whose\n * `capabilities.detachedTasks` is `'durable'` (tasks live on a remote\n * runner and survive worker crashes) and are disabled for everything\n * else (host-local processes don't survive process death). Hosts on\n * plain `ProcessContext` see the same effective behavior as the old\n * blanket `true`.\n */\nexport function restateBehavior(overrides: AgentBehavior = {}): AgentBehavior {\n return {\n maxConcurrentTools: 1,\n disableBackgroundTasks: 'non-durable',\n cache: true,\n shouldRethrowToolError: shouldRethrowRestateControlError,\n ...overrides,\n }\n}\n","/**\n * Restate-backed {@link AgentClock} — journals `now()` + `randomUUID()` so\n * `SessionTurn.createdAt`, `runId`, and fallback / synthetic turn ids stay\n * byte-identical across replays of the same invocation.\n *\n * Pass to `agent.run({ clock: restateClock(ctx) })` or bake it into\n * `createAgent({ clock: restateClock(ctx) })` for the duration of a\n * handler invocation. The clock holds a reference to `ctx`; do not reuse\n * a clock instance across invocations.\n */\n\nimport type { AgentClock } from '../types'\nimport type { RestateContextLike } from './types'\n\n/**\n * Wrap a Restate context's `date.now()` + `rand.uuidv4()` into the\n * {@link AgentClock} shape Zidane consumes for journaled metadata.\n *\n * The returned clock is a thin live view onto `ctx` — each call hits\n * the SDK afresh. No caching, no closure-captured values. Restate's\n * journal handles determinism on its end.\n */\nexport function restateClock(ctx: RestateContextLike): AgentClock {\n return {\n now: () => ctx.date.now(),\n randomUUID: () => ctx.rand.uuidv4(),\n }\n}\n","import type { ToolResult } from '../providers'\nimport type { ToolBatchExecutionContext, ToolBatchExecutor } from '../types'\n\nexport interface RestatePromiseAllLike<TPromise extends Promise<unknown> = RestatePromiseLike<unknown>> {\n all: (values: readonly TPromise[]) => Promise<readonly unknown[]>\n}\n\nexport type RestatePromiseLike<T> = Promise<T> & {\n map: <U>(mapper: (value?: T, failure?: unknown) => U) => Promise<U>\n}\n\nexport interface RestateToolBatchExecutorOptions<TPromise extends Promise<unknown> = RestatePromiseLike<unknown>> {\n /**\n * Wait for a safe fleet through Restate's durable combinator. Pass\n * `RestatePromise` from `@restatedev/restate-sdk` in real deployments.\n */\n promises: RestatePromiseAllLike<TPromise>\n}\n\n/**\n * Tool batch scheduler for Restate-backed agents.\n *\n * It preserves unsafe-tool barriers and emits results in model submission\n * order. Safe fleets are started in submission order, capped by\n * `maxConcurrentTools`, and joined via the supplied durable `all()`.\n */\nexport function restateToolBatchExecutor<TPromise extends Promise<unknown> = RestatePromiseLike<unknown>>(\n options: RestateToolBatchExecutorOptions<TPromise>,\n): ToolBatchExecutor {\n return async (ctx: ToolBatchExecutionContext): Promise<ToolResult[]> => {\n const results: ToolResult[] = []\n const { toolCalls } = ctx\n\n const fillRemaining = (from: number, factory: (index: number) => ToolResult): ToolResult[] => {\n for (let i = from; i < toolCalls.length; i++)\n results[i] = factory(i)\n return results\n }\n\n for (let i = 0; i < toolCalls.length;) {\n if (ctx.signal.aborted)\n return fillRemaining(i, ctx.interruptedResult)\n if (ctx.steeringQueue.length > 0)\n return fillRemaining(i, ctx.skippedResult)\n\n if (!ctx.isConcurrencySafe(i)) {\n results[i] = await ctx.execute(i)\n i += 1\n continue\n }\n\n const start = i\n const fleet: number[] = []\n while (\n i < toolCalls.length\n && ctx.isConcurrencySafe(i)\n && fleet.length < ctx.maxConcurrentTools\n ) {\n fleet.push(i)\n i += 1\n }\n\n const fleetResults = fleet.every(index => ctx.canExecuteDurably(index))\n ? await ctx.executeDurable(\n fleet,\n async <T>(values: readonly Promise<T>[]): Promise<readonly T[]> => {\n const results = await options.promises.all(values as readonly TPromise[])\n return results as readonly T[]\n },\n )\n : await executeSequential(ctx, fleet)\n for (let offset = 0; offset < fleetResults.length; offset++)\n results[start + offset] = fleetResults[offset]\n }\n\n return results\n }\n}\n\nasync function executeSequential(\n ctx: ToolBatchExecutionContext,\n indices: readonly number[],\n): Promise<ToolResult[]> {\n const results: ToolResult[] = []\n for (const index of indices) {\n if (ctx.signal.aborted) {\n results.push(ctx.interruptedResult(index))\n continue\n }\n if (ctx.steeringQueue.length > 0) {\n results.push(ctx.skippedResult(index))\n continue\n }\n results.push(await ctx.execute(index))\n }\n return results\n}\n","/**\n * Restate-backed Provider wrapper.\n *\n * Wraps `Provider.stream` so every assistant turn (the LLM call as a\n * whole) is journaled. The model is invoked once per turn — the live\n * stream still fires `onText` / `onThinking` callbacks for UX during the\n * original execution. On replay, `ctx.run` returns the journaled\n * `TurnResult` immediately and the inner provider is NEVER re-invoked →\n * no duplicate billed tokens, no duplicate stream chunks.\n *\n * Replay caveat: stream callbacks don't re-fire on replay. Consumers\n * that paint live UI from `stream:text` should reconstruct transcripts\n * from `session.turns` via `eventsFromTurns` (zidane/chat) instead of\n * relying on hook re-emission. The chat layer already does this.\n *\n * Journal-entry naming: a monotonic per-wrapper counter (`llm-call-1`,\n * `llm-call-2`, …). One wrapper instance per Restate handler invocation\n * resets the counter; Restate's journal is invocation-scoped so names\n * don't need to be globally unique. The counter ticks the same way on\n * replay because the wrapper is constructed fresh each attempt.\n */\n\nimport type { Provider, StreamCallbacks, StreamOptions, TurnResult } from '../providers'\nimport type { ReplayTracker } from './replay'\nimport type { RestateContextLike, RestateRunOptions } from './types'\n\nexport interface RestateProviderOptions {\n /**\n * `ctx.run` options forwarded on every wrapped stream call.\n *\n * Default depends on {@link streamCallbacks}:\n *\n * - `'suppress'` → `{ maxRetryAttempts: 3 }` — provider transients\n * (rate limits, 5xx, connection resets) get retried inside the\n * journal entry; nothing streams to the host, so a retry is\n * invisible.\n * - `'live'` (the default) → `{ maxRetryAttempts: 1 }`. A live stream\n * that fails AFTER emitting deltas would be re-run inside the same\n * journal entry, re-firing `onText` into the loop's accumulating\n * text buffer and producing duplicated partial1+full2 output. The\n * loop's own retry (which only re-issues on a clean slate) is the\n * correct retry layer in live mode.\n *\n * Once the cap is reached the failure becomes terminal and surfaces\n * through the loop's normal `AgentProviderError` path. Set this\n * explicitly to override either default — but with live callbacks,\n * `maxRetryAttempts > 1` re-introduces the duplicate-delta hazard.\n */\n runOptions?: RestateRunOptions\n /**\n * Override the journal-entry name. Receives the 1-indexed sequence\n * number for this wrapper instance and the `StreamOptions` of the\n * call. Default: `llm-call-<n>`. Override to inject extra structure\n * (e.g. `${agentName}/llm-call-${n}`) when multiple agents share a\n * service.\n */\n entryName?: (seq: number, opts: StreamOptions) => string\n /**\n * Whether live stream callbacks should fire during the original execution.\n * Use `'suppress'` for strict durable mode: the model call still returns a\n * full TurnResult, and UIs reconstruct from persisted session turns.\n *\n * Default: `'live'`.\n */\n streamCallbacks?: 'live' | 'suppress'\n /**\n * Per-invocation replay tracker (see `createReplayTracker`). When set,\n * the wrapper marks it live from inside the journaled closure — i.e.\n * the first time the inner provider is ACTUALLY invoked rather than\n * replayed from the journal. Share one tracker across the provider\n * and tool wrappers of the same handler invocation.\n */\n replayTracker?: ReplayTracker\n}\n\n/**\n * Wrap a Zidane `Provider` so its `stream()` method journals each turn's\n * response inside `ctx.run`. All other Provider methods (`formatTools`,\n * `userMessage`, `toolResultsMessage`, `classifyError`, …) pass through\n * unchanged — they're pure functions over already-deterministic inputs.\n */\nexport function restateProvider(\n inner: Provider,\n ctx: RestateContextLike,\n options: RestateProviderOptions = {},\n): Provider {\n // Live callbacks must not retry inside the journal entry — a stream that\n // fails after emitting deltas would re-fire `onText` on the retry and\n // duplicate text in the loop's accumulator. See the `runOptions` doc.\n const runOpts: RestateRunOptions = options.runOptions\n ?? (options.streamCallbacks === 'suppress' ? { maxRetryAttempts: 3 } : { maxRetryAttempts: 1 })\n const nameFor = options.entryName ?? ((seq: number) => `llm-call-${seq}`)\n let seq = 0\n\n return {\n ...inner,\n stream(streamOpts: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n seq += 1\n const durableCallbacks = options.streamCallbacks === 'suppress'\n ? suppressStreamingCallbacks(callbacks)\n : callbacks\n return ctx.run<TurnResult>(\n nameFor(seq, streamOpts),\n () => {\n // Reached only when the journal cursor is past this entry —\n // a replayed call returns the journaled TurnResult without\n // executing this closure.\n options.replayTracker?.markLive()\n return inner.stream(streamOpts, durableCallbacks)\n },\n runOpts,\n )\n },\n }\n}\n\nfunction suppressStreamingCallbacks(callbacks: StreamCallbacks): StreamCallbacks {\n return {\n ...callbacks,\n onText: () => {},\n onThinking: undefined,\n onServerToolUse: undefined,\n onServerToolResult: undefined,\n }\n}\n","/**\n * Replay-state tracking + replay-aware abort for Restate-backed agents.\n *\n * ## The problem\n *\n * `agent.abort()` during a journal replay is undefined behavior: the\n * loop throws `AgentAbortedError` at whatever point the replay cursor\n * happens to be, the host's catch arm typically journals something\n * (`report-fail`, a status write) mid-journal, and the invocation dies\n * with `[570 Journal mismatch]` — permanently, because every retry\n * replays into the same mismatch. Hosts have been inferring liveness\n * from side channels (e.g. counting live provider stream calls), which\n * works only because `restateProvider` replays `llm-call-N` without\n * touching the live provider — incidental coupling, not API.\n *\n * ## The seam\n *\n * The wrappers already know the answer: on replay, `ctx.run` returns\n * the journaled value WITHOUT invoking the wrapped closure; the first\n * time a closure actually executes, the journal cursor is past the\n * replayed prefix and the invocation is live. {@link createReplayTracker}\n * packages that signal:\n *\n * - `restateProvider(inner, ctx, { replayTracker })` and\n * `restateTool(tool, ctx, { replayTracker })` mark the tracker live\n * from inside their journaled closures.\n * - `tracker.isReplaying()` is the public liveness probe.\n * - `tracker.onLive(cb)` fires once at the replay→live transition\n * (immediately when already live).\n * - {@link replayAwareAbort} wraps `agent.abort` so external aborts\n * queue during replay and apply at the first live boundary — the\n * earliest point where the abort's side effects (loop throw, host\n * catch-arm journaling) append to the journal instead of forking it.\n *\n * ## Wiring\n *\n * ```ts\n * const tracker = createReplayTracker({ hooks: agent.hooks })\n * const provider = restateProvider(inner, ctx, { replayTracker: tracker })\n * const tools = wrapAgentTools(agentTools, ctx, { replayTracker: tracker })\n * const abort = replayAwareAbort(tracker, () => agent.abort())\n * // external cancel signal → abort() instead of agent.abort()\n * ```\n *\n * One tracker per handler invocation — like the wrappers themselves, it\n * is constructed fresh each attempt and must not be reused across\n * invocations.\n *\n * ## Semantics fine print\n *\n * \"Replaying\" is defined conservatively: `isReplaying()` returns `true`\n * until the first live closure executes. A fresh (non-resumed)\n * invocation therefore reports \"replaying\" for the short window before\n * its first journaled call — harmless for the abort use case (the abort\n * applies moments later, at that first live execution) and the honest\n * answer for journal safety: before the first `ctx.run` settles you\n * cannot know whether the SDK is about to feed you journal entries.\n *\n * A replay that completes without ever going live (the journaled run\n * already finished; the handler exits during replay) never fires\n * `onLive` — queued aborts are dropped with the invocation, which is\n * correct: there is nothing left to abort.\n */\n\n/** Minimal structural slice of the agent's Hookable for `replay:done`. */\nexport interface ReplayDoneHooks {\n callHook: (name: 'replay:done', ctx: Record<string, never>) => unknown\n}\n\nexport interface ReplayTrackerOptions {\n /**\n * Agent hooks to notify at the replay→live transition. When set, the\n * tracker fires `replay:done` exactly once, right after the `onLive`\n * callbacks. Rejections are swallowed (surfaced under `ZIDANE_DEBUG`)\n * — a misbehaving listener must not break the live execution that\n * triggered the transition.\n */\n hooks?: ReplayDoneHooks\n}\n\nexport interface ReplayTracker {\n /**\n * `true` until the first live (non-journaled) execution of a wrapped\n * provider stream / tool body. See the module doc for the fresh-\n * invocation caveat.\n */\n isReplaying: () => boolean\n /**\n * Register a callback for the replay→live transition. Fires once.\n * When the tracker is already live, the callback runs synchronously\n * before `onLive` returns. Returns an unregister function (no-op\n * after firing).\n */\n onLive: (cb: () => void) => () => void\n /**\n * Mark the invocation live. Called by the wrappers from inside their\n * journaled closures — a closure only executes when the journal\n * cursor is past it. Idempotent; only the first call transitions.\n */\n markLive: () => void\n}\n\n/**\n * Create a per-invocation replay tracker. Pass it to `restateProvider`\n * / `restateTool` / `wrapAgentTools` via their `replayTracker` option;\n * consume it via `isReplaying()` / `onLive()` / {@link replayAwareAbort}.\n */\nexport function createReplayTracker(options: ReplayTrackerOptions = {}): ReplayTracker {\n let live = false\n let callbacks: (() => void)[] = []\n\n return {\n isReplaying: () => !live,\n markLive: () => {\n if (live)\n return\n live = true\n const pending = callbacks\n callbacks = []\n for (const cb of pending) {\n try {\n cb()\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/restate] onLive callback threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }\n if (options.hooks) {\n Promise.resolve(options.hooks.callHook('replay:done', {})).catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/restate] replay:done hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n }\n },\n onLive: (cb) => {\n if (live) {\n cb()\n return () => {}\n }\n callbacks.push(cb)\n return () => {\n const idx = callbacks.indexOf(cb)\n if (idx >= 0)\n callbacks.splice(idx, 1)\n }\n },\n }\n}\n\n/**\n * Wrap an abort function so it defers during journal replay.\n *\n * Live invocation → `abort()` runs immediately, exactly like calling\n * `agent.abort()` directly. Replaying invocation → the abort is queued\n * and applied at the first live boundary (the start of the first\n * non-journaled provider stream or tool body), where the resulting\n * `AgentAbortedError` + host catch-arm journaling append to the journal\n * instead of forking it mid-replay. Multiple calls during replay\n * collapse into one queued abort.\n */\nexport function replayAwareAbort(tracker: ReplayTracker, abort: () => void): () => void {\n let queued = false\n return () => {\n if (!tracker.isReplaying()) {\n abort()\n return\n }\n if (queued)\n return\n queued = true\n tracker.onLive(abort)\n }\n}\n","/**\n * Restate-backed `SessionStore` — turn / run state lives in the bound\n * virtual object's K/V state instead of SQLite / file / remote HTTP.\n *\n * Trade-offs vs. journal-only replay\n * ----------------------------------\n * If you only care about durable execution within ONE invocation,\n * skip this — the journaled `ctx.run` entries (LLM calls, tool\n * executions) are enough to replay the loop to byte-identical state\n * after a crash. The in-memory `createMemoryStore()` is the right\n * choice; turns rematerialize from the journal each replay.\n *\n * Pick this store when EXTERNAL consumers (TUI, web GUI, support\n * tooling) need to read a session's history WITHOUT re-entering the\n * agent handler. Virtual-object state is exposed via the SDK's read\n * APIs (`ctx.get` from another handler, `restate-cli`, the Restate\n * web UI) — same data, different access path. The journal is\n * invocation-local; this state is the externally-visible projection\n * of it.\n *\n * Storage layout\n * --------------\n * One virtual-object key per session, three K/V slots:\n *\n * `session-data` → `SessionData` minus the `turns` array (small header)\n * `session-turns` → `SessionTurn[]` (the conversation history)\n * `session-runs` → `SessionRun[]` (run lifecycle records)\n *\n * Splitting `turns` / `runs` out keeps the header light for `load()` —\n * a handler that only needs metadata doesn't pull the full history.\n *\n * Listing / discovery\n * -------------------\n * `list()` returns `[]` because virtual objects don't expose cross-key\n * enumeration. `delete()` clears the current object's session keys only.\n * Use a separate index handler if you need discovery (one virtual object\n * per `projectRoot` that maintains an indexed list of session ids; this\n * store doesn't own that concern).\n * `generateSessionId` is omitted so the agent falls back to its\n * default UUID generator — for a session bound 1:1 to a virtual-\n * object key, pass `id: ctx.key` to `createSession()` instead and\n * skip ID generation entirely.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '../session'\nimport type { SessionTurn } from '../types'\nimport type { RestateObjectContextLike } from './types'\n\nconst KEY_DATA = 'session-data'\nconst KEY_TURNS = 'session-turns'\nconst KEY_RUNS = 'session-runs'\n\n/**\n * Construct a `SessionStore` backed by the bound virtual-object's K/V\n * state. The store is single-tenant — every method ignores the\n * `sessionId` argument because the virtual object is already keyed by\n * `ctx.key`. Calling `appendTurns('other-id', …)` writes to the\n * CURRENT object's state, not the one named in the argument. Bind one\n * virtual-object key per session and the contract holds.\n */\nexport function restateSessionStore(ctx: RestateObjectContextLike): SessionStore {\n let nextTurnSeq: number | undefined\n\n async function generateTurnId(): Promise<string> {\n if (nextTurnSeq === undefined) {\n const existing = (await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? []\n nextTurnSeq = existing.length\n }\n nextTurnSeq += 1\n return `turn_${nextTurnSeq}`\n }\n\n return {\n /**\n * Turn ids are deterministic from the persisted turn count. Random ids\n * are replay-stable only when the whole handler replays from the same\n * point; restarting from an arbitrary journal step can shift the RNG\n * sequence before a state write.\n */\n generateTurnId,\n\n async load(_sessionId: string): Promise<SessionData | null> {\n const header = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n const turns = sanitizeTurnsForState((await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? [])\n const runs = sanitizeRunsForState((await ctx.get<SessionRun[]>(KEY_RUNS)) ?? [])\n if (!header) {\n if (turns.length === 0 && runs.length === 0)\n return null\n return {\n id: _sessionId,\n turns,\n runs,\n status: inferStatus(runs),\n metadata: {},\n createdAt: inferCreatedAt(turns, runs),\n updatedAt: inferUpdatedAt(turns, runs),\n }\n }\n return { ...header, turns, runs }\n },\n\n async save(session: SessionData): Promise<void> {\n const { turns, runs, ...header } = session\n // Journal-stable header timestamps. The in-memory `Session` stamps\n // `createdAt` / `updatedAt` with raw `Date.now()` (its `touch()`\n // helper is synchronous and can't await a journaled clock), so the\n // values arriving here differ on every replay — and a replayed\n // `ctx.set` with drifting values is exactly the non-determinism\n // Restate exists to prevent. Source both from the journal instead:\n // `createdAt` sticks to the first persisted value, `updatedAt`\n // comes from `ctx.date.now()` (journaled — replay returns the\n // recorded reading).\n const prior = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n const now = await ctx.date.now()\n ctx.set(KEY_DATA, {\n ...header,\n createdAt: prior?.createdAt ?? now,\n updatedAt: now,\n })\n ctx.set(KEY_TURNS, sanitizeTurnsForState(turns))\n ctx.set(KEY_RUNS, sanitizeRunsForState(runs))\n },\n\n async delete(_sessionId: string): Promise<void> {\n ctx.clear(KEY_DATA)\n ctx.clear(KEY_TURNS)\n ctx.clear(KEY_RUNS)\n },\n\n /**\n * Always empty — virtual objects don't support cross-key\n * enumeration. Maintain a separate index handler if discovery\n * matters.\n */\n async list(): Promise<string[]> {\n return []\n },\n\n async appendTurns(_sessionId: string, turns: SessionTurn[]): Promise<void> {\n const existing = sanitizeTurnsForState((await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? [])\n ctx.set(KEY_TURNS, [...existing, ...sanitizeTurnsForState(turns)])\n },\n\n async getTurns(_sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n const turns = sanitizeTurnsForState((await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? [])\n return turns.slice(from, limit !== undefined ? from + limit : undefined)\n },\n\n async updateRun(_sessionId: string, run: SessionRun): Promise<void> {\n const runs = sanitizeRunsForState((await ctx.get<SessionRun[]>(KEY_RUNS)) ?? [])\n const idx = runs.findIndex(r => r.id === run.id)\n const next = [...runs]\n if (idx >= 0) {\n // Journal-stable `startedAt`: the in-memory `Session.startRun`\n // stamps it with raw `Date.now()`, which re-evaluates on every\n // replay. Keep the previously persisted value (mirrors the\n // `createdAt: prior?.createdAt ?? now` pattern in `save()`) so a\n // replayed `ctx.set` payload doesn't drift.\n next[idx] = { ...sanitizeRunForState(run), startedAt: runs[idx].startedAt }\n }\n else {\n next.push(sanitizeRunForState(run))\n }\n ctx.set(KEY_RUNS, next)\n },\n\n async updateStatus(_sessionId: string, status: SessionData['status']): Promise<void> {\n const header = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n const now = await ctx.date.now()\n if (header) {\n ctx.set(KEY_DATA, { ...header, status, updatedAt: now })\n }\n else {\n ctx.set(KEY_DATA, {\n id: _sessionId,\n status,\n metadata: {},\n createdAt: now,\n updatedAt: now,\n })\n }\n },\n }\n}\n\nfunction sanitizeTurnsForState(turns: readonly SessionTurn[]): SessionTurn[] {\n return turns.map(sanitizeTurnForState)\n}\n\nfunction sanitizeTurnForState(turn: SessionTurn): SessionTurn {\n if (!turn.usage || turn.usage.timeToFirstTokenMs === undefined)\n return turn\n\n const { timeToFirstTokenMs: _timeToFirstTokenMs, ...usage } = turn.usage\n return { ...turn, usage }\n}\n\nfunction sanitizeRunsForState(runs: readonly SessionRun[]): SessionRun[] {\n return runs.map(sanitizeRunForState)\n}\n\nfunction sanitizeRunForState(run: SessionRun): SessionRun {\n if (!run.turnUsage?.some(usage => usage.timeToFirstTokenMs !== undefined))\n return run\n\n return {\n ...run,\n turnUsage: run.turnUsage.map((usage) => {\n if (usage.timeToFirstTokenMs === undefined)\n return usage\n const { timeToFirstTokenMs: _timeToFirstTokenMs, ...stableUsage } = usage\n return stableUsage\n }),\n }\n}\n\nfunction inferStatus(runs: readonly SessionRun[]): SessionData['status'] {\n if (runs.some(run => run.status === 'running'))\n return 'running'\n if (runs.some(run => run.status === 'error'))\n return 'error'\n if (runs.some(run => run.status === 'completed'))\n return 'completed'\n return 'idle'\n}\n\nfunction inferCreatedAt(turns: readonly SessionTurn[], runs: readonly SessionRun[]): number {\n return minDefined([\n ...turns.map(turn => turn.createdAt),\n ...runs.map(run => run.startedAt),\n ]) ?? 0\n}\n\nfunction inferUpdatedAt(turns: readonly SessionTurn[], runs: readonly SessionRun[]): number {\n return maxDefined([\n ...turns.map(turn => turn.createdAt),\n ...runs.map(run => run.endedAt ?? run.startedAt),\n ]) ?? inferCreatedAt(turns, runs)\n}\n\nfunction minDefined(values: readonly number[]): number | undefined {\n let min: number | undefined\n for (const value of values) {\n if (min === undefined || value < min)\n min = value\n }\n return min\n}\n\nfunction maxDefined(values: readonly number[]): number | undefined {\n let max: number | undefined\n for (const value of values) {\n if (max === undefined || value > max)\n max = value\n }\n return max\n}\n","/**\n * Restate-backed tool wrappers.\n *\n * Wraps `ToolDef.execute` so every tool invocation is journaled. The\n * tool body runs on the original execution; after Restate persists the\n * journal entry, replay returns the recorded result without re-executing.\n * If the process dies before the entry is durable, the latest in-flight\n * tool can run again, so non-idempotent tools still need idempotency keys.\n *\n * `tool:gate`, `tool:transform`, `tool:after` hooks fire OUTSIDE the\n * journaled boundary, so they re-execute on replay. Built-in handlers\n * are deterministic given the journaled input + output (truncation,\n * image-stripping, `<edit-outcomes>` merge — all pure). Consumer hooks\n * that hit external services should themselves wrap side effects in\n * `ctx.run` so the substitution / annotation is replay-stable.\n *\n * `isConcurrencySafe` is preserved — Restate's per-key serialization\n * doesn't preclude in-handler parallelism. The loop's scheduler still\n * fans out safe tools up to `behavior.maxConcurrentTools`, each call\n * lands in its own journal entry. Order-of-journal-completion does NOT\n * affect replay correctness: Restate journals by call-site name, not\n * by completion order.\n */\n\nimport type { DetachedTasksCapability } from '../contexts'\nimport type { Session } from '../session'\nimport type { ToolContext, ToolDef } from '../tools/types'\nimport type { ToolResultContent } from '../types'\nimport type { RestatePromiseRaceLike } from './awakeable'\nimport type { ReplayTracker } from './replay'\nimport type { RestateContextLike, RestateRunOptions } from './types'\nimport { hydrateReadStateFromSessionAsync } from '../tools/read-state'\nimport { awakeableWithTimeout } from './awakeable'\n\n/**\n * Opt-in two-phase journaling for long-running, re-attachable tools.\n *\n * The default `restateTool` wrapping journals only the COMPLETED result —\n * a crash mid-execute re-runs the tool on replay, which is unacceptable\n * for builds / migrations / deploys. `durableStart` splits the execution:\n *\n * 1. `start` runs inside its own `ctx.run` entry, journaled BEFORE the\n * body — it kicks off the side effect and returns a re-attach token\n * (a background task id, a job handle, an idempotency key).\n * 2. the wait — either `attach` (one `ctx.run` holding the wait in the\n * result entry; only for short, bounded waits) or `wait` (awakeable\n * park: register a resumer in a journaled entry, suspend outside\n * the journal, synthesize the result in a final entry). On replay\n * both receive the SAME token and re-attach to the already-running\n * work instead of starting it again.\n *\n * The wrapped tool's own `execute` is bypassed; the start + wait pair IS\n * the execution.\n */\nexport interface RestateDurableStart<Token = unknown, Resumed = unknown> {\n /** Kick off the side effect; journaled before `attach`. Must be cheap + idempotent-safe up to journal persistence. */\n start: (input: Record<string, unknown>, toolCtx: ToolContext) => Promise<Token> | Token\n /**\n * Await / re-attach to the work identified by `token` and produce the\n * tool result. Runs inside ONE `ctx.run` entry, so the body must\n * settle within the runtime's attempt lifecycle — a long busy-poll in\n * here starves the journal, trips the server's inactivity/abort\n * timeouts, and each retry restarts the wait from scratch. Use this\n * only for waits bounded well below the inactivity timeout (or as the\n * degraded mode on hosts without a push seam); prefer {@link wait}\n * for anything long-running.\n *\n * Required unless {@link wait} is provided; ignored when both are set.\n */\n attach?: (token: Token, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<string | ToolResultContent[]>\n /**\n * Awakeable-based completion wait — the durable alternative to\n * {@link attach} for long-running work on hosts with a push seam\n * (e.g. a runner that fires `task:exit` → resumer → `ctx.resolveAwakeable`).\n * `executeTwoPhase` orchestrates three phases with the same journal\n * discipline as `restateAwakeableTool`:\n *\n * 1. `register` — a short, journaled `ctx.run`: hand the awakeable\n * id to the host as the exit resumer for `token`. Fires exactly\n * once across replays (marker invariant included).\n * 2. park — `await awakeable.promise`, OUTSIDE any `ctx.run`. The\n * invocation suspends cleanly: no held worker, immune to\n * inactivity/abort timeouts. With `timeoutMs`, the park is a\n * durable race against `ctx.sleep` (replay-stable — no\n * `Date.now()` deadline that resets per attempt).\n * 3. `result` — a final journaled `ctx.run`: synthesize the tool\n * result from the resumer payload (`null` = timeout; kill the\n * job and shape the timeout result here).\n */\n wait?: RestateDurableWait<Token, Resumed>\n /**\n * Best-effort cleanup when the per-call abort signal fires before the\n * wait settles — `attach` or the awakeable park alike (run abort,\n * `agent.cancelTool`, sibling-cascade\n * cancel, `agent.destroy()` mid-run). Without it, the journaled token\n * identifies a remote job that keeps running with nobody attached —\n * a leak for hosts whose jobs outlive the session.\n *\n * Contract:\n * - Fires at most once per call, outside the journal (an aborting\n * invocation can't open new `ctx.run` entries reliably) — journal\n * your own cleanup on the host side if it must be durable.\n * - May run concurrently with a still-unwinding `attach`; make it\n * idempotent against \"job already gone\".\n * - NOT called on process death — that's not termination: the\n * journaled token re-attaches on replay instead. This seam is for\n * deliberate aborts, where no replay is coming.\n * - Errors are swallowed (surfaced under `ZIDANE_DEBUG`) — cleanup\n * failure must not mask the abort itself.\n */\n onAbort?: (token: Token, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<void> | void\n}\n\n/**\n * Awakeable-based completion wait for {@link RestateDurableStart.wait}.\n *\n * Replay contract (mirrors `restateAwakeableTool`):\n * - The awakeable is minted BEFORE the journaled `register` entry, so on\n * replay both come back in the same order with the same id — a replayed\n * call re-parks on the SAME awakeable without re-registering.\n * - The journaled marker records the awakeable id it was registered for;\n * a mismatch on replay (journal drift) throws instead of parking on an\n * id nobody will ever resolve.\n */\nexport interface RestateDurableWait<Token = unknown, Resumed = unknown> {\n /**\n * Register `awakeableId` as the exit resumer for the work identified\n * by `token` — the host must call `ctx.resolveAwakeable(awakeableId,\n * payload)` when the work completes. Runs inside `ctx.run`; must be\n * short and idempotent-safe up to journal persistence.\n */\n register: (token: Token, awakeableId: string, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<void> | void\n /**\n * Synthesize the tool result once the park settles. `resumed` is the\n * resumer's payload, or `null` when `timeoutMs` elapsed first — kill\n * the still-running job and shape the timeout result in that branch.\n * Runs inside the final journaled `ctx.run` (the result entry).\n */\n result: (token: Token, resumed: Resumed | null, input: Record<string, unknown>, toolCtx: ToolContext) => Promise<string | ToolResultContent[]> | string | ToolResultContent[]\n /**\n * Durable park deadline in milliseconds — becomes the\n * `awakeableWithTimeout` duration (a `ctx.sleep` raced against the\n * awakeable, replay-stable). A function receives the call's input so\n * the model's own `timeout` parameter can drive it. Unset /\n * non-positive = park indefinitely.\n */\n timeoutMs?: number | ((input: Record<string, unknown>, toolCtx: ToolContext) => number | undefined)\n /**\n * Durable race combinator for the timeout race — pass `RestatePromise`\n * from the SDK. See `awakeableWithTimeout`'s rationale; native\n * `Promise.race` is the fallback.\n */\n promises?: RestatePromiseRaceLike\n /**\n * Per-call fast path: when this returns `true` for the journaled\n * `token`, the awakeable park is SKIPPED entirely — no awakeable is\n * minted, no `-register` entry is journaled, and the invocation never\n * suspends. `result` is invoked directly with `resumed = null` inside\n * the final result entry.\n *\n * This is for `start` phases that sometimes have nothing to wait on\n * (e.g. a shell tool that only spawns a real background task above a\n * duration threshold and otherwise runs the command inline in\n * `result`). Parking such calls pays a full suspend → resolve →\n * re-invoke → replay round-trip for nothing.\n *\n * MUST be a pure function of the journaled `token` (+ input) so the\n * decision is identical across replays — a token-derived branch keeps\n * the journal entry shape deterministic. When omitted, every call\n * parks (the original behaviour).\n */\n inline?: (token: Token, input: Record<string, unknown>, toolCtx: ToolContext) => boolean\n}\n\n/** Journaled registration marker — see the marker invariant in {@link RestateDurableWait}. */\ninterface DurableWaitRegisterMarker {\n registered: true\n awakeableId: string\n}\n\nexport interface RestateToolOptions<Token = unknown, Resumed = unknown> {\n /**\n * `ctx.run` options forwarded on every wrapped tool call. Defaults to\n * `{ maxRetryAttempts: 1 }` — tool errors are typically deterministic\n * (validation failures, missing files) so blind retries waste budget.\n * Override per-tool for network-heavy custom tools that benefit from\n * SDK-level retries.\n */\n runOptions?: RestateRunOptions\n /**\n * Override the journal-entry name. Receives the canonical tool name\n * and the call's input. Default: `tool-<name>-<seq>` where `<seq>` is\n * a per-wrapper monotonic counter. Override to inject the model's\n * `callId` (read it off `ToolContext.callId` inside `execute` if you\n * need an LLM-visible key — but the default counter is sufficient\n * for journal correctness).\n */\n entryName?: (seq: number, name: string, input: Record<string, unknown>) => string\n /**\n * Hide and reject the built-in shell tool's background mode.\n *\n * Default: derived from {@link RestateToolOptions.detachedTasks} —\n * disabled unless the execution context declares `'durable'` detached\n * tasks. An explicit boolean always wins.\n */\n disableShellBackground?: boolean\n /**\n * `detachedTasks` capability of the execution context the agent will\n * run with (see `ContextCapabilities.detachedTasks` /\n * `resolveDetachedTasksCapability`). Drives the\n * `disableShellBackground` default: a `'durable'` context keeps its\n * background tasks across worker crashes, so the wrapper leaves the\n * shell tool's background mode intact.\n *\n * Default: `undefined`, treated as non-durable (background disabled).\n */\n detachedTasks?: DetachedTasksCapability\n /**\n * Two-phase start/attach journaling for crash-safe long tools — see\n * {@link RestateDurableStart}. Per-tool by nature; not accepted by\n * `wrapAgentTools` (a single start/attach pair can't fit a whole map).\n */\n durableStart?: RestateDurableStart<Token, Resumed>\n /**\n * Per-invocation replay tracker (see `createReplayTracker`). When set,\n * the wrapper marks it live from inside its journaled closures — i.e.\n * the first time a tool body (or a `durableStart.start` phase)\n * ACTUALLY executes rather than replaying from the journal. Share one\n * tracker across the provider and tool wrappers of the same handler\n * invocation.\n */\n replayTracker?: ReplayTracker\n}\n\nconst SHELL_BACKGROUND_DISABLED_MESSAGE = 'shell error: background mode is disabled for Restate-backed agents. Fall back to foreground (drop `run_in_background`).'\nconst HYDRATED_READ_STATE_SESSIONS = new WeakSet<Session>()\n\n/**\n * Wrap a single `ToolDef` so its `execute` runs inside `ctx.run`. The\n * tool's `spec` and `isConcurrencySafe` pass through unchanged.\n */\nexport function restateTool<Token = unknown, Resumed = unknown>(\n inner: ToolDef,\n ctx: RestateContextLike,\n options: RestateToolOptions<Token, Resumed> = {},\n): ToolDef {\n const runOpts: RestateRunOptions = options.runOptions ?? { maxRetryAttempts: 1 }\n const nameFor = options.entryName ?? ((seq: number, name: string) => `tool-${name}-${seq}`)\n const canonicalName = inner.spec.name\n const disableShellBackground = options.disableShellBackground ?? (options.detachedTasks !== 'durable')\n const shellBackgroundDisabled = disableShellBackground && canonicalName === 'shell'\n const durableStart = options.durableStart\n if (durableStart && !durableStart.attach && !durableStart.wait)\n throw new Error(`restateTool(${canonicalName}): durableStart needs an \\`attach\\` (in-journal wait) or a \\`wait\\` (awakeable park) phase`)\n let seq = 0\n const hydrateOnce = async (toolCtx: Parameters<ToolDef['execute']>[1]) => {\n if (toolCtx.session && !HYDRATED_READ_STATE_SESSIONS.has(toolCtx.session)) {\n HYDRATED_READ_STATE_SESSIONS.add(toolCtx.session)\n await hydrateReadStateFromSessionAsync(toolCtx.session, toolCtx.handle.cwd, {\n defaultLineNumbers: toolCtx.behavior?.readLineNumbers ?? true,\n readFileForHash: async (path) => {\n try {\n return await toolCtx.execution.readFile(toolCtx.handle, path)\n }\n catch {\n return null\n }\n },\n })\n }\n }\n // IMPORTANT: NOT async, and the `ctx.run` promise is returned without\n // native combinators — `durableExecute` consumers (the Restate batch\n // scheduler) need the runtime's own promise shape (`.map`) intact.\n const executeDurably = (input: Record<string, unknown>, toolCtx: Parameters<ToolDef['execute']>[1]) => {\n seq += 1\n return ctx.run(\n nameFor(seq, canonicalName, input),\n async () => {\n // Closure executes only when live — replays return the journaled\n // result without entering here.\n options.replayTracker?.markLive()\n await hydrateOnce(toolCtx)\n if (shellBackgroundDisabled && input.run_in_background === true)\n return SHELL_BACKGROUND_DISABLED_MESSAGE\n return inner.execute(input, toolCtx)\n },\n runOpts,\n )\n }\n\n // Two-phase mode: journal the start token BEFORE the body so a crash\n // mid-execution re-attaches on replay instead of re-running. The await\n // between the two entries makes this a native-async composition, so the\n // tool deliberately does NOT advertise `durableExecute` — the batch\n // scheduler runs it sequentially via plain `execute`, which is the\n // right shape for long re-attachable work anyway.\n const executeTwoPhase = async (input: Record<string, unknown>, toolCtx: Parameters<ToolDef['execute']>[1]) => {\n const start = durableStart!\n seq += 1\n const entry = nameFor(seq, canonicalName, input)\n const token = await ctx.run(\n `${entry}-start`,\n async () => {\n options.replayTracker?.markLive()\n await hydrateOnce(toolCtx)\n return start.start(input, toolCtx)\n },\n runOpts,\n )\n\n // Abort seam — see RestateDurableStart.onAbort. At-most-once via the\n // `fired` latch; armed only while the wait is in flight.\n const onAbort = start.onAbort\n const signal = toolCtx.signal as AbortSignal | undefined\n let fired = false\n const fireAbortCleanup = (): void => {\n if (fired || !onAbort)\n return\n fired = true\n Promise.resolve(onAbort(token, input, toolCtx)).catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/restate] durableStart onAbort threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n }\n\n const wait = start.wait\n if (wait) {\n // Per-call fast path: nothing to wait on for this token (decided\n // purely from the journaled token, so replay-stable). Skip minting\n // the awakeable, the `-register` entry, and the suspend/resume\n // round-trip entirely — run the result inline. A short command that\n // executes in the result entry shouldn't pay a park it never uses.\n if (wait.inline?.(token, input, toolCtx)) {\n if (!onAbort || !signal) {\n return await ctx.run(entry, () => {\n options.replayTracker?.markLive()\n return wait.result(token, null, input, toolCtx)\n }, runOpts)\n }\n if (signal.aborted)\n fireAbortCleanup()\n else\n signal.addEventListener('abort', fireAbortCleanup, { once: true })\n try {\n return await ctx.run(entry, () => {\n options.replayTracker?.markLive()\n return wait.result(token, null, input, toolCtx)\n }, runOpts)\n }\n finally {\n signal.removeEventListener('abort', fireAbortCleanup)\n }\n }\n // Awakeable path — register / park / result. The park lives\n // OUTSIDE any ctx.run, so the invocation suspends cleanly instead\n // of busy-holding an attempt until the inactivity timeout fires.\n //\n // Pre-aborted: nobody will consume the result — clean up the\n // journaled start and bail without registering a resumer.\n if (signal?.aborted) {\n fireAbortCleanup()\n throw new Error(`${canonicalName}: aborted before durable wait registration`)\n }\n signal?.addEventListener('abort', fireAbortCleanup, { once: true })\n try {\n const timeoutMs = typeof wait.timeoutMs === 'function' ? wait.timeoutMs(input, toolCtx) : wait.timeoutMs\n // Mint BEFORE the journaled registration (same ordering contract\n // as restateAwakeableTool) — replay returns the same id.\n const awakeable = awakeableWithTimeout<Resumed>(ctx, {\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n ...(wait.promises ? { promises: wait.promises } : {}),\n })\n const marker = await ctx.run<DurableWaitRegisterMarker>(\n `${entry}-register`,\n async () => {\n options.replayTracker?.markLive()\n await wait.register(token, awakeable.id, input, toolCtx)\n return { registered: true, awakeableId: awakeable.id }\n },\n runOpts,\n )\n if (marker.awakeableId !== awakeable.id) {\n throw new Error(\n `restateTool(${canonicalName}): journal drift — registration marker carries awakeable \"${marker.awakeableId}\" but this replay minted \"${awakeable.id}\". Refusing to park on an unreachable awakeable.`,\n )\n }\n const resumed = await parkWithSignal(awakeable.promise, signal, canonicalName)\n return await ctx.run(\n entry,\n () => {\n options.replayTracker?.markLive()\n return wait.result(token, resumed, input, toolCtx)\n },\n runOpts,\n )\n }\n finally {\n signal?.removeEventListener('abort', fireAbortCleanup)\n }\n }\n\n const attach = () => ctx.run(\n entry,\n () => {\n options.replayTracker?.markLive()\n return start.attach!(token, input, toolCtx)\n },\n runOpts,\n )\n\n if (!onAbort || !signal)\n return attach()\n\n // Pre-aborted: the journaled start already launched the job — clean\n // it up, then still run attach so the call surfaces the host's own\n // cancellation shape (it should fail fast on an aborted signal).\n if (signal.aborted) {\n fireAbortCleanup()\n return attach()\n }\n signal.addEventListener('abort', fireAbortCleanup, { once: true })\n try {\n return await attach()\n }\n finally {\n signal.removeEventListener('abort', fireAbortCleanup)\n }\n }\n\n if (durableStart) {\n // Strip any pre-existing `durableExecute` — see the executeTwoPhase\n // comment; advertising one here would hand the batch scheduler a\n // native promise it can't `.map`.\n const { durableExecute: _dropped, ...rest } = inner\n return {\n ...rest,\n spec: shellBackgroundDisabled ? withoutShellBackgroundInput(inner.spec) : inner.spec,\n execute: executeTwoPhase,\n }\n }\n\n return {\n ...inner,\n spec: shellBackgroundDisabled ? withoutShellBackgroundInput(inner.spec) : inner.spec,\n execute: executeDurably,\n durableExecute: executeDurably,\n }\n}\n\n/**\n * Park on `promise`, but stop waiting when the per-call abort signal\n * fires (run abort, `cancelTool`, sibling-cascade cancel) — a deliberate\n * abort means nobody will consume the result, and like `onAbort` it\n * implies no replay is coming, so the native (non-journaled) race is\n * replay-safe. The underlying awakeable stays registered; a late\n * resolution lands on a completed invocation and is dropped by the\n * runtime.\n */\nfunction parkWithSignal<T>(promise: Promise<T>, signal: AbortSignal | undefined, name: string): Promise<T> {\n if (!signal)\n return promise\n if (signal.aborted)\n return Promise.reject(new Error(`${name}: aborted while parked on durable wait`))\n return new Promise<T>((resolve, reject) => {\n const onAbortEvt = (): void => reject(new Error(`${name}: aborted while parked on durable wait`))\n signal.addEventListener('abort', onAbortEvt, { once: true })\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbortEvt)\n resolve(value)\n },\n (err: unknown) => {\n signal.removeEventListener('abort', onAbortEvt)\n reject(err instanceof Error ? err : new Error(String(err)))\n },\n )\n })\n}\n\nfunction withoutShellBackgroundInput(spec: ToolDef['spec']): ToolDef['spec'] {\n const inputSchema = spec.inputSchema\n const properties = isRecord(inputSchema.properties) ? inputSchema.properties : undefined\n if (!properties || !Object.hasOwn(properties, 'run_in_background'))\n return spec\n\n const { run_in_background: _runInBackground, ...nextProperties } = properties\n const required = Array.isArray(inputSchema.required)\n ? inputSchema.required.filter(name => name !== 'run_in_background')\n : inputSchema.required\n return {\n ...spec,\n description: stripShellBackgroundDescription(spec.description),\n inputSchema: {\n ...inputSchema,\n properties: nextProperties,\n ...(required !== inputSchema.required ? { required } : {}),\n },\n }\n}\n\nfunction stripShellBackgroundDescription(description: string): string {\n return description\n .split('\\n')\n .filter(line =>\n !line.includes('run_in_background')\n && !line.includes('<task-notification>')\n && !line.includes('background task'),\n )\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Wrap an entire tool map at once — convenience for the common case of\n * \"journal every tool the agent has\". Pass-through for tools whose\n * names are listed in `opts.exclude` (e.g. fully-deterministic local\n * helpers where journaling is pure overhead).\n *\n * The returned map is a fresh object; the input is untouched.\n */\nexport function wrapAgentTools(\n tools: Record<string, ToolDef>,\n ctx: RestateContextLike,\n opts: Omit<RestateToolOptions, 'durableStart'> & { exclude?: readonly string[] } = {},\n): Record<string, ToolDef> {\n const excludeSet = new Set(opts.exclude ?? [])\n // `detachedTasks: 'durable'` implies the host's `waitBackground` parks\n // on an awakeable (that's the durable wait pattern) — and a Restate\n // suspension point must NOT nest inside a journaled `ctx.run` closure.\n // Auto-exclude the built-in `wait_task` from wrapping so its park runs\n // outside the journal; the wait result is replay-safe anyway (a replayed\n // wait re-attaches to an already-terminated task and resolves from the\n // registry). Hosts with a journal-safe wait (e.g. a bridge RPC poll)\n // can re-wrap explicitly via `restateTool(waitTask, ctx, …)`.\n const skipWaitTask = opts.detachedTasks === 'durable'\n const wrapped: Record<string, ToolDef> = {}\n for (const [name, def] of Object.entries(tools)) {\n const passThrough = excludeSet.has(name) || (skipWaitTask && def.spec.name === 'wait_task')\n wrapped[name] = passThrough ? def : restateTool(def, ctx, opts)\n }\n return wrapped\n}\n"],"mappings":"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"}
|
package/dist/session/sqlite.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","names":[],"sources":["../../src/session/sqlite.ts"],"mappings":";;;UAoFiB,kBAAA;;EAEf,IAAI;AAAA;AAAA,iBAGU,iBAAA,CAAkB,OAAA,EAAS,kBAAA,GAAqB,YAAY;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","names":[],"sources":["../../src/session/sqlite.ts"],"mappings":";;;UAoFiB,kBAAA;;EAEf,IAAI;AAAA;AAAA,iBAGU,iBAAA,CAAkB,OAAA,EAAS,kBAAA,GAAqB,YAAY;;;;;;;;;;;iBAkmB5D,cAAA,CAAe,MAAA,WAAiB,YAAY"}
|
package/dist/session/sqlite.js
CHANGED
|
@@ -81,8 +81,8 @@ function createSqliteStore(options) {
|
|
|
81
81
|
id: header.id,
|
|
82
82
|
...header.agent_id ? { agentId: header.agent_id } : {},
|
|
83
83
|
...header.project_root ? { projectRoot: header.project_root } : {},
|
|
84
|
-
turns: turnRows.map((r) =>
|
|
85
|
-
runs: runRows.map((r) =>
|
|
84
|
+
turns: turnRows.map((r) => safeParseRow(r.data, header.id, "turn")).filter((t) => t !== null),
|
|
85
|
+
runs: runRows.map((r) => safeParseRow(r.data, header.id, "run")).filter((r) => r !== null),
|
|
86
86
|
status: header.status,
|
|
87
87
|
metadata: safeParseRecord(header.metadata),
|
|
88
88
|
createdAt: header.created_at,
|
|
@@ -151,13 +151,16 @@ function createSqliteStore(options) {
|
|
|
151
151
|
},
|
|
152
152
|
async getTurns(sessionId, from = 0, limit) {
|
|
153
153
|
const lim = typeof limit === "number" ? limit : -1;
|
|
154
|
-
return stmtLoadTurnsRange.all(sessionId, lim, from).map((r) =>
|
|
154
|
+
return stmtLoadTurnsRange.all(sessionId, lim, from).map((r) => safeParseRow(r.data, sessionId, "turn")).filter((t) => t !== null);
|
|
155
155
|
},
|
|
156
156
|
async updateRun(sessionId, run) {
|
|
157
157
|
txnUpdateRun.immediate(sessionId, run);
|
|
158
158
|
},
|
|
159
159
|
async updateStatus(sessionId, status) {
|
|
160
160
|
txnUpdateStatus.immediate(sessionId, status);
|
|
161
|
+
},
|
|
162
|
+
close() {
|
|
163
|
+
db.close(false);
|
|
161
164
|
}
|
|
162
165
|
};
|
|
163
166
|
}
|
|
@@ -180,6 +183,8 @@ function createSqliteStore(options) {
|
|
|
180
183
|
* `INSERT`. The `.bak` file lets the user roll back and report the bug.
|
|
181
184
|
*/
|
|
182
185
|
function migrate(db, dbPath) {
|
|
186
|
+
const onDiskVersion = db.query("PRAGMA user_version").get()?.user_version ?? 0;
|
|
187
|
+
if (onDiskVersion > SCHEMA_VERSION) throw new Error(`[zidane/sqlite] "${dbPath}" has schema version ${onDiskVersion}, but this zidane build only supports up to ${SCHEMA_VERSION}. It was written by a newer zidane — upgrade zidane, or point this build at a different sessions DB.`);
|
|
183
188
|
const sessionCols = db.query("PRAGMA table_info(sessions)").all();
|
|
184
189
|
const hasCol = (n) => sessionCols.some((c) => c.name === n);
|
|
185
190
|
if (!hasCol("project_root")) db.run("ALTER TABLE sessions ADD COLUMN project_root TEXT");
|
|
@@ -224,11 +229,18 @@ function backupBeforeV3Migration(db, dbPath) {
|
|
|
224
229
|
if (!existsSync(dbPath)) return;
|
|
225
230
|
const backupPath = `${dbPath}.zidane-pre-v4.bak`;
|
|
226
231
|
if (existsSync(backupPath)) return;
|
|
232
|
+
let checkpointed = true;
|
|
227
233
|
try {
|
|
228
234
|
db.run("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
229
|
-
} catch {
|
|
235
|
+
} catch {
|
|
236
|
+
checkpointed = false;
|
|
237
|
+
}
|
|
230
238
|
try {
|
|
231
239
|
copyFileSync(dbPath, backupPath);
|
|
240
|
+
if (!checkpointed) for (const ext of ["-wal", "-shm"]) {
|
|
241
|
+
const sidecar = `${dbPath}${ext}`;
|
|
242
|
+
if (existsSync(sidecar)) copyFileSync(sidecar, `${backupPath}${ext}`);
|
|
243
|
+
}
|
|
232
244
|
process.stderr.write(`[zidane/sqlite] v3→v4 migration: backed up your sessions DB to "${backupPath}".\n To roll back if anything looks wrong:\n 1. quit zidane\n 2. cp "${backupPath}" "${dbPath}" && rm -f "${dbPath}-wal" "${dbPath}-shm"\n 3. pin an older zidane that knows the v3 schema, or upgrade to a fix\n Delete the .bak once every session loads correctly.\n`);
|
|
233
245
|
} catch (err) {
|
|
234
246
|
throw new Error(`[zidane/sqlite] failed to back up "${dbPath}" before v3→v4 migration: ${errorMessage(err)}. Refusing to modify the DB without a recovery path. Move the file aside or fix the permissions and retry.`);
|
|
@@ -282,6 +294,26 @@ function backfillV3Blobs(db) {
|
|
|
282
294
|
}
|
|
283
295
|
}
|
|
284
296
|
/**
|
|
297
|
+
* Guarded per-row parse for `turns.data` / `runs.data`. One corrupt row
|
|
298
|
+
* (truncated write, hand-edited DB, disk bitflip) must not make the whole
|
|
299
|
+
* session unloadable — log + skip it instead, mirroring the migration's
|
|
300
|
+
* per-row backfill policy. The structural check matches the minimal shape
|
|
301
|
+
* both `SessionTurn` and `SessionRun` share (an object with a string `id`).
|
|
302
|
+
*/
|
|
303
|
+
function safeParseRow(text, sessionId, kind) {
|
|
304
|
+
try {
|
|
305
|
+
const parsed = JSON.parse(text);
|
|
306
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.id !== "string") {
|
|
307
|
+
console.error(`[zidane/sqlite] session "${sessionId}": skipping malformed ${kind} row (not a ${kind}-shaped object)`);
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
return parsed;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
console.error(`[zidane/sqlite] session "${sessionId}": skipping corrupt ${kind} row: ${errorMessage(err)}`);
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
285
317
|
* `JSON.parse` with a fallback to `{}` for the metadata column. v3→v4
|
|
286
318
|
* migration writes well-formed JSON, fresh v4 writes go through
|
|
287
319
|
* `JSON.stringify(metadata ?? {})` — but a hand-edited DB or a botched
|