zidane 6.2.36 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/dist/{acp-DZNCQaTQ.js → acp-BCg03Fqc.js} +6 -6
- package/dist/{acp-DZNCQaTQ.js.map → acp-BCg03Fqc.js.map} +1 -1
- package/dist/acp-cli.js +7 -7
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{agent-CrPwqpLI.js → agent-DO8s7bDf.js} +168 -23
- package/dist/agent-DO8s7bDf.js.map +1 -0
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +4 -4
- package/dist/{anthropic-asnZVFS_.js → anthropic-B3hj9OMm.js} +32 -13
- package/dist/anthropic-B3hj9OMm.js.map +1 -0
- package/dist/{auth-C5YTniaq.js → auth-kqdSYLMU.js} +4 -4
- package/dist/{auth-C5YTniaq.js.map → auth-kqdSYLMU.js.map} +1 -1
- package/dist/{cache-telemetry-BhTryxFT.js → cache-telemetry-DzyfqSds.js} +87 -5
- package/dist/cache-telemetry-DzyfqSds.js.map +1 -0
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +42 -4
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +31 -3
- package/dist/chat.js.map +1 -1
- package/dist/context-transport-CTLvmkj8.js +84 -0
- package/dist/context-transport-CTLvmkj8.js.map +1 -0
- package/dist/contexts/daytona.d.ts +33 -4
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +136 -0
- package/dist/contexts/daytona.js.map +1 -1
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +85 -2
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +34 -4
- package/dist/contexts/e2b.d.ts.map +1 -1
- package/dist/contexts/e2b.js +92 -0
- package/dist/contexts/e2b.js.map +1 -1
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/contexts/sandbox.js +4 -2
- package/dist/contexts/sandbox.js.map +1 -1
- package/dist/{contexts-o8y5B3Tm.js → contexts-DbMuUPjS.js} +92 -5
- package/dist/contexts-DbMuUPjS.js.map +1 -0
- package/dist/contexts.d.ts +4 -4
- package/dist/contexts.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +3 -3
- package/dist/{exec-file-size-PXkr-MGA.js → exec-file-size-BRpoB6Io.js} +3 -32
- package/dist/exec-file-size-BRpoB6Io.js.map +1 -0
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +4 -4
- package/dist/{index-DsPQbyUn.d.ts → index-B9jSo84a.d.ts} +2 -2
- package/dist/index-B9jSo84a.d.ts.map +1 -0
- package/dist/{index-CohO0oaX.d.ts → index-Bht3PKx6.d.ts} +5588 -5202
- package/dist/index-Bht3PKx6.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +12 -12
- package/dist/{login-CWwu9Sex.js → login-DRy_-rQh.js} +2 -2
- package/dist/{login-CWwu9Sex.js.map → login-DRy_-rQh.js.map} +1 -1
- package/dist/{mcp-Ct5tF4oj.js → mcp-DHKk_tLV.js} +77 -34
- package/dist/mcp-DHKk_tLV.js.map +1 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +2 -2
- package/dist/{messages-Cpb4g_KV.js → messages-DHzFOdx_.js} +2 -2
- package/dist/{messages-Cpb4g_KV.js.map → messages-DHzFOdx_.js.map} +1 -1
- package/dist/{openai-compat-C77gOaHh.js → openai-compat-OI68avOl.js} +59 -16
- package/dist/openai-compat-OI68avOl.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +14 -0
- package/dist/prompt.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +2 -2
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-DnHPW-dL.js → providers-y_l5VkAZ.js} +3 -3
- package/dist/{providers-DnHPW-dL.js.map → providers-y_l5VkAZ.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/{read-state-rDmfeLmz.js → read-state-CNyeMG7W.js} +7 -4
- package/dist/read-state-CNyeMG7W.js.map +1 -0
- package/dist/restate.d.ts +2 -2
- package/dist/restate.js +2 -2
- package/dist/restate.js.map +1 -1
- package/dist/{sandbox-CgjG2VvQ.d.ts → sandbox-DqwpKX18.d.ts} +14 -2
- package/dist/{sandbox-CgjG2VvQ.d.ts.map → sandbox-DqwpKX18.d.ts.map} +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-DpDBnktP.js → session-DdT2xT_d.js} +2 -2
- package/dist/{session-DpDBnktP.js.map → session-DdT2xT_d.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/shell-quote-BmnhZmdM.js +33 -0
- package/dist/shell-quote-BmnhZmdM.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/{stdio-loader-Ce68wUmM.js → stdio-DU3ljLqy.js} +8 -215
- package/dist/stdio-DU3ljLqy.js.map +1 -0
- package/dist/stdio-loader-9PnQWshg.js +217 -0
- package/dist/stdio-loader-9PnQWshg.js.map +1 -0
- package/dist/stream-queue-Cd9OJxl7.js +71 -0
- package/dist/stream-queue-Cd9OJxl7.js.map +1 -0
- package/dist/{system-prompt-BlsdjcUi.d.ts → system-prompt-MNVHatSD.d.ts} +15 -1
- package/dist/system-prompt-MNVHatSD.d.ts.map +1 -0
- package/dist/{tool-formatters-bZYz74qU.d.ts → tool-formatters-B0ZXWJTe.d.ts} +2 -2
- package/dist/{tool-formatters-bZYz74qU.d.ts.map → tool-formatters-B0ZXWJTe.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.d.ts +1 -1
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-ZZYwCTjo.js → transcript-anchors-ChQH1fMm.js} +18 -8
- package/dist/transcript-anchors-ChQH1fMm.js.map +1 -0
- package/dist/{transcript-anchors-EBchHUMo.d.ts → transcript-anchors-yt0sGL0w.d.ts} +2 -2
- package/dist/{transcript-anchors-EBchHUMo.d.ts.map → transcript-anchors-yt0sGL0w.d.ts.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +59 -7
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-DygcPreU.d.ts → turn-operations-XlnBT7p9.d.ts} +3 -3
- package/dist/{turn-operations-DygcPreU.d.ts.map → turn-operations-XlnBT7p9.d.ts.map} +1 -1
- package/dist/types-BIarq1qE.js.map +1 -1
- package/dist/{types-BDccavwj.d.ts → types-DdZMQxfT.d.ts} +117 -2
- package/dist/types-DdZMQxfT.d.ts.map +1 -0
- package/dist/types.d.ts +3 -3
- package/dist/{xai-B2hIs5We.js → xai-DxdI27ds.js} +2 -2
- package/dist/{xai-B2hIs5We.js.map → xai-DxdI27ds.js.map} +1 -1
- package/docs/ARCHITECTURE.md +62 -4
- package/docs/CHAT.md +11 -4
- package/docs/RESTATE.md +1 -1
- package/docs/SKILL.md +21 -1
- package/docs/TUI.md +2 -0
- package/package.json +1 -1
- package/dist/agent-CrPwqpLI.js.map +0 -1
- package/dist/anthropic-asnZVFS_.js.map +0 -1
- package/dist/cache-telemetry-BhTryxFT.js.map +0 -1
- package/dist/contexts-o8y5B3Tm.js.map +0 -1
- package/dist/exec-file-size-PXkr-MGA.js.map +0 -1
- package/dist/index-CohO0oaX.d.ts.map +0 -1
- package/dist/index-DsPQbyUn.d.ts.map +0 -1
- package/dist/mcp-Ct5tF4oj.js.map +0 -1
- package/dist/openai-compat-C77gOaHh.js.map +0 -1
- package/dist/read-state-rDmfeLmz.js.map +0 -1
- package/dist/stdio-loader-Ce68wUmM.js.map +0 -1
- package/dist/system-prompt-BlsdjcUi.d.ts.map +0 -1
- package/dist/transcript-anchors-ZZYwCTjo.js.map +0 -1
- package/dist/types-BDccavwj.d.ts.map +0 -1
package/dist/prompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.js","names":[],"sources":["../src/system-prompt.ts"],"sourcesContent":["/**\n * System-prompt boundary marker — splits a system prompt into a stable static\n * prefix (cached) and a per-turn dynamic suffix (NOT cached).\n *\n * Why this exists: providers attach `cache_control` markers on the last block\n * of the system prompt, so the cached prefix covers the entire system text.\n * Any byte change anywhere — including a per-turn `<env>` rewrite — busts the\n * cache for the doctrine that sits below. A literal marker in the system\n * string lets providers split it into:\n *\n * ┌──────────────┐ cache_control: ephemeral\n * │ STATIC half │ — doctrine, skills catalog, tool catalog,\n * │ │ user instructions\n * ├──────────────┤ ← SYSTEM_PROMPT_BOUNDARY\n * │ DYNAMIC half │ — env, cwd, mtimes, anything per-turn\n * └──────────────┘ (no cache_control)\n *\n * The static prefix rides the prompt cache across turns/sessions; the dynamic\n * suffix re-bills per turn. Net effect: a cwd change between turns no longer\n * invalidates 4 KB of doctrine.\n *\n * Wire contract:\n *\n * - `splitSystemPrompt(s)` is pure; missing marker ⇒ entire string is static\n * (current behavior — no caller is forced to opt in).\n * - `renderSystemForWire(s)` strips the marker so it never reaches the model;\n * used by every provider before the bytes hit the wire, including the\n * cache-disabled path on providers that DO support `cache_control`.\n * - The marker uses underscores rather than XML/punctuation so it's\n * unambiguous when scanning prompts manually and unlikely to collide with\n * model-written content.\n * - Providers handle the split internally (Anthropic emits a 2-block array;\n * OpenAI-compat splits the leading `system` message into multi-part text).\n * Callers always pass a single `string` — no API break.\n */\n\n/**\n * Literal marker inserted in a system prompt to separate cacheable doctrine\n * from per-turn dynamic content. Providers split on this token.\n *\n * Underscored on both sides for visual distinctiveness — a stray instance in\n * model-written prose is implausible. Don't change the value without shipping\n * a migration; existing sessions carry the old marker in their cached\n * prompts.\n */\nexport const SYSTEM_PROMPT_BOUNDARY = '__ZIDANE_SYSTEM_PROMPT_BOUNDARY__'\n\n/** Result of {@link splitSystemPrompt} — both halves stripped of the marker. */\nexport interface SystemPromptParts {\n /** Bytes BEFORE the marker (or the entire string when no marker present). Cacheable. */\n static: string\n /** Bytes AFTER the marker. Empty when no marker present. NOT cached. */\n dynamic: string\n}\n\n/**\n * Split a system prompt around the first {@link SYSTEM_PROMPT_BOUNDARY}.\n *\n * Splits on the FIRST occurrence — subsequent markers are folded into the\n * dynamic half. This way callers can append additional `<env>` style blocks\n * with extra markers without each one creating a new cache layer (Anthropic\n * caps breakpoints; we use the budget elsewhere). The marker itself is\n * stripped from both sides — providers attach `cache_control` directly to\n * the static half's text content.\n *\n * A single blank line (`\\n\\n`) immediately adjacent to the marker on each\n * side is trimmed — callers conventionally write\n * `<doctrine>\\n\\n<MARKER>\\n\\n<env>` and expect the rendered wire bytes to\n * read as one logical paragraph. Blank lines beyond that immediate pair\n * (e.g. `\\n\\n\\n<env>`) are preserved verbatim so callers composing their\n * own spacing don't lose intentional gaps.\n *\n * Pure / no I/O / no allocation when the marker is absent (returns the input\n * verbatim on the static side and the empty string on the dynamic side).\n */\nexport function splitSystemPrompt(system: string): SystemPromptParts {\n if (system.length === 0)\n return { static: '', dynamic: '' }\n const idx = system.indexOf(SYSTEM_PROMPT_BOUNDARY)\n if (idx < 0)\n return { static: system, dynamic: '' }\n const staticPart = system.slice(0, idx)\n const dynamicPart = system.slice(idx + SYSTEM_PROMPT_BOUNDARY.length)\n // Strip a single immediately-adjacent newline pair on each side. Callers\n // typically write `<doctrine>\\n\\n<MARKER>\\n\\n<env>` and expect the rendered\n // wire bytes to read as one logical paragraph. The trim is bounded so\n // intentional blank lines further out (e.g. `\\n\\n\\n<env>`) are preserved\n // for callers that compose their own spacing.\n return {\n static: trimTrailingBlankLine(staticPart),\n dynamic: trimLeadingBlankLine(dynamicPart),\n }\n}\n\n/**\n * Compose a system prompt from a static prefix and an optional dynamic\n * suffix. Inserts {@link SYSTEM_PROMPT_BOUNDARY} between them only when the\n * dynamic side is non-empty — single-block prompts stay marker-free, so\n * callers that never opt in pay zero overhead and providers fall back to the\n * existing whole-string caching path.\n *\n * Spacing is `\\n\\n` on both sides of the marker so doctrine fragments,\n * which conventionally separate sections with a blank line, read cleanly\n * around the boundary.\n */\nexport function joinSystemPrompt(staticPart: string, dynamicPart: string): string {\n if (dynamicPart.length === 0)\n return staticPart\n if (staticPart.length === 0)\n return `${SYSTEM_PROMPT_BOUNDARY}\\n\\n${dynamicPart}`\n return `${staticPart}\\n\\n${SYSTEM_PROMPT_BOUNDARY}\\n\\n${dynamicPart}`\n}\n\n/**\n * Append `extra` to the STATIC half of a system prompt, preserving any\n * existing dynamic suffix.\n *\n * Used by the agent to fold in run-stable content (skills catalog, lazy tool\n * catalog) without bumping it into the dynamic half — both catalogs are\n * built once per run and remain byte-stable for the duration, so they\n * belong in the cached prefix.\n *\n * Returns a new string; the input is not mutated. When `extra` is empty,\n * returns the input verbatim.\n */\nexport function appendStaticSection(system: string, extra: string): string {\n if (extra.length === 0)\n return system\n const { static: staticPart, dynamic } = splitSystemPrompt(system)\n const nextStatic = staticPart.length === 0 ? extra : `${staticPart}\\n\\n${extra}`\n return joinSystemPrompt(nextStatic, dynamic)\n}\n\n/**\n * Append `extra` to the DYNAMIC half of a system prompt. Inserts the boundary\n * marker if the input didn't already carry one.\n *\n * Used by hosts (typically the TUI) to inject per-turn state — current cwd,\n * IDE selection, project root — without forcing every caller to know the\n * marker format. The host's `system:transform` hook rewrites the dynamic\n * half each turn; the static doctrine above stays byte-stable and rides the\n * cache.\n *\n * Returns a new string; the input is not mutated. When `extra` is empty,\n * returns the input verbatim.\n */\nexport function appendDynamicSection(system: string, extra: string): string {\n if (extra.length === 0)\n return system\n const { static: staticPart, dynamic } = splitSystemPrompt(system)\n const nextDynamic = dynamic.length === 0 ? extra : `${dynamic}\\n\\n${extra}`\n return joinSystemPrompt(staticPart, nextDynamic)\n}\n\n/**\n * Replace the entire dynamic half of a system prompt with `next`. Used by\n * the TUI's `<env>` rewriter where the entire dynamic section is regenerated\n * each turn rather than appended to.\n *\n * When `next` is empty, drops the dynamic half (and the marker) entirely.\n */\nexport function replaceDynamicSection(system: string, next: string): string {\n const { static: staticPart } = splitSystemPrompt(system)\n return joinSystemPrompt(staticPart, next)\n}\n\n/**\n * Strip the boundary marker so it never reaches the wire — collapses\n * `<static>${BOUNDARY}<dynamic>` into `<static>\\n\\n<dynamic>` for providers\n * that can't honor `cache_control` (vanilla OpenAI Chat Completions, Codex,\n * Cerebras, ...) or for the cache-disabled path on any provider.\n *\n * Cache-aware providers re-derive the split from the original (un-rendered)\n * system string via `splitSystemPrompt` — `renderSystemForWire` is the\n * marker-free counterpart used to build the actual wire bytes.\n *\n * No-op when the input has no marker. Pure / no I/O.\n */\nexport function renderSystemForWire(system: string): string {\n if (system.length === 0)\n return system\n const parts = splitSystemPrompt(system)\n if (parts.dynamic.length === 0)\n return parts.static\n if (parts.static.length === 0)\n return parts.dynamic\n return `${parts.static}\\n\\n${parts.dynamic}`\n}\n\n/** True when `system` contains the boundary marker. */\nexport function hasSystemPromptBoundary(system: string): boolean {\n return system.includes(SYSTEM_PROMPT_BOUNDARY)\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/** Strip a single trailing `\\n\\n` (and any leftover trailing whitespace within that pair). */\nfunction trimTrailingBlankLine(s: string): string {\n // Walk back across at most two `\\n` separated only by spaces/tabs.\n let end = s.length\n let newlines = 0\n while (end > 0 && newlines < 2) {\n const ch = s.charCodeAt(end - 1)\n if (ch === 0x0A) {\n newlines += 1\n end -= 1\n continue\n }\n if (ch === 0x20 || ch === 0x09) {\n end -= 1\n continue\n }\n break\n }\n return newlines > 0 ? s.slice(0, end) : s\n}\n\n/** Strip a single leading `\\n\\n` (and any leftover leading whitespace within that pair). */\nfunction trimLeadingBlankLine(s: string): string {\n let start = 0\n let newlines = 0\n while (start < s.length && newlines < 2) {\n const ch = s.charCodeAt(start)\n if (ch === 0x0A) {\n newlines += 1\n start += 1\n continue\n }\n if (ch === 0x20 || ch === 0x09) {\n start += 1\n continue\n }\n break\n }\n return newlines > 0 ? s.slice(start) : s\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;AA8BtC,SAAgB,kBAAkB,QAAmC;CACnE,IAAI,OAAO,WAAW,GACpB,OAAO;EAAE,QAAQ;EAAI,SAAS;CAAG;CACnC,MAAM,MAAM,OAAO,QAAQ,sBAAsB;CACjD,IAAI,MAAM,GACR,OAAO;EAAE,QAAQ;EAAQ,SAAS;CAAG;CACvC,MAAM,aAAa,OAAO,MAAM,GAAG,GAAG;CACtC,MAAM,cAAc,OAAO,MAAM,MAAM,EAA6B;CAMpE,OAAO;EACL,QAAQ,sBAAsB,UAAU;EACxC,SAAS,qBAAqB,WAAW;CAC3C;AACF;;;;;;;;;;;;AAaA,SAAgB,iBAAiB,YAAoB,aAA6B;CAChF,IAAI,YAAY,WAAW,GACzB,OAAO;CACT,IAAI,WAAW,WAAW,GACxB,OAAO,GAAG,uBAAuB,MAAM;CACzC,OAAO,GAAG,WAAW,MAAM,uBAAuB,MAAM;AAC1D;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,QAAgB,OAAuB;CACzE,IAAI,MAAM,WAAW,GACnB,OAAO;CACT,MAAM,EAAE,QAAQ,YAAY,YAAY,kBAAkB,MAAM;CAEhE,OAAO,iBADY,WAAW,WAAW,IAAI,QAAQ,GAAG,WAAW,MAAM,SACrC,OAAO;AAC7C;;;;;;;;;;;;;;AAeA,SAAgB,qBAAqB,QAAgB,OAAuB;CAC1E,IAAI,MAAM,WAAW,GACnB,OAAO;CACT,MAAM,EAAE,QAAQ,YAAY,YAAY,kBAAkB,MAAM;CAEhE,OAAO,iBAAiB,YADJ,QAAQ,WAAW,IAAI,QAAQ,GAAG,QAAQ,MAAM,OACrB;AACjD;;;;;;;;AASA,SAAgB,sBAAsB,QAAgB,MAAsB;CAC1E,MAAM,EAAE,QAAQ,eAAe,kBAAkB,MAAM;CACvD,OAAO,iBAAiB,YAAY,IAAI;AAC1C;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,QAAwB;CAC1D,IAAI,OAAO,WAAW,GACpB,OAAO;CACT,MAAM,QAAQ,kBAAkB,MAAM;CACtC,IAAI,MAAM,QAAQ,WAAW,GAC3B,OAAO,MAAM;CACf,IAAI,MAAM,OAAO,WAAW,GAC1B,OAAO,MAAM;CACf,OAAO,GAAG,MAAM,OAAO,MAAM,MAAM;AACrC;;AAGA,SAAgB,wBAAwB,QAAyB;CAC/D,OAAO,OAAO,SAAS,sBAAsB;AAC/C;;AAOA,SAAS,sBAAsB,GAAmB;CAEhD,IAAI,MAAM,EAAE;CACZ,IAAI,WAAW;CACf,OAAO,MAAM,KAAK,WAAW,GAAG;EAC9B,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;EAC/B,IAAI,OAAO,IAAM;GACf,YAAY;GACZ,OAAO;GACP;EACF;EACA,IAAI,OAAO,MAAQ,OAAO,GAAM;GAC9B,OAAO;GACP;EACF;EACA;CACF;CACA,OAAO,WAAW,IAAI,EAAE,MAAM,GAAG,GAAG,IAAI;AAC1C;;AAGA,SAAS,qBAAqB,GAAmB;CAC/C,IAAI,QAAQ;CACZ,IAAI,WAAW;CACf,OAAO,QAAQ,EAAE,UAAU,WAAW,GAAG;EACvC,MAAM,KAAK,EAAE,WAAW,KAAK;EAC7B,IAAI,OAAO,IAAM;GACf,YAAY;GACZ,SAAS;GACT;EACF;EACA,IAAI,OAAO,MAAQ,OAAO,GAAM;GAC9B,SAAS;GACT;EACF;EACA;CACF;CACA,OAAO,WAAW,IAAI,EAAE,MAAM,KAAK,IAAI;AACzC"}
|
|
1
|
+
{"version":3,"file":"prompt.js","names":[],"sources":["../src/system-prompt.ts"],"sourcesContent":["/**\n * System-prompt boundary marker — splits a system prompt into a stable static\n * prefix (cached) and a per-turn dynamic suffix (NOT cached).\n *\n * Why this exists: providers attach `cache_control` markers on the last block\n * of the system prompt, so the cached prefix covers the entire system text.\n * Any byte change anywhere — including a per-turn `<env>` rewrite — busts the\n * cache for the doctrine that sits below. A literal marker in the system\n * string lets providers split it into:\n *\n * ┌──────────────┐ cache_control: ephemeral\n * │ STATIC half │ — doctrine, skills catalog, tool catalog,\n * │ │ user instructions\n * ├──────────────┤ ← SYSTEM_PROMPT_BOUNDARY\n * │ DYNAMIC half │ — env, cwd, mtimes, anything per-turn\n * └──────────────┘ (no cache_control)\n *\n * The static prefix rides the prompt cache across turns/sessions; the dynamic\n * suffix re-bills per turn. Net effect: a cwd change between turns no longer\n * invalidates 4 KB of doctrine.\n *\n * IMPORTANT — \"not cached\" does NOT mean \"free to churn\". The dynamic half\n * carries no `cache_control` marker of its own, but it still sits inside the\n * prefix that the MESSAGE-level breakpoints hash (provider order is\n * tools → system → messages, cumulative). Rewriting the dynamic half between\n * turns therefore invalidates the whole conversation and leaves only `tools` +\n * the static half readable — a partial break that shows up as a suspiciously\n * CONSTANT `cacheRead`, not a zero one. A live counter appended below the marker\n * is enough to lose the transcript on most turns of a run.\n *\n * So the split buys exactly one thing: content that varies ACROSS runs (cwd,\n * env, project root) stops invalidating doctrine shared BETWEEN runs. It buys\n * nothing for content that varies between turns OF a run — that has to be\n * byte-stable wherever it lives, or be persisted into the transcript.\n *\n * Wire contract:\n *\n * - `splitSystemPrompt(s)` is pure; missing marker ⇒ entire string is static\n * (current behavior — no caller is forced to opt in).\n * - `renderSystemForWire(s)` strips the marker so it never reaches the model;\n * used by every provider before the bytes hit the wire, including the\n * cache-disabled path on providers that DO support `cache_control`.\n * - The marker uses underscores rather than XML/punctuation so it's\n * unambiguous when scanning prompts manually and unlikely to collide with\n * model-written content.\n * - Providers handle the split internally (Anthropic emits a 2-block array;\n * OpenAI-compat splits the leading `system` message into multi-part text).\n * Callers always pass a single `string` — no API break.\n */\n\n/**\n * Literal marker inserted in a system prompt to separate cacheable doctrine\n * from per-turn dynamic content. Providers split on this token.\n *\n * Underscored on both sides for visual distinctiveness — a stray instance in\n * model-written prose is implausible. Don't change the value without shipping\n * a migration; existing sessions carry the old marker in their cached\n * prompts.\n */\nexport const SYSTEM_PROMPT_BOUNDARY = '__ZIDANE_SYSTEM_PROMPT_BOUNDARY__'\n\n/** Result of {@link splitSystemPrompt} — both halves stripped of the marker. */\nexport interface SystemPromptParts {\n /** Bytes BEFORE the marker (or the entire string when no marker present). Cacheable. */\n static: string\n /** Bytes AFTER the marker. Empty when no marker present. NOT cached. */\n dynamic: string\n}\n\n/**\n * Split a system prompt around the first {@link SYSTEM_PROMPT_BOUNDARY}.\n *\n * Splits on the FIRST occurrence — subsequent markers are folded into the\n * dynamic half. This way callers can append additional `<env>` style blocks\n * with extra markers without each one creating a new cache layer (Anthropic\n * caps breakpoints; we use the budget elsewhere). The marker itself is\n * stripped from both sides — providers attach `cache_control` directly to\n * the static half's text content.\n *\n * A single blank line (`\\n\\n`) immediately adjacent to the marker on each\n * side is trimmed — callers conventionally write\n * `<doctrine>\\n\\n<MARKER>\\n\\n<env>` and expect the rendered wire bytes to\n * read as one logical paragraph. Blank lines beyond that immediate pair\n * (e.g. `\\n\\n\\n<env>`) are preserved verbatim so callers composing their\n * own spacing don't lose intentional gaps.\n *\n * Pure / no I/O / no allocation when the marker is absent (returns the input\n * verbatim on the static side and the empty string on the dynamic side).\n */\nexport function splitSystemPrompt(system: string): SystemPromptParts {\n if (system.length === 0)\n return { static: '', dynamic: '' }\n const idx = system.indexOf(SYSTEM_PROMPT_BOUNDARY)\n if (idx < 0)\n return { static: system, dynamic: '' }\n const staticPart = system.slice(0, idx)\n const dynamicPart = system.slice(idx + SYSTEM_PROMPT_BOUNDARY.length)\n // Strip a single immediately-adjacent newline pair on each side. Callers\n // typically write `<doctrine>\\n\\n<MARKER>\\n\\n<env>` and expect the rendered\n // wire bytes to read as one logical paragraph. The trim is bounded so\n // intentional blank lines further out (e.g. `\\n\\n\\n<env>`) are preserved\n // for callers that compose their own spacing.\n return {\n static: trimTrailingBlankLine(staticPart),\n dynamic: trimLeadingBlankLine(dynamicPart),\n }\n}\n\n/**\n * Compose a system prompt from a static prefix and an optional dynamic\n * suffix. Inserts {@link SYSTEM_PROMPT_BOUNDARY} between them only when the\n * dynamic side is non-empty — single-block prompts stay marker-free, so\n * callers that never opt in pay zero overhead and providers fall back to the\n * existing whole-string caching path.\n *\n * Spacing is `\\n\\n` on both sides of the marker so doctrine fragments,\n * which conventionally separate sections with a blank line, read cleanly\n * around the boundary.\n */\nexport function joinSystemPrompt(staticPart: string, dynamicPart: string): string {\n if (dynamicPart.length === 0)\n return staticPart\n if (staticPart.length === 0)\n return `${SYSTEM_PROMPT_BOUNDARY}\\n\\n${dynamicPart}`\n return `${staticPart}\\n\\n${SYSTEM_PROMPT_BOUNDARY}\\n\\n${dynamicPart}`\n}\n\n/**\n * Append `extra` to the STATIC half of a system prompt, preserving any\n * existing dynamic suffix.\n *\n * Used by the agent to fold in run-stable content (skills catalog, lazy tool\n * catalog) without bumping it into the dynamic half — both catalogs are\n * built once per run and remain byte-stable for the duration, so they\n * belong in the cached prefix.\n *\n * Returns a new string; the input is not mutated. When `extra` is empty,\n * returns the input verbatim.\n */\nexport function appendStaticSection(system: string, extra: string): string {\n if (extra.length === 0)\n return system\n const { static: staticPart, dynamic } = splitSystemPrompt(system)\n const nextStatic = staticPart.length === 0 ? extra : `${staticPart}\\n\\n${extra}`\n return joinSystemPrompt(nextStatic, dynamic)\n}\n\n/**\n * Append `extra` to the DYNAMIC half of a system prompt. Inserts the boundary\n * marker if the input didn't already carry one.\n *\n * Used by hosts (typically the TUI) to inject per-turn state — current cwd,\n * IDE selection, project root — without forcing every caller to know the\n * marker format. The host's `system:transform` hook rewrites the dynamic\n * half each turn; the static doctrine above stays byte-stable and rides the\n * cache.\n *\n * Returns a new string; the input is not mutated. When `extra` is empty,\n * returns the input verbatim.\n */\nexport function appendDynamicSection(system: string, extra: string): string {\n if (extra.length === 0)\n return system\n const { static: staticPart, dynamic } = splitSystemPrompt(system)\n const nextDynamic = dynamic.length === 0 ? extra : `${dynamic}\\n\\n${extra}`\n return joinSystemPrompt(staticPart, nextDynamic)\n}\n\n/**\n * Replace the entire dynamic half of a system prompt with `next`. Used by\n * the TUI's `<env>` rewriter where the entire dynamic section is regenerated\n * each turn rather than appended to.\n *\n * When `next` is empty, drops the dynamic half (and the marker) entirely.\n */\nexport function replaceDynamicSection(system: string, next: string): string {\n const { static: staticPart } = splitSystemPrompt(system)\n return joinSystemPrompt(staticPart, next)\n}\n\n/**\n * Strip the boundary marker so it never reaches the wire — collapses\n * `<static>${BOUNDARY}<dynamic>` into `<static>\\n\\n<dynamic>` for providers\n * that can't honor `cache_control` (vanilla OpenAI Chat Completions, Codex,\n * Cerebras, ...) or for the cache-disabled path on any provider.\n *\n * Cache-aware providers re-derive the split from the original (un-rendered)\n * system string via `splitSystemPrompt` — `renderSystemForWire` is the\n * marker-free counterpart used to build the actual wire bytes.\n *\n * No-op when the input has no marker. Pure / no I/O.\n */\nexport function renderSystemForWire(system: string): string {\n if (system.length === 0)\n return system\n const parts = splitSystemPrompt(system)\n if (parts.dynamic.length === 0)\n return parts.static\n if (parts.static.length === 0)\n return parts.dynamic\n return `${parts.static}\\n\\n${parts.dynamic}`\n}\n\n/** True when `system` contains the boundary marker. */\nexport function hasSystemPromptBoundary(system: string): boolean {\n return system.includes(SYSTEM_PROMPT_BOUNDARY)\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/** Strip a single trailing `\\n\\n` (and any leftover trailing whitespace within that pair). */\nfunction trimTrailingBlankLine(s: string): string {\n // Walk back across at most two `\\n` separated only by spaces/tabs.\n let end = s.length\n let newlines = 0\n while (end > 0 && newlines < 2) {\n const ch = s.charCodeAt(end - 1)\n if (ch === 0x0A) {\n newlines += 1\n end -= 1\n continue\n }\n if (ch === 0x20 || ch === 0x09) {\n end -= 1\n continue\n }\n break\n }\n return newlines > 0 ? s.slice(0, end) : s\n}\n\n/** Strip a single leading `\\n\\n` (and any leftover leading whitespace within that pair). */\nfunction trimLeadingBlankLine(s: string): string {\n let start = 0\n let newlines = 0\n while (start < s.length && newlines < 2) {\n const ch = s.charCodeAt(start)\n if (ch === 0x0A) {\n newlines += 1\n start += 1\n continue\n }\n if (ch === 0x20 || ch === 0x09) {\n start += 1\n continue\n }\n break\n }\n return newlines > 0 ? s.slice(start) : s\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;AA8BtC,SAAgB,kBAAkB,QAAmC;CACnE,IAAI,OAAO,WAAW,GACpB,OAAO;EAAE,QAAQ;EAAI,SAAS;CAAG;CACnC,MAAM,MAAM,OAAO,QAAQ,sBAAsB;CACjD,IAAI,MAAM,GACR,OAAO;EAAE,QAAQ;EAAQ,SAAS;CAAG;CACvC,MAAM,aAAa,OAAO,MAAM,GAAG,GAAG;CACtC,MAAM,cAAc,OAAO,MAAM,MAAM,EAA6B;CAMpE,OAAO;EACL,QAAQ,sBAAsB,UAAU;EACxC,SAAS,qBAAqB,WAAW;CAC3C;AACF;;;;;;;;;;;;AAaA,SAAgB,iBAAiB,YAAoB,aAA6B;CAChF,IAAI,YAAY,WAAW,GACzB,OAAO;CACT,IAAI,WAAW,WAAW,GACxB,OAAO,GAAG,uBAAuB,MAAM;CACzC,OAAO,GAAG,WAAW,MAAM,uBAAuB,MAAM;AAC1D;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,QAAgB,OAAuB;CACzE,IAAI,MAAM,WAAW,GACnB,OAAO;CACT,MAAM,EAAE,QAAQ,YAAY,YAAY,kBAAkB,MAAM;CAEhE,OAAO,iBADY,WAAW,WAAW,IAAI,QAAQ,GAAG,WAAW,MAAM,SACrC,OAAO;AAC7C;;;;;;;;;;;;;;AAeA,SAAgB,qBAAqB,QAAgB,OAAuB;CAC1E,IAAI,MAAM,WAAW,GACnB,OAAO;CACT,MAAM,EAAE,QAAQ,YAAY,YAAY,kBAAkB,MAAM;CAEhE,OAAO,iBAAiB,YADJ,QAAQ,WAAW,IAAI,QAAQ,GAAG,QAAQ,MAAM,OACrB;AACjD;;;;;;;;AASA,SAAgB,sBAAsB,QAAgB,MAAsB;CAC1E,MAAM,EAAE,QAAQ,eAAe,kBAAkB,MAAM;CACvD,OAAO,iBAAiB,YAAY,IAAI;AAC1C;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,QAAwB;CAC1D,IAAI,OAAO,WAAW,GACpB,OAAO;CACT,MAAM,QAAQ,kBAAkB,MAAM;CACtC,IAAI,MAAM,QAAQ,WAAW,GAC3B,OAAO,MAAM;CACf,IAAI,MAAM,OAAO,WAAW,GAC1B,OAAO,MAAM;CACf,OAAO,GAAG,MAAM,OAAO,MAAM,MAAM;AACrC;;AAGA,SAAgB,wBAAwB,QAAyB;CAC/D,OAAO,OAAO,SAAS,sBAAsB;AAC/C;;AAOA,SAAS,sBAAsB,GAAmB;CAEhD,IAAI,MAAM,EAAE;CACZ,IAAI,WAAW;CACf,OAAO,MAAM,KAAK,WAAW,GAAG;EAC9B,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;EAC/B,IAAI,OAAO,IAAM;GACf,YAAY;GACZ,OAAO;GACP;EACF;EACA,IAAI,OAAO,MAAQ,OAAO,GAAM;GAC9B,OAAO;GACP;EACF;EACA;CACF;CACA,OAAO,WAAW,IAAI,EAAE,MAAM,GAAG,GAAG,IAAI;AAC1C;;AAGA,SAAS,qBAAqB,GAAmB;CAC/C,IAAI,QAAQ;CACZ,IAAI,WAAW;CACf,OAAO,QAAQ,EAAE,UAAU,WAAW,GAAG;EACvC,MAAM,KAAK,EAAE,WAAW,KAAK;EAC7B,IAAI,OAAO,IAAM;GACf,YAAY;GACZ,SAAS;GACT;EACF;EACA,IAAI,OAAO,MAAQ,OAAO,GAAM;GAC9B,SAAS;GACT;EACF;EACA;CACF;CACA,OAAO,WAAW,IAAI,EAAE,MAAM,KAAK,IAAI;AACzC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { AnthropicContextManagement, AnthropicParams, AnthropicThinkingPlan, anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput };
|
|
1
|
+
import { $f as AnthropicParams, Qf as AnthropicContextManagement, ap as classifyAnthropicError, cp as planAnthropicThinking, dp as resolveAnthropicMaxOutput, ep as AnthropicThinkingPlan, ip as buildAnthropicClientOptions, lp as resolveAnthropicAuthShape, np as anthropic, op as credentialIdentity, rp as applyAnthropicCacheBreakpoints, sp as defaultAnthropicMaxTokens, tp as EXTENDED_CACHE_TTL_BETA, up as resolveAnthropicBetas } from "../index-Bht3PKx6.js";
|
|
2
|
+
export { AnthropicContextManagement, AnthropicParams, AnthropicThinkingPlan, EXTENDED_CACHE_TTL_BETA, anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput };
|
|
1
|
+
import { a as classifyAnthropicError, c as planAnthropicThinking, d as resolveAnthropicMaxOutput, i as buildAnthropicClientOptions, l as resolveAnthropicAuthShape, n as anthropic, o as credentialIdentity, r as applyAnthropicCacheBreakpoints, s as defaultAnthropicMaxTokens, t as EXTENDED_CACHE_TTL_BETA, u as resolveAnthropicBetas } from "../anthropic-B3hj9OMm.js";
|
|
2
|
+
export { EXTENDED_CACHE_TTL_BETA, anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Xf as ArceeParams, Zf as arcee } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { ArceeParams, arcee };
|
package/dist/providers/arcee.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Jf as baseten, Yf as planBasetenReasoning, qf as BasetenParams } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { BasetenParams, baseten, planBasetenReasoning };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Gf as CerebrasParams, Kf as cerebras } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { CerebrasParams, cerebras };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Hf as local, Vf as LocalParams } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { LocalParams, local };
|
package/dist/providers/local.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as openaiCompat } from "../openai-compat-
|
|
1
|
+
import { m as openaiCompat } from "../openai-compat-OI68avOl.js";
|
|
2
2
|
//#region src/providers/local.ts
|
|
3
3
|
function getBaseURL(params) {
|
|
4
4
|
if (typeof params?.baseURL === "string" && params.baseURL.length > 0) return params.baseURL;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Af as mapOAIFinishReason, Cf as applyOAICacheBreakpoints, Df as classifyOpenAICompatError, Ef as buildAssistantContent, Ff as userMessage, Mf as openaiCompat, Nf as toOAIMessages, Of as consumeSSE, Pf as toolResultsMessage, Sf as TOOL_RESULTS_TAG, Tf as assistantMessage, _f as OAITool, bf as OpenAICompatParams, gf as OAIMessage, hf as ASSISTANT_TOOL_CALLS_TAG, jf as openAICompatPromptMessage, kf as formatTools, vf as OpenAICompatAuthHeader, wf as applyOAIToolCacheBreakpoint, xf as OpenAICompatStreamError, yf as OpenAICompatHttpError } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { ASSISTANT_TOOL_CALLS_TAG, OAIMessage, OAITool, OpenAICompatAuthHeader, OpenAICompatHttpError, OpenAICompatParams, OpenAICompatStreamError, TOOL_RESULTS_TAG, applyOAICacheBreakpoints, applyOAIToolCacheBreakpoint, assistantMessage, buildAssistantContent, classifyOpenAICompatError, consumeSSE, formatTools, mapOAIFinishReason, openAICompatPromptMessage, openaiCompat, toOAIMessages, toolResultsMessage, userMessage };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as userMessage, a as applyOAICacheBreakpoints, c as buildAssistantContent, d as formatTools, f as mapOAIFinishReason, g as toolResultsMessage, h as toOAIMessages, i as TOOL_RESULTS_TAG, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, o as applyOAIToolCacheBreakpoint, p as openAICompatPromptMessage, r as OpenAICompatStreamError, s as assistantMessage, t as ASSISTANT_TOOL_CALLS_TAG, u as consumeSSE } from "../openai-compat-
|
|
1
|
+
import { _ as userMessage, a as applyOAICacheBreakpoints, c as buildAssistantContent, d as formatTools, f as mapOAIFinishReason, g as toolResultsMessage, h as toOAIMessages, i as TOOL_RESULTS_TAG, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, o as applyOAIToolCacheBreakpoint, p as openAICompatPromptMessage, r as OpenAICompatStreamError, s as assistantMessage, t as ASSISTANT_TOOL_CALLS_TAG, u as consumeSSE } from "../openai-compat-OI68avOl.js";
|
|
2
2
|
export { ASSISTANT_TOOL_CALLS_TAG, OpenAICompatHttpError, OpenAICompatStreamError, TOOL_RESULTS_TAG, applyOAICacheBreakpoints, applyOAIToolCacheBreakpoint, assistantMessage, buildAssistantContent, classifyOpenAICompatError, consumeSSE, formatTools, mapOAIFinishReason, openAICompatPromptMessage, openaiCompat, toOAIMessages, toolResultsMessage, userMessage };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Bf as toPiMessages, If as OpenAIParams, Lf as classifyOpenAIError, Rf as mapCodexStopReason, zf as openai } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { OpenAIParams, classifyOpenAIError, mapCodexStopReason, openai, toPiMessages };
|
package/dist/providers/openai.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { i as documentBlockMarker, n as assertResolvedMediaBlock } from "../types-BIarq1qE.js";
|
|
2
2
|
import { c as classifyErrorPrelude, d as isRetryableHttpStatus, f as matchesContextExceeded, p as matchesToolPairingError } from "../errors-DJUxZg9b.js";
|
|
3
|
-
import {
|
|
3
|
+
import { E as unsupportedMediaError, S as fillEstimatedCost, _ as userMessage, b as createOpenAIToolNameBoundary, g as toolResultsMessage, s as assistantMessage, y as sanitizeToolSpecs } from "../openai-compat-OI68avOl.js";
|
|
4
4
|
import { renderSystemForWire } from "../prompt.js";
|
|
5
|
-
import { f as reasoningWireAction, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER } from "../messages-
|
|
5
|
+
import { f as reasoningWireAction, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER } from "../messages-DHzFOdx_.js";
|
|
6
6
|
import { r as extractRuntimeCredentials, s as resolveOAuthApiKey } from "../oauth-CBi9eymX.js";
|
|
7
7
|
import { stream } from "@earendil-works/pi-ai/api/openai-codex-responses";
|
|
8
8
|
import { stream as stream$1 } from "@earendil-works/pi-ai/api/openai-responses";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ff as OpenRouterParams, mf as openrouter, pf as normalizeOpenRouterModelId } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { OpenRouterParams, normalizeOpenRouterModelId, openrouter };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as openaiCompat } from "../openai-compat-
|
|
1
|
+
import { m as openaiCompat } from "../openai-compat-OI68avOl.js";
|
|
2
2
|
//#region src/providers/openrouter.ts
|
|
3
3
|
const BASE_URL = "https://openrouter.ai/api/v1";
|
|
4
4
|
const DEFAULT_MODEL = "anthropic/claude-sonnet-4.6";
|
package/dist/providers/xai.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { af as XaiParams, of as xai } from "../index-Bht3PKx6.js";
|
|
2
2
|
export { XaiParams, xai };
|
package/dist/providers/xai.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as xai } from "../xai-
|
|
1
|
+
import { t as xai } from "../xai-DxdI27ds.js";
|
|
2
2
|
export { xai };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { m as openaiCompat } from "./openai-compat-
|
|
1
|
+
import { m as openaiCompat } from "./openai-compat-OI68avOl.js";
|
|
2
2
|
import { s as resolveOAuthApiKey } from "./oauth-CBi9eymX.js";
|
|
3
|
-
import { r as generatePkce } from "./xai-
|
|
3
|
+
import { r as generatePkce } from "./xai-DxdI27ds.js";
|
|
4
4
|
//#region src/chat/oauth-page/cursor.ts
|
|
5
5
|
/** pi-ai / zidane OAuth provider id. Also the credentials-file key. */
|
|
6
6
|
const CURSOR_OAUTH_PROVIDER_ID = "cursor";
|
|
@@ -165,4 +165,4 @@ function cursor(params) {
|
|
|
165
165
|
//#endregion
|
|
166
166
|
export { createCursorOAuthProvider as n, cursor as t };
|
|
167
167
|
|
|
168
|
-
//# sourceMappingURL=providers-
|
|
168
|
+
//# sourceMappingURL=providers-y_l5VkAZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers-DnHPW-dL.js","names":[],"sources":["../src/chat/oauth-page/cursor.ts","../src/providers/cursor.ts"],"sourcesContent":["/**\n * Cursor OAuth flow.\n *\n * Unlike Anthropic / OpenAI Codex (loopback-redirect PKCE), Cursor uses a\n * **poll-based** PKCE flow with no local callback server:\n *\n * 1. Generate a PKCE verifier + challenge and a random UUID.\n * 2. Open the browser to `cursor.com/loginDeepControl?challenge&uuid&mode&redirectTarget`.\n * 3. Poll `api2.cursor.sh/auth/poll?uuid&verifier` until tokens come back\n * (HTTP 404 = \"not ready yet\", keep polling with backoff).\n * 4. Refresh via `POST api2.cursor.sh/auth/exchange_user_api_key` with\n * `Authorization: Bearer <refresh>`.\n *\n * Token expiry is derived from the access token's JWT `exp` claim (minus a\n * 5-minute safety margin), falling back to +1h when the claim is missing.\n *\n * Flow adapted from https://github.com/ndraiman/pi-cursor-provider, itself\n * derived from https://github.com/ephraimduncan/opencode-cursor.\n *\n * Cursor's auth + inference protocol is unofficial and undocumented — re-verify\n * the endpoints/params below if login starts failing.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n OAuthProviderInterface,\n} from '@earendil-works/pi-ai/oauth'\nimport { generatePkce } from './pkce'\n\n/** pi-ai / zidane OAuth provider id. Also the credentials-file key. */\nexport const CURSOR_OAUTH_PROVIDER_ID = 'cursor'\n\nconst CURSOR_LOGIN_URL = 'https://cursor.com/loginDeepControl'\nconst CURSOR_POLL_URL = 'https://api2.cursor.sh/auth/poll'\nconst CURSOR_REFRESH_URL = 'https://api2.cursor.sh/auth/exchange_user_api_key'\n\nconst POLL_MAX_ATTEMPTS = 150\nconst POLL_BASE_DELAY_MS = 1000\nconst POLL_MAX_DELAY_MS = 10_000\nconst POLL_BACKOFF_MULTIPLIER = 1.2\nconst POLL_MAX_CONSECUTIVE_ERRORS = 3\n\n/** Fallback lifetime when the JWT carries no `exp`. */\nconst DEFAULT_TOKEN_TTL_MS = 3600 * 1000\n/** Refresh slightly early so requests don't race expiry. */\nconst TOKEN_EXPIRY_SKEW_MS = 5 * 60 * 1000\n\ninterface CursorTokenResponse {\n accessToken: string\n refreshToken: string\n}\n\n/**\n * Derive expiry (epoch ms) from a JWT's `exp` claim, minus a safety skew.\n * Returns `now + 1h` for malformed tokens so a missing claim never wedges\n * refresh into an immediate-expire loop.\n */\nexport function cursorTokenExpiry(token: string): number {\n try {\n const parts = token.split('.')\n const payload = parts[1]\n if (parts.length !== 3 || !payload)\n return Date.now() + DEFAULT_TOKEN_TTL_MS\n\n const json = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/'))) as { exp?: unknown }\n if (typeof json.exp === 'number')\n return json.exp * 1000 - TOKEN_EXPIRY_SKEW_MS\n }\n catch {\n // fall through to default\n }\n return Date.now() + DEFAULT_TOKEN_TTL_MS\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms))\n}\n\n/**\n * Poll Cursor's auth endpoint until login completes. `404` means the user\n * hasn't finished the browser flow yet; anything else non-2xx is a hard error.\n */\nasync function pollForTokens(\n uuid: string,\n verifier: string,\n callbacks: OAuthLoginCallbacks,\n): Promise<CursorTokenResponse> {\n let wait = POLL_BASE_DELAY_MS\n let consecutiveErrors = 0\n\n for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {\n if (callbacks.signal?.aborted)\n throw new Error('Cursor login aborted')\n\n await delay(wait)\n\n try {\n const url = `${CURSOR_POLL_URL}?uuid=${encodeURIComponent(uuid)}&verifier=${encodeURIComponent(verifier)}`\n const response = await fetch(url, { signal: callbacks.signal })\n\n if (response.status === 404) {\n consecutiveErrors = 0\n wait = Math.min(wait * POLL_BACKOFF_MULTIPLIER, POLL_MAX_DELAY_MS)\n continue\n }\n\n if (response.ok) {\n const data = await response.json() as Partial<CursorTokenResponse>\n if (!data.accessToken || !data.refreshToken)\n throw new Error(`Cursor poll response missing tokens: ${JSON.stringify(data)}`)\n return { accessToken: data.accessToken, refreshToken: data.refreshToken }\n }\n\n throw new Error(`Cursor poll failed (${response.status})`)\n }\n catch (err) {\n if (callbacks.signal?.aborted)\n throw new Error('Cursor login aborted')\n consecutiveErrors++\n if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS)\n throw err instanceof Error ? err : new Error(String(err))\n callbacks.onProgress?.('Waiting for Cursor login to complete…')\n }\n }\n\n throw new Error('Cursor authentication timed out')\n}\n\nexport async function loginCursor(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n const { verifier, challenge } = await generatePkce()\n const uuid = crypto.randomUUID()\n\n const authUrl = new URL(CURSOR_LOGIN_URL)\n authUrl.searchParams.set('challenge', challenge)\n authUrl.searchParams.set('uuid', uuid)\n authUrl.searchParams.set('mode', 'login')\n authUrl.searchParams.set('redirectTarget', 'cli')\n\n callbacks.onAuth({\n url: authUrl.toString(),\n instructions: 'A browser window should open. Complete login in Cursor to finish.',\n })\n\n const tokens = await pollForTokens(uuid, verifier, callbacks)\n\n return {\n access: tokens.accessToken,\n refresh: tokens.refreshToken,\n expires: cursorTokenExpiry(tokens.accessToken),\n }\n}\n\nexport async function refreshCursorToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n const response = await fetch(CURSOR_REFRESH_URL, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${credentials.refresh}`,\n 'Content-Type': 'application/json',\n },\n body: '{}',\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`Cursor token refresh failed (${response.status}): ${text || response.statusText}`)\n }\n\n const data = await response.json() as Partial<CursorTokenResponse>\n if (!data.accessToken)\n throw new Error(`Cursor token refresh response missing access token: ${JSON.stringify(data)}`)\n\n return {\n access: data.accessToken,\n // Cursor may omit a rotated refresh token — keep the existing one.\n refresh: data.refreshToken || credentials.refresh,\n expires: cursorTokenExpiry(data.accessToken),\n }\n}\n\n/**\n * Build a Cursor `OAuthProviderInterface`. Shape matches Anthropic / Codex so\n * it drops into `BUILTIN_PROVIDERS` and `src/auth.ts` unchanged.\n *\n * No `renderPage` parameter: Cursor's poll flow has no local callback server,\n * so there is no post-redirect page to theme.\n */\nexport function createCursorOAuthProvider(): OAuthProviderInterface {\n return {\n id: CURSOR_OAUTH_PROVIDER_ID,\n name: 'Cursor',\n usesCallbackServer: false,\n login: loginCursor,\n refreshToken: refreshCursorToken,\n getApiKey: credentials => credentials.access,\n }\n}\n","import type { Provider, StreamCallbacks } from '.'\nimport { CURSOR_OAUTH_PROVIDER_ID } from '../chat/oauth-page/cursor'\nimport { resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\n/**\n * Cursor provider (OAuth).\n *\n * Auth is fully wired: `bun run auth --cursor` (or the TUI wizard) logs in via\n * Cursor's poll-based PKCE flow and persists credentials; this provider resolves\n * + refreshes the OAuth token on every turn via {@link resolveOAuthApiKey}.\n *\n * Inference is **not** wired yet. Cursor does not expose an OpenAI-compatible\n * HTTP endpoint — it speaks a protobuf/Connect agent protocol over HTTP/2 to\n * `api2.cursor.sh`. Implementing `stream()` requires porting that transport\n * (generated protobuf schemas + an HTTP/2 streaming client) in-process, which\n * is intentionally left as a follow-up. Until then `stream()` resolves the\n * token (proving auth works) and then throws a clear, actionable error.\n *\n * The `openaiCompat` base supplies the message-format helpers (`formatTools`,\n * `userMessage`, `toolResultsMessage`, …) so the provider satisfies the full\n * `Provider` contract and slots into the registry; only `stream` is overridden.\n */\nexport interface CursorParams {\n /** Bypass OAuth resolution with an explicit token (mainly for tests). */\n apiKey?: string\n defaultModel?: string\n /**\n * Extra HTTP headers for the eventual Cursor transport. Accepted for API\n * parity with the other providers and forwarded to the openai-compat base,\n * but inert until Cursor inference is implemented (the `stream` override\n * currently throws before any request is made).\n */\n extraHeaders?: Record<string, string>\n}\n\nconst DEFAULT_MODEL = 'claude-4.6-sonnet'\n\nconst NOT_IMPLEMENTED_MESSAGE\n = 'Cursor OAuth login works, but inference over Cursor is not implemented yet. '\n + 'Cursor uses a protobuf/HTTP-2 agent protocol (not an OpenAI-compatible API), '\n + 'so the streaming transport still needs to be ported. Use another provider for now.'\n\nexport function cursor(params?: CursorParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n\n // Base only provides the format/serialization methods; `baseURL` is never\n // hit because we override `stream` below.\n const base = openaiCompat({\n name: 'cursor',\n apiKey: params?.apiKey ?? 'oauth',\n baseURL: 'https://api2.cursor.sh',\n defaultModel,\n capabilities: { vision: true, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n\n return {\n ...base,\n meta: { ...base.meta, defaultModel },\n async stream(options, callbacks: StreamCallbacks) {\n // Resolve (and lazily refresh) the OAuth token so auth failures surface\n // here with the right remediation, distinct from the not-implemented path.\n await resolveOAuthApiKey(\n {\n provider: 'cursor',\n providerId: CURSOR_OAUTH_PROVIDER_ID,\n params,\n missingError: 'No Cursor credentials found. Run `bun run auth --cursor` first.',\n refreshError: reason => `Cursor OAuth token refresh failed. Run \\`bun run auth --cursor\\` again. ${reason}`,\n },\n callbacks,\n )\n void options\n throw new Error(NOT_IMPLEMENTED_MESSAGE)\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"providers-y_l5VkAZ.js","names":[],"sources":["../src/chat/oauth-page/cursor.ts","../src/providers/cursor.ts"],"sourcesContent":["/**\n * Cursor OAuth flow.\n *\n * Unlike Anthropic / OpenAI Codex (loopback-redirect PKCE), Cursor uses a\n * **poll-based** PKCE flow with no local callback server:\n *\n * 1. Generate a PKCE verifier + challenge and a random UUID.\n * 2. Open the browser to `cursor.com/loginDeepControl?challenge&uuid&mode&redirectTarget`.\n * 3. Poll `api2.cursor.sh/auth/poll?uuid&verifier` until tokens come back\n * (HTTP 404 = \"not ready yet\", keep polling with backoff).\n * 4. Refresh via `POST api2.cursor.sh/auth/exchange_user_api_key` with\n * `Authorization: Bearer <refresh>`.\n *\n * Token expiry is derived from the access token's JWT `exp` claim (minus a\n * 5-minute safety margin), falling back to +1h when the claim is missing.\n *\n * Flow adapted from https://github.com/ndraiman/pi-cursor-provider, itself\n * derived from https://github.com/ephraimduncan/opencode-cursor.\n *\n * Cursor's auth + inference protocol is unofficial and undocumented — re-verify\n * the endpoints/params below if login starts failing.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n OAuthProviderInterface,\n} from '@earendil-works/pi-ai/oauth'\nimport { generatePkce } from './pkce'\n\n/** pi-ai / zidane OAuth provider id. Also the credentials-file key. */\nexport const CURSOR_OAUTH_PROVIDER_ID = 'cursor'\n\nconst CURSOR_LOGIN_URL = 'https://cursor.com/loginDeepControl'\nconst CURSOR_POLL_URL = 'https://api2.cursor.sh/auth/poll'\nconst CURSOR_REFRESH_URL = 'https://api2.cursor.sh/auth/exchange_user_api_key'\n\nconst POLL_MAX_ATTEMPTS = 150\nconst POLL_BASE_DELAY_MS = 1000\nconst POLL_MAX_DELAY_MS = 10_000\nconst POLL_BACKOFF_MULTIPLIER = 1.2\nconst POLL_MAX_CONSECUTIVE_ERRORS = 3\n\n/** Fallback lifetime when the JWT carries no `exp`. */\nconst DEFAULT_TOKEN_TTL_MS = 3600 * 1000\n/** Refresh slightly early so requests don't race expiry. */\nconst TOKEN_EXPIRY_SKEW_MS = 5 * 60 * 1000\n\ninterface CursorTokenResponse {\n accessToken: string\n refreshToken: string\n}\n\n/**\n * Derive expiry (epoch ms) from a JWT's `exp` claim, minus a safety skew.\n * Returns `now + 1h` for malformed tokens so a missing claim never wedges\n * refresh into an immediate-expire loop.\n */\nexport function cursorTokenExpiry(token: string): number {\n try {\n const parts = token.split('.')\n const payload = parts[1]\n if (parts.length !== 3 || !payload)\n return Date.now() + DEFAULT_TOKEN_TTL_MS\n\n const json = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/'))) as { exp?: unknown }\n if (typeof json.exp === 'number')\n return json.exp * 1000 - TOKEN_EXPIRY_SKEW_MS\n }\n catch {\n // fall through to default\n }\n return Date.now() + DEFAULT_TOKEN_TTL_MS\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms))\n}\n\n/**\n * Poll Cursor's auth endpoint until login completes. `404` means the user\n * hasn't finished the browser flow yet; anything else non-2xx is a hard error.\n */\nasync function pollForTokens(\n uuid: string,\n verifier: string,\n callbacks: OAuthLoginCallbacks,\n): Promise<CursorTokenResponse> {\n let wait = POLL_BASE_DELAY_MS\n let consecutiveErrors = 0\n\n for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {\n if (callbacks.signal?.aborted)\n throw new Error('Cursor login aborted')\n\n await delay(wait)\n\n try {\n const url = `${CURSOR_POLL_URL}?uuid=${encodeURIComponent(uuid)}&verifier=${encodeURIComponent(verifier)}`\n const response = await fetch(url, { signal: callbacks.signal })\n\n if (response.status === 404) {\n consecutiveErrors = 0\n wait = Math.min(wait * POLL_BACKOFF_MULTIPLIER, POLL_MAX_DELAY_MS)\n continue\n }\n\n if (response.ok) {\n const data = await response.json() as Partial<CursorTokenResponse>\n if (!data.accessToken || !data.refreshToken)\n throw new Error(`Cursor poll response missing tokens: ${JSON.stringify(data)}`)\n return { accessToken: data.accessToken, refreshToken: data.refreshToken }\n }\n\n throw new Error(`Cursor poll failed (${response.status})`)\n }\n catch (err) {\n if (callbacks.signal?.aborted)\n throw new Error('Cursor login aborted')\n consecutiveErrors++\n if (consecutiveErrors >= POLL_MAX_CONSECUTIVE_ERRORS)\n throw err instanceof Error ? err : new Error(String(err))\n callbacks.onProgress?.('Waiting for Cursor login to complete…')\n }\n }\n\n throw new Error('Cursor authentication timed out')\n}\n\nexport async function loginCursor(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n const { verifier, challenge } = await generatePkce()\n const uuid = crypto.randomUUID()\n\n const authUrl = new URL(CURSOR_LOGIN_URL)\n authUrl.searchParams.set('challenge', challenge)\n authUrl.searchParams.set('uuid', uuid)\n authUrl.searchParams.set('mode', 'login')\n authUrl.searchParams.set('redirectTarget', 'cli')\n\n callbacks.onAuth({\n url: authUrl.toString(),\n instructions: 'A browser window should open. Complete login in Cursor to finish.',\n })\n\n const tokens = await pollForTokens(uuid, verifier, callbacks)\n\n return {\n access: tokens.accessToken,\n refresh: tokens.refreshToken,\n expires: cursorTokenExpiry(tokens.accessToken),\n }\n}\n\nexport async function refreshCursorToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n const response = await fetch(CURSOR_REFRESH_URL, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${credentials.refresh}`,\n 'Content-Type': 'application/json',\n },\n body: '{}',\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`Cursor token refresh failed (${response.status}): ${text || response.statusText}`)\n }\n\n const data = await response.json() as Partial<CursorTokenResponse>\n if (!data.accessToken)\n throw new Error(`Cursor token refresh response missing access token: ${JSON.stringify(data)}`)\n\n return {\n access: data.accessToken,\n // Cursor may omit a rotated refresh token — keep the existing one.\n refresh: data.refreshToken || credentials.refresh,\n expires: cursorTokenExpiry(data.accessToken),\n }\n}\n\n/**\n * Build a Cursor `OAuthProviderInterface`. Shape matches Anthropic / Codex so\n * it drops into `BUILTIN_PROVIDERS` and `src/auth.ts` unchanged.\n *\n * No `renderPage` parameter: Cursor's poll flow has no local callback server,\n * so there is no post-redirect page to theme.\n */\nexport function createCursorOAuthProvider(): OAuthProviderInterface {\n return {\n id: CURSOR_OAUTH_PROVIDER_ID,\n name: 'Cursor',\n usesCallbackServer: false,\n login: loginCursor,\n refreshToken: refreshCursorToken,\n getApiKey: credentials => credentials.access,\n }\n}\n","import type { Provider, StreamCallbacks } from '.'\nimport { CURSOR_OAUTH_PROVIDER_ID } from '../chat/oauth-page/cursor'\nimport { resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\n/**\n * Cursor provider (OAuth).\n *\n * Auth is fully wired: `bun run auth --cursor` (or the TUI wizard) logs in via\n * Cursor's poll-based PKCE flow and persists credentials; this provider resolves\n * + refreshes the OAuth token on every turn via {@link resolveOAuthApiKey}.\n *\n * Inference is **not** wired yet. Cursor does not expose an OpenAI-compatible\n * HTTP endpoint — it speaks a protobuf/Connect agent protocol over HTTP/2 to\n * `api2.cursor.sh`. Implementing `stream()` requires porting that transport\n * (generated protobuf schemas + an HTTP/2 streaming client) in-process, which\n * is intentionally left as a follow-up. Until then `stream()` resolves the\n * token (proving auth works) and then throws a clear, actionable error.\n *\n * The `openaiCompat` base supplies the message-format helpers (`formatTools`,\n * `userMessage`, `toolResultsMessage`, …) so the provider satisfies the full\n * `Provider` contract and slots into the registry; only `stream` is overridden.\n */\nexport interface CursorParams {\n /** Bypass OAuth resolution with an explicit token (mainly for tests). */\n apiKey?: string\n defaultModel?: string\n /**\n * Extra HTTP headers for the eventual Cursor transport. Accepted for API\n * parity with the other providers and forwarded to the openai-compat base,\n * but inert until Cursor inference is implemented (the `stream` override\n * currently throws before any request is made).\n */\n extraHeaders?: Record<string, string>\n}\n\nconst DEFAULT_MODEL = 'claude-4.6-sonnet'\n\nconst NOT_IMPLEMENTED_MESSAGE\n = 'Cursor OAuth login works, but inference over Cursor is not implemented yet. '\n + 'Cursor uses a protobuf/HTTP-2 agent protocol (not an OpenAI-compatible API), '\n + 'so the streaming transport still needs to be ported. Use another provider for now.'\n\nexport function cursor(params?: CursorParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n\n // Base only provides the format/serialization methods; `baseURL` is never\n // hit because we override `stream` below.\n const base = openaiCompat({\n name: 'cursor',\n apiKey: params?.apiKey ?? 'oauth',\n baseURL: 'https://api2.cursor.sh',\n defaultModel,\n capabilities: { vision: true, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n\n return {\n ...base,\n meta: { ...base.meta, defaultModel },\n async stream(options, callbacks: StreamCallbacks) {\n // Resolve (and lazily refresh) the OAuth token so auth failures surface\n // here with the right remediation, distinct from the not-implemented path.\n await resolveOAuthApiKey(\n {\n provider: 'cursor',\n providerId: CURSOR_OAUTH_PROVIDER_ID,\n params,\n missingError: 'No Cursor credentials found. Run `bun run auth --cursor` first.',\n refreshError: reason => `Cursor OAuth token refresh failed. Run \\`bun run auth --cursor\\` again. ${reason}`,\n },\n callbacks,\n )\n void options\n throw new Error(NOT_IMPLEMENTED_MESSAGE)\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/providers.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $
|
|
1
|
+
import { $d as StreamOptions, $f as AnthropicParams, Af as mapOAIFinishReason, Df as classifyOpenAICompatError, Gf as CerebrasParams, Hf as local, If as OpenAIParams, Jf as baseten, Kf as cerebras, Mf as openaiCompat, Qd as StreamCallbacks, Uf as CursorParams, Vf as LocalParams, Wf as cursor, Xd as Provider, Xf as ArceeParams, Yd as HistoryRepair, Yf as planBasetenReasoning, Zd as ProviderCapabilities, Zf as arcee, _p as isOAuthAccessToken, af as XaiParams, bf as OpenAICompatParams, bp as resolveOAuthApiKey, cf as SchemaSanitizeProfile, df as sanitizeToolSpecs, ef as TokenCountPayload, ff as OpenRouterParams, fp as OAuthParams, gp as extractRuntimeCredentials, hp as credentialsFromParams, if as TurnResult, lf as SchemaSanitizeResult, mf as openrouter, mp as credentialsFilePath, nf as ToolResult, np as anthropic, of as xai, pp as ResolveOAuthApiKeyOptions, qf as BasetenParams, rf as ToolSpec, rp as applyAnthropicCacheBreakpoints, sf as SchemaSanitizeOptions, tf as ToolCall, uf as sanitizeToolSchema, vf as OpenAICompatAuthHeader, vp as readOAuthCredentials, xp as writeOAuthCredentials, yf as OpenAICompatHttpError, yp as registerCustomOAuthProvider, zf as openai } from "./index-Bht3PKx6.js";
|
|
2
2
|
export { type AnthropicParams, type ArceeParams, type BasetenParams, type CerebrasParams, type CursorParams, HistoryRepair, type LocalParams, type OAuthParams, type OpenAICompatAuthHeader, OpenAICompatHttpError, type OpenAICompatParams, type OpenAIParams, type OpenRouterParams, Provider, ProviderCapabilities, type ResolveOAuthApiKeyOptions, type SchemaSanitizeOptions, type SchemaSanitizeProfile, type SchemaSanitizeResult, StreamCallbacks, StreamOptions, TokenCountPayload, ToolCall, ToolResult, ToolSpec, TurnResult, type XaiParams, anthropic, applyAnthropicCacheBreakpoints, arcee, baseten, cerebras, classifyOpenAICompatError, credentialsFilePath, credentialsFromParams, cursor, extractRuntimeCredentials, isOAuthAccessToken, local, mapOAIFinishReason, openai, openaiCompat, openrouter, planBasetenReasoning, readOAuthCredentials, registerCustomOAuthProvider, resolveOAuthApiKey, sanitizeToolSchema, sanitizeToolSpecs, writeOAuthCredentials, xai };
|
package/dist/providers.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { f as mapOAIFinishReason, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, v as sanitizeToolSchema, y as sanitizeToolSpecs } from "./openai-compat-
|
|
2
|
-
import { n as
|
|
1
|
+
import { f as mapOAIFinishReason, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, v as sanitizeToolSchema, y as sanitizeToolSpecs } from "./openai-compat-OI68avOl.js";
|
|
2
|
+
import { n as anthropic, r as applyAnthropicCacheBreakpoints } from "./anthropic-B3hj9OMm.js";
|
|
3
3
|
import { a as readOAuthCredentials, c as writeOAuthCredentials, i as isOAuthAccessToken, n as credentialsFromParams, o as registerCustomOAuthProvider, r as extractRuntimeCredentials, s as resolveOAuthApiKey, t as credentialsFilePath } from "./oauth-CBi9eymX.js";
|
|
4
4
|
import { arcee } from "./providers/arcee.js";
|
|
5
5
|
import { baseten, planBasetenReasoning } from "./providers/baseten.js";
|
|
6
6
|
import { cerebras } from "./providers/cerebras.js";
|
|
7
|
-
import { t as xai } from "./xai-
|
|
8
|
-
import { t as cursor } from "./providers-
|
|
7
|
+
import { t as xai } from "./xai-DxdI27ds.js";
|
|
8
|
+
import { t as cursor } from "./providers-y_l5VkAZ.js";
|
|
9
9
|
import { local } from "./providers/local.js";
|
|
10
10
|
import { openai } from "./providers/openai.js";
|
|
11
11
|
import { openrouter } from "./providers/openrouter.js";
|
|
@@ -290,7 +290,8 @@ function writeFileStateFromResult(input, output, resultTurn, cwd) {
|
|
|
290
290
|
offset: 0,
|
|
291
291
|
limit: Number.POSITIVE_INFINITY,
|
|
292
292
|
maxBytes: Number.POSITIVE_INFINITY,
|
|
293
|
-
mtimeMs: resultTurn.createdAt
|
|
293
|
+
mtimeMs: resultTurn.createdAt,
|
|
294
|
+
elided: true
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
};
|
|
@@ -313,7 +314,8 @@ function editStateFromResult(input, output, resultTurn, cwd, known) {
|
|
|
313
314
|
entry: {
|
|
314
315
|
...prior.entry,
|
|
315
316
|
contentHash: hashContent(nextContent),
|
|
316
|
-
mtimeMs: resultTurn.createdAt
|
|
317
|
+
mtimeMs: resultTurn.createdAt,
|
|
318
|
+
elided: true
|
|
317
319
|
}
|
|
318
320
|
}
|
|
319
321
|
};
|
|
@@ -343,7 +345,8 @@ function multiEditStateFromResult(input, output, resultTurn, cwd, known) {
|
|
|
343
345
|
entry: {
|
|
344
346
|
...prior.entry,
|
|
345
347
|
contentHash: hashContent(current),
|
|
346
|
-
mtimeMs: resultTurn.createdAt
|
|
348
|
+
mtimeMs: resultTurn.createdAt,
|
|
349
|
+
elided: true
|
|
347
350
|
}
|
|
348
351
|
}
|
|
349
352
|
};
|
|
@@ -407,4 +410,4 @@ function getToolDedupState(session) {
|
|
|
407
410
|
//#endregion
|
|
408
411
|
export { hydrateReadStateFromSessionAsync as a, resolveReadStateMap as c, hydrateReadStateFromSession as i, getToolDedupState as n, markReadStateElided as o, hashContent as r, readStateKey as s, getReadState as t };
|
|
409
412
|
|
|
410
|
-
//# sourceMappingURL=read-state-
|
|
413
|
+
//# sourceMappingURL=read-state-CNyeMG7W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-state-CNyeMG7W.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 { AliasMaps } from '../aliasing'\nimport type { Session } from '../session'\nimport type { SessionTurn, ToolResultContent } from '../types'\nimport { resolve } from 'node:path'\nimport { toCanonicalName } from '../aliasing'\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 the on-wire read output this entry tracks can no longer be\n * trusted as-is, so `read_file`'s dedup must MISS on the next identical\n * re-read — the \"unchanged since the previous read\" stub would otherwise\n * point at content the model can no longer see, or that is no longer true.\n *\n * Two writers:\n * - the wire transforms (stale-read elision, tail compaction), which REMOVE\n * the output from the on-wire history;\n * - the mutating tools (`edit` / `multi_edit` / `write_file`), which refresh\n * `contentHash` to the post-mutation bytes — without the flag that refresh\n * would make the very next re-read hash-match and dedup would claim the\n * PRE-mutation body is \"still current\". Marking at the mutation keeps\n * dedup honest in every `elideStaleReads` mode, including off.\n *\n * The entry stays registered either way — `requireReadBeforeEdit` must keep\n * passing (the contentHash still guards against disk drift; un-registering\n * here is what degraded models into shell-edit one-liner workarounds).\n * Cleared by the next fresh read (`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 * Tool-name aliases in effect for the transcript being replayed. zidane's\n * loop canonicalizes tool-call names before persisting, so durable turns\n * normally already carry canonical names — but a transcript captured from a\n * host that persists WIRE names (the aliased form the model saw, e.g.\n * `Read` / `Edit`), or one imported from another stack, would otherwise be\n * unrecognized and hydrate nothing. When provided, each tool-call name is\n * resolved to canonical (`toCanonicalName`) before matching, so both the\n * canonical and the aliased shape are honored. No-op for already-canonical\n * names.\n */\n aliasMaps?: AliasMaps\n}\n\ntype NormalizedHydrateReadStateOptions = Required<Omit<HydrateReadStateOptions, 'readFileForHash' | 'aliasMaps'>> & {\n readFileForHash?: HydrateReadStateOptions['readFileForHash']\n aliasMaps?: AliasMaps\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 // Resolve wire aliases (e.g. `Read`/`Edit`) to canonical before\n // matching, and STORE the canonical name in `pending` so the\n // downstream `stateFromToolResult` dispatch (which keys off the\n // canonical tool-name options) lines up regardless of how the\n // transcript spelled the call.\n const canonicalName = canonicalToolCallName(block.name, normalized)\n if (\n canonicalName === normalized.readFileToolName\n || canonicalName === normalized.writeFileToolName\n || canonicalName === normalized.editToolName\n || canonicalName === normalized.multiEditToolName\n ) {\n pending.set(block.id, { name: canonicalName, 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 // Resolve wire aliases (e.g. `Read`/`Edit`) to canonical before\n // matching, and STORE the canonical name in `pending` so the\n // downstream `stateFromToolResult` dispatch (which keys off the\n // canonical tool-name options) lines up regardless of how the\n // transcript spelled the call.\n const canonicalName = canonicalToolCallName(block.name, normalized)\n if (\n canonicalName === normalized.readFileToolName\n || canonicalName === normalized.writeFileToolName\n || canonicalName === normalized.editToolName\n || canonicalName === normalized.multiEditToolName\n ) {\n pending.set(block.id, { name: canonicalName, 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 ...(options.aliasMaps ? { aliasMaps: options.aliasMaps } : {}),\n }\n}\n\n/**\n * Resolve a transcript tool-call name to its canonical form for matching.\n * Honors `aliasMaps` (wire → canonical) when present; otherwise the name is\n * already canonical (zidane persists canonical names) and passes through.\n */\nfunction canonicalToolCallName(name: string, options: NormalizedHydrateReadStateOptions): string {\n return options.aliasMaps ? toCanonicalName(name, options.aliasMaps) : name\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 // Mirrors the live seed in `write-file.ts`: the write superseded any\n // prior on-wire read output, so the first re-read on the resumed\n // session must miss dedup and serve the fresh body — otherwise the\n // mutation-site honesty fix evaporates across a process restart.\n elided: true,\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 // `elided: true` mirrors the live refresh in `edit.ts`: the hash now\n // matches post-edit bytes while the replayed wire still shows the\n // pre-edit read, so dedup must miss on the next re-read.\n entry: { ...prior.entry, contentHash: hashContent(nextContent), mtimeMs: resultTurn.createdAt, elided: true },\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 // same rationale as `editStateFromResult` above\n entry: { ...prior.entry, contentHash: hashContent(current), mtimeMs: resultTurn.createdAt, elided: true },\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 * The recorded result was a failure or noop verdict (wire `isError`, or\n * `outcome: 'failed' | 'noop'`). Such verdicts are state-dependent —\n * replaying one would freeze a transient failure into a permanent one —\n * so `dedup-tools` never substitutes them on a hash hit; the call\n * re-executes instead. The entry still exists so identical failing\n * calls keep counting toward `mode: 'block-after'` thresholds.\n */\n failed?: boolean\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":";;;;;AA0FA,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,CAAC,CAAC,SAAS,GAAG,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;AAC3E;AAgDA,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,8BAA8B;AACpC,MAAM,sBAAsB;AAC5B,MAAM,oBAAoB;;;;;;;;;;;;;;;AAgB1B,SAAgB,4BACd,SACA,KACA,UAAmC,CAAC,GAC5B;CACR,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;GAM9B,MAAM,gBAAgB,sBAAsB,MAAM,MAAM,UAAU;GAClE,IACE,kBAAkB,WAAW,oBAC1B,kBAAkB,WAAW,qBAC7B,kBAAkB,WAAW,gBAC7B,kBAAkB,WAAW,mBAEhC,QAAQ,IAAI,MAAM,IAAI;IAAE,MAAM;IAAe,OAAO,MAAM;GAAM,CAAC;GAEnE;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,oBAAoB,MADlB,iBAAiB,MAAM,MACM,GAAG,MAAM,KAAK,YAAY,KAAK;EAC3E,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;;;;;;;AAQA,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;GAM9B,MAAM,gBAAgB,sBAAsB,MAAM,MAAM,UAAU;GAClE,IACE,kBAAkB,WAAW,oBAC1B,kBAAkB,WAAW,qBAC7B,kBAAkB,WAAW,gBAC7B,kBAAkB,WAAW,mBAEhC,QAAQ,IAAI,MAAM,IAAI;IAAE,MAAM;IAAe,OAAO,MAAM;GAAM,CAAC;GAEnE;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;EAC9E,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;CAC9D;AACF;;;;;;AAOA,SAAS,sBAAsB,MAAc,SAAoD;CAC/F,OAAO,QAAQ,YAAY,gBAAgB,MAAM,QAAQ,SAAS,IAAI;AACxE;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;IAKpB,QAAQ;GACV;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;GAIT,OAAO;IAAE,GAAG,MAAM;IAAO,aAAa,YAAY,WAAW;IAAG,SAAS,WAAW;IAAW,QAAQ;GAAK;EAC9G;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;GAET,OAAO;IAAE,GAAG,MAAM;IAAO,aAAa,YAAY,OAAO;IAAG,SAAS,WAAW;IAAW,QAAQ;GAAK;EAC1G;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,CAAC,CAAC,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,EAAE,CAAC,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;AAqCA,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 {
|
|
2
|
-
import {
|
|
1
|
+
import { g as TaskExitInfo, h as TaskEntry, l as ExecutionHandle, o as DetachedTasksCapability } from "./types-DdZMQxfT.js";
|
|
2
|
+
import { $d as StreamOptions, Cp as AgentClock, Nm as ToolContext, Pm as ToolDef, Sp as AgentBehavior, Ui as McpToolMeta, Wi as McpToolWrap, Xd as Provider, bm as ToolResultContent, gm as ToolBatchExecutor, rh as SessionStore } from "./index-Bht3PKx6.js";
|
|
3
3
|
|
|
4
4
|
//#region src/restate/types.d.ts
|
|
5
5
|
/**
|
package/dist/restate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as buildAliasMaps } from "./aliasing-lVFbm81n.js";
|
|
2
2
|
import { r as coerceToolResultContent } from "./types-BIarq1qE.js";
|
|
3
|
-
import { a as hydrateReadStateFromSessionAsync } from "./read-state-
|
|
3
|
+
import { a as hydrateReadStateFromSessionAsync } from "./read-state-CNyeMG7W.js";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
//#region src/restate/awakeable.ts
|
|
6
6
|
/**
|
|
@@ -140,7 +140,7 @@ function restateBehavior(overrides = {}) {
|
|
|
140
140
|
compactStrategy: "tail",
|
|
141
141
|
compactThreshold: 128 * 1024,
|
|
142
142
|
compactKeepTurns: 8,
|
|
143
|
-
elideStaleReads:
|
|
143
|
+
elideStaleReads: "stub",
|
|
144
144
|
mediaKeepTurns: 8,
|
|
145
145
|
toolOutputBudget: 64 * 1024,
|
|
146
146
|
autoCompact: { threshold: .85 },
|