zidane 5.13.11 → 5.13.13
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/dist/{acp-HSVnQBfG.js → acp-CqXcM2Km.js} +7 -7
- package/dist/{acp-HSVnQBfG.js.map → acp-CqXcM2Km.js.map} +1 -1
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{agent-w6htlFlx.d.ts → agent-NkKgz5Dh.d.ts} +38 -2
- package/dist/agent-NkKgz5Dh.d.ts.map +1 -0
- package/dist/{auth-nA0ZMTbM.js → auth-CGTf8v1_.js} +2 -2
- package/dist/{auth-nA0ZMTbM.js.map → auth-CGTf8v1_.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +6 -6
- package/dist/chat.js +3 -3
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +2 -2
- package/dist/{headless-0O6HMNBQ.js → headless-C6Idunwh.js} +6 -6
- package/dist/{headless-0O6HMNBQ.js.map → headless-C6Idunwh.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-BH0lvT7N.d.ts → index-BFY7mcar.d.ts} +2 -2
- package/dist/{index-BH0lvT7N.d.ts.map → index-BFY7mcar.d.ts.map} +1 -1
- package/dist/{index-C4sPfyg6.d.ts → index-BgB_425D.d.ts} +43 -3
- package/dist/index-BgB_425D.d.ts.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -10
- package/dist/{logger-MCqSKpby.d.ts → logger-LQmSBfD_.d.ts} +2 -2
- package/dist/{logger-MCqSKpby.d.ts.map → logger-LQmSBfD_.d.ts.map} +1 -1
- package/dist/{login-D5rb4IG8.js → login-DE-_d045.js} +2 -2
- package/dist/{login-D5rb4IG8.js.map → login-DE-_d045.js.map} +1 -1
- package/dist/{mcp-C_TIj91j.js → mcp-2OGi_NQu.js} +2 -2
- package/dist/{mcp-C_TIj91j.js.map → mcp-2OGi_NQu.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-DEsLGBB9.js → messages-U_87Z7GH.js} +2 -2
- package/dist/{messages-DEsLGBB9.js.map → messages-U_87Z7GH.js.map} +1 -1
- 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/{presets-HDIxliiq.js → presets-eC4VwuHh.js} +2 -2
- package/dist/{presets-HDIxliiq.js.map → presets-eC4VwuHh.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-OBIysrMe.js → providers-DyMPTo51.js} +3 -3
- package/dist/{providers-OBIysrMe.js.map → providers-DyMPTo51.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/{read-state-DH2IuQHX.js → read-state-CLK9yVpm.js} +2 -2
- package/dist/{read-state-DH2IuQHX.js.map → read-state-CLK9yVpm.js.map} +1 -1
- package/dist/restate.d.ts +88 -3
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +185 -59
- package/dist/restate.js.map +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-BDWZZaYa.js → session-DQ4bEncf.js} +2 -2
- package/dist/{session-BDWZZaYa.js.map → session-DQ4bEncf.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/{tool-formatters-C7N1Pb1q.d.ts → tool-formatters-DvtGhbJN.d.ts} +2 -2
- package/dist/{tool-formatters-C7N1Pb1q.d.ts.map → tool-formatters-DvtGhbJN.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-DhzKzB1y.js → tools-BvATiiCO.js} +192 -54
- package/dist/tools-BvATiiCO.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +3 -3
- package/dist/{transcript-anchors-Cycsq0w1.js → transcript-anchors-Cn1Unhn-.js} +7 -7
- package/dist/{transcript-anchors-Cycsq0w1.js.map → transcript-anchors-Cn1Unhn-.js.map} +1 -1
- package/dist/{transcript-anchors-CJsVfcaq.d.ts → transcript-anchors-DFmfOesU.d.ts} +4 -4
- package/dist/{transcript-anchors-CJsVfcaq.d.ts.map → transcript-anchors-DFmfOesU.d.ts.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.js +8 -8
- package/dist/{turn-operations-BtyfLdPi.d.ts → turn-operations-DWUN8cHo.d.ts} +3 -3
- package/dist/{turn-operations-BtyfLdPi.d.ts.map → turn-operations-DWUN8cHo.d.ts.map} +1 -1
- package/dist/{types-DxHDaqN7.js → types-CyVGdbia.js} +45 -2
- package/dist/{types-DxHDaqN7.js.map → types-CyVGdbia.js.map} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/RESTATE.md +38 -11
- package/docs/RUN_IN_BACKGROUND.md +4 -6
- package/docs/SKILL.md +2 -1
- package/package.json +1 -1
- package/dist/agent-w6htlFlx.d.ts.map +0 -1
- package/dist/index-C4sPfyg6.d.ts.map +0 -1
- package/dist/tools-DhzKzB1y.js.map +0 -1
package/dist/providers.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $t as planBasetenReasoning, At as SchemaSanitizeOptions, Bt as classifyOpenAICompatError, Ct as TokenCountPayload, Dt as TurnResult, Et as ToolSpec, Ft as OpenRouterParams, Gt as LocalParams, Ht as openaiCompat, It as openrouter, Jt as cursor, Kt as local, Lt as OpenAICompatAuthHeader, Mt as SchemaSanitizeResult, Nt as sanitizeToolSchema, Ot as XaiParams, Pt as sanitizeToolSpecs, Qt as baseten, Rt as OpenAICompatHttpError, St as StreamOptions, Tt as ToolResult, Ut as OpenAIParams, Vt as mapOAIFinishReason, Wt as openai, Xt as cerebras, Yt as CerebrasParams, Zt as BasetenParams, bt as ProviderCapabilities, en as ArceeParams, in as applyAnthropicCacheBreakpoints, jt as SchemaSanitizeProfile, kt as xai, nn as AnthropicParams, qt as CursorParams, rn as anthropic, tn as arcee, wt as ToolCall, xt as StreamCallbacks, yt as Provider, zt as OpenAICompatParams } from "./agent-
|
|
1
|
+
import { $t as planBasetenReasoning, At as SchemaSanitizeOptions, Bt as classifyOpenAICompatError, Ct as TokenCountPayload, Dt as TurnResult, Et as ToolSpec, Ft as OpenRouterParams, Gt as LocalParams, Ht as openaiCompat, It as openrouter, Jt as cursor, Kt as local, Lt as OpenAICompatAuthHeader, Mt as SchemaSanitizeResult, Nt as sanitizeToolSchema, Ot as XaiParams, Pt as sanitizeToolSpecs, Qt as baseten, Rt as OpenAICompatHttpError, St as StreamOptions, Tt as ToolResult, Ut as OpenAIParams, Vt as mapOAIFinishReason, Wt as openai, Xt as cerebras, Yt as CerebrasParams, Zt as BasetenParams, bt as ProviderCapabilities, en as ArceeParams, in as applyAnthropicCacheBreakpoints, jt as SchemaSanitizeProfile, kt as xai, nn as AnthropicParams, qt as CursorParams, rn as anthropic, tn as arcee, wt as ToolCall, xt as StreamCallbacks, yt as Provider, zt as OpenAICompatParams } from "./agent-NkKgz5Dh.js";
|
|
2
2
|
export { type AnthropicParams, type ArceeParams, type BasetenParams, type CerebrasParams, type CursorParams, type LocalParams, type OpenAICompatAuthHeader, OpenAICompatHttpError, type OpenAICompatParams, type OpenAIParams, type OpenRouterParams, Provider, ProviderCapabilities, type SchemaSanitizeOptions, type SchemaSanitizeProfile, type SchemaSanitizeResult, StreamCallbacks, StreamOptions, TokenCountPayload, ToolCall, ToolResult, ToolSpec, TurnResult, type XaiParams, anthropic, applyAnthropicCacheBreakpoints, arcee, baseten, cerebras, classifyOpenAICompatError, cursor, local, mapOAIFinishReason, openai, openaiCompat, openrouter, planBasetenReasoning, sanitizeToolSchema, sanitizeToolSpecs, xai };
|
package/dist/providers.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as sanitizeToolSchema, b as openaiCompat, g as OpenAICompatHttpError, v as classifyOpenAICompatError, w as sanitizeToolSpecs, y as mapOAIFinishReason } from "./messages-
|
|
2
|
-
import { a as local, d as planBasetenReasoning, f as arcee, i as openai, l as cerebras, m as applyAnthropicCacheBreakpoints, o as cursor, p as anthropic, r as openrouter, t as xai, u as baseten } from "./providers-
|
|
1
|
+
import { C as sanitizeToolSchema, b as openaiCompat, g as OpenAICompatHttpError, v as classifyOpenAICompatError, w as sanitizeToolSpecs, y as mapOAIFinishReason } from "./messages-U_87Z7GH.js";
|
|
2
|
+
import { a as local, d as planBasetenReasoning, f as arcee, i as openai, l as cerebras, m as applyAnthropicCacheBreakpoints, o as cursor, p as anthropic, r as openrouter, t as xai, u as baseten } from "./providers-DyMPTo51.js";
|
|
3
3
|
export { OpenAICompatHttpError, anthropic, applyAnthropicCacheBreakpoints, arcee, baseten, cerebras, classifyOpenAICompatError, cursor, local, mapOAIFinishReason, openai, openaiCompat, openrouter, planBasetenReasoning, sanitizeToolSchema, sanitizeToolSpecs, xai };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { s as toolResultToText } from "./types-CyVGdbia.js";
|
|
2
2
|
import { i as styleReplacementForVia, n as resolveOldString } from "./edit-utils-EGosADZq.js";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
//#region src/tools/read-state.ts
|
|
@@ -349,4 +349,4 @@ function getToolDedupState(session) {
|
|
|
349
349
|
//#endregion
|
|
350
350
|
export { markReadStateElided as a, hydrateReadStateFromSessionAsync as i, getToolDedupState as n, readStateKey as o, hashContent as r, resolveReadStateMap as s, getReadState as t };
|
|
351
351
|
|
|
352
|
-
//# sourceMappingURL=read-state-
|
|
352
|
+
//# sourceMappingURL=read-state-CLK9yVpm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-state-DH2IuQHX.js","names":[],"sources":["../src/tools/read-state.ts"],"sourcesContent":["/**\n * Per-session file read tracking + tool-dedup state.\n *\n * Two concerns, one module so they can share the `WeakMap<Session, …>`\n * pattern (state lives as long as the session does, GC'd alongside it —\n * no public schema changes, no persistence). Each section below stands\n * on its own; the two maps don't share keys or behavior.\n *\n * ## Read-state\n *\n * Shared between `read_file` (dedup re-reads) and `edit` / `multi_edit`\n * (`requireReadBeforeEdit` guard). The state map records, per canonical\n * absolute file path:\n *\n * - `contentHash` — 64-bit FNV-style hash of the bytes the model last saw. Identical\n * re-reads return a stub; edits compare against this hash to detect\n * stale content.\n * - `(offset, limit, maxBytes, lineNumbers)` — slice / shape of the last\n * read, so a wider re-read invalidates the prior stub and a different\n * `lineNumbers` mode doesn't dedup against the previous shape.\n * - `mtimeMs` — wall-clock at last read, diagnostic only.\n *\n * Resolution is split between two helpers so consumers don't have to\n * juggle the `Session` vs. explicit-map cases themselves:\n *\n * - {@link getReadState} returns the map keyed by `Session` instance.\n * - {@link resolveReadStateMap} prefers an explicit `ctx.readState`\n * (the path `spawn`'s `shareReadState` uses to forward the parent's\n * map into a sessionless child) and falls back to the session-keyed\n * lookup. Tools should call this helper, not `getReadState` directly.\n *\n * ## Tool-dedup\n *\n * Backs `behavior.dedupTools`. Records the most recent `(hash, result)`\n * per canonical tool name within a session. Same WeakMap pattern; no\n * cross-talk with the read-state map.\n */\n\nimport type { Session } from '../session'\nimport type { SessionTurn, ToolResultContent } from '../types'\nimport { resolve } from 'node:path'\nimport { toolResultToText } from '../types'\nimport { resolveOldString, styleReplacementForVia } from './edit-utils'\n\n// ---------------------------------------------------------------------------\n// Read-state\n// ---------------------------------------------------------------------------\n\nexport interface ReadStateEntry {\n contentHash: string\n /** Slice parameters for the last read. */\n offset: number\n limit: number\n maxBytes: number\n /**\n * Whether the prior read emitted line-number prefixes. Tracked so a\n * read with `lineNumbers: true` doesn't dedup against one with\n * `lineNumbers: false` (or vice versa) — the dedup stub would mislead\n * the model about the prior output's shape.\n */\n lineNumbers?: boolean\n /** Wall-clock at last read — diagnostic only, not used for invalidation. */\n mtimeMs: number\n /**\n * Set when stale-read elision removed this path's read output from the\n * on-wire history. The entry stays registered — `requireReadBeforeEdit`\n * must keep passing (the contentHash still guards against disk drift;\n * un-registering here is what degraded models into shell-edit one-liner\n * workarounds) — but `read_file`'s dedup must MISS on the next identical\n * re-read: the \"unchanged since the previous read\" stub would point at\n * content the model can no longer see. Cleared by the next fresh read\n * (`rememberRead` writes a flag-less entry).\n */\n elided?: true\n}\n\nexport type ReadStateMap = Map<string, ReadStateEntry>\n\nconst STATE = new WeakMap<Session, ReadStateMap>()\n\n/**\n * Get or lazily create the per-session read-state map. Returns `undefined`\n * when no session is provided.\n *\n * Most tool callers should prefer {@link resolveReadStateMap}, which\n * additionally honors an explicit `ctx.readState` (the path\n * `spawn`'s `shareReadState: true` uses to forward the parent's map\n * into a sessionless child). Use this helper directly only when the\n * Session-keyed map is exactly what you want and you don't need to\n * accept an override.\n */\nexport function getReadState(session: Session | undefined): ReadStateMap | undefined {\n if (!session)\n return undefined\n let map = STATE.get(session)\n if (!map) {\n map = new Map()\n STATE.set(session, map)\n }\n return map\n}\n\n/**\n * Resolve the active read-state map from a tool context. An explicit\n * `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to\n * forward the parent's map into a sessionless child); otherwise the\n * usual `Session`-keyed lookup applies.\n *\n * Tools should call this helper instead of `getReadState(ctx.session)`\n * directly so the `shareReadState` plumbing is honored uniformly.\n */\nexport function resolveReadStateMap(ctx: {\n session?: Session\n readState?: ReadStateMap\n}): ReadStateMap | undefined {\n return ctx.readState ?? getReadState(ctx.session)\n}\n\n/**\n * Mark a read-state entry as elided from the on-wire history WITHOUT\n * un-registering it.\n *\n * Used by the loop's `elideStaleReads` pass. The entry must survive —\n * deleting it made the `requireReadBeforeEdit` gate reject the very next\n * edit on a file the model had demonstrably read and just edited (the\n * gate's failure mode then defeats the gate: models degrade to raw shell\n * edits that bypass the guard entirely). The `elided` flag instead\n * defeats only the dedup stub: the next identical re-read returns the\n * full body again, and `rememberRead` clears the flag.\n *\n * No-op when the path has no entry in the resolved map(s).\n */\nexport function markReadStateElided(ctx: {\n session?: Session\n readState?: ReadStateMap\n}, cwd: string, path: string): void {\n const key = readStateKey(cwd, path)\n for (const map of [ctx.readState, ctx.session ? STATE.get(ctx.session) : undefined]) {\n const entry = map?.get(key)\n if (entry && entry.elided !== true)\n map!.set(key, { ...entry, elided: true })\n }\n}\n\n/**\n * Canonical read-state key for a `(cwd, path)` pair.\n *\n * `ctx.execution.readFile(handle, path)` resolves the model-provided\n * path against `handle.cwd` before touching disk — so `src/App.tsx`,\n * `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**\n * bytes. The read-state map MUST mirror that resolution: without it,\n * a model that reads `src/App.tsx` and then edits `./src/App.tsx`\n * trips the `requireReadBeforeEdit` gate for a file it demonstrably\n * already saw (the gate's \"has not been read in this session\" message\n * fires on a stale key).\n *\n * `node:path`'s `resolve(cwd, path)` short-circuits when `path` is\n * already absolute, so absolute and relative shapes converge on the\n * same canonical form. We don't `realpath()` symlinks — the file IS\n * the path the model addressed, not the realpath behind it; the host's\n * execution context decides how to dereference.\n */\nexport function readStateKey(cwd: string, path: string): string {\n return resolve(cwd, path)\n}\n\n/**\n * 64-bit content hash: two independent 32-bit FNV-1a-style lanes (different\n * offset bases + multipliers) concatenated as 16 hex chars. Fast,\n * non-cryptographic — but wide enough that the `requireReadBeforeEdit`\n * drift gate, which silently corrupts a file on a false \"unchanged\"\n * verdict, isn't betting on a 32-bit birthday bound (~1 in 4 billion per\n * pair; uncomfortably reachable over many files × many sessions). Entries\n * live only in per-session in-memory maps, so widening needs no\n * stored-format migration. Still cheaper than allocating a Buffer and\n * pulling in `crypto`.\n */\nexport function hashContent(text: string): string {\n let h1 = 0x811C9DC5 // FNV-1a offset basis\n let h2 = 0x7EE36237 // arbitrary distinct basis for the second lane\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i)\n h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0 // FNV prime 16777619\n h2 = Math.imul(h2 ^ c, 0x85EBCA6B) >>> 0 // murmur3 fmix multiplier\n }\n return h1.toString(16).padStart(8, '0') + h2.toString(16).padStart(8, '0')\n}\n\nexport interface HydrateReadStateOptions {\n /** Canonical read-file tool name. Defaults to `read_file`. */\n readFileToolName?: string\n /** Canonical write-file tool name. Defaults to `write_file`. */\n writeFileToolName?: string\n /** Canonical edit tool name. Defaults to `edit`. */\n editToolName?: string\n /** Canonical multi-edit tool name. Defaults to `multi_edit`. */\n multiEditToolName?: string\n /** Agent-level default for omitted `read_file.lineNumbers`. */\n defaultLineNumbers?: boolean\n /**\n * Optional replay-only fallback for read_file results whose transcript output\n * is partial/truncated. Receives the model-provided path and should return\n * the current full text bytes to hash exactly like live read_file.\n */\n readFileForHash?: (path: string) => Promise<string | null | undefined>\n}\n\ntype NormalizedHydrateReadStateOptions = Required<Omit<HydrateReadStateOptions, 'readFileForHash'>> & {\n readFileForHash?: HydrateReadStateOptions['readFileForHash']\n}\n\ninterface PendingToolCall {\n name: string\n input: Record<string, unknown>\n}\n\ninterface KnownFile {\n content: string\n entry: ReadStateEntry\n}\n\nconst READ_FILE_DEFAULT_OFFSET = 1\nconst READ_FILE_DEFAULT_LIMIT = 2000\nconst READ_FILE_DEFAULT_MAX_BYTES = 262_144\nconst READ_FILE_FOOTER_RE = /\\n\\n…(?:read lines|truncated)/\nconst READ_FILE_LINE_RE = /^\\d{1,9}\\t(.*)$/\n\n/**\n * Rebuild the per-session read-state map from durable session turns.\n *\n * Durable replay runtimes may return recorded tool results without re-running\n * the tool body, so in-memory side effects like `readState.set(...)` can be\n * missing even though the transcript contains the successful `read_file`.\n * This helper is intentionally opt-in: live hosts keep the normal side-effect\n * behavior unless they call it themselves.\n *\n * Hydration only fills missing keys. Live in-memory state may already reflect a\n * newer read or edit from the current run, and durable history cannot prove it\n * is fresher. Relative paths are resolved against the current execution cwd,\n * matching replay hosts that run sessions from a stable workspace.\n */\nexport function hydrateReadStateFromSession(\n session: Session | undefined,\n cwd: string,\n options: HydrateReadStateOptions = {},\n): number {\n if (!session)\n return 0\n\n const readState = getReadState(session)\n if (!readState)\n return 0\n\n const normalized = normalizeHydrateReadStateOptions(options)\n const pending = new Map<string, PendingToolCall>()\n const known = new Map<string, KnownFile>()\n const preexistingKeys = new Set(readState.keys())\n let hydrated = 0\n\n for (const turn of session.turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call') {\n if (\n block.name === normalized.readFileToolName\n || block.name === normalized.writeFileToolName\n || block.name === normalized.editToolName\n || block.name === normalized.multiEditToolName\n ) {\n pending.set(block.id, { name: block.name, input: block.input })\n }\n continue\n }\n\n if (block.type !== 'tool_result')\n continue\n\n const call = pending.get(block.callId)\n if (!call)\n continue\n pending.delete(block.callId)\n if (block.isError === true)\n continue\n\n const output = toolResultToText(block.output)\n const next = stateFromToolResult(call, output, turn, cwd, normalized, known)\n if (!next)\n continue\n\n if (!preexistingKeys.has(next.key)) {\n readState.set(next.key, next.file.entry)\n hydrated += 1\n }\n known.set(next.key, next.file)\n }\n }\n\n return hydrated\n}\n\n/**\n * Async replay hydrator for durable hosts that can cheaply read the current\n * full file through their execution context. Transcript-only reconstruction is\n * still preferred for full reads; partial/truncated read_file entries fall back\n * to `readFileForHash` and hash those full current bytes.\n */\nexport async function hydrateReadStateFromSessionAsync(\n session: Session | undefined,\n cwd: string,\n options: HydrateReadStateOptions = {},\n): Promise<number> {\n if (!session)\n return 0\n\n const readState = getReadState(session)\n if (!readState)\n return 0\n\n const normalized = normalizeHydrateReadStateOptions(options)\n const pending = new Map<string, PendingToolCall>()\n const known = new Map<string, KnownFile>()\n const preexistingKeys = new Set(readState.keys())\n let hydrated = 0\n\n for (const turn of session.turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call') {\n if (\n block.name === normalized.readFileToolName\n || block.name === normalized.writeFileToolName\n || block.name === normalized.editToolName\n || block.name === normalized.multiEditToolName\n ) {\n pending.set(block.id, { name: block.name, input: block.input })\n }\n continue\n }\n\n if (block.type !== 'tool_result')\n continue\n\n const call = pending.get(block.callId)\n if (!call)\n continue\n pending.delete(block.callId)\n if (block.isError === true)\n continue\n\n const output = toolResultToText(block.output)\n const next = await stateFromToolResultAsync(call, output, turn, cwd, normalized, known)\n if (!next)\n continue\n\n if (!preexistingKeys.has(next.key)) {\n readState.set(next.key, next.file.entry)\n hydrated += 1\n }\n known.set(next.key, next.file)\n }\n }\n\n return hydrated\n}\n\nfunction normalizeHydrateReadStateOptions(options: HydrateReadStateOptions): NormalizedHydrateReadStateOptions {\n return {\n readFileToolName: options.readFileToolName ?? 'read_file',\n writeFileToolName: options.writeFileToolName ?? 'write_file',\n editToolName: options.editToolName ?? 'edit',\n multiEditToolName: options.multiEditToolName ?? 'multi_edit',\n defaultLineNumbers: options.defaultLineNumbers ?? true,\n ...(options.readFileForHash ? { readFileForHash: options.readFileForHash } : {}),\n }\n}\n\nfunction stateFromToolResult(\n call: PendingToolCall,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n options: NormalizedHydrateReadStateOptions,\n known: Map<string, KnownFile>,\n): { key: string, file: KnownFile } | undefined {\n if (call.name === options.readFileToolName)\n return readFileStateFromResult(call.input, output, resultTurn, cwd, options.defaultLineNumbers)\n if (call.name === options.writeFileToolName)\n return writeFileStateFromResult(call.input, output, resultTurn, cwd)\n if (call.name === options.editToolName)\n return editStateFromResult(call.input, output, resultTurn, cwd, known)\n if (call.name === options.multiEditToolName)\n return multiEditStateFromResult(call.input, output, resultTurn, cwd, known)\n return undefined\n}\n\nasync function stateFromToolResultAsync(\n call: PendingToolCall,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n options: NormalizedHydrateReadStateOptions,\n known: Map<string, KnownFile>,\n): Promise<{ key: string, file: KnownFile } | undefined> {\n if (call.name === options.readFileToolName) {\n return await readFileStateFromResultAsync(call.input, output, resultTurn, cwd, {\n defaultLineNumbers: options.defaultLineNumbers,\n readFileForHash: options.readFileForHash,\n })\n }\n return stateFromToolResult(call, output, resultTurn, cwd, options, known)\n}\n\nfunction readFileStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n defaultLineNumbers: boolean,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n if (!path || !isSuccessfulFullReadOutput(output))\n return undefined\n\n const lineNumbers = typeof input.lineNumbers === 'boolean' ? input.lineNumbers : defaultLineNumbers\n const content = lineNumbers ? stripReadFileLineNumbers(output) : output\n if (content === undefined)\n return undefined\n\n const entry: ReadStateEntry = {\n contentHash: hashContent(content),\n offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),\n limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),\n maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),\n lineNumbers,\n mtimeMs: resultTurn.createdAt,\n }\n return { key: readStateKey(cwd, path), file: { content, entry } }\n}\n\nasync function readFileStateFromResultAsync(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n options: {\n defaultLineNumbers: boolean\n readFileForHash?: HydrateReadStateOptions['readFileForHash']\n },\n): Promise<{ key: string, file: KnownFile } | undefined> {\n const reconstructed = readFileStateFromResult(input, output, resultTurn, cwd, options.defaultLineNumbers)\n if (reconstructed)\n return reconstructed\n\n const path = typeof input.path === 'string' ? input.path : undefined\n if (!path || !options.readFileForHash || !isSuccessfulReadOutput(output))\n return undefined\n\n let content: string | null | undefined\n try {\n content = await options.readFileForHash(path)\n }\n catch {\n return undefined\n }\n if (typeof content !== 'string')\n return undefined\n\n const lineNumbers = typeof input.lineNumbers === 'boolean' ? input.lineNumbers : options.defaultLineNumbers\n const entry: ReadStateEntry = {\n contentHash: hashContent(content),\n offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),\n limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),\n maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),\n lineNumbers,\n mtimeMs: resultTurn.createdAt,\n }\n return { key: readStateKey(cwd, path), file: { content, entry } }\n}\n\nfunction writeFileStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n const content = typeof input.content === 'string' ? input.content : undefined\n if (!path || content === undefined)\n return undefined\n if (!output.startsWith(`Created ${path} (`) && !output.startsWith(`Updated ${path} (`))\n return undefined\n\n return {\n key: readStateKey(cwd, path),\n file: {\n content,\n entry: {\n contentHash: hashContent(content),\n offset: 0,\n limit: Number.POSITIVE_INFINITY,\n maxBytes: Number.POSITIVE_INFINITY,\n mtimeMs: resultTurn.createdAt,\n },\n },\n }\n}\n\nfunction editStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n known: Map<string, KnownFile>,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n const oldString = typeof input.old_string === 'string' ? input.old_string : undefined\n const newString = typeof input.new_string === 'string' ? input.new_string : undefined\n if (!path || oldString === undefined || newString === undefined)\n return undefined\n if (!output.startsWith(`Edited ${path}: replaced `))\n return undefined\n\n const key = readStateKey(cwd, path)\n const prior = known.get(key)\n if (!prior)\n return undefined\n\n const nextContent = applyEdit(prior.content, oldString, newString, input.replace_all === true)\n if (nextContent === undefined)\n return undefined\n\n return {\n key,\n file: {\n content: nextContent,\n entry: { ...prior.entry, contentHash: hashContent(nextContent), mtimeMs: resultTurn.createdAt },\n },\n }\n}\n\ninterface MultiEditStep {\n old_string?: unknown\n new_string?: unknown\n replace_all?: unknown\n}\n\nfunction multiEditStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n known: Map<string, KnownFile>,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n const edits = Array.isArray(input.edits) ? (input.edits as MultiEditStep[]) : undefined\n if (!path || !edits)\n return undefined\n if (!output.startsWith(`Edited ${path}: applied `))\n return undefined\n\n const key = readStateKey(cwd, path)\n const prior = known.get(key)\n if (!prior)\n return undefined\n\n const applied = parseMultiEditAppliedSteps(output, edits.length)\n let current = prior.content\n for (let i = 0; i < edits.length; i++) {\n if (!applied.has(i))\n continue\n const step = edits[i]\n if (typeof step.old_string !== 'string' || typeof step.new_string !== 'string')\n return undefined\n const next = applyEdit(current, step.old_string, step.new_string, step.replace_all === true)\n if (next === undefined)\n return undefined\n current = next\n }\n\n return {\n key,\n file: {\n content: current,\n entry: { ...prior.entry, contentHash: hashContent(current), mtimeMs: resultTurn.createdAt },\n },\n }\n}\n\nfunction applyEdit(content: string, oldString: string, newString: string, replaceAll: boolean): string | undefined {\n const match = resolveOldString(content, oldString)\n if (!match)\n return undefined\n if (match.occurrences > 1 && !replaceAll)\n return undefined\n const styledReplacement = styleReplacementForVia(newString, match.via, match.actual)\n return replaceAll\n ? content.split(match.actual).join(styledReplacement)\n : content.replace(match.actual, styledReplacement)\n}\n\nfunction parseMultiEditAppliedSteps(output: string, editCount: number): Set<number> {\n const annotationMatch = /<edit-outcomes>\\n([\\s\\S]*?)\\n<\\/edit-outcomes>/.exec(output)\n if (!annotationMatch) {\n return new Set(Array.from({ length: editCount }, (_, i) => i))\n }\n\n const applied = new Set<number>()\n for (const line of annotationMatch[1].split('\\n')) {\n const match = /^#(\\d+) applied$/.exec(line.trim())\n if (!match)\n continue\n const index = Number(match[1]) - 1\n if (index >= 0 && index < editCount)\n applied.add(index)\n }\n return applied\n}\n\nfunction isSuccessfulFullReadOutput(output: string): boolean {\n // Live read_file stores a hash of the raw full file before pagination or\n // truncation. Durable turns only contain rendered output, so partial reads\n // cannot be reconstructed safely; skip them and require a real re-read.\n if (!isSuccessfulReadOutput(output) || READ_FILE_FOOTER_RE.test(output))\n return false\n return true\n}\n\nfunction isSuccessfulReadOutput(output: string): boolean {\n if (output.length === 0)\n return true\n if (\n output.startsWith('File not found:')\n || output.startsWith('[binary file:')\n || output.startsWith('Image:')\n || output.startsWith('Document:')\n || output.startsWith('[image too large to inline:')\n || output.startsWith('[document:')\n || output.startsWith('[document too large to attach:')\n || output.startsWith('Image read failed:')\n || output.startsWith('Document read failed:')\n || output.includes(' unchanged since the previous read in this session ')\n ) {\n return false\n }\n return true\n}\n\nfunction stripReadFileLineNumbers(output: string): string | undefined {\n const lines = output.split('\\n')\n const stripped: string[] = []\n for (const line of lines) {\n const match = READ_FILE_LINE_RE.exec(line)\n if (!match)\n return undefined\n stripped.push(match[1])\n }\n return stripped.join('\\n')\n}\n\nfunction normalizeReadInteger(value: unknown, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0)\n return fallback\n return Math.floor(value)\n}\n\n// ---------------------------------------------------------------------------\n// Tool-dedup\n// ---------------------------------------------------------------------------\n\nexport interface ToolDedupEntry {\n hash: string\n result: string | ToolResultContent[]\n /**\n * Number of replay-hits accumulated against this `(name, hash)` since\n * the most recent fresh dispatch. `0` immediately after a fresh\n * dispatch; incremented every time a `tool:gate` handler returns the\n * cached result for an identical input. Used by `dedup-tools` to\n * implement `mode: 'block-after'` — when the count crosses the\n * configured threshold, the gate stops replaying and refuses the call\n * with a `Blocked:` tool_result so the model breaks out of the loop.\n *\n * Resets to `0` on a hash change (a different input under the same\n * tool name is treated as a fresh sequence). Optional for back-compat\n * with consumers that built the entry directly; absent is treated as\n * `0`.\n */\n repeats?: number\n}\n\nexport type ToolDedupMap = Map<string, ToolDedupEntry>\n\nconst TOOL_DEDUP_STATE = new WeakMap<Session, ToolDedupMap>()\n\n/**\n * Get or lazily create the per-session tool-dedup map. Returns `undefined`\n * when no session is provided — middleware should treat that as \"no dedup\".\n */\nexport function getToolDedupState(session: Session | undefined): ToolDedupMap | undefined {\n if (!session)\n return undefined\n let map = TOOL_DEDUP_STATE.get(session)\n if (!map) {\n map = new Map()\n TOOL_DEDUP_STATE.set(session, map)\n }\n return map\n}\n"],"mappings":";;;;AA8EA,MAAM,wBAAQ,IAAI,QAA+B;;;;;;;;;;;;AAajD,SAAgB,aAAa,SAAwD;CACnF,IAAI,CAAC,SACH,OAAO,KAAA;CACT,IAAI,MAAM,MAAM,IAAI,OAAO;CAC3B,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,MAAM,IAAI,SAAS,GAAG;CACxB;CACA,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,oBAAoB,KAGP;CAC3B,OAAO,IAAI,aAAa,aAAa,IAAI,OAAO;AAClD;;;;;;;;;;;;;;;AAgBA,SAAgB,oBAAoB,KAGjC,KAAa,MAAoB;CAClC,MAAM,MAAM,aAAa,KAAK,IAAI;CAClC,KAAK,MAAM,OAAO,CAAC,IAAI,WAAW,IAAI,UAAU,MAAM,IAAI,IAAI,OAAO,IAAI,KAAA,CAAS,GAAG;EACnF,MAAM,QAAQ,KAAK,IAAI,GAAG;EAC1B,IAAI,SAAS,MAAM,WAAW,MAC5B,IAAK,IAAI,KAAK;GAAE,GAAG;GAAO,QAAQ;EAAK,CAAC;CAC5C;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,aAAa,KAAa,MAAsB;CAC9D,OAAO,QAAQ,KAAK,IAAI;AAC1B;;;;;;;;;;;;AAaA,SAAgB,YAAY,MAAsB;CAChD,IAAI,KAAK;CACT,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,IAAI,KAAK,WAAW,CAAC;EAC3B,KAAK,KAAK,KAAK,KAAK,GAAG,QAAU,MAAM;EACvC,KAAK,KAAK,KAAK,KAAK,GAAG,UAAU,MAAM;CACzC;CACA,OAAO,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,IAAI,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC3E;AAmCA,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,8BAA8B;AACpC,MAAM,sBAAsB;AAC5B,MAAM,oBAAoB;;;;;;;AAgF1B,eAAsB,iCACpB,SACA,KACA,UAAmC,CAAC,GACnB;CACjB,IAAI,CAAC,SACH,OAAO;CAET,MAAM,YAAY,aAAa,OAAO;CACtC,IAAI,CAAC,WACH,OAAO;CAET,MAAM,aAAa,iCAAiC,OAAO;CAC3D,MAAM,0BAAU,IAAI,IAA6B;CACjD,MAAM,wBAAQ,IAAI,IAAuB;CACzC,MAAM,kBAAkB,IAAI,IAAI,UAAU,KAAK,CAAC;CAChD,IAAI,WAAW;CAEf,KAAK,MAAM,QAAQ,QAAQ,OACzB,KAAK,MAAM,SAAS,KAAK,SAAS;EAChC,IAAI,MAAM,SAAS,aAAa;GAC9B,IACE,MAAM,SAAS,WAAW,oBACvB,MAAM,SAAS,WAAW,qBAC1B,MAAM,SAAS,WAAW,gBAC1B,MAAM,SAAS,WAAW,mBAE7B,QAAQ,IAAI,MAAM,IAAI;IAAE,MAAM,MAAM;IAAM,OAAO,MAAM;GAAM,CAAC;GAEhE;EACF;EAEA,IAAI,MAAM,SAAS,eACjB;EAEF,MAAM,OAAO,QAAQ,IAAI,MAAM,MAAM;EACrC,IAAI,CAAC,MACH;EACF,QAAQ,OAAO,MAAM,MAAM;EAC3B,IAAI,MAAM,YAAY,MACpB;EAGF,MAAM,OAAO,MAAM,yBAAyB,MAD7B,iBAAiB,MAAM,MACiB,GAAG,MAAM,KAAK,YAAY,KAAK;EACtF,IAAI,CAAC,MACH;EAEF,IAAI,CAAC,gBAAgB,IAAI,KAAK,GAAG,GAAG;GAClC,UAAU,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK;GACvC,YAAY;EACd;EACA,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI;CAC/B;CAGF,OAAO;AACT;AAEA,SAAS,iCAAiC,SAAqE;CAC7G,OAAO;EACL,kBAAkB,QAAQ,oBAAoB;EAC9C,mBAAmB,QAAQ,qBAAqB;EAChD,cAAc,QAAQ,gBAAgB;EACtC,mBAAmB,QAAQ,qBAAqB;EAChD,oBAAoB,QAAQ,sBAAsB;EAClD,GAAI,QAAQ,kBAAkB,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI,CAAC;CAChF;AACF;AAEA,SAAS,oBACP,MACA,QACA,YACA,KACA,SACA,OAC8C;CAC9C,IAAI,KAAK,SAAS,QAAQ,kBACxB,OAAO,wBAAwB,KAAK,OAAO,QAAQ,YAAY,KAAK,QAAQ,kBAAkB;CAChG,IAAI,KAAK,SAAS,QAAQ,mBACxB,OAAO,yBAAyB,KAAK,OAAO,QAAQ,YAAY,GAAG;CACrE,IAAI,KAAK,SAAS,QAAQ,cACxB,OAAO,oBAAoB,KAAK,OAAO,QAAQ,YAAY,KAAK,KAAK;CACvE,IAAI,KAAK,SAAS,QAAQ,mBACxB,OAAO,yBAAyB,KAAK,OAAO,QAAQ,YAAY,KAAK,KAAK;AAE9E;AAEA,eAAe,yBACb,MACA,QACA,YACA,KACA,SACA,OACuD;CACvD,IAAI,KAAK,SAAS,QAAQ,kBACxB,OAAO,MAAM,6BAA6B,KAAK,OAAO,QAAQ,YAAY,KAAK;EAC7E,oBAAoB,QAAQ;EAC5B,iBAAiB,QAAQ;CAC3B,CAAC;CAEH,OAAO,oBAAoB,MAAM,QAAQ,YAAY,KAAK,SAAS,KAAK;AAC1E;AAEA,SAAS,wBACP,OACA,QACA,YACA,KACA,oBAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,IAAI,CAAC,QAAQ,CAAC,2BAA2B,MAAM,GAC7C,OAAO,KAAA;CAET,MAAM,cAAc,OAAO,MAAM,gBAAgB,YAAY,MAAM,cAAc;CACjF,MAAM,UAAU,cAAc,yBAAyB,MAAM,IAAI;CACjE,IAAI,YAAY,KAAA,GACd,OAAO,KAAA;CAET,MAAM,QAAwB;EAC5B,aAAa,YAAY,OAAO;EAChC,QAAQ,qBAAqB,MAAM,QAAQ,wBAAwB;EACnE,OAAO,qBAAqB,MAAM,OAAO,uBAAuB;EAChE,UAAU,qBAAqB,MAAM,UAAU,2BAA2B;EAC1E;EACA,SAAS,WAAW;CACtB;CACA,OAAO;EAAE,KAAK,aAAa,KAAK,IAAI;EAAG,MAAM;GAAE;GAAS;EAAM;CAAE;AAClE;AAEA,eAAe,6BACb,OACA,QACA,YACA,KACA,SAIuD;CACvD,MAAM,gBAAgB,wBAAwB,OAAO,QAAQ,YAAY,KAAK,QAAQ,kBAAkB;CACxG,IAAI,eACF,OAAO;CAET,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,IAAI,CAAC,QAAQ,CAAC,QAAQ,mBAAmB,CAAC,uBAAuB,MAAM,GACrE,OAAO,KAAA;CAET,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,QAAQ,gBAAgB,IAAI;CAC9C,QACM;EACJ;CACF;CACA,IAAI,OAAO,YAAY,UACrB,OAAO,KAAA;CAET,MAAM,cAAc,OAAO,MAAM,gBAAgB,YAAY,MAAM,cAAc,QAAQ;CACzF,MAAM,QAAwB;EAC5B,aAAa,YAAY,OAAO;EAChC,QAAQ,qBAAqB,MAAM,QAAQ,wBAAwB;EACnE,OAAO,qBAAqB,MAAM,OAAO,uBAAuB;EAChE,UAAU,qBAAqB,MAAM,UAAU,2BAA2B;EAC1E;EACA,SAAS,WAAW;CACtB;CACA,OAAO;EAAE,KAAK,aAAa,KAAK,IAAI;EAAG,MAAM;GAAE;GAAS;EAAM;CAAE;AAClE;AAEA,SAAS,yBACP,OACA,QACA,YACA,KAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,MAAM,UAAU,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU,KAAA;CACpE,IAAI,CAAC,QAAQ,YAAY,KAAA,GACvB,OAAO,KAAA;CACT,IAAI,CAAC,OAAO,WAAW,WAAW,KAAK,GAAG,KAAK,CAAC,OAAO,WAAW,WAAW,KAAK,GAAG,GACnF,OAAO,KAAA;CAET,OAAO;EACL,KAAK,aAAa,KAAK,IAAI;EAC3B,MAAM;GACJ;GACA,OAAO;IACL,aAAa,YAAY,OAAO;IAChC,QAAQ;IACR,OAAO,OAAO;IACd,UAAU,OAAO;IACjB,SAAS,WAAW;GACtB;EACF;CACF;AACF;AAEA,SAAS,oBACP,OACA,QACA,YACA,KACA,OAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,MAAM,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAA;CAC5E,MAAM,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAA;CAC5E,IAAI,CAAC,QAAQ,cAAc,KAAA,KAAa,cAAc,KAAA,GACpD,OAAO,KAAA;CACT,IAAI,CAAC,OAAO,WAAW,UAAU,KAAK,YAAY,GAChD,OAAO,KAAA;CAET,MAAM,MAAM,aAAa,KAAK,IAAI;CAClC,MAAM,QAAQ,MAAM,IAAI,GAAG;CAC3B,IAAI,CAAC,OACH,OAAO,KAAA;CAET,MAAM,cAAc,UAAU,MAAM,SAAS,WAAW,WAAW,MAAM,gBAAgB,IAAI;CAC7F,IAAI,gBAAgB,KAAA,GAClB,OAAO,KAAA;CAET,OAAO;EACL;EACA,MAAM;GACJ,SAAS;GACT,OAAO;IAAE,GAAG,MAAM;IAAO,aAAa,YAAY,WAAW;IAAG,SAAS,WAAW;GAAU;EAChG;CACF;AACF;AAQA,SAAS,yBACP,OACA,QACA,YACA,KACA,OAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,MAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAK,MAAM,QAA4B,KAAA;CAC9E,IAAI,CAAC,QAAQ,CAAC,OACZ,OAAO,KAAA;CACT,IAAI,CAAC,OAAO,WAAW,UAAU,KAAK,WAAW,GAC/C,OAAO,KAAA;CAET,MAAM,MAAM,aAAa,KAAK,IAAI;CAClC,MAAM,QAAQ,MAAM,IAAI,GAAG;CAC3B,IAAI,CAAC,OACH,OAAO,KAAA;CAET,MAAM,UAAU,2BAA2B,QAAQ,MAAM,MAAM;CAC/D,IAAI,UAAU,MAAM;CACpB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,IAAI,CAAC,QAAQ,IAAI,CAAC,GAChB;EACF,MAAM,OAAO,MAAM;EACnB,IAAI,OAAO,KAAK,eAAe,YAAY,OAAO,KAAK,eAAe,UACpE,OAAO,KAAA;EACT,MAAM,OAAO,UAAU,SAAS,KAAK,YAAY,KAAK,YAAY,KAAK,gBAAgB,IAAI;EAC3F,IAAI,SAAS,KAAA,GACX,OAAO,KAAA;EACT,UAAU;CACZ;CAEA,OAAO;EACL;EACA,MAAM;GACJ,SAAS;GACT,OAAO;IAAE,GAAG,MAAM;IAAO,aAAa,YAAY,OAAO;IAAG,SAAS,WAAW;GAAU;EAC5F;CACF;AACF;AAEA,SAAS,UAAU,SAAiB,WAAmB,WAAmB,YAAyC;CACjH,MAAM,QAAQ,iBAAiB,SAAS,SAAS;CACjD,IAAI,CAAC,OACH,OAAO,KAAA;CACT,IAAI,MAAM,cAAc,KAAK,CAAC,YAC5B,OAAO,KAAA;CACT,MAAM,oBAAoB,uBAAuB,WAAW,MAAM,KAAK,MAAM,MAAM;CACnF,OAAO,aACH,QAAQ,MAAM,MAAM,MAAM,EAAE,KAAK,iBAAiB,IAClD,QAAQ,QAAQ,MAAM,QAAQ,iBAAiB;AACrD;AAEA,SAAS,2BAA2B,QAAgB,WAAgC;CAClF,MAAM,kBAAkB,iDAAiD,KAAK,MAAM;CACpF,IAAI,CAAC,iBACH,OAAO,IAAI,IAAI,MAAM,KAAK,EAAE,QAAQ,UAAU,IAAI,GAAG,MAAM,CAAC,CAAC;CAG/D,MAAM,0BAAU,IAAI,IAAY;CAChC,KAAK,MAAM,QAAQ,gBAAgB,GAAG,MAAM,IAAI,GAAG;EACjD,MAAM,QAAQ,mBAAmB,KAAK,KAAK,KAAK,CAAC;EACjD,IAAI,CAAC,OACH;EACF,MAAM,QAAQ,OAAO,MAAM,EAAE,IAAI;EACjC,IAAI,SAAS,KAAK,QAAQ,WACxB,QAAQ,IAAI,KAAK;CACrB;CACA,OAAO;AACT;AAEA,SAAS,2BAA2B,QAAyB;CAI3D,IAAI,CAAC,uBAAuB,MAAM,KAAK,oBAAoB,KAAK,MAAM,GACpE,OAAO;CACT,OAAO;AACT;AAEA,SAAS,uBAAuB,QAAyB;CACvD,IAAI,OAAO,WAAW,GACpB,OAAO;CACT,IACE,OAAO,WAAW,iBAAiB,KAChC,OAAO,WAAW,eAAe,KACjC,OAAO,WAAW,QAAQ,KAC1B,OAAO,WAAW,WAAW,KAC7B,OAAO,WAAW,6BAA6B,KAC/C,OAAO,WAAW,YAAY,KAC9B,OAAO,WAAW,gCAAgC,KAClD,OAAO,WAAW,oBAAoB,KACtC,OAAO,WAAW,uBAAuB,KACzC,OAAO,SAAS,qDAAqD,GAExE,OAAO;CAET,OAAO;AACT;AAEA,SAAS,yBAAyB,QAAoC;CACpE,MAAM,QAAQ,OAAO,MAAM,IAAI;CAC/B,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,kBAAkB,KAAK,IAAI;EACzC,IAAI,CAAC,OACH,OAAO,KAAA;EACT,SAAS,KAAK,MAAM,EAAE;CACxB;CACA,OAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,qBAAqB,OAAgB,UAA0B;CACtE,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAClE,OAAO;CACT,OAAO,KAAK,MAAM,KAAK;AACzB;AA4BA,MAAM,mCAAmB,IAAI,QAA+B;;;;;AAM5D,SAAgB,kBAAkB,SAAwD;CACxF,IAAI,CAAC,SACH,OAAO,KAAA;CACT,IAAI,MAAM,iBAAiB,IAAI,OAAO;CACtC,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,iBAAiB,IAAI,SAAS,GAAG;CACnC;CACA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"read-state-CLK9yVpm.js","names":[],"sources":["../src/tools/read-state.ts"],"sourcesContent":["/**\n * Per-session file read tracking + tool-dedup state.\n *\n * Two concerns, one module so they can share the `WeakMap<Session, …>`\n * pattern (state lives as long as the session does, GC'd alongside it —\n * no public schema changes, no persistence). Each section below stands\n * on its own; the two maps don't share keys or behavior.\n *\n * ## Read-state\n *\n * Shared between `read_file` (dedup re-reads) and `edit` / `multi_edit`\n * (`requireReadBeforeEdit` guard). The state map records, per canonical\n * absolute file path:\n *\n * - `contentHash` — 64-bit FNV-style hash of the bytes the model last saw. Identical\n * re-reads return a stub; edits compare against this hash to detect\n * stale content.\n * - `(offset, limit, maxBytes, lineNumbers)` — slice / shape of the last\n * read, so a wider re-read invalidates the prior stub and a different\n * `lineNumbers` mode doesn't dedup against the previous shape.\n * - `mtimeMs` — wall-clock at last read, diagnostic only.\n *\n * Resolution is split between two helpers so consumers don't have to\n * juggle the `Session` vs. explicit-map cases themselves:\n *\n * - {@link getReadState} returns the map keyed by `Session` instance.\n * - {@link resolveReadStateMap} prefers an explicit `ctx.readState`\n * (the path `spawn`'s `shareReadState` uses to forward the parent's\n * map into a sessionless child) and falls back to the session-keyed\n * lookup. Tools should call this helper, not `getReadState` directly.\n *\n * ## Tool-dedup\n *\n * Backs `behavior.dedupTools`. Records the most recent `(hash, result)`\n * per canonical tool name within a session. Same WeakMap pattern; no\n * cross-talk with the read-state map.\n */\n\nimport type { Session } from '../session'\nimport type { SessionTurn, ToolResultContent } from '../types'\nimport { resolve } from 'node:path'\nimport { toolResultToText } from '../types'\nimport { resolveOldString, styleReplacementForVia } from './edit-utils'\n\n// ---------------------------------------------------------------------------\n// Read-state\n// ---------------------------------------------------------------------------\n\nexport interface ReadStateEntry {\n contentHash: string\n /** Slice parameters for the last read. */\n offset: number\n limit: number\n maxBytes: number\n /**\n * Whether the prior read emitted line-number prefixes. Tracked so a\n * read with `lineNumbers: true` doesn't dedup against one with\n * `lineNumbers: false` (or vice versa) — the dedup stub would mislead\n * the model about the prior output's shape.\n */\n lineNumbers?: boolean\n /** Wall-clock at last read — diagnostic only, not used for invalidation. */\n mtimeMs: number\n /**\n * Set when stale-read elision removed this path's read output from the\n * on-wire history. The entry stays registered — `requireReadBeforeEdit`\n * must keep passing (the contentHash still guards against disk drift;\n * un-registering here is what degraded models into shell-edit one-liner\n * workarounds) — but `read_file`'s dedup must MISS on the next identical\n * re-read: the \"unchanged since the previous read\" stub would point at\n * content the model can no longer see. Cleared by the next fresh read\n * (`rememberRead` writes a flag-less entry).\n */\n elided?: true\n}\n\nexport type ReadStateMap = Map<string, ReadStateEntry>\n\nconst STATE = new WeakMap<Session, ReadStateMap>()\n\n/**\n * Get or lazily create the per-session read-state map. Returns `undefined`\n * when no session is provided.\n *\n * Most tool callers should prefer {@link resolveReadStateMap}, which\n * additionally honors an explicit `ctx.readState` (the path\n * `spawn`'s `shareReadState: true` uses to forward the parent's map\n * into a sessionless child). Use this helper directly only when the\n * Session-keyed map is exactly what you want and you don't need to\n * accept an override.\n */\nexport function getReadState(session: Session | undefined): ReadStateMap | undefined {\n if (!session)\n return undefined\n let map = STATE.get(session)\n if (!map) {\n map = new Map()\n STATE.set(session, map)\n }\n return map\n}\n\n/**\n * Resolve the active read-state map from a tool context. An explicit\n * `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to\n * forward the parent's map into a sessionless child); otherwise the\n * usual `Session`-keyed lookup applies.\n *\n * Tools should call this helper instead of `getReadState(ctx.session)`\n * directly so the `shareReadState` plumbing is honored uniformly.\n */\nexport function resolveReadStateMap(ctx: {\n session?: Session\n readState?: ReadStateMap\n}): ReadStateMap | undefined {\n return ctx.readState ?? getReadState(ctx.session)\n}\n\n/**\n * Mark a read-state entry as elided from the on-wire history WITHOUT\n * un-registering it.\n *\n * Used by the loop's `elideStaleReads` pass. The entry must survive —\n * deleting it made the `requireReadBeforeEdit` gate reject the very next\n * edit on a file the model had demonstrably read and just edited (the\n * gate's failure mode then defeats the gate: models degrade to raw shell\n * edits that bypass the guard entirely). The `elided` flag instead\n * defeats only the dedup stub: the next identical re-read returns the\n * full body again, and `rememberRead` clears the flag.\n *\n * No-op when the path has no entry in the resolved map(s).\n */\nexport function markReadStateElided(ctx: {\n session?: Session\n readState?: ReadStateMap\n}, cwd: string, path: string): void {\n const key = readStateKey(cwd, path)\n for (const map of [ctx.readState, ctx.session ? STATE.get(ctx.session) : undefined]) {\n const entry = map?.get(key)\n if (entry && entry.elided !== true)\n map!.set(key, { ...entry, elided: true })\n }\n}\n\n/**\n * Canonical read-state key for a `(cwd, path)` pair.\n *\n * `ctx.execution.readFile(handle, path)` resolves the model-provided\n * path against `handle.cwd` before touching disk — so `src/App.tsx`,\n * `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**\n * bytes. The read-state map MUST mirror that resolution: without it,\n * a model that reads `src/App.tsx` and then edits `./src/App.tsx`\n * trips the `requireReadBeforeEdit` gate for a file it demonstrably\n * already saw (the gate's \"has not been read in this session\" message\n * fires on a stale key).\n *\n * `node:path`'s `resolve(cwd, path)` short-circuits when `path` is\n * already absolute, so absolute and relative shapes converge on the\n * same canonical form. We don't `realpath()` symlinks — the file IS\n * the path the model addressed, not the realpath behind it; the host's\n * execution context decides how to dereference.\n */\nexport function readStateKey(cwd: string, path: string): string {\n return resolve(cwd, path)\n}\n\n/**\n * 64-bit content hash: two independent 32-bit FNV-1a-style lanes (different\n * offset bases + multipliers) concatenated as 16 hex chars. Fast,\n * non-cryptographic — but wide enough that the `requireReadBeforeEdit`\n * drift gate, which silently corrupts a file on a false \"unchanged\"\n * verdict, isn't betting on a 32-bit birthday bound (~1 in 4 billion per\n * pair; uncomfortably reachable over many files × many sessions). Entries\n * live only in per-session in-memory maps, so widening needs no\n * stored-format migration. Still cheaper than allocating a Buffer and\n * pulling in `crypto`.\n */\nexport function hashContent(text: string): string {\n let h1 = 0x811C9DC5 // FNV-1a offset basis\n let h2 = 0x7EE36237 // arbitrary distinct basis for the second lane\n for (let i = 0; i < text.length; i++) {\n const c = text.charCodeAt(i)\n h1 = Math.imul(h1 ^ c, 0x01000193) >>> 0 // FNV prime 16777619\n h2 = Math.imul(h2 ^ c, 0x85EBCA6B) >>> 0 // murmur3 fmix multiplier\n }\n return h1.toString(16).padStart(8, '0') + h2.toString(16).padStart(8, '0')\n}\n\nexport interface HydrateReadStateOptions {\n /** Canonical read-file tool name. Defaults to `read_file`. */\n readFileToolName?: string\n /** Canonical write-file tool name. Defaults to `write_file`. */\n writeFileToolName?: string\n /** Canonical edit tool name. Defaults to `edit`. */\n editToolName?: string\n /** Canonical multi-edit tool name. Defaults to `multi_edit`. */\n multiEditToolName?: string\n /** Agent-level default for omitted `read_file.lineNumbers`. */\n defaultLineNumbers?: boolean\n /**\n * Optional replay-only fallback for read_file results whose transcript output\n * is partial/truncated. Receives the model-provided path and should return\n * the current full text bytes to hash exactly like live read_file.\n */\n readFileForHash?: (path: string) => Promise<string | null | undefined>\n}\n\ntype NormalizedHydrateReadStateOptions = Required<Omit<HydrateReadStateOptions, 'readFileForHash'>> & {\n readFileForHash?: HydrateReadStateOptions['readFileForHash']\n}\n\ninterface PendingToolCall {\n name: string\n input: Record<string, unknown>\n}\n\ninterface KnownFile {\n content: string\n entry: ReadStateEntry\n}\n\nconst READ_FILE_DEFAULT_OFFSET = 1\nconst READ_FILE_DEFAULT_LIMIT = 2000\nconst READ_FILE_DEFAULT_MAX_BYTES = 262_144\nconst READ_FILE_FOOTER_RE = /\\n\\n…(?:read lines|truncated)/\nconst READ_FILE_LINE_RE = /^\\d{1,9}\\t(.*)$/\n\n/**\n * Rebuild the per-session read-state map from durable session turns.\n *\n * Durable replay runtimes may return recorded tool results without re-running\n * the tool body, so in-memory side effects like `readState.set(...)` can be\n * missing even though the transcript contains the successful `read_file`.\n * This helper is intentionally opt-in: live hosts keep the normal side-effect\n * behavior unless they call it themselves.\n *\n * Hydration only fills missing keys. Live in-memory state may already reflect a\n * newer read or edit from the current run, and durable history cannot prove it\n * is fresher. Relative paths are resolved against the current execution cwd,\n * matching replay hosts that run sessions from a stable workspace.\n */\nexport function hydrateReadStateFromSession(\n session: Session | undefined,\n cwd: string,\n options: HydrateReadStateOptions = {},\n): number {\n if (!session)\n return 0\n\n const readState = getReadState(session)\n if (!readState)\n return 0\n\n const normalized = normalizeHydrateReadStateOptions(options)\n const pending = new Map<string, PendingToolCall>()\n const known = new Map<string, KnownFile>()\n const preexistingKeys = new Set(readState.keys())\n let hydrated = 0\n\n for (const turn of session.turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call') {\n if (\n block.name === normalized.readFileToolName\n || block.name === normalized.writeFileToolName\n || block.name === normalized.editToolName\n || block.name === normalized.multiEditToolName\n ) {\n pending.set(block.id, { name: block.name, input: block.input })\n }\n continue\n }\n\n if (block.type !== 'tool_result')\n continue\n\n const call = pending.get(block.callId)\n if (!call)\n continue\n pending.delete(block.callId)\n if (block.isError === true)\n continue\n\n const output = toolResultToText(block.output)\n const next = stateFromToolResult(call, output, turn, cwd, normalized, known)\n if (!next)\n continue\n\n if (!preexistingKeys.has(next.key)) {\n readState.set(next.key, next.file.entry)\n hydrated += 1\n }\n known.set(next.key, next.file)\n }\n }\n\n return hydrated\n}\n\n/**\n * Async replay hydrator for durable hosts that can cheaply read the current\n * full file through their execution context. Transcript-only reconstruction is\n * still preferred for full reads; partial/truncated read_file entries fall back\n * to `readFileForHash` and hash those full current bytes.\n */\nexport async function hydrateReadStateFromSessionAsync(\n session: Session | undefined,\n cwd: string,\n options: HydrateReadStateOptions = {},\n): Promise<number> {\n if (!session)\n return 0\n\n const readState = getReadState(session)\n if (!readState)\n return 0\n\n const normalized = normalizeHydrateReadStateOptions(options)\n const pending = new Map<string, PendingToolCall>()\n const known = new Map<string, KnownFile>()\n const preexistingKeys = new Set(readState.keys())\n let hydrated = 0\n\n for (const turn of session.turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call') {\n if (\n block.name === normalized.readFileToolName\n || block.name === normalized.writeFileToolName\n || block.name === normalized.editToolName\n || block.name === normalized.multiEditToolName\n ) {\n pending.set(block.id, { name: block.name, input: block.input })\n }\n continue\n }\n\n if (block.type !== 'tool_result')\n continue\n\n const call = pending.get(block.callId)\n if (!call)\n continue\n pending.delete(block.callId)\n if (block.isError === true)\n continue\n\n const output = toolResultToText(block.output)\n const next = await stateFromToolResultAsync(call, output, turn, cwd, normalized, known)\n if (!next)\n continue\n\n if (!preexistingKeys.has(next.key)) {\n readState.set(next.key, next.file.entry)\n hydrated += 1\n }\n known.set(next.key, next.file)\n }\n }\n\n return hydrated\n}\n\nfunction normalizeHydrateReadStateOptions(options: HydrateReadStateOptions): NormalizedHydrateReadStateOptions {\n return {\n readFileToolName: options.readFileToolName ?? 'read_file',\n writeFileToolName: options.writeFileToolName ?? 'write_file',\n editToolName: options.editToolName ?? 'edit',\n multiEditToolName: options.multiEditToolName ?? 'multi_edit',\n defaultLineNumbers: options.defaultLineNumbers ?? true,\n ...(options.readFileForHash ? { readFileForHash: options.readFileForHash } : {}),\n }\n}\n\nfunction stateFromToolResult(\n call: PendingToolCall,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n options: NormalizedHydrateReadStateOptions,\n known: Map<string, KnownFile>,\n): { key: string, file: KnownFile } | undefined {\n if (call.name === options.readFileToolName)\n return readFileStateFromResult(call.input, output, resultTurn, cwd, options.defaultLineNumbers)\n if (call.name === options.writeFileToolName)\n return writeFileStateFromResult(call.input, output, resultTurn, cwd)\n if (call.name === options.editToolName)\n return editStateFromResult(call.input, output, resultTurn, cwd, known)\n if (call.name === options.multiEditToolName)\n return multiEditStateFromResult(call.input, output, resultTurn, cwd, known)\n return undefined\n}\n\nasync function stateFromToolResultAsync(\n call: PendingToolCall,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n options: NormalizedHydrateReadStateOptions,\n known: Map<string, KnownFile>,\n): Promise<{ key: string, file: KnownFile } | undefined> {\n if (call.name === options.readFileToolName) {\n return await readFileStateFromResultAsync(call.input, output, resultTurn, cwd, {\n defaultLineNumbers: options.defaultLineNumbers,\n readFileForHash: options.readFileForHash,\n })\n }\n return stateFromToolResult(call, output, resultTurn, cwd, options, known)\n}\n\nfunction readFileStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n defaultLineNumbers: boolean,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n if (!path || !isSuccessfulFullReadOutput(output))\n return undefined\n\n const lineNumbers = typeof input.lineNumbers === 'boolean' ? input.lineNumbers : defaultLineNumbers\n const content = lineNumbers ? stripReadFileLineNumbers(output) : output\n if (content === undefined)\n return undefined\n\n const entry: ReadStateEntry = {\n contentHash: hashContent(content),\n offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),\n limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),\n maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),\n lineNumbers,\n mtimeMs: resultTurn.createdAt,\n }\n return { key: readStateKey(cwd, path), file: { content, entry } }\n}\n\nasync function readFileStateFromResultAsync(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n options: {\n defaultLineNumbers: boolean\n readFileForHash?: HydrateReadStateOptions['readFileForHash']\n },\n): Promise<{ key: string, file: KnownFile } | undefined> {\n const reconstructed = readFileStateFromResult(input, output, resultTurn, cwd, options.defaultLineNumbers)\n if (reconstructed)\n return reconstructed\n\n const path = typeof input.path === 'string' ? input.path : undefined\n if (!path || !options.readFileForHash || !isSuccessfulReadOutput(output))\n return undefined\n\n let content: string | null | undefined\n try {\n content = await options.readFileForHash(path)\n }\n catch {\n return undefined\n }\n if (typeof content !== 'string')\n return undefined\n\n const lineNumbers = typeof input.lineNumbers === 'boolean' ? input.lineNumbers : options.defaultLineNumbers\n const entry: ReadStateEntry = {\n contentHash: hashContent(content),\n offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),\n limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),\n maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),\n lineNumbers,\n mtimeMs: resultTurn.createdAt,\n }\n return { key: readStateKey(cwd, path), file: { content, entry } }\n}\n\nfunction writeFileStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n const content = typeof input.content === 'string' ? input.content : undefined\n if (!path || content === undefined)\n return undefined\n if (!output.startsWith(`Created ${path} (`) && !output.startsWith(`Updated ${path} (`))\n return undefined\n\n return {\n key: readStateKey(cwd, path),\n file: {\n content,\n entry: {\n contentHash: hashContent(content),\n offset: 0,\n limit: Number.POSITIVE_INFINITY,\n maxBytes: Number.POSITIVE_INFINITY,\n mtimeMs: resultTurn.createdAt,\n },\n },\n }\n}\n\nfunction editStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n known: Map<string, KnownFile>,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n const oldString = typeof input.old_string === 'string' ? input.old_string : undefined\n const newString = typeof input.new_string === 'string' ? input.new_string : undefined\n if (!path || oldString === undefined || newString === undefined)\n return undefined\n if (!output.startsWith(`Edited ${path}: replaced `))\n return undefined\n\n const key = readStateKey(cwd, path)\n const prior = known.get(key)\n if (!prior)\n return undefined\n\n const nextContent = applyEdit(prior.content, oldString, newString, input.replace_all === true)\n if (nextContent === undefined)\n return undefined\n\n return {\n key,\n file: {\n content: nextContent,\n entry: { ...prior.entry, contentHash: hashContent(nextContent), mtimeMs: resultTurn.createdAt },\n },\n }\n}\n\ninterface MultiEditStep {\n old_string?: unknown\n new_string?: unknown\n replace_all?: unknown\n}\n\nfunction multiEditStateFromResult(\n input: Record<string, unknown>,\n output: string,\n resultTurn: SessionTurn,\n cwd: string,\n known: Map<string, KnownFile>,\n): { key: string, file: KnownFile } | undefined {\n const path = typeof input.path === 'string' ? input.path : undefined\n const edits = Array.isArray(input.edits) ? (input.edits as MultiEditStep[]) : undefined\n if (!path || !edits)\n return undefined\n if (!output.startsWith(`Edited ${path}: applied `))\n return undefined\n\n const key = readStateKey(cwd, path)\n const prior = known.get(key)\n if (!prior)\n return undefined\n\n const applied = parseMultiEditAppliedSteps(output, edits.length)\n let current = prior.content\n for (let i = 0; i < edits.length; i++) {\n if (!applied.has(i))\n continue\n const step = edits[i]\n if (typeof step.old_string !== 'string' || typeof step.new_string !== 'string')\n return undefined\n const next = applyEdit(current, step.old_string, step.new_string, step.replace_all === true)\n if (next === undefined)\n return undefined\n current = next\n }\n\n return {\n key,\n file: {\n content: current,\n entry: { ...prior.entry, contentHash: hashContent(current), mtimeMs: resultTurn.createdAt },\n },\n }\n}\n\nfunction applyEdit(content: string, oldString: string, newString: string, replaceAll: boolean): string | undefined {\n const match = resolveOldString(content, oldString)\n if (!match)\n return undefined\n if (match.occurrences > 1 && !replaceAll)\n return undefined\n const styledReplacement = styleReplacementForVia(newString, match.via, match.actual)\n return replaceAll\n ? content.split(match.actual).join(styledReplacement)\n : content.replace(match.actual, styledReplacement)\n}\n\nfunction parseMultiEditAppliedSteps(output: string, editCount: number): Set<number> {\n const annotationMatch = /<edit-outcomes>\\n([\\s\\S]*?)\\n<\\/edit-outcomes>/.exec(output)\n if (!annotationMatch) {\n return new Set(Array.from({ length: editCount }, (_, i) => i))\n }\n\n const applied = new Set<number>()\n for (const line of annotationMatch[1].split('\\n')) {\n const match = /^#(\\d+) applied$/.exec(line.trim())\n if (!match)\n continue\n const index = Number(match[1]) - 1\n if (index >= 0 && index < editCount)\n applied.add(index)\n }\n return applied\n}\n\nfunction isSuccessfulFullReadOutput(output: string): boolean {\n // Live read_file stores a hash of the raw full file before pagination or\n // truncation. Durable turns only contain rendered output, so partial reads\n // cannot be reconstructed safely; skip them and require a real re-read.\n if (!isSuccessfulReadOutput(output) || READ_FILE_FOOTER_RE.test(output))\n return false\n return true\n}\n\nfunction isSuccessfulReadOutput(output: string): boolean {\n if (output.length === 0)\n return true\n if (\n output.startsWith('File not found:')\n || output.startsWith('[binary file:')\n || output.startsWith('Image:')\n || output.startsWith('Document:')\n || output.startsWith('[image too large to inline:')\n || output.startsWith('[document:')\n || output.startsWith('[document too large to attach:')\n || output.startsWith('Image read failed:')\n || output.startsWith('Document read failed:')\n || output.includes(' unchanged since the previous read in this session ')\n ) {\n return false\n }\n return true\n}\n\nfunction stripReadFileLineNumbers(output: string): string | undefined {\n const lines = output.split('\\n')\n const stripped: string[] = []\n for (const line of lines) {\n const match = READ_FILE_LINE_RE.exec(line)\n if (!match)\n return undefined\n stripped.push(match[1])\n }\n return stripped.join('\\n')\n}\n\nfunction normalizeReadInteger(value: unknown, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0)\n return fallback\n return Math.floor(value)\n}\n\n// ---------------------------------------------------------------------------\n// Tool-dedup\n// ---------------------------------------------------------------------------\n\nexport interface ToolDedupEntry {\n hash: string\n result: string | ToolResultContent[]\n /**\n * Number of replay-hits accumulated against this `(name, hash)` since\n * the most recent fresh dispatch. `0` immediately after a fresh\n * dispatch; incremented every time a `tool:gate` handler returns the\n * cached result for an identical input. Used by `dedup-tools` to\n * implement `mode: 'block-after'` — when the count crosses the\n * configured threshold, the gate stops replaying and refuses the call\n * with a `Blocked:` tool_result so the model breaks out of the loop.\n *\n * Resets to `0` on a hash change (a different input under the same\n * tool name is treated as a fresh sequence). Optional for back-compat\n * with consumers that built the entry directly; absent is treated as\n * `0`.\n */\n repeats?: number\n}\n\nexport type ToolDedupMap = Map<string, ToolDedupEntry>\n\nconst TOOL_DEDUP_STATE = new WeakMap<Session, ToolDedupMap>()\n\n/**\n * Get or lazily create the per-session tool-dedup map. Returns `undefined`\n * when no session is provided — middleware should treat that as \"no dedup\".\n */\nexport function getToolDedupState(session: Session | undefined): ToolDedupMap | undefined {\n if (!session)\n return undefined\n let map = TOOL_DEDUP_STATE.get(session)\n if (!map) {\n map = new Map()\n TOOL_DEDUP_STATE.set(session, map)\n }\n return map\n}\n"],"mappings":";;;;AA8EA,MAAM,wBAAQ,IAAI,QAA+B;;;;;;;;;;;;AAajD,SAAgB,aAAa,SAAwD;CACnF,IAAI,CAAC,SACH,OAAO,KAAA;CACT,IAAI,MAAM,MAAM,IAAI,OAAO;CAC3B,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,MAAM,IAAI,SAAS,GAAG;CACxB;CACA,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,oBAAoB,KAGP;CAC3B,OAAO,IAAI,aAAa,aAAa,IAAI,OAAO;AAClD;;;;;;;;;;;;;;;AAgBA,SAAgB,oBAAoB,KAGjC,KAAa,MAAoB;CAClC,MAAM,MAAM,aAAa,KAAK,IAAI;CAClC,KAAK,MAAM,OAAO,CAAC,IAAI,WAAW,IAAI,UAAU,MAAM,IAAI,IAAI,OAAO,IAAI,KAAA,CAAS,GAAG;EACnF,MAAM,QAAQ,KAAK,IAAI,GAAG;EAC1B,IAAI,SAAS,MAAM,WAAW,MAC5B,IAAK,IAAI,KAAK;GAAE,GAAG;GAAO,QAAQ;EAAK,CAAC;CAC5C;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,aAAa,KAAa,MAAsB;CAC9D,OAAO,QAAQ,KAAK,IAAI;AAC1B;;;;;;;;;;;;AAaA,SAAgB,YAAY,MAAsB;CAChD,IAAI,KAAK;CACT,IAAI,KAAK;CACT,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,IAAI,KAAK,WAAW,CAAC;EAC3B,KAAK,KAAK,KAAK,KAAK,GAAG,QAAU,MAAM;EACvC,KAAK,KAAK,KAAK,KAAK,GAAG,UAAU,MAAM;CACzC;CACA,OAAO,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,IAAI,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC3E;AAmCA,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,8BAA8B;AACpC,MAAM,sBAAsB;AAC5B,MAAM,oBAAoB;;;;;;;AAgF1B,eAAsB,iCACpB,SACA,KACA,UAAmC,CAAC,GACnB;CACjB,IAAI,CAAC,SACH,OAAO;CAET,MAAM,YAAY,aAAa,OAAO;CACtC,IAAI,CAAC,WACH,OAAO;CAET,MAAM,aAAa,iCAAiC,OAAO;CAC3D,MAAM,0BAAU,IAAI,IAA6B;CACjD,MAAM,wBAAQ,IAAI,IAAuB;CACzC,MAAM,kBAAkB,IAAI,IAAI,UAAU,KAAK,CAAC;CAChD,IAAI,WAAW;CAEf,KAAK,MAAM,QAAQ,QAAQ,OACzB,KAAK,MAAM,SAAS,KAAK,SAAS;EAChC,IAAI,MAAM,SAAS,aAAa;GAC9B,IACE,MAAM,SAAS,WAAW,oBACvB,MAAM,SAAS,WAAW,qBAC1B,MAAM,SAAS,WAAW,gBAC1B,MAAM,SAAS,WAAW,mBAE7B,QAAQ,IAAI,MAAM,IAAI;IAAE,MAAM,MAAM;IAAM,OAAO,MAAM;GAAM,CAAC;GAEhE;EACF;EAEA,IAAI,MAAM,SAAS,eACjB;EAEF,MAAM,OAAO,QAAQ,IAAI,MAAM,MAAM;EACrC,IAAI,CAAC,MACH;EACF,QAAQ,OAAO,MAAM,MAAM;EAC3B,IAAI,MAAM,YAAY,MACpB;EAGF,MAAM,OAAO,MAAM,yBAAyB,MAD7B,iBAAiB,MAAM,MACiB,GAAG,MAAM,KAAK,YAAY,KAAK;EACtF,IAAI,CAAC,MACH;EAEF,IAAI,CAAC,gBAAgB,IAAI,KAAK,GAAG,GAAG;GAClC,UAAU,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK;GACvC,YAAY;EACd;EACA,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI;CAC/B;CAGF,OAAO;AACT;AAEA,SAAS,iCAAiC,SAAqE;CAC7G,OAAO;EACL,kBAAkB,QAAQ,oBAAoB;EAC9C,mBAAmB,QAAQ,qBAAqB;EAChD,cAAc,QAAQ,gBAAgB;EACtC,mBAAmB,QAAQ,qBAAqB;EAChD,oBAAoB,QAAQ,sBAAsB;EAClD,GAAI,QAAQ,kBAAkB,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI,CAAC;CAChF;AACF;AAEA,SAAS,oBACP,MACA,QACA,YACA,KACA,SACA,OAC8C;CAC9C,IAAI,KAAK,SAAS,QAAQ,kBACxB,OAAO,wBAAwB,KAAK,OAAO,QAAQ,YAAY,KAAK,QAAQ,kBAAkB;CAChG,IAAI,KAAK,SAAS,QAAQ,mBACxB,OAAO,yBAAyB,KAAK,OAAO,QAAQ,YAAY,GAAG;CACrE,IAAI,KAAK,SAAS,QAAQ,cACxB,OAAO,oBAAoB,KAAK,OAAO,QAAQ,YAAY,KAAK,KAAK;CACvE,IAAI,KAAK,SAAS,QAAQ,mBACxB,OAAO,yBAAyB,KAAK,OAAO,QAAQ,YAAY,KAAK,KAAK;AAE9E;AAEA,eAAe,yBACb,MACA,QACA,YACA,KACA,SACA,OACuD;CACvD,IAAI,KAAK,SAAS,QAAQ,kBACxB,OAAO,MAAM,6BAA6B,KAAK,OAAO,QAAQ,YAAY,KAAK;EAC7E,oBAAoB,QAAQ;EAC5B,iBAAiB,QAAQ;CAC3B,CAAC;CAEH,OAAO,oBAAoB,MAAM,QAAQ,YAAY,KAAK,SAAS,KAAK;AAC1E;AAEA,SAAS,wBACP,OACA,QACA,YACA,KACA,oBAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,IAAI,CAAC,QAAQ,CAAC,2BAA2B,MAAM,GAC7C,OAAO,KAAA;CAET,MAAM,cAAc,OAAO,MAAM,gBAAgB,YAAY,MAAM,cAAc;CACjF,MAAM,UAAU,cAAc,yBAAyB,MAAM,IAAI;CACjE,IAAI,YAAY,KAAA,GACd,OAAO,KAAA;CAET,MAAM,QAAwB;EAC5B,aAAa,YAAY,OAAO;EAChC,QAAQ,qBAAqB,MAAM,QAAQ,wBAAwB;EACnE,OAAO,qBAAqB,MAAM,OAAO,uBAAuB;EAChE,UAAU,qBAAqB,MAAM,UAAU,2BAA2B;EAC1E;EACA,SAAS,WAAW;CACtB;CACA,OAAO;EAAE,KAAK,aAAa,KAAK,IAAI;EAAG,MAAM;GAAE;GAAS;EAAM;CAAE;AAClE;AAEA,eAAe,6BACb,OACA,QACA,YACA,KACA,SAIuD;CACvD,MAAM,gBAAgB,wBAAwB,OAAO,QAAQ,YAAY,KAAK,QAAQ,kBAAkB;CACxG,IAAI,eACF,OAAO;CAET,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,IAAI,CAAC,QAAQ,CAAC,QAAQ,mBAAmB,CAAC,uBAAuB,MAAM,GACrE,OAAO,KAAA;CAET,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,QAAQ,gBAAgB,IAAI;CAC9C,QACM;EACJ;CACF;CACA,IAAI,OAAO,YAAY,UACrB,OAAO,KAAA;CAET,MAAM,cAAc,OAAO,MAAM,gBAAgB,YAAY,MAAM,cAAc,QAAQ;CACzF,MAAM,QAAwB;EAC5B,aAAa,YAAY,OAAO;EAChC,QAAQ,qBAAqB,MAAM,QAAQ,wBAAwB;EACnE,OAAO,qBAAqB,MAAM,OAAO,uBAAuB;EAChE,UAAU,qBAAqB,MAAM,UAAU,2BAA2B;EAC1E;EACA,SAAS,WAAW;CACtB;CACA,OAAO;EAAE,KAAK,aAAa,KAAK,IAAI;EAAG,MAAM;GAAE;GAAS;EAAM;CAAE;AAClE;AAEA,SAAS,yBACP,OACA,QACA,YACA,KAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,MAAM,UAAU,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU,KAAA;CACpE,IAAI,CAAC,QAAQ,YAAY,KAAA,GACvB,OAAO,KAAA;CACT,IAAI,CAAC,OAAO,WAAW,WAAW,KAAK,GAAG,KAAK,CAAC,OAAO,WAAW,WAAW,KAAK,GAAG,GACnF,OAAO,KAAA;CAET,OAAO;EACL,KAAK,aAAa,KAAK,IAAI;EAC3B,MAAM;GACJ;GACA,OAAO;IACL,aAAa,YAAY,OAAO;IAChC,QAAQ;IACR,OAAO,OAAO;IACd,UAAU,OAAO;IACjB,SAAS,WAAW;GACtB;EACF;CACF;AACF;AAEA,SAAS,oBACP,OACA,QACA,YACA,KACA,OAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,MAAM,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAA;CAC5E,MAAM,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAA;CAC5E,IAAI,CAAC,QAAQ,cAAc,KAAA,KAAa,cAAc,KAAA,GACpD,OAAO,KAAA;CACT,IAAI,CAAC,OAAO,WAAW,UAAU,KAAK,YAAY,GAChD,OAAO,KAAA;CAET,MAAM,MAAM,aAAa,KAAK,IAAI;CAClC,MAAM,QAAQ,MAAM,IAAI,GAAG;CAC3B,IAAI,CAAC,OACH,OAAO,KAAA;CAET,MAAM,cAAc,UAAU,MAAM,SAAS,WAAW,WAAW,MAAM,gBAAgB,IAAI;CAC7F,IAAI,gBAAgB,KAAA,GAClB,OAAO,KAAA;CAET,OAAO;EACL;EACA,MAAM;GACJ,SAAS;GACT,OAAO;IAAE,GAAG,MAAM;IAAO,aAAa,YAAY,WAAW;IAAG,SAAS,WAAW;GAAU;EAChG;CACF;AACF;AAQA,SAAS,yBACP,OACA,QACA,YACA,KACA,OAC8C;CAC9C,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;CAC3D,MAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAK,MAAM,QAA4B,KAAA;CAC9E,IAAI,CAAC,QAAQ,CAAC,OACZ,OAAO,KAAA;CACT,IAAI,CAAC,OAAO,WAAW,UAAU,KAAK,WAAW,GAC/C,OAAO,KAAA;CAET,MAAM,MAAM,aAAa,KAAK,IAAI;CAClC,MAAM,QAAQ,MAAM,IAAI,GAAG;CAC3B,IAAI,CAAC,OACH,OAAO,KAAA;CAET,MAAM,UAAU,2BAA2B,QAAQ,MAAM,MAAM;CAC/D,IAAI,UAAU,MAAM;CACpB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,IAAI,CAAC,QAAQ,IAAI,CAAC,GAChB;EACF,MAAM,OAAO,MAAM;EACnB,IAAI,OAAO,KAAK,eAAe,YAAY,OAAO,KAAK,eAAe,UACpE,OAAO,KAAA;EACT,MAAM,OAAO,UAAU,SAAS,KAAK,YAAY,KAAK,YAAY,KAAK,gBAAgB,IAAI;EAC3F,IAAI,SAAS,KAAA,GACX,OAAO,KAAA;EACT,UAAU;CACZ;CAEA,OAAO;EACL;EACA,MAAM;GACJ,SAAS;GACT,OAAO;IAAE,GAAG,MAAM;IAAO,aAAa,YAAY,OAAO;IAAG,SAAS,WAAW;GAAU;EAC5F;CACF;AACF;AAEA,SAAS,UAAU,SAAiB,WAAmB,WAAmB,YAAyC;CACjH,MAAM,QAAQ,iBAAiB,SAAS,SAAS;CACjD,IAAI,CAAC,OACH,OAAO,KAAA;CACT,IAAI,MAAM,cAAc,KAAK,CAAC,YAC5B,OAAO,KAAA;CACT,MAAM,oBAAoB,uBAAuB,WAAW,MAAM,KAAK,MAAM,MAAM;CACnF,OAAO,aACH,QAAQ,MAAM,MAAM,MAAM,EAAE,KAAK,iBAAiB,IAClD,QAAQ,QAAQ,MAAM,QAAQ,iBAAiB;AACrD;AAEA,SAAS,2BAA2B,QAAgB,WAAgC;CAClF,MAAM,kBAAkB,iDAAiD,KAAK,MAAM;CACpF,IAAI,CAAC,iBACH,OAAO,IAAI,IAAI,MAAM,KAAK,EAAE,QAAQ,UAAU,IAAI,GAAG,MAAM,CAAC,CAAC;CAG/D,MAAM,0BAAU,IAAI,IAAY;CAChC,KAAK,MAAM,QAAQ,gBAAgB,GAAG,MAAM,IAAI,GAAG;EACjD,MAAM,QAAQ,mBAAmB,KAAK,KAAK,KAAK,CAAC;EACjD,IAAI,CAAC,OACH;EACF,MAAM,QAAQ,OAAO,MAAM,EAAE,IAAI;EACjC,IAAI,SAAS,KAAK,QAAQ,WACxB,QAAQ,IAAI,KAAK;CACrB;CACA,OAAO;AACT;AAEA,SAAS,2BAA2B,QAAyB;CAI3D,IAAI,CAAC,uBAAuB,MAAM,KAAK,oBAAoB,KAAK,MAAM,GACpE,OAAO;CACT,OAAO;AACT;AAEA,SAAS,uBAAuB,QAAyB;CACvD,IAAI,OAAO,WAAW,GACpB,OAAO;CACT,IACE,OAAO,WAAW,iBAAiB,KAChC,OAAO,WAAW,eAAe,KACjC,OAAO,WAAW,QAAQ,KAC1B,OAAO,WAAW,WAAW,KAC7B,OAAO,WAAW,6BAA6B,KAC/C,OAAO,WAAW,YAAY,KAC9B,OAAO,WAAW,gCAAgC,KAClD,OAAO,WAAW,oBAAoB,KACtC,OAAO,WAAW,uBAAuB,KACzC,OAAO,SAAS,qDAAqD,GAExE,OAAO;CAET,OAAO;AACT;AAEA,SAAS,yBAAyB,QAAoC;CACpE,MAAM,QAAQ,OAAO,MAAM,IAAI;CAC/B,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,kBAAkB,KAAK,IAAI;EACzC,IAAI,CAAC,OACH,OAAO,KAAA;EACT,SAAS,KAAK,MAAM,EAAE;CACxB;CACA,OAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,qBAAqB,OAAgB,UAA0B;CACtE,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAClE,OAAO;CACT,OAAO,KAAK,MAAM,KAAK;AACzB;AA4BA,MAAM,mCAAmB,IAAI,QAA+B;;;;;AAM5D,SAAgB,kBAAkB,SAAwD;CACxF,IAAI,CAAC,SACH,OAAO,KAAA;CACT,IAAI,MAAM,iBAAiB,IAAI,OAAO;CACtC,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,iBAAiB,IAAI,SAAS,GAAG;CACnC;CACA,OAAO;AACT"}
|
package/dist/restate.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { St as StreamOptions, T as ToolDef, U as SessionStore, Un as ToolBatchExecutor, an as AgentBehavior, o as McpToolMeta, on as AgentClock, qn as ToolResultContent, s as McpToolWrap, w as ToolContext, yt as Provider } from "./agent-
|
|
2
|
-
import { o as DetachedTasksCapability } from "./types-Bs2oY7Ux.js";
|
|
1
|
+
import { St as StreamOptions, T as ToolDef, U as SessionStore, Un as ToolBatchExecutor, an as AgentBehavior, o as McpToolMeta, on as AgentClock, qn as ToolResultContent, s as McpToolWrap, w as ToolContext, yt as Provider } from "./agent-NkKgz5Dh.js";
|
|
2
|
+
import { h as TaskExitInfo, l as ExecutionHandle, m as TaskEntry, o as DetachedTasksCapability } from "./types-Bs2oY7Ux.js";
|
|
3
3
|
|
|
4
4
|
//#region src/restate/types.d.ts
|
|
5
5
|
/**
|
|
@@ -679,6 +679,38 @@ interface RestateToolOptions<Token = unknown, Resumed = unknown> {
|
|
|
679
679
|
* invocation.
|
|
680
680
|
*/
|
|
681
681
|
replayTracker?: ReplayTracker;
|
|
682
|
+
/**
|
|
683
|
+
* Keep an ordinary, recoverable tool throw (a failed shell command, a
|
|
684
|
+
* transport error, an MCP `isError`) NON-fatal under Restate.
|
|
685
|
+
*
|
|
686
|
+
* Without this, the throw rides out of the journaled `ctx.run` action,
|
|
687
|
+
* exhausts the retry budget, and surfaces as a Restate `TerminalError` —
|
|
688
|
+
* which `restateBehavior`'s `shouldRethrowToolError` re-throws, aborting the
|
|
689
|
+
* whole run. With it, the throw is captured INSIDE the journal as a sentinel
|
|
690
|
+
* value and re-thrown as a plain (non-control) error OUTSIDE it, so the loop
|
|
691
|
+
* produces a model-visible `tool_result { isError }` and continues —
|
|
692
|
+
* identical to the non-Restate soft-error model, and the same mechanism
|
|
693
|
+
* `restateMcpToolWrap` already applies to MCP tools. Real Restate control
|
|
694
|
+
* errors and deliberate aborts are never softened.
|
|
695
|
+
*
|
|
696
|
+
* Applies to the single-phase journaling path (the default wrapping and the
|
|
697
|
+
* MCP path). The two-phase {@link RestateToolOptions.durableStart} mode owns
|
|
698
|
+
* its own start/wait error shape and is unaffected.
|
|
699
|
+
*
|
|
700
|
+
* Default: `true`.
|
|
701
|
+
*/
|
|
702
|
+
softErrors?: boolean;
|
|
703
|
+
/**
|
|
704
|
+
* Collapse malformed structured result blocks (`{ type: 'image' }` with no
|
|
705
|
+
* payload, foreign block shapes) to a text placeholder before the result is
|
|
706
|
+
* journaled, so a downstream provider wire serializer never has to project an
|
|
707
|
+
* unprojectable block (which, under Restate, would throw inside the journaled
|
|
708
|
+
* provider call and become fatal). Well-formed results pass through
|
|
709
|
+
* untouched. Applies to the single-phase journaling path.
|
|
710
|
+
*
|
|
711
|
+
* Default: `true`.
|
|
712
|
+
*/
|
|
713
|
+
coerceResultBlocks?: boolean;
|
|
682
714
|
}
|
|
683
715
|
/**
|
|
684
716
|
* Wrap a single `ToolDef` so its `execute` runs inside `ctx.run`. The
|
|
@@ -697,5 +729,58 @@ declare function wrapAgentTools(tools: Record<string, ToolDef>, ctx: RestateCont
|
|
|
697
729
|
exclude?: readonly string[];
|
|
698
730
|
}): Record<string, ToolDef>;
|
|
699
731
|
//#endregion
|
|
700
|
-
|
|
732
|
+
//#region src/restate/wait-background.d.ts
|
|
733
|
+
interface RestateWaitBackgroundOptions {
|
|
734
|
+
/** The Restate context the awakeable + journal entries bind to. */
|
|
735
|
+
ctx: RestateContextLike;
|
|
736
|
+
/**
|
|
737
|
+
* Snapshot the host's task registry (the `listBackground` shape). Called
|
|
738
|
+
* AFTER `register`, inside a journaled `ctx.run`. The helper finds `taskId`
|
|
739
|
+
* in the result: terminated → fast-path return, running → park, absent →
|
|
740
|
+
* `null`.
|
|
741
|
+
*/
|
|
742
|
+
probe: (handle: ExecutionHandle, taskId: string) => Promise<readonly TaskEntry[]> | readonly TaskEntry[];
|
|
743
|
+
/**
|
|
744
|
+
* Persist `awakeableId` as the exit resumer for `taskId` BEFORE the probe —
|
|
745
|
+
* the host's task-exit path must later call `ctx.resolveAwakeable(awakeableId,
|
|
746
|
+
* exitInfo)`. Runs inside a journaled `ctx.run`; keep it short and
|
|
747
|
+
* idempotent-safe up to journal persistence.
|
|
748
|
+
*/
|
|
749
|
+
register: (awakeableId: string, taskId: string, handle: ExecutionHandle) => Promise<void> | void;
|
|
750
|
+
/**
|
|
751
|
+
* Finalize the resumer row once the wait resolves — `exit` is the task's
|
|
752
|
+
* {@link TaskExitInfo}, or `null` on timeout / abort / unknown task. Runs
|
|
753
|
+
* inside a journaled `ctx.run`. Optional.
|
|
754
|
+
*/
|
|
755
|
+
settle?: (awakeableId: string, taskId: string, exit: TaskExitInfo | null, handle: ExecutionHandle) => Promise<void> | void;
|
|
756
|
+
/**
|
|
757
|
+
* Default park deadline in milliseconds. A per-call `options.timeoutMs`
|
|
758
|
+
* (what the `wait_task` tool passes) wins. Unset / non-positive = park until
|
|
759
|
+
* the resumer fires or the caller aborts.
|
|
760
|
+
*/
|
|
761
|
+
timeoutMs?: number;
|
|
762
|
+
/**
|
|
763
|
+
* Durable race combinator for the timeout race — pass `RestatePromise` from
|
|
764
|
+
* the SDK. See {@link awakeableWithTimeout}; native `Promise.race` is the
|
|
765
|
+
* replay-safe fallback.
|
|
766
|
+
*/
|
|
767
|
+
promises?: RestatePromiseRaceLike;
|
|
768
|
+
/** `ctx.run` options for the journaled phases. Defaults to `{ maxRetryAttempts: 1 }`. */
|
|
769
|
+
runOptions?: RestateRunOptions;
|
|
770
|
+
/** Journal-entry prefix. Default `wait-background`. */
|
|
771
|
+
entryPrefix?: string;
|
|
772
|
+
/** Per-invocation replay tracker (see `createReplayTracker`). */
|
|
773
|
+
replayTracker?: ReplayTracker;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Build a durable `ExecutionContext.waitBackground` — see the module doc for
|
|
777
|
+
* the ordering contract. The returned function is the seam itself:
|
|
778
|
+
* `(handle, taskId, options?) => Promise<TaskExitInfo | null>`.
|
|
779
|
+
*/
|
|
780
|
+
declare function restateWaitBackground(options: RestateWaitBackgroundOptions): (handle: ExecutionHandle, taskId: string, opts?: {
|
|
781
|
+
timeoutMs?: number;
|
|
782
|
+
signal?: AbortSignal;
|
|
783
|
+
}) => Promise<TaskExitInfo | null>;
|
|
784
|
+
//#endregion
|
|
785
|
+
export { type AwakeableWithTimeoutOptions, type ReplayDoneHooks, type ReplayTracker, type ReplayTrackerOptions, type RestateAwakeable, type RestateAwakeableToolOptions, type RestateContextLike, type RestateDurableStart, type RestateDurableWait, type RestateMcpToolWrapOptions, type RestateObjectContextLike, type RestatePromiseAllLike, type RestatePromiseLike, type RestatePromiseRaceLike, type RestateProviderOptions, type RestateRunOptions, type RestateToolBatchExecutorOptions, type RestateToolOptions, type RestateWaitBackgroundOptions, awakeableWithTimeout, createReplayTracker, replayAwareAbort, restateAwakeableTool, restateBehavior, restateClock, restateMcpToolWrap, restateProvider, restateSessionStore, restateTool, restateToolBatchExecutor, restateWaitBackground, shouldRethrowRestateControlError, wrapAgentTools };
|
|
701
786
|
//# sourceMappingURL=restate.d.ts.map
|
package/dist/restate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restate.d.ts","names":[],"sources":["../src/restate/types.ts","../src/restate/awakeable.ts","../src/restate/awakeable-tool.ts","../src/restate/behavior.ts","../src/restate/clock.ts","../src/restate/errors.ts","../src/restate/replay.ts","../src/restate/mcp.ts","../src/restate/parallel.ts","../src/restate/provider.ts","../src/restate/session.ts","../src/restate/tool.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"restate.d.ts","names":[],"sources":["../src/restate/types.ts","../src/restate/awakeable.ts","../src/restate/awakeable-tool.ts","../src/restate/behavior.ts","../src/restate/clock.ts","../src/restate/errors.ts","../src/restate/replay.ts","../src/restate/mcp.ts","../src/restate/parallel.ts","../src/restate/provider.ts","../src/restate/session.ts","../src/restate/tool.ts","../src/restate/wait-background.ts"],"mappings":";;;;;;;;AAmBA;;;;;;;;;;;AAYO;AAUP;;UAtBiB,iBAAA;EAwBC;EAtBhB,gBAAA;EAqBA;EAnBA,gBAAA;EAoBS;EAlBT,oBAAA;EAkBkB;EAhBlB,gBAAA;EAuCe;EArCf,mBAAA;EAqCiC;EAnCjC,KAAA;AAAA;;;;;;;;UAUe,gBAAA;EACf,EAAA;EACA,OAAA,EAAS,OAAO,CAAC,CAAA;AAAA;;;;;;;;;;;;;;;;;;;;;UAuBF,kBAAA;EACf,GAAA,MACE,IAAA,UACA,EAAA,QAAU,OAAA,CAAQ,CAAA,IAAK,CAAA,EACvB,OAAA,GAAU,iBAAA,KACP,OAAA,CAAQ,CAAA;EACb,IAAA;IAAQ,GAAA,QAAW,OAAA;EAAA;EACnB,IAAA;IAAQ,MAAA;EAAA;EACR,SAAA,WAAoB,gBAAA,CAAiB,CAAA;EA0BP;;;;;;;;;;;;EAb9B,KAAA,GAAQ,MAAA,aAAmB,OAAA;AAAA;;;;;;AAcP;;UAJL,wBAAA,SAAiC,kBAAA;EAChD,GAAA;EACA,GAAA,MAAS,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAClC,GAAA,MAAS,IAAA,UAAc,KAAA,EAAO,CAAA;EAC9B,KAAA,GAAQ,IAAA;AAAA;;;;;AAtEH;AAUP;;;;;UClBiB,sBAAA,kBAAwC,OAAA,YAAmB,OAAA;EAC1E,IAAA,GAAO,MAAA,WAAiB,QAAA,OAAe,OAAA;AAAA;AAAA,UAGxB,2BAAA,kBAA6C,OAAA,YAAmB,OAAA;EDgB7D;AAAA;AAuBpB;;EClCE,SAAA;EDqCoB;;;;;;;EC7BpB,QAAA,GAAW,sBAAA,CAAuB,QAAA;AAAA;;;;;;;;;;;;;;;;;iBAmBpB,oBAAA,qBAAyC,OAAA,YAAmB,OAAA,UAAA,CAC1E,GAAA,EAAK,kBAAA,EACL,OAAA,GAAS,2BAAA,CAA4B,QAAA,IACpC,gBAAA,CAAiB,CAAA;;;UC5BH,2BAAA;EFgCA;EE9Bf,IAAA;EF8BiC;EE5BjC,WAAA;EF+BY;EE7BZ,WAAA,EAAa,MAAA;EF8BD;EE5BZ,GAAA,EAAK,kBAAA;EF6BA;;;;;EEvBL,MAAA,GAAS,IAAA;IACP,WAAA;IACA,KAAA,EAAO,MAAA;IACP,OAAA,EAAS,WAAA;EAAA,aACE,OAAA;EFiBD;;;;EEZZ,QAAA,IAAY,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,MAAA,+BAAqC,iBAAA;EFahE;;;;EERF,UAAA,GAAa,iBAAA;EFUM;;;;EELnB,iBAAA;AAAA;;;;;;AFoBkC;AAUpC;;iBEnBgB,oBAAA,aAAA,CACd,OAAA,EAAS,2BAAA,CAA4B,CAAA,IACpC,OAAA;;;;;;AF7DH;;;;;;;;;;;iBGFgB,eAAA,CAAgB,SAAA,GAAW,aAAA,GAAqB,aAAa;;;;;;AHctE;AAUP;;;;iBInBgB,YAAA,CAAa,GAAA,EAAK,kBAAA,GAAqB,UAAU;;;;;;;AJHjE;;;iBKZgB,gCAAA,CAAiC,KAAc;;;;;;;ALY/D;;;;;;;;;;;AAYO;AAUP;;;;;;;;;;AAEoB;AAuBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UMDiB,eAAA;EACf,QAAA,GAAW,IAAA,iBAAqB,GAAA,EAAK,MAAM;AAAA;AAAA,UAG5B,oBAAA;EN4ByB;;;;;;;EMpBxC,KAAA,GAAQ,eAAe;AAAA;AAAA,UAGR,aAAA;ENmBf;;;;;EMbA,WAAA;ENcM;;;;;;EMPN,MAAA,GAAS,EAAA;ENQW;;;;AC9EtB;EK4EE,QAAA;AAAA;;;;;;iBAQc,mBAAA,CAAoB,OAAA,GAAS,oBAAA,GAA4B,aAAa;;;;;;;;;ALnFtC;AAGhD;;iBKsIgB,gBAAA,CAAiB,OAAA,EAAS,aAAa,EAAE,KAAA;;;UC5FxC,yBAAA;EPkBmB;AAUpC;;;;;EOrBE,aAAA,GAAgB,aAAA;EPqBgC;;;;;;;EObhD,UAAA,GAAa,iBAAA;EPea;;;;;EOT1B,SAAA,IAAa,GAAA,UAAa,IAAA,UAAc,KAAA,EAAO,MAAA,mBAAyB,MAAA;EPUjD;;;;AACH;;EOJpB,OAAA;;AN1EF;;;;;;;EMmFE,QAAA,IAAY,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,WAAA,KAAgB,OAAA;AAAA;;;;;;;iBAkCnC,kBAAA,CACd,GAAA,EAAK,kBAAA,EACL,OAAA,GAAS,yBAAA,GACR,WAAA;;;UC5Ic,qBAAA,kBAAuC,OAAA,YAAmB,kBAAA;EACzE,GAAA,GAAM,MAAA,WAAiB,QAAA,OAAe,OAAA;AAAA;AAAA,KAG5B,kBAAA,MAAwB,OAAA,CAAQ,CAAA;EAC1C,GAAA,MAAS,MAAA,GAAS,KAAA,GAAQ,CAAA,EAAG,OAAA,eAAsB,CAAA,KAAM,OAAA,CAAQ,CAAA;AAAA;AAAA,UAGlD,+BAAA,kBAAiD,OAAA,YAAmB,kBAAA;ERUnF;;;;EQLA,QAAA,EAAU,qBAAA,CAAsB,QAAA;AAAA;;ARe3B;AAUP;;;;;iBQfgB,wBAAA,kBAA0C,OAAA,YAAmB,kBAAA,UAAA,CAC3E,OAAA,EAAS,+BAAA,CAAgC,QAAA,IACxC,iBAAA;;;UCFc,sBAAA;ETiBG;AAAA;AAuBpB;;;;;;;;;;;;;;;;;;;ESlBE,UAAA,GAAa,iBAAA;ETqBY;;;;;;;ESbzB,SAAA,IAAa,GAAA,UAAa,IAAA,EAAM,aAAA;ETgBb;;;;;;;ESRnB,eAAA;ETuBQ;;;AAA0B;AAUpC;;;ESzBE,aAAA,GAAgB,aAAA;AAAA;;;;;;;iBASF,eAAA,CACd,KAAA,EAAO,QAAA,EACP,GAAA,EAAK,kBAAA,EACL,OAAA,GAAS,sBAAA,GACR,QAAA;;;;;;;;;;;iBCzBa,mBAAA,CAAoB,GAAA,EAAK,wBAAA,GAA2B,YAAY;;;;;;;;;;;;;;;;;;;;;;;UCH/D,mBAAA;EXab;EWXF,KAAA,GAAQ,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA,KAAgB,OAAA,CAAQ,KAAA,IAAS,KAAA;EXYrE;;;;;;;;;;;;EWCb,MAAA,IAAU,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA,KAAgB,OAAA,UAAiB,iBAAA;EXehE;AAAA;AAUpC;;;;;;;;;;;;;;;;;EWLE,IAAA,GAAO,kBAAA,CAAmB,KAAA,EAAO,OAAA;EXQxB;;;;;;AACW;;;;AC9EtB;;;;;;;;;;EU0FE,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA,KAAgB,OAAA;AAAA;;;;;;AVzFpC;AAGhD;;;;;UUoGiB,kBAAA;EVvFJ;;;;;;EU8FX,QAAA,GAAW,KAAA,EAAO,KAAA,EAAO,WAAA,UAAqB,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA,KAAgB,OAAA;EV9FvG;;;;AAA0C;AAmB5C;EUkFE,MAAA,GAAS,KAAA,EAAO,KAAA,EAAO,OAAA,EAAS,OAAA,SAAgB,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA,KAAgB,OAAA,UAAiB,iBAAA,eAAgC,iBAAA;EVlFxH;;;;;;;EU0FlC,SAAA,cAAuB,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA;EVvF/C;;;;;EU6FjB,QAAA,GAAW,sBAAA;EV/FN;;;;;;;;AAEc;;;;AC5BrB;;;;;;ES4IE,MAAA,IAAU,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,mBAAyB,OAAA,EAAS,WAAA;AAAA;AAAA,UASlD,kBAAA;ET9Hc;;;;;;;ESsI7B,UAAA,GAAa,iBAAA;ETvJb;;;;;;;;ESgKA,SAAA,IAAa,GAAA,UAAa,IAAA,UAAc,KAAA,EAAO,MAAA,mBAAyB,MAAA;ETrJ7D;;;;;;;ES6JX,sBAAA;ETvJkE;;;;;AAUjD;AAWnB;;;;ES6IE,aAAA,GAAgB,uBAAA;ET3If;;;;;ESiJD,YAAA,GAAe,mBAAA,CAAoB,KAAA,EAAO,OAAA;ETlJ1C;;;AACQ;;;;AC/DV;EQyNE,aAAA,GAAgB,aAAA;;;;;;;ARzN2D;;;;ACK7E;;;;;;;;AAAiE;;EOyO/D,UAAA;;ANxPF;;;;AAA+D;;;;AC0D/D;EKyME,kBAAA;AAAA;;;;;iBAUc,WAAA,oCAAA,CACd,KAAA,EAAO,OAAA,EACP,GAAA,EAAK,kBAAA,EACL,OAAA,GAAS,kBAAA,CAAmB,KAAA,EAAO,OAAA,IAClC,OAAA;;ALtNyD;AAG5D;;;;AAQyB;AAGzB;iBK2hBgB,cAAA,CACd,KAAA,EAAO,MAAA,SAAe,OAAA,GACtB,GAAA,EAAK,kBAAA,EACL,IAAA,GAAM,IAAA,CAAK,kBAAA;EAAwC,OAAA;AAAA,IAClD,MAAA,SAAe,OAAA;;;UClkBD,4BAAA;EZ6BsB;EY3BrC,GAAA,EAAK,kBAAA;EZwCsB;;;;;;EYjC3B,KAAA,GAAQ,MAAA,EAAQ,eAAA,EAAiB,MAAA,aAAmB,OAAA,UAAiB,SAAA,eAAwB,SAAA;EZezE;;;;;;EYRpB,QAAA,GAAW,WAAA,UAAqB,MAAA,UAAgB,MAAA,EAAQ,eAAA,KAAoB,OAAA;EZW5E;;;;;EYLA,MAAA,IAAU,WAAA,UAAqB,MAAA,UAAgB,IAAA,EAAM,YAAA,SAAqB,MAAA,EAAQ,eAAA,KAAoB,OAAA;EZO1F;;;;;EYDZ,SAAA;EZckC;AAAA;AAUpC;;;EYlBE,QAAA,GAAW,sBAAA;EZoBe;EYlB1B,UAAA,GAAa,iBAAA;EZgBmC;EYdhD,WAAA;EZckE;EYZlE,aAAA,GAAgB,aAAA;AAAA;;;;;;iBAiDF,qBAAA,CACd,OAAA,EAAS,4BAAA,IACP,MAAA,EAAQ,eAAA,EAAiB,MAAA,UAAgB,IAAA;EAAS,SAAA;EAAoB,MAAA,GAAS,WAAA;AAAA,MAAkB,OAAA,CAAQ,YAAA"}
|
package/dist/restate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as coerceToolResultContent } from "./types-CyVGdbia.js";
|
|
2
|
+
import { i as hydrateReadStateFromSessionAsync } from "./read-state-CLK9yVpm.js";
|
|
2
3
|
//#region src/restate/awakeable.ts
|
|
3
4
|
/**
|
|
4
5
|
* Mint an awakeable that resolves with the resumer's payload, or `null`
|
|
@@ -134,6 +135,65 @@ function restateClock(ctx) {
|
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
//#endregion
|
|
138
|
+
//#region src/restate/soft-errors.ts
|
|
139
|
+
/**
|
|
140
|
+
* Soft tool-error plumbing shared by {@link restateTool} and
|
|
141
|
+
* {@link restateMcpToolWrap}.
|
|
142
|
+
*
|
|
143
|
+
* The problem: `restateTool` runs a tool body inside `ctx.run(...,
|
|
144
|
+
* { maxRetryAttempts: 1 })`. An ordinary, recoverable tool throw (a failed
|
|
145
|
+
* shell command, a transport error, an MCP `isError` result) would exhaust the
|
|
146
|
+
* retry budget and surface as a Restate `TerminalError` — which
|
|
147
|
+
* `restateBehavior`'s `shouldRethrowToolError` re-throws, aborting the whole
|
|
148
|
+
* run. That's the opposite of the non-Restate model, where a tool throw becomes
|
|
149
|
+
* a model-visible `tool_result { isError }` and the loop keeps going.
|
|
150
|
+
*
|
|
151
|
+
* The fix (same shape the MCP wrapper has always used): catch the recoverable
|
|
152
|
+
* throw INSIDE the journaled closure and record it as a JSON-serializable
|
|
153
|
+
* sentinel — so `ctx.run` journals a *value*, never a failed action. OUTSIDE the
|
|
154
|
+
* journal (on both live and replay) the sentinel is turned back into a plain,
|
|
155
|
+
* non-control `Error`, so the loop produces a `tool_result { isError }` and
|
|
156
|
+
* continues. Real Restate control errors and deliberate aborts are never
|
|
157
|
+
* softened — they must escape so the runtime can suspend / retry / cancel.
|
|
158
|
+
*/
|
|
159
|
+
/** Journal value recorded for a softened tool throw. */
|
|
160
|
+
const SOFT_ERROR_KEY = "__zidaneRestateSoftError";
|
|
161
|
+
function isSoftToolError(value) {
|
|
162
|
+
return typeof value === "object" && value !== null && value[SOFT_ERROR_KEY] === true;
|
|
163
|
+
}
|
|
164
|
+
/** Build the sentinel recorded as the journal value for a softened throw. */
|
|
165
|
+
function makeSoftToolError(error) {
|
|
166
|
+
return {
|
|
167
|
+
[SOFT_ERROR_KEY]: true,
|
|
168
|
+
message: error instanceof Error ? error.message : String(error)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Recoverable throws soften; control-flow + abort throws do not.
|
|
173
|
+
*
|
|
174
|
+
* - Restate control errors (`TerminalError`, `SuspendedError`, …) must escape
|
|
175
|
+
* so the runtime can act on them.
|
|
176
|
+
* - Deliberate aborts (run cancel, `cancelTool`, sibling cascade) are a
|
|
177
|
+
* timing-dependent, non-deterministic outcome — journaling one would bake it
|
|
178
|
+
* into replay. The `signal.aborted` check also catches transports (the MCP
|
|
179
|
+
* layer) that reject an aborted call with a plain `Error` carrying no
|
|
180
|
+
* `AbortError` name.
|
|
181
|
+
*/
|
|
182
|
+
function isRestateControlOrAbort(error, signal) {
|
|
183
|
+
if (shouldRethrowRestateControlError(error)) return true;
|
|
184
|
+
if (error instanceof Error && error.name === "AbortError") return true;
|
|
185
|
+
return signal?.aborted === true;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Outside-the-journal counterpart to {@link makeSoftToolError}: re-throw a
|
|
189
|
+
* recorded sentinel as a plain (non-control) `Error`, else pass the value
|
|
190
|
+
* through untouched.
|
|
191
|
+
*/
|
|
192
|
+
function rethrowSoftToolError(value) {
|
|
193
|
+
if (isSoftToolError(value)) throw new Error(value.message);
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
137
197
|
//#region src/restate/tool.ts
|
|
138
198
|
const SHELL_BACKGROUND_DISABLED_MESSAGE = "shell error: background mode is disabled for Restate-backed agents. Fall back to foreground (drop `run_in_background`).";
|
|
139
199
|
const HYDRATED_READ_STATE_SESSIONS = /* @__PURE__ */ new WeakSet();
|
|
@@ -164,15 +224,29 @@ function restateTool(inner, ctx, options = {}) {
|
|
|
164
224
|
});
|
|
165
225
|
}
|
|
166
226
|
};
|
|
167
|
-
const
|
|
227
|
+
const softErrors = options.softErrors ?? true;
|
|
228
|
+
const coerceBlocks = options.coerceResultBlocks ?? true;
|
|
229
|
+
const runJournaled = (input, toolCtx) => {
|
|
168
230
|
seq += 1;
|
|
169
231
|
return ctx.run(nameFor(seq, canonicalName, input, toolCtx.callId), async () => {
|
|
170
232
|
options.replayTracker?.markLive();
|
|
171
233
|
await hydrateOnce(toolCtx);
|
|
172
234
|
if (shellBackgroundDisabled && input.run_in_background === true) return SHELL_BACKGROUND_DISABLED_MESSAGE;
|
|
173
|
-
|
|
235
|
+
if (!softErrors) {
|
|
236
|
+
const out = await inner.execute(input, toolCtx);
|
|
237
|
+
return coerceBlocks ? coerceToolResultContent(out) : out;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const out = await inner.execute(input, toolCtx);
|
|
241
|
+
return coerceBlocks ? coerceToolResultContent(out) : out;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
if (isRestateControlOrAbort(err, toolCtx.signal)) throw err;
|
|
244
|
+
return makeSoftToolError(err);
|
|
245
|
+
}
|
|
174
246
|
}, runOpts);
|
|
175
247
|
};
|
|
248
|
+
const executeDurably = softErrors ? async (input, toolCtx) => rethrowSoftToolError(await runJournaled(input, toolCtx)) : runJournaled;
|
|
249
|
+
const durableExecuteImpl = softErrors ? (input, toolCtx) => unwrapDurableSoftError(runJournaled(input, toolCtx)) : runJournaled;
|
|
176
250
|
const executeTwoPhase = async (input, toolCtx) => {
|
|
177
251
|
const start = durableStart;
|
|
178
252
|
seq += 1;
|
|
@@ -267,10 +341,26 @@ function restateTool(inner, ctx, options = {}) {
|
|
|
267
341
|
...inner,
|
|
268
342
|
spec: shellBackgroundDisabled ? withoutShellBackgroundInput(inner.spec) : inner.spec,
|
|
269
343
|
execute: executeDurably,
|
|
270
|
-
durableExecute:
|
|
344
|
+
durableExecute: durableExecuteImpl
|
|
271
345
|
};
|
|
272
346
|
}
|
|
273
347
|
/**
|
|
348
|
+
* Compose the soft-error unwrap into the runtime's mappable durable promise.
|
|
349
|
+
* A journaled sentinel becomes a thrown (rejected) value so the Restate batch
|
|
350
|
+
* scheduler classifies it as a tool failure → `tool_result { isError }`; a real
|
|
351
|
+
* action failure (control error) is rethrown verbatim. Non-mappable promises
|
|
352
|
+
* (a ctx without the SDK's `RestatePromise`) pass through so the loop's durable
|
|
353
|
+
* guard surfaces the "not a mappable durable promise" error instead.
|
|
354
|
+
*/
|
|
355
|
+
function unwrapDurableSoftError(promise) {
|
|
356
|
+
const mappable = promise;
|
|
357
|
+
if (typeof mappable.map !== "function") return promise;
|
|
358
|
+
return mappable.map((value, failure) => {
|
|
359
|
+
if (failure !== void 0) throw failure instanceof Error ? failure : new Error(String(failure));
|
|
360
|
+
return rethrowSoftToolError(value);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
274
364
|
* Park on `promise`, but stop waiting when the per-call abort signal
|
|
275
365
|
* fires (run abort, `cancelTool`, sibling-cascade cancel) — a deliberate
|
|
276
366
|
* abort means nobody will consume the result, and like `onAbort` it
|
|
@@ -333,63 +423,21 @@ function wrapAgentTools(tools, ctx, opts = {}) {
|
|
|
333
423
|
}
|
|
334
424
|
//#endregion
|
|
335
425
|
//#region src/restate/mcp.ts
|
|
336
|
-
/** Journal value recorded when a wrapped MCP call fails — see module doc. */
|
|
337
|
-
const MCP_SOFT_ERROR_KEY = "__zidaneRestateMcpError";
|
|
338
|
-
function isMcpSoftError(value) {
|
|
339
|
-
return typeof value === "object" && value !== null && value[MCP_SOFT_ERROR_KEY] === true;
|
|
340
|
-
}
|
|
341
|
-
/** Real Restate control errors + deliberate aborts must escape, not soften. */
|
|
342
|
-
function isControlOrAbort(error) {
|
|
343
|
-
return shouldRethrowRestateControlError(error) || error instanceof Error && error.name === "AbortError";
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Inner layer (runs INSIDE the journaled closure): turn a recoverable MCP
|
|
347
|
-
* throw into a JSON-serializable sentinel so `ctx.run` records it as the entry
|
|
348
|
-
* value instead of failing the action. Control / abort errors propagate.
|
|
349
|
-
*/
|
|
350
|
-
function captureMcpErrors(tool) {
|
|
351
|
-
return {
|
|
352
|
-
...tool,
|
|
353
|
-
execute: async (input, toolCtx) => {
|
|
354
|
-
try {
|
|
355
|
-
return await tool.execute(input, toolCtx);
|
|
356
|
-
} catch (err) {
|
|
357
|
-
if (isControlOrAbort(err) || toolCtx.signal?.aborted) throw err;
|
|
358
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
359
|
-
return {
|
|
360
|
-
[MCP_SOFT_ERROR_KEY]: true,
|
|
361
|
-
message
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Outer layer (runs OUTSIDE `ctx.run`, on both live + replay): re-throw a
|
|
369
|
-
* recorded sentinel as a plain non-control error so the loop produces a
|
|
370
|
-
* `tool_result { isError }` and continues — identical to the non-Restate path.
|
|
371
|
-
*/
|
|
372
|
-
function rethrowMcpErrors(execute) {
|
|
373
|
-
return async (input, toolCtx) => {
|
|
374
|
-
const out = await execute(input, toolCtx);
|
|
375
|
-
if (isMcpSoftError(out)) throw new Error(out.message);
|
|
376
|
-
return out;
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
426
|
/**
|
|
380
|
-
* Journal one discovered MCP tool
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
427
|
+
* Journal one discovered MCP tool via `restateTool` with `softErrors` on (a
|
|
428
|
+
* recoverable MCP throw becomes a journaled `tool_result { isError }`, not a
|
|
429
|
+
* fatal run — see the module's "Error semantics"). `durableExecute` is dropped:
|
|
430
|
+
* MCP tools are concurrency barriers, so the batch scheduler runs them via the
|
|
431
|
+
* (still journaled) `execute`, and dropping the mappable promise keeps the
|
|
432
|
+
* scheduler from treating an MCP tool as a parallel-fleet member.
|
|
385
433
|
*/
|
|
386
434
|
function journalMcpTool(tool, ctx, toolOptions) {
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
435
|
+
const { durableExecute: _dropped, ...rest } = restateTool(tool, ctx, {
|
|
436
|
+
...toolOptions,
|
|
437
|
+
softErrors: true,
|
|
438
|
+
coerceResultBlocks: true
|
|
439
|
+
});
|
|
440
|
+
return rest;
|
|
393
441
|
}
|
|
394
442
|
function toToolOptions(options) {
|
|
395
443
|
return {
|
|
@@ -721,6 +769,84 @@ function maxDefined(values) {
|
|
|
721
769
|
return max;
|
|
722
770
|
}
|
|
723
771
|
//#endregion
|
|
724
|
-
|
|
772
|
+
//#region src/restate/wait-background.ts
|
|
773
|
+
function entryToExit(entry) {
|
|
774
|
+
return {
|
|
775
|
+
taskId: entry.taskId,
|
|
776
|
+
status: entry.status === "running" ? "exited" : entry.status,
|
|
777
|
+
exitCode: entry.exitCode ?? 0,
|
|
778
|
+
...entry.signal ? { signal: entry.signal } : {},
|
|
779
|
+
outputPath: entry.outputPath,
|
|
780
|
+
durationMs: Math.max(0, (entry.endedAt ?? entry.startedAt) - entry.startedAt),
|
|
781
|
+
command: entry.command
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Park on `promise` until it settles, the caller aborts, or (already handled
|
|
786
|
+
* by `awakeableWithTimeout`) the durable deadline elapses. Abort and rejection
|
|
787
|
+
* both resolve `null` — the `waitBackground` seam's contract is "exit info, or
|
|
788
|
+
* null when the task didn't cleanly exit before we stopped waiting".
|
|
789
|
+
*/
|
|
790
|
+
function parkUntilAbort(promise, signal) {
|
|
791
|
+
if (!signal) return promise.then((value) => value, () => null);
|
|
792
|
+
if (signal.aborted) return Promise.resolve(null);
|
|
793
|
+
return new Promise((resolve) => {
|
|
794
|
+
const onAbort = () => resolve(null);
|
|
795
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
796
|
+
promise.then((value) => {
|
|
797
|
+
signal.removeEventListener("abort", onAbort);
|
|
798
|
+
resolve(value);
|
|
799
|
+
}, () => {
|
|
800
|
+
signal.removeEventListener("abort", onAbort);
|
|
801
|
+
resolve(null);
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Build a durable `ExecutionContext.waitBackground` — see the module doc for
|
|
807
|
+
* the ordering contract. The returned function is the seam itself:
|
|
808
|
+
* `(handle, taskId, options?) => Promise<TaskExitInfo | null>`.
|
|
809
|
+
*/
|
|
810
|
+
function restateWaitBackground(options) {
|
|
811
|
+
const { ctx, probe, register, settle } = options;
|
|
812
|
+
const runOpts = options.runOptions ?? { maxRetryAttempts: 1 };
|
|
813
|
+
const prefix = options.entryPrefix ?? "wait-background";
|
|
814
|
+
let seq = 0;
|
|
815
|
+
return async (handle, taskId, callOpts) => {
|
|
816
|
+
seq += 1;
|
|
817
|
+
const entry = `${prefix}-${taskId}-${seq}`;
|
|
818
|
+
const timeoutMs = callOpts?.timeoutMs ?? options.timeoutMs;
|
|
819
|
+
const signal = callOpts?.signal;
|
|
820
|
+
const finalize = async (exit, awakeableId) => {
|
|
821
|
+
if (settle) await ctx.run(`${entry}-settle`, async () => {
|
|
822
|
+
options.replayTracker?.markLive();
|
|
823
|
+
await settle(awakeableId, taskId, exit, handle);
|
|
824
|
+
}, runOpts);
|
|
825
|
+
return exit;
|
|
826
|
+
};
|
|
827
|
+
const awakeable = awakeableWithTimeout(ctx, {
|
|
828
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
829
|
+
...options.promises ? { promises: options.promises } : {}
|
|
830
|
+
});
|
|
831
|
+
const marker = await ctx.run(`${entry}-register`, async () => {
|
|
832
|
+
options.replayTracker?.markLive();
|
|
833
|
+
await register(awakeable.id, taskId, handle);
|
|
834
|
+
return {
|
|
835
|
+
registered: true,
|
|
836
|
+
awakeableId: awakeable.id
|
|
837
|
+
};
|
|
838
|
+
}, runOpts);
|
|
839
|
+
if (marker.awakeableId !== awakeable.id) throw new Error(`restateWaitBackground(${taskId}): journal drift — registration marker carries awakeable "${marker.awakeableId}" but this replay minted "${awakeable.id}". Refusing to park on an unreachable awakeable.`);
|
|
840
|
+
const found = (await ctx.run(`${entry}-probe`, async () => {
|
|
841
|
+
options.replayTracker?.markLive();
|
|
842
|
+
return [...await probe(handle, taskId)];
|
|
843
|
+
}, runOpts)).find((e) => e.taskId === taskId);
|
|
844
|
+
if (!found) return finalize(null, awakeable.id);
|
|
845
|
+
if (found.status !== "running") return finalize(entryToExit(found), awakeable.id);
|
|
846
|
+
return finalize(await parkUntilAbort(awakeable.promise, signal), awakeable.id);
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
//#endregion
|
|
850
|
+
export { awakeableWithTimeout, createReplayTracker, replayAwareAbort, restateAwakeableTool, restateBehavior, restateClock, restateMcpToolWrap, restateProvider, restateSessionStore, restateTool, restateToolBatchExecutor, restateWaitBackground, shouldRethrowRestateControlError, wrapAgentTools };
|
|
725
851
|
|
|
726
852
|
//# sourceMappingURL=restate.js.map
|