zidane 5.13.26 → 5.14.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.
Files changed (145) hide show
  1. package/dist/{acp-CjAoILwj.js → acp-DgKEJH3P.js} +7 -7
  2. package/dist/{acp-CjAoILwj.js.map → acp-DgKEJH3P.js.map} +1 -1
  3. package/dist/acp-cli.js +11 -6
  4. package/dist/acp-cli.js.map +1 -1
  5. package/dist/acp.d.ts +2 -2
  6. package/dist/acp.js +1 -1
  7. package/dist/{tools-B32lZXLE.js → agent-B-cKanSC.js} +8035 -9459
  8. package/dist/agent-B-cKanSC.js.map +1 -0
  9. package/dist/{agent-DQIL5BFk.d.ts → agent-BAgOgS37.d.ts} +367 -208
  10. package/dist/agent-BAgOgS37.d.ts.map +1 -0
  11. package/dist/agent.d.ts +3 -0
  12. package/dist/agent.js +5 -0
  13. package/dist/anthropic-RGqsJlC8.js +807 -0
  14. package/dist/anthropic-RGqsJlC8.js.map +1 -0
  15. package/dist/{atomic-write-Bgtr5JPu.js → atomic-write-B7zr1ddF.js} +2 -2
  16. package/dist/{atomic-write-Bgtr5JPu.js.map → atomic-write-B7zr1ddF.js.map} +1 -1
  17. package/dist/{auth-DPrSP1YV.js → auth-C-ms5N2x.js} +33 -5
  18. package/dist/{auth-DPrSP1YV.js.map → auth-C-ms5N2x.js.map} +1 -1
  19. package/dist/cache-telemetry-BKAwsNxm.js +169 -0
  20. package/dist/cache-telemetry-BKAwsNxm.js.map +1 -0
  21. package/dist/chat/pure.d.ts +3 -3
  22. package/dist/chat/pure.js +1 -1
  23. package/dist/chat.d.ts +21 -7
  24. package/dist/chat.d.ts.map +1 -1
  25. package/dist/chat.js +30 -5
  26. package/dist/chat.js.map +1 -1
  27. package/dist/contexts/daytona.d.ts +1 -1
  28. package/dist/contexts/docker.js +0 -1
  29. package/dist/contexts/docker.js.map +1 -1
  30. package/dist/contexts/e2b.d.ts +1 -1
  31. package/dist/errors-CkTvg97v.d.ts +203 -0
  32. package/dist/errors-CkTvg97v.d.ts.map +1 -0
  33. package/dist/errors.d.ts +3 -0
  34. package/dist/errors.js +3 -0
  35. package/dist/eval.d.ts +1 -1
  36. package/dist/eval.js +2 -2
  37. package/dist/headless.d.ts +1 -1
  38. package/dist/headless.js +406 -1
  39. package/dist/headless.js.map +1 -0
  40. package/dist/{index-YY3vp2F2.d.ts → index-CLuLFFLa.d.ts} +88 -418
  41. package/dist/index-CLuLFFLa.d.ts.map +1 -0
  42. package/dist/{index-C5qw3pj7.d.ts → index-CgBIl2mX.d.ts} +2 -2
  43. package/dist/{index-C5qw3pj7.d.ts.map → index-CgBIl2mX.d.ts.map} +1 -1
  44. package/dist/index.d.ts +9 -5
  45. package/dist/index.js +113 -167
  46. package/dist/index.js.map +1 -1
  47. package/dist/{logger-C2ngE8CY.d.ts → logger-CJ-lpKPy.d.ts} +2 -2
  48. package/dist/{logger-C2ngE8CY.d.ts.map → logger-CJ-lpKPy.d.ts.map} +1 -1
  49. package/dist/mcp.d.ts +1 -1
  50. package/dist/messages-YZQLKaz2.js +1157 -0
  51. package/dist/messages-YZQLKaz2.js.map +1 -0
  52. package/dist/oauth-8LqbeI_Q.js +292 -0
  53. package/dist/oauth-8LqbeI_Q.js.map +1 -0
  54. package/dist/{messages-Iy_vEycX.js → openai-compat-0Y7jcncn.js} +27 -1379
  55. package/dist/openai-compat-0Y7jcncn.js.map +1 -0
  56. package/dist/output/stream-json.d.ts +2 -2
  57. package/dist/output/stream-json.js +1 -1
  58. package/dist/output/terminal.d.ts +2 -2
  59. package/dist/{presets-B9nDwmaz.js → presets-Pr02ZNcy.js} +3 -2
  60. package/dist/{presets-B9nDwmaz.js.map → presets-Pr02ZNcy.js.map} +1 -1
  61. package/dist/presets.d.ts +2 -2
  62. package/dist/presets.js +1 -1
  63. package/dist/prompt.d.ts +2 -0
  64. package/dist/prompt.js +210 -0
  65. package/dist/prompt.js.map +1 -0
  66. package/dist/providers/anthropic.d.ts +2 -0
  67. package/dist/providers/anthropic.js +2 -0
  68. package/dist/providers/arcee.d.ts +2 -0
  69. package/dist/providers/arcee.js +42 -0
  70. package/dist/providers/arcee.js.map +1 -0
  71. package/dist/providers/baseten.d.ts +2 -0
  72. package/dist/providers/baseten.js +66 -0
  73. package/dist/providers/baseten.js.map +1 -0
  74. package/dist/providers/cerebras.d.ts +2 -0
  75. package/dist/providers/cerebras.js +31 -0
  76. package/dist/providers/cerebras.js.map +1 -0
  77. package/dist/providers/local.d.ts +2 -0
  78. package/dist/providers/local.js +44 -0
  79. package/dist/providers/local.js.map +1 -0
  80. package/dist/providers/openai-compat.d.ts +2 -0
  81. package/dist/providers/openai-compat.js +2 -0
  82. package/dist/providers/openai.d.ts +2 -0
  83. package/dist/providers/openai.js +504 -0
  84. package/dist/providers/openai.js.map +1 -0
  85. package/dist/providers/openrouter.d.ts +2 -0
  86. package/dist/providers/openrouter.js +52 -0
  87. package/dist/providers/openrouter.js.map +1 -0
  88. package/dist/providers/xai.d.ts +2 -0
  89. package/dist/providers/xai.js +2 -0
  90. package/dist/providers-DgmdYGKm.js +168 -0
  91. package/dist/providers-DgmdYGKm.js.map +1 -0
  92. package/dist/providers.d.ts +1 -1
  93. package/dist/providers.js +10 -2
  94. package/dist/restate.d.ts +1 -1
  95. package/dist/run-summary-DAnBZnrt.d.ts +249 -0
  96. package/dist/run-summary-DAnBZnrt.d.ts.map +1 -0
  97. package/dist/run-summary-nD3IQ4ZQ.js +253 -0
  98. package/dist/run-summary-nD3IQ4ZQ.js.map +1 -0
  99. package/dist/session/sqlite.d.ts +1 -1
  100. package/dist/{session-CiA1_8SB.js → session-CqrAFAKJ.js} +2 -2
  101. package/dist/{session-CiA1_8SB.js.map → session-CqrAFAKJ.js.map} +1 -1
  102. package/dist/session.d.ts +1 -1
  103. package/dist/session.js +2 -2
  104. package/dist/skills.d.ts +2 -2
  105. package/dist/system-prompt-BlsdjcUi.d.ts +139 -0
  106. package/dist/system-prompt-BlsdjcUi.d.ts.map +1 -0
  107. package/dist/timeout-CpFm0jJ6.d.ts +10 -0
  108. package/dist/timeout-CpFm0jJ6.d.ts.map +1 -0
  109. package/dist/timeout-kGGSXagK.js +84 -0
  110. package/dist/timeout-kGGSXagK.js.map +1 -0
  111. package/dist/{tool-formatters-w5oaoCS4.d.ts → tool-formatters-Ch8oRpYR.d.ts} +2 -2
  112. package/dist/{tool-formatters-w5oaoCS4.d.ts.map → tool-formatters-Ch8oRpYR.d.ts.map} +1 -1
  113. package/dist/tools/fetch-url.d.ts +1 -1
  114. package/dist/tools/web-search.d.ts +1 -1
  115. package/dist/tools-00_Nqazv.js +1689 -0
  116. package/dist/tools-00_Nqazv.js.map +1 -0
  117. package/dist/tools.d.ts +2 -2
  118. package/dist/tools.js +2 -1
  119. package/dist/{transcript-anchors-BUtOd-qa.js → transcript-anchors-BUmvPnis.js} +10 -9
  120. package/dist/{transcript-anchors-BUtOd-qa.js.map → transcript-anchors-BUmvPnis.js.map} +1 -1
  121. package/dist/{transcript-anchors-BJ-ESWnm.d.ts → transcript-anchors-CL9UM_M4.d.ts} +4 -4
  122. package/dist/{transcript-anchors-BJ-ESWnm.d.ts.map → transcript-anchors-CL9UM_M4.d.ts.map} +1 -1
  123. package/dist/tui.d.ts +3 -3
  124. package/dist/tui.js +38 -13
  125. package/dist/tui.js.map +1 -1
  126. package/dist/{turn-operations-BSSQHNq1.d.ts → turn-operations-Cj6v_ypW.d.ts} +3 -3
  127. package/dist/{turn-operations-BSSQHNq1.d.ts.map → turn-operations-Cj6v_ypW.d.ts.map} +1 -1
  128. package/dist/{turn-operations-B_XeuNp-.js → turn-operations-tG0vuBNc.js} +2 -2
  129. package/dist/{turn-operations-B_XeuNp-.js.map → turn-operations-tG0vuBNc.js.map} +1 -1
  130. package/dist/types-CyVGdbia.js.map +1 -1
  131. package/dist/types.d.ts +4 -3
  132. package/dist/xai-tPdbAGkq.js +379 -0
  133. package/dist/xai-tPdbAGkq.js.map +1 -0
  134. package/docs/ARCHITECTURE.md +4 -2
  135. package/docs/CHAT.md +4 -0
  136. package/docs/SKILL.md +31 -5
  137. package/package.json +63 -1
  138. package/dist/agent-DQIL5BFk.d.ts.map +0 -1
  139. package/dist/headless-2SH7dvoW.js +0 -656
  140. package/dist/headless-2SH7dvoW.js.map +0 -1
  141. package/dist/index-YY3vp2F2.d.ts.map +0 -1
  142. package/dist/messages-Iy_vEycX.js.map +0 -1
  143. package/dist/providers-BMw1D_lj.js +0 -2332
  144. package/dist/providers-BMw1D_lj.js.map +0 -1
  145. package/dist/tools-B32lZXLE.js.map +0 -1
@@ -1,9 +1,8 @@
1
- import { t as fnv1aBase36 } from "./hash-DJRzmcPI.js";
2
- import { n as estimateTokens } from "./utils-ngQzYzZD.js";
3
1
  import { i as documentBlockMarker, n as assertResolvedMediaBlock } from "./types-CyVGdbia.js";
2
+ import { n as estimateTokens } from "./utils-ngQzYzZD.js";
4
3
  import { c as classifyErrorPrelude, d as isRetryableHttpStatus, f as matchesContextExceeded, l as errorMessage, p as matchesToolPairingError } from "./errors-DJUxZg9b.js";
5
- import { n as reconcileImageMediaType, t as audioFormatFromMediaType } from "./media-sniff-Bn76JxAu.js";
6
- import { getModel } from "@earendil-works/pi-ai";
4
+ import { renderSystemForWire, splitSystemPrompt } from "./prompt.js";
5
+ import { t as audioFormatFromMediaType } from "./media-sniff-Bn76JxAu.js";
7
6
  //#region src/prompt.ts
8
7
  /**
9
8
  * Coerce the run-level prompt into a `PromptPart[]`.
@@ -123,214 +122,12 @@ function buildPromptMessage(provider, parts) {
123
122
  return defaultPromptMessage(parts);
124
123
  }
125
124
  //#endregion
126
- //#region src/system-prompt.ts
127
- /**
128
- * System-prompt boundary marker — splits a system prompt into a stable static
129
- * prefix (cached) and a per-turn dynamic suffix (NOT cached).
130
- *
131
- * Why this exists: providers attach `cache_control` markers on the last block
132
- * of the system prompt, so the cached prefix covers the entire system text.
133
- * Any byte change anywhere — including a per-turn `<env>` rewrite — busts the
134
- * cache for the doctrine that sits below. A literal marker in the system
135
- * string lets providers split it into:
136
- *
137
- * ┌──────────────┐ cache_control: ephemeral
138
- * │ STATIC half │ — doctrine, skills catalog, tool catalog,
139
- * │ │ user instructions
140
- * ├──────────────┤ ← SYSTEM_PROMPT_BOUNDARY
141
- * │ DYNAMIC half │ — env, cwd, mtimes, anything per-turn
142
- * └──────────────┘ (no cache_control)
143
- *
144
- * The static prefix rides the prompt cache across turns/sessions; the dynamic
145
- * suffix re-bills per turn. Net effect: a cwd change between turns no longer
146
- * invalidates 4 KB of doctrine.
147
- *
148
- * Wire contract:
149
- *
150
- * - `splitSystemPrompt(s)` is pure; missing marker ⇒ entire string is static
151
- * (current behavior — no caller is forced to opt in).
152
- * - `renderSystemForWire(s)` strips the marker so it never reaches the model;
153
- * used by every provider before the bytes hit the wire, including the
154
- * cache-disabled path on providers that DO support `cache_control`.
155
- * - The marker uses underscores rather than XML/punctuation so it's
156
- * unambiguous when scanning prompts manually and unlikely to collide with
157
- * model-written content.
158
- * - Providers handle the split internally (Anthropic emits a 2-block array;
159
- * OpenAI-compat splits the leading `system` message into multi-part text).
160
- * Callers always pass a single `string` — no API break.
161
- */
162
- /**
163
- * Literal marker inserted in a system prompt to separate cacheable doctrine
164
- * from per-turn dynamic content. Providers split on this token.
165
- *
166
- * Underscored on both sides for visual distinctiveness — a stray instance in
167
- * model-written prose is implausible. Don't change the value without shipping
168
- * a migration; existing sessions carry the old marker in their cached
169
- * prompts.
170
- */
171
- const SYSTEM_PROMPT_BOUNDARY = "__ZIDANE_SYSTEM_PROMPT_BOUNDARY__";
172
- /**
173
- * Split a system prompt around the first {@link SYSTEM_PROMPT_BOUNDARY}.
174
- *
175
- * Splits on the FIRST occurrence — subsequent markers are folded into the
176
- * dynamic half. This way callers can append additional `<env>` style blocks
177
- * with extra markers without each one creating a new cache layer (Anthropic
178
- * caps breakpoints; we use the budget elsewhere). The marker itself is
179
- * stripped from both sides — providers attach `cache_control` directly to
180
- * the static half's text content.
181
- *
182
- * A single blank line (`\n\n`) immediately adjacent to the marker on each
183
- * side is trimmed — callers conventionally write
184
- * `<doctrine>\n\n<MARKER>\n\n<env>` and expect the rendered wire bytes to
185
- * read as one logical paragraph. Blank lines beyond that immediate pair
186
- * (e.g. `\n\n\n<env>`) are preserved verbatim so callers composing their
187
- * own spacing don't lose intentional gaps.
188
- *
189
- * Pure / no I/O / no allocation when the marker is absent (returns the input
190
- * verbatim on the static side and the empty string on the dynamic side).
191
- */
192
- function splitSystemPrompt(system) {
193
- if (system.length === 0) return {
194
- static: "",
195
- dynamic: ""
196
- };
197
- const idx = system.indexOf(SYSTEM_PROMPT_BOUNDARY);
198
- if (idx < 0) return {
199
- static: system,
200
- dynamic: ""
201
- };
202
- const staticPart = system.slice(0, idx);
203
- const dynamicPart = system.slice(idx + 33);
204
- return {
205
- static: trimTrailingBlankLine(staticPart),
206
- dynamic: trimLeadingBlankLine(dynamicPart)
207
- };
208
- }
209
- /**
210
- * Compose a system prompt from a static prefix and an optional dynamic
211
- * suffix. Inserts {@link SYSTEM_PROMPT_BOUNDARY} between them only when the
212
- * dynamic side is non-empty — single-block prompts stay marker-free, so
213
- * callers that never opt in pay zero overhead and providers fall back to the
214
- * existing whole-string caching path.
215
- *
216
- * Spacing is `\n\n` on both sides of the marker so doctrine fragments,
217
- * which conventionally separate sections with a blank line, read cleanly
218
- * around the boundary.
219
- */
220
- function joinSystemPrompt(staticPart, dynamicPart) {
221
- if (dynamicPart.length === 0) return staticPart;
222
- if (staticPart.length === 0) return `${SYSTEM_PROMPT_BOUNDARY}\n\n${dynamicPart}`;
223
- return `${staticPart}\n\n${SYSTEM_PROMPT_BOUNDARY}\n\n${dynamicPart}`;
224
- }
225
- /**
226
- * Append `extra` to the STATIC half of a system prompt, preserving any
227
- * existing dynamic suffix.
228
- *
229
- * Used by the agent to fold in run-stable content (skills catalog, lazy tool
230
- * catalog) without bumping it into the dynamic half — both catalogs are
231
- * built once per run and remain byte-stable for the duration, so they
232
- * belong in the cached prefix.
233
- *
234
- * Returns a new string; the input is not mutated. When `extra` is empty,
235
- * returns the input verbatim.
236
- */
237
- function appendStaticSection(system, extra) {
238
- if (extra.length === 0) return system;
239
- const { static: staticPart, dynamic } = splitSystemPrompt(system);
240
- return joinSystemPrompt(staticPart.length === 0 ? extra : `${staticPart}\n\n${extra}`, dynamic);
241
- }
242
- /**
243
- * Append `extra` to the DYNAMIC half of a system prompt. Inserts the boundary
244
- * marker if the input didn't already carry one.
245
- *
246
- * Used by hosts (typically the TUI) to inject per-turn state — current cwd,
247
- * IDE selection, project root — without forcing every caller to know the
248
- * marker format. The host's `system:transform` hook rewrites the dynamic
249
- * half each turn; the static doctrine above stays byte-stable and rides the
250
- * cache.
251
- *
252
- * Returns a new string; the input is not mutated. When `extra` is empty,
253
- * returns the input verbatim.
254
- */
255
- function appendDynamicSection(system, extra) {
256
- if (extra.length === 0) return system;
257
- const { static: staticPart, dynamic } = splitSystemPrompt(system);
258
- return joinSystemPrompt(staticPart, dynamic.length === 0 ? extra : `${dynamic}\n\n${extra}`);
259
- }
260
- /**
261
- * Replace the entire dynamic half of a system prompt with `next`. Used by
262
- * the TUI's `<env>` rewriter where the entire dynamic section is regenerated
263
- * each turn rather than appended to.
264
- *
265
- * When `next` is empty, drops the dynamic half (and the marker) entirely.
266
- */
267
- function replaceDynamicSection(system, next) {
268
- const { static: staticPart } = splitSystemPrompt(system);
269
- return joinSystemPrompt(staticPart, next);
270
- }
271
- /**
272
- * Strip the boundary marker so it never reaches the wire — collapses
273
- * `<static>${BOUNDARY}<dynamic>` into `<static>\n\n<dynamic>` for providers
274
- * that can't honor `cache_control` (vanilla OpenAI Chat Completions, Codex,
275
- * Cerebras, ...) or for the cache-disabled path on any provider.
276
- *
277
- * Cache-aware providers re-derive the split from the original (un-rendered)
278
- * system string via `splitSystemPrompt` — `renderSystemForWire` is the
279
- * marker-free counterpart used to build the actual wire bytes.
280
- *
281
- * No-op when the input has no marker. Pure / no I/O.
282
- */
283
- function renderSystemForWire(system) {
284
- if (system.length === 0) return system;
285
- const parts = splitSystemPrompt(system);
286
- if (parts.dynamic.length === 0) return parts.static;
287
- if (parts.static.length === 0) return parts.dynamic;
288
- return `${parts.static}\n\n${parts.dynamic}`;
289
- }
290
- /** True when `system` contains the boundary marker. */
291
- function hasSystemPromptBoundary(system) {
292
- return system.includes(SYSTEM_PROMPT_BOUNDARY);
293
- }
294
- /** Strip a single trailing `\n\n` (and any leftover trailing whitespace within that pair). */
295
- function trimTrailingBlankLine(s) {
296
- let end = s.length;
297
- let newlines = 0;
298
- while (end > 0 && newlines < 2) {
299
- const ch = s.charCodeAt(end - 1);
300
- if (ch === 10) {
301
- newlines += 1;
302
- end -= 1;
303
- continue;
304
- }
305
- if (ch === 32 || ch === 9) {
306
- end -= 1;
307
- continue;
308
- }
309
- break;
310
- }
311
- return newlines > 0 ? s.slice(0, end) : s;
312
- }
313
- /** Strip a single leading `\n\n` (and any leftover leading whitespace within that pair). */
314
- function trimLeadingBlankLine(s) {
315
- let start = 0;
316
- let newlines = 0;
317
- while (start < s.length && newlines < 2) {
318
- const ch = s.charCodeAt(start);
319
- if (ch === 10) {
320
- newlines += 1;
321
- start += 1;
322
- continue;
323
- }
324
- if (ch === 32 || ch === 9) {
325
- start += 1;
326
- continue;
327
- }
328
- break;
329
- }
330
- return newlines > 0 ? s.slice(start) : s;
331
- }
332
- //#endregion
333
125
  //#region src/providers/cost.ts
126
+ let getModelPromise;
127
+ function loadGetModel() {
128
+ getModelPromise ??= import("@earendil-works/pi-ai").then((mod) => mod.getModel).catch(() => null);
129
+ return getModelPromise;
130
+ }
334
131
  /**
335
132
  * Per-MTok rates for models not yet in pi-ai's bundled registry. Keyed by
336
133
  * `provider:modelId`. Consulted only when pi-ai's `getModel` misses, so a
@@ -412,13 +209,13 @@ const EXTRA_COST_RATES = {
412
209
  * will drift until the dep updates. An optional `multiplier` scales the
413
210
  * base rates for premium modes (e.g. fast mode) before totaling.
414
211
  */
415
- function fillEstimatedCost(usage, provider, multiplier) {
212
+ async function fillEstimatedCost(usage, provider, multiplier) {
416
213
  if (usage.cost !== void 0) return usage;
417
214
  if (!usage.modelId) return usage;
418
- const lookup = getModel;
215
+ const getModel = await loadGetModel();
419
216
  let model;
420
- try {
421
- model = lookup(provider, usage.modelId);
217
+ if (getModel) try {
218
+ model = getModel(provider, usage.modelId);
422
219
  } catch {
423
220
  model = void 0;
424
221
  }
@@ -995,7 +792,7 @@ function toVideoUrlPart(video) {
995
792
  video_url: { url: `data:${video.mediaType};base64,${video.data}` }
996
793
  };
997
794
  }
998
- function documentMarker$1(doc) {
795
+ function documentMarker(doc) {
999
796
  return documentBlockMarker(doc, "document omitted");
1000
797
  }
1001
798
  /**
@@ -1072,7 +869,7 @@ function summarizeToolResultOutput(output, documentsEnabled = false) {
1072
869
  });
1073
870
  } else if (block.type === "document") {
1074
871
  assertResolvedMediaBlock(block, "OpenAI-compatible wire messages");
1075
- texts.push(documentMarker$1(block));
872
+ texts.push(documentMarker(block));
1076
873
  }
1077
874
  return {
1078
875
  text: texts.join("\n"),
@@ -1245,7 +1042,7 @@ function toOAIMessages(system, messages, options = {}) {
1245
1042
  if (documentsEnabled && isForwardableDocument(b)) parts.push(toFilePart(b));
1246
1043
  else parts.push({
1247
1044
  type: "text",
1248
- text: documentMarker$1(b)
1045
+ text: documentMarker(b)
1249
1046
  });
1250
1047
  }
1251
1048
  for (const b of textBlocks) parts.push({
@@ -1791,1175 +1588,26 @@ function openaiCompat(params) {
1791
1588
  model: modelId
1792
1589
  };
1793
1590
  }
1591
+ const filledUsage = await fillEstimatedCost({
1592
+ input: usage.input,
1593
+ output: usage.output,
1594
+ ...usage.cacheRead !== void 0 ? { cacheRead: usage.cacheRead } : {},
1595
+ ...usage.cacheCreation !== void 0 ? { cacheCreation: usage.cacheCreation } : {},
1596
+ ...usage.cost !== void 0 ? { cost: usage.cost } : {},
1597
+ ...finishReason ? { finishReason } : {},
1598
+ modelId
1599
+ }, name);
1794
1600
  return {
1795
1601
  assistantMessage: buildAssistantContent(result.text, result.toolCalls, thinkingForBlock, reasoningArg),
1796
1602
  text: result.text,
1797
1603
  toolCalls: result.toolCalls,
1798
1604
  done: result.toolCalls.length === 0,
1799
- usage: fillEstimatedCost({
1800
- input: usage.input,
1801
- output: usage.output,
1802
- ...usage.cacheRead !== void 0 ? { cacheRead: usage.cacheRead } : {},
1803
- ...usage.cacheCreation !== void 0 ? { cacheCreation: usage.cacheCreation } : {},
1804
- ...usage.cost !== void 0 ? { cost: usage.cost } : {},
1805
- ...finishReason ? { finishReason } : {},
1806
- modelId
1807
- }, name)
1605
+ usage: filledUsage
1808
1606
  };
1809
1607
  }
1810
1608
  };
1811
1609
  }
1812
1610
  //#endregion
1813
- //#region src/session/messages.ts
1814
- /**
1815
- * Decode an Anthropic `tool_result.content` wire value back to zidane's canonical
1816
- * `string | ToolResultContent[]` shape.
1817
- *
1818
- * Anthropic accepts three shapes in the wire: `string`, `Array<{type:'text',text}>`,
1819
- * or `Array<{type:'text'|'image', ...}>`. We collapse to `string` only when every
1820
- * block is text to keep simple cases readable.
1821
- */
1822
- function decodeAnthropicToolResultContent(content) {
1823
- if (typeof content === "string") return content;
1824
- if (!Array.isArray(content)) return JSON.stringify(content);
1825
- const blocks = [];
1826
- for (const raw of content) {
1827
- if (!raw || typeof raw !== "object") continue;
1828
- const b = raw;
1829
- if (b.type === "text" && typeof b.text === "string") {
1830
- blocks.push({
1831
- type: "text",
1832
- text: b.text
1833
- });
1834
- continue;
1835
- }
1836
- if (b.type === "image" && b.source && typeof b.source === "object") {
1837
- const src = b.source;
1838
- if (src.type === "base64" && typeof src.data === "string" && typeof src.media_type === "string") {
1839
- blocks.push({
1840
- type: "image",
1841
- mediaType: src.media_type,
1842
- data: src.data
1843
- });
1844
- continue;
1845
- }
1846
- }
1847
- blocks.push({
1848
- type: "text",
1849
- text: JSON.stringify(raw)
1850
- });
1851
- }
1852
- if (blocks.length === 0) return "";
1853
- if (!blocks.some((b) => b.type !== "text")) return blocks.map((b) => b.text).join("\n");
1854
- return blocks;
1855
- }
1856
- /**
1857
- * Build an Anthropic `image.source` block, reconciling the declared media
1858
- * type against the actual magic bytes.
1859
- *
1860
- * Anthropic's API rejects the entire request when `media_type` disagrees
1861
- * with the payload (`messages.N.content.K.image.source.base64: The image
1862
- * was specified using the X media type, but the image appears to be a
1863
- * Y image`). The upstream sources of `{type:'image'}` blocks each sniff
1864
- * before producing the block (read_file / MCP / TUI paste), but this
1865
- * wire-boundary reconciliation is a final safety net for any future code
1866
- * path or host-supplied prompt part that slips through without sniffing.
1867
- *
1868
- * The reconciler is conservative: it overrides only when the magic bytes
1869
- * positively identify a *different* known format. Unknown / too-short
1870
- * payloads pass `mediaType` through unchanged.
1871
- */
1872
- function anthropicImageSource(mediaType, data) {
1873
- return {
1874
- type: "base64",
1875
- media_type: reconcileImageMediaType(mediaType, data),
1876
- data
1877
- };
1878
- }
1879
- function anthropicDocumentBlocks(doc) {
1880
- if (doc.encoding === "base64" && doc.mediaType === "application/pdf") return [{
1881
- type: "document",
1882
- source: {
1883
- type: "base64",
1884
- media_type: "application/pdf",
1885
- data: doc.data
1886
- },
1887
- ...doc.name ? { title: doc.name } : {}
1888
- }];
1889
- return [{
1890
- type: "text",
1891
- text: `${doc.name ? `<attachment name="${doc.name}" media_type="${doc.mediaType}" encoding="${doc.encoding}">` : `<attachment media_type="${doc.mediaType}" encoding="${doc.encoding}">`}\n${doc.data}\n</attachment>`
1892
- }];
1893
- }
1894
- function documentMarker(doc) {
1895
- return documentBlockMarker(doc, "document attached");
1896
- }
1897
- function isContentRefBlock(block) {
1898
- return (block.type === "image" || block.type === "document") && typeof block.ref === "string";
1899
- }
1900
- function unresolvedContentRefError(block) {
1901
- return /* @__PURE__ */ new Error(`Cannot project content ref "${block.ref}" to provider wire messages without resolveContentRef.`);
1902
- }
1903
- /**
1904
- * Coerce a tool-call id to Anthropic's wire constraint `^[a-zA-Z0-9_-]+$`.
1905
- *
1906
- * Sessions started under another provider (OpenAI-compat / OpenRouter /
1907
- * Cerebras) or carrying MCP-namespaced calls can hold ids with `.`, `:`,
1908
- * `/` or other characters that 400 the request the moment the user switches
1909
- * to an Anthropic model (`messages.N.content.M.tool_use.id: String should
1910
- * match pattern '^[a-zA-Z0-9_-]+$'`).
1911
- *
1912
- * The mapping is deterministic — every disallowed char becomes `_` — so a
1913
- * `tool_use` and its paired `tool_result` (sanitized identically below) keep
1914
- * matching ids and the pairing survives. When sanitization actually changed
1915
- * the id, a short hash of the ORIGINAL id is appended so distinct inputs
1916
- * that would collapse to the same replacement (`call.1` vs `call_1` vs
1917
- * `call:1`) stay distinct on the wire. Empty ids get a stable placeholder
1918
- * so the block stays well-formed.
1919
- */
1920
- const ANTHROPIC_TOOL_ID_INVALID = /[^\w-]/g;
1921
- function sanitizeAnthropicToolId(id) {
1922
- if (!id) return "tool_0";
1923
- const sanitized = id.replace(ANTHROPIC_TOOL_ID_INVALID, "_");
1924
- if (sanitized === id) return id;
1925
- return `${sanitized}_${fnv1aBase36(id)}`;
1926
- }
1927
- /**
1928
- * Encode zidane's canonical `string | ToolResultContent[]` shape to Anthropic's
1929
- * wire format for `tool_result.content`.
1930
- */
1931
- function encodeAnthropicToolResultContent(output) {
1932
- if (typeof output === "string") return output;
1933
- return output.map((b) => {
1934
- if (b.type === "text") return {
1935
- type: "text",
1936
- text: b.text
1937
- };
1938
- if (b.type === "document") {
1939
- assertResolvedMediaBlock(b, "Anthropic wire messages");
1940
- return {
1941
- type: "text",
1942
- text: documentMarker(b)
1943
- };
1944
- }
1945
- if (b.type === "audio") return {
1946
- type: "text",
1947
- text: `[audio omitted: ${b.mediaType}]`
1948
- };
1949
- if (b.type === "video") return {
1950
- type: "text",
1951
- text: `[video omitted: ${b.mediaType}]`
1952
- };
1953
- assertResolvedMediaBlock(b, "Anthropic wire messages");
1954
- return {
1955
- type: "image",
1956
- source: anthropicImageSource(b.mediaType, b.data)
1957
- };
1958
- });
1959
- }
1960
- function fromAnthropic(msg) {
1961
- const role = msg.role;
1962
- const content = [];
1963
- if (typeof msg.content === "string") {
1964
- content.push({
1965
- type: "text",
1966
- text: msg.content
1967
- });
1968
- return {
1969
- role,
1970
- content
1971
- };
1972
- }
1973
- if (Array.isArray(msg.content)) for (const block of msg.content) {
1974
- if (!block || typeof block !== "object") continue;
1975
- const b = block;
1976
- if (b.type === "text") content.push({
1977
- type: "text",
1978
- text: b.text
1979
- });
1980
- else if (b.type === "image") {
1981
- const source = b.source;
1982
- if (source?.type === "base64") content.push({
1983
- type: "image",
1984
- mediaType: source.media_type,
1985
- data: source.data
1986
- });
1987
- } else if (b.type === "document") {
1988
- const source = b.source;
1989
- if (source?.type === "base64" && typeof source.media_type === "string" && typeof source.data === "string") content.push({
1990
- type: "document",
1991
- mediaType: source.media_type,
1992
- data: source.data,
1993
- encoding: "base64",
1994
- ...typeof b.title === "string" ? { name: b.title } : {}
1995
- });
1996
- else if (source?.type === "text" && typeof source.media_type === "string" && typeof source.data === "string") content.push({
1997
- type: "document",
1998
- mediaType: source.media_type,
1999
- data: source.data,
2000
- encoding: "text",
2001
- ...typeof b.title === "string" ? { name: b.title } : {}
2002
- });
2003
- } else if (b.type === "tool_use") content.push({
2004
- type: "tool_call",
2005
- id: b.id,
2006
- name: b.name,
2007
- input: b.input
2008
- });
2009
- else if (b.type === "tool_result") {
2010
- const output = decodeAnthropicToolResultContent(b.content);
2011
- const block = {
2012
- type: "tool_result",
2013
- callId: b.tool_use_id,
2014
- output
2015
- };
2016
- if (b.is_error === true) block.isError = true;
2017
- content.push(block);
2018
- } else if (b.type === "thinking") {
2019
- const block = {
2020
- type: "thinking",
2021
- text: b.thinking ?? ""
2022
- };
2023
- if (typeof b.signature === "string") {
2024
- block.signature = b.signature;
2025
- block.signatureProducer = "anthropic";
2026
- }
2027
- content.push(block);
2028
- } else if (b.type === "redacted_thinking") content.push({
2029
- type: "redacted_thinking",
2030
- data: b.data ?? ""
2031
- });
2032
- else if (b.type === "server_tool_use") content.push({
2033
- type: "server_tool_use",
2034
- producer: "anthropic",
2035
- id: b.id ?? "",
2036
- name: b.name ?? "",
2037
- input: b.input ?? {}
2038
- });
2039
- else if (b.type === "web_search_tool_result") content.push({
2040
- type: "server_tool_result",
2041
- producer: "anthropic",
2042
- toolUseId: b.tool_use_id ?? "",
2043
- toolName: "web_search",
2044
- content: b.content
2045
- });
2046
- }
2047
- return {
2048
- role,
2049
- content
2050
- };
2051
- }
2052
- function fromOpenAI(msg) {
2053
- const role = msg.role;
2054
- const content = [];
2055
- const c = msg.content;
2056
- if (c == null) return {
2057
- role,
2058
- content
2059
- };
2060
- if (typeof c === "string") {
2061
- content.push({
2062
- type: "text",
2063
- text: c
2064
- });
2065
- return {
2066
- role,
2067
- content
2068
- };
2069
- }
2070
- if (typeof c === "object" && !Array.isArray(c) && c._tag === "__zidane_assistant_tc__") {
2071
- const tagged = c;
2072
- if (typeof tagged.text === "string" && tagged.text) content.push({
2073
- type: "text",
2074
- text: tagged.text
2075
- });
2076
- if (Array.isArray(tagged.tool_calls)) for (const raw of tagged.tool_calls) {
2077
- if (!raw || typeof raw !== "object") continue;
2078
- const tc = raw;
2079
- const rawArgs = tc.function?.arguments;
2080
- let input = {};
2081
- if (typeof rawArgs === "string") try {
2082
- input = JSON.parse(rawArgs);
2083
- } catch {
2084
- input = {};
2085
- }
2086
- else if (rawArgs && typeof rawArgs === "object") input = rawArgs;
2087
- content.push({
2088
- type: "tool_call",
2089
- id: tc.id ?? "",
2090
- name: tc.function?.name ?? "",
2091
- input
2092
- });
2093
- }
2094
- return {
2095
- role,
2096
- content
2097
- };
2098
- }
2099
- if (typeof c === "object" && !Array.isArray(c) && c._tag === "__zidane_tool_results__") {
2100
- const tagged = c;
2101
- if (Array.isArray(tagged.results)) for (const raw of tagged.results) {
2102
- if (!raw || typeof raw !== "object") continue;
2103
- const r = raw;
2104
- content.push({
2105
- type: "tool_result",
2106
- callId: r.tool_call_id ?? "",
2107
- output: r.content ?? ""
2108
- });
2109
- }
2110
- if (typeof tagged.text === "string" && tagged.text) content.push({
2111
- type: "text",
2112
- text: tagged.text
2113
- });
2114
- return {
2115
- role,
2116
- content
2117
- };
2118
- }
2119
- if (Array.isArray(c)) {
2120
- for (const block of c) {
2121
- if (!block || typeof block !== "object") continue;
2122
- const b = block;
2123
- if (b.type === "text") content.push({
2124
- type: "text",
2125
- text: b.text
2126
- });
2127
- else if (b.type === "image_url") {
2128
- const imageUrl = b.image_url?.url;
2129
- if (imageUrl?.startsWith("data:")) {
2130
- const [meta, data] = imageUrl.slice(5).split(",", 2);
2131
- const mediaType = meta.replace(";base64", "");
2132
- content.push({
2133
- type: "image",
2134
- mediaType,
2135
- data
2136
- });
2137
- }
2138
- }
2139
- }
2140
- return {
2141
- role,
2142
- content
2143
- };
2144
- }
2145
- return {
2146
- role,
2147
- content
2148
- };
2149
- }
2150
- function toAnthropic(msg) {
2151
- const blocks = msg.content.filter((b) => {
2152
- if (b.type === "provider_reasoning") return false;
2153
- if (b.type !== "thinking") return true;
2154
- if (b.signatureProducer === "openai") return false;
2155
- if (!b.signature) return false;
2156
- return true;
2157
- }).flatMap((block) => {
2158
- switch (block.type) {
2159
- case "text": return [{
2160
- type: "text",
2161
- text: block.text
2162
- }];
2163
- case "image":
2164
- assertResolvedMediaBlock(block, "Anthropic wire messages");
2165
- return [{
2166
- type: "image",
2167
- source: anthropicImageSource(block.mediaType, block.data)
2168
- }];
2169
- case "audio": return [{
2170
- type: "text",
2171
- text: `[audio omitted: ${block.mediaType}]`
2172
- }];
2173
- case "video": return [{
2174
- type: "text",
2175
- text: `[video omitted: ${block.mediaType}]`
2176
- }];
2177
- case "document":
2178
- assertResolvedMediaBlock(block, "Anthropic wire messages");
2179
- return anthropicDocumentBlocks(block);
2180
- case "tool_call": return [{
2181
- type: "tool_use",
2182
- id: sanitizeAnthropicToolId(block.id),
2183
- name: block.name,
2184
- input: block.input
2185
- }];
2186
- case "tool_result": {
2187
- const out = {
2188
- type: "tool_result",
2189
- tool_use_id: sanitizeAnthropicToolId(block.callId),
2190
- content: encodeAnthropicToolResultContent(block.output)
2191
- };
2192
- if (block.isError) out.is_error = true;
2193
- return [out, ...(typeof block.output === "string" ? [] : block.output.filter((part) => part.type === "document" && typeof part.data === "string")).flatMap(anthropicDocumentBlocks)];
2194
- }
2195
- case "thinking": return [{
2196
- type: "thinking",
2197
- thinking: block.text,
2198
- signature: block.signature ?? ""
2199
- }];
2200
- case "redacted_thinking": return [{
2201
- type: "redacted_thinking",
2202
- data: block.data
2203
- }];
2204
- case "server_tool_use": return [{
2205
- type: "server_tool_use",
2206
- id: sanitizeAnthropicToolId(block.id),
2207
- name: block.name,
2208
- input: block.input
2209
- }];
2210
- case "server_tool_result": return [{
2211
- type: "web_search_tool_result",
2212
- tool_use_id: sanitizeAnthropicToolId(block.toolUseId),
2213
- content: block.content
2214
- }];
2215
- default: return [{
2216
- type: "text",
2217
- text: ""
2218
- }];
2219
- }
2220
- });
2221
- const first = blocks[0];
2222
- if (blocks.length === 1 && first.type === "text") return {
2223
- role: msg.role,
2224
- content: first.text
2225
- };
2226
- return {
2227
- role: msg.role,
2228
- content: blocks
2229
- };
2230
- }
2231
- function toOpenAI(msg) {
2232
- const toolCalls = msg.content.filter((b) => b.type === "tool_call");
2233
- const toolResults = msg.content.filter((b) => b.type === "tool_result");
2234
- const textBlocks = msg.content.filter((b) => b.type === "text");
2235
- const imageBlocks = msg.content.filter((b) => b.type === "image");
2236
- const audioBlocks = msg.content.filter((b) => b.type === "audio");
2237
- const videoBlocks = msg.content.filter((b) => b.type === "video");
2238
- const documentBlocks = msg.content.filter((b) => b.type === "document");
2239
- if (toolResults.length > 0) {
2240
- const siblingText = textBlocks.map((b) => b.text).join("\n");
2241
- return {
2242
- role: msg.role,
2243
- content: {
2244
- _tag: TOOL_RESULTS_TAG,
2245
- results: toolResults.map((tr) => ({
2246
- tool_call_id: tr.callId,
2247
- content: tr.output
2248
- })),
2249
- ...siblingText ? { text: siblingText } : {}
2250
- }
2251
- };
2252
- }
2253
- if (toolCalls.length > 0) {
2254
- const textContent = textBlocks.length > 0 ? textBlocks[0].text : null;
2255
- return {
2256
- role: msg.role,
2257
- content: {
2258
- _tag: ASSISTANT_TOOL_CALLS_TAG,
2259
- text: textContent,
2260
- tool_calls: toolCalls.map((tc) => ({
2261
- id: tc.id,
2262
- type: "function",
2263
- function: {
2264
- name: tc.name,
2265
- arguments: JSON.stringify(tc.input)
2266
- }
2267
- }))
2268
- }
2269
- };
2270
- }
2271
- if (imageBlocks.length > 0 || audioBlocks.length > 0 || videoBlocks.length > 0 || documentBlocks.length > 0) {
2272
- const parts = [];
2273
- for (const img of imageBlocks) {
2274
- assertResolvedMediaBlock(img, "OpenAI-compatible wire messages");
2275
- parts.push({
2276
- type: "image_url",
2277
- image_url: { url: `data:${img.mediaType};base64,${img.data}` }
2278
- });
2279
- }
2280
- for (const b of audioBlocks) parts.push({
2281
- type: "input_audio",
2282
- input_audio: {
2283
- data: b.data,
2284
- format: audioFormatFromMediaType(b.mediaType)
2285
- }
2286
- });
2287
- for (const b of videoBlocks) parts.push({
2288
- type: "video_url",
2289
- video_url: { url: `data:${b.mediaType};base64,${b.data}` }
2290
- });
2291
- for (const b of documentBlocks) {
2292
- assertResolvedMediaBlock(b, "OpenAI-compatible wire messages");
2293
- parts.push({
2294
- type: "text",
2295
- text: documentMarker(b)
2296
- });
2297
- }
2298
- for (const b of textBlocks) parts.push({
2299
- type: "text",
2300
- text: b.text
2301
- });
2302
- return {
2303
- role: msg.role,
2304
- content: parts
2305
- };
2306
- }
2307
- if (textBlocks.length === 1) return {
2308
- role: msg.role,
2309
- content: textBlocks[0].text
2310
- };
2311
- if (textBlocks.length > 1) return {
2312
- role: msg.role,
2313
- content: textBlocks.map((b) => ({
2314
- type: "text",
2315
- text: b.text
2316
- }))
2317
- };
2318
- return {
2319
- role: msg.role,
2320
- content: null
2321
- };
2322
- }
2323
- /**
2324
- * Placeholder content inserted into a synthetic `tool_result` when the harness
2325
- * has to repair an orphan `tool_use`. Exported so downstream consumers
2326
- * (training-data collectors, HFI submission) can reject any payload containing
2327
- * it — the marker satisfies the wire-level pairing contract structurally but
2328
- * the content itself is fake and would poison fine-tuning data.
2329
- */
2330
- const SYNTHETIC_TOOL_RESULT_PLACEHOLDER = "[Tool result missing due to internal error]";
2331
- /**
2332
- * Replacement text for an assistant message whose every content block was
2333
- * stripped during pairing repair (e.g. the only blocks were orphan
2334
- * `tool_call`s). Providers reject empty `content` arrays — the marker keeps
2335
- * the turn shape valid while signaling "this assistant turn lost its
2336
- * outputs" to the model so it can recover.
2337
- */
2338
- const TOOL_USE_INTERRUPTED_MARKER = "[Tool use interrupted]";
2339
- /**
2340
- * Replacement text for a user message whose every content block was a
2341
- * `tool_result` with no matching upstream `tool_call` (e.g. the assistant
2342
- * pair was stripped by an earlier compaction). Same role as
2343
- * {@link TOOL_USE_INTERRUPTED_MARKER} on the user side.
2344
- */
2345
- const ORPHANED_TOOL_RESULT_MARKER = "[Orphaned tool result removed due to conversation resume]";
2346
- /**
2347
- * Defensive repair pass that rewrites a message list so it satisfies the
2348
- * wire-level `tool_use` ↔ `tool_result` adjacency contract every modern
2349
- * provider enforces.
2350
- *
2351
- * Anthropic 400s on orphans with `'tool_use' ids were found without
2352
- * 'tool_result' blocks immediately after` (and its inverse, `tool_result
2353
- * must be preceded by a tool_call with the same toolCallId`). OpenAI's
2354
- * Chat Completions API rejects most mismatches with a 400 too.
2355
- *
2356
- * Six repair modes — modeled after Anthropic's Claude Code defenses:
2357
- *
2358
- * | # | Corruption | Repair |
2359
- * |---|------------|--------|
2360
- * | 1 | assistant `tool_use` whose next user msg lacks a matching `tool_result` | **Prepend** a synthetic `tool_result` block carrying {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} with `isError: true` |
2361
- * | 2 | assistant `tool_use` followed by nothing (or by a non-user msg) | **Insert** a new synthetic user message with the same placeholder |
2362
- * | 3 | user `tool_result` with no preceding assistant `tool_call` | **Strip** the orphan block; if it empties the msg, replace with {@link ORPHANED_TOOL_RESULT_MARKER} text block |
2363
- * | 4 | duplicate `tool_call.id` across assistant messages (CC-1212) | **Strip** later instances + their matching tool_results |
2364
- * | 5 | duplicate `tool_result.callId` within a single user message | **Dedupe** by `callId`, keep first |
2365
- * | 6 | assistant message emptied by mode-4 stripping | **Replace** content with {@link TOOL_USE_INTERRUPTED_MARKER} text block |
2366
- *
2367
- * **Repair, not drop.** Earlier versions of this pass simply dropped orphan
2368
- * blocks, which (a) silently rewrote history the model had reasoned over and
2369
- * (b) cascaded — dropping an assistant tool_call would orphan its
2370
- * tool_result, which would then get dropped too, removing any trace of
2371
- * "what the model tried to do" from the transcript. The repair-based pass
2372
- * preserves the model's tool_use shape and patches the dangling result with
2373
- * an `is_error` placeholder so the model sees "I tried X, the result was
2374
- * lost" and can retry intelligently.
2375
- *
2376
- * Adjacency contract: `tool_result` blocks must live in the user message
2377
- * IMMEDIATELY following the assistant message that emitted the matching
2378
- * `tool_use`. The pass enforces strict adjacency — a tool_result two
2379
- * messages downstream of its tool_call is still an orphan.
2380
- *
2381
- * Idempotent: returns the input reference unchanged when no repairs were
2382
- * necessary. Re-running on already-repaired output is a no-op.
2383
- *
2384
- * Pure: does not mutate input messages or their content arrays — every
2385
- * repair allocates a fresh array / object.
2386
- */
2387
- function ensureToolResultPairing(messages, options = {}) {
2388
- if (messages.length === 0) return messages;
2389
- const fireRepair = options.onRepair ?? (() => {});
2390
- const firstSeenAt = /* @__PURE__ */ new Map();
2391
- for (let i = 0; i < messages.length; i++) {
2392
- const msg = messages[i];
2393
- if (msg.role !== "assistant") continue;
2394
- for (const block of msg.content) if (block.type === "tool_call" && !firstSeenAt.has(block.id)) firstSeenAt.set(block.id, i);
2395
- }
2396
- const pendingOrphansByUserIndex = /* @__PURE__ */ new Map();
2397
- let changed = false;
2398
- const markChanged = () => {
2399
- changed = true;
2400
- };
2401
- const out = [];
2402
- for (let i = 0; i < messages.length; i++) {
2403
- const msg = messages[i];
2404
- if (msg.role === "assistant") processAssistantMessage({
2405
- index: i,
2406
- msg,
2407
- messages,
2408
- firstSeenAt,
2409
- pendingOrphansByUserIndex,
2410
- out,
2411
- fireRepair,
2412
- markChanged
2413
- });
2414
- else processUserMessage({
2415
- index: i,
2416
- msg,
2417
- pendingOrphansByUserIndex,
2418
- out,
2419
- fireRepair,
2420
- markChanged
2421
- });
2422
- }
2423
- return changed ? out : messages;
2424
- }
2425
- /**
2426
- * Process one assistant message: strip mode-4 duplicates, queue mode-1 / 2
2427
- * orphan-tool-use repairs for the next user message (or insert a synthetic
2428
- * one), and emit a mode-6 marker when dedup empties the message.
2429
- */
2430
- function processAssistantMessage(args) {
2431
- const { index, msg, messages, firstSeenAt, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args;
2432
- const dedupedContent = [];
2433
- const seenHere = /* @__PURE__ */ new Set();
2434
- let dedupedChanged = false;
2435
- for (const block of msg.content) {
2436
- if (block.type === "tool_call") {
2437
- const isCrossMsgDup = firstSeenAt.get(block.id) !== index;
2438
- const isIntraMsgDup = seenHere.has(block.id);
2439
- if (isCrossMsgDup || isIntraMsgDup) {
2440
- dedupedChanged = true;
2441
- fireRepair({
2442
- mode: "duplicate-tool-use-strip",
2443
- callId: block.id,
2444
- messageIndex: index
2445
- });
2446
- continue;
2447
- }
2448
- seenHere.add(block.id);
2449
- }
2450
- dedupedContent.push(block);
2451
- }
2452
- const surviving = collectIds(dedupedContent, "tool_call", (b) => b.id);
2453
- const next = messages[index + 1];
2454
- const nextIsUser = next && next.role === "user";
2455
- const nextResultIds = nextIsUser ? collectResultIds(next.content) : /* @__PURE__ */ new Set();
2456
- const orphans = [];
2457
- for (const id of surviving) if (!nextResultIds.has(id)) orphans.push(id);
2458
- emitAssistantAfterDedup({
2459
- index,
2460
- msg,
2461
- dedupedContent,
2462
- dedupedChanged,
2463
- out,
2464
- fireRepair,
2465
- markChanged
2466
- });
2467
- if (orphans.length === 0) return;
2468
- if (nextIsUser) {
2469
- markChanged();
2470
- pendingOrphansByUserIndex.set(index + 1, orphans);
2471
- for (const callId of orphans) fireRepair({
2472
- mode: "orphan-tool-use-prepend",
2473
- callId,
2474
- messageIndex: index
2475
- });
2476
- return;
2477
- }
2478
- markChanged();
2479
- out.push({
2480
- role: "user",
2481
- content: orphans.map((callId) => syntheticResultBlock(callId))
2482
- });
2483
- for (const callId of orphans) fireRepair({
2484
- mode: "orphan-tool-use-append",
2485
- callId,
2486
- messageIndex: index
2487
- });
2488
- }
2489
- function emitAssistantAfterDedup(args) {
2490
- const { index, msg, dedupedContent, dedupedChanged, out, fireRepair, markChanged } = args;
2491
- if (!dedupedChanged) {
2492
- out.push(msg);
2493
- return;
2494
- }
2495
- markChanged();
2496
- if (dedupedContent.length === 0) {
2497
- out.push({
2498
- ...msg,
2499
- content: [{
2500
- type: "text",
2501
- text: TOOL_USE_INTERRUPTED_MARKER
2502
- }]
2503
- });
2504
- fireRepair({
2505
- mode: "empty-assistant-marker",
2506
- messageIndex: index
2507
- });
2508
- return;
2509
- }
2510
- out.push({
2511
- ...msg,
2512
- content: dedupedContent
2513
- });
2514
- }
2515
- /**
2516
- * Process one user message: dedup mode-5 duplicate tool_results, strip
2517
- * mode-3 orphans whose tool_call is missing from the previously-emitted
2518
- * assistant message, and prepend any synthetic results queued by the prior
2519
- * assistant's mode-1 repair.
2520
- */
2521
- function processUserMessage(args) {
2522
- const { index, msg, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args;
2523
- const queuedOrphans = pendingOrphansByUserIndex.get(index);
2524
- pendingOrphansByUserIndex.delete(index);
2525
- const prev = out.length > 0 ? out[out.length - 1] : null;
2526
- const validIds = prev && prev.role === "assistant" ? collectIds(prev.content, "tool_call", (b) => b.id) : /* @__PURE__ */ new Set();
2527
- const seenCallIds = /* @__PURE__ */ new Set();
2528
- let modifiedHere = false;
2529
- const afterDedup = [];
2530
- for (const block of msg.content) {
2531
- if (block.type !== "tool_result") {
2532
- afterDedup.push(block);
2533
- continue;
2534
- }
2535
- if (seenCallIds.has(block.callId)) {
2536
- modifiedHere = true;
2537
- fireRepair({
2538
- mode: "duplicate-tool-result-strip",
2539
- callId: block.callId,
2540
- messageIndex: index
2541
- });
2542
- continue;
2543
- }
2544
- seenCallIds.add(block.callId);
2545
- afterDedup.push(block);
2546
- }
2547
- const afterStrip = [];
2548
- for (const block of afterDedup) {
2549
- if (block.type === "tool_result" && !validIds.has(block.callId)) {
2550
- modifiedHere = true;
2551
- fireRepair({
2552
- mode: "orphan-tool-result-strip",
2553
- callId: block.callId,
2554
- messageIndex: index
2555
- });
2556
- continue;
2557
- }
2558
- afterStrip.push(block);
2559
- }
2560
- const finalContent = queuedOrphans ? [...queuedOrphans.map(syntheticResultBlock), ...afterStrip] : afterStrip;
2561
- if (!modifiedHere && queuedOrphans === void 0) {
2562
- out.push(msg);
2563
- return;
2564
- }
2565
- markChanged();
2566
- if (finalContent.length === 0) {
2567
- out.push({
2568
- ...msg,
2569
- content: [{
2570
- type: "text",
2571
- text: ORPHANED_TOOL_RESULT_MARKER
2572
- }]
2573
- });
2574
- return;
2575
- }
2576
- out.push({
2577
- ...msg,
2578
- content: finalContent
2579
- });
2580
- }
2581
- /**
2582
- * Re-mint tool-call ids that collide with ids already present in the
2583
- * transcript (or with an earlier sibling in the same turn), BEFORE the turn
2584
- * is persisted and its tools dispatched.
2585
- *
2586
- * Why this exists: some providers/serving stacks emit non-globally-unique
2587
- * tool_use ids (per-turn counters like `call_0`, retry-replay duplicates,
2588
- * Kimi-K2.6 on Fireworks). The wire-time pairing pass
2589
- * ({@link ensureToolResultPairing}) can only STRIP such duplicates — which
2590
- * erases the model's tool calls and results from the wire view, stalls
2591
- * progress, and re-accumulates repairs quadratically because the persisted
2592
- * session keeps the corrupted ids. Re-minting at persist time keeps the
2593
- * pairing intact instead: the call, its execution, and its tool_result all
2594
- * carry the fresh id, so the pairing pass has nothing to strip on any later
2595
- * turn.
2596
- *
2597
- * Fresh ids are `${oldId}_r{n}` with `n` bumped until unique — deterministic
2598
- * shape, Anthropic-wire-safe charset (given a safe input id), and visibly
2599
- * traceable back to the original.
2600
- *
2601
- * Alignment contract: `toolCalls` and the `tool_call` blocks inside
2602
- * `content` are two views of the same emission and MUST be in the same
2603
- * order (true for every provider — both are built from one source array).
2604
- * Occurrences are matched positionally per old id so intra-turn duplicates
2605
- * map to distinct fresh ids on both views.
2606
- *
2607
- * Pure: returns the input references unchanged when no id collides.
2608
- */
2609
- function remintDuplicateToolCallIds(toolCalls, content, priorIds) {
2610
- const seen = new Set(priorIds);
2611
- const reminted = [];
2612
- const finalIdQueues = /* @__PURE__ */ new Map();
2613
- let changed = false;
2614
- const outCalls = toolCalls.map((tc) => {
2615
- let finalId = tc.id;
2616
- if (seen.has(tc.id)) {
2617
- let n = 1;
2618
- while (seen.has(`${tc.id}_r${n}`)) n++;
2619
- finalId = `${tc.id}_r${n}`;
2620
- reminted.push({
2621
- oldId: tc.id,
2622
- newId: finalId
2623
- });
2624
- changed = true;
2625
- }
2626
- seen.add(finalId);
2627
- const queue = finalIdQueues.get(tc.id);
2628
- if (queue) queue.push(finalId);
2629
- else finalIdQueues.set(tc.id, [finalId]);
2630
- return finalId === tc.id ? tc : {
2631
- ...tc,
2632
- id: finalId
2633
- };
2634
- });
2635
- if (!changed) return {
2636
- toolCalls,
2637
- content,
2638
- reminted
2639
- };
2640
- return {
2641
- toolCalls: outCalls,
2642
- content: content.map((block) => {
2643
- if (block.type !== "tool_call") return block;
2644
- const finalId = finalIdQueues.get(block.id)?.shift();
2645
- if (finalId === void 0 || finalId === block.id) return block;
2646
- return {
2647
- ...block,
2648
- id: finalId
2649
- };
2650
- }),
2651
- reminted
2652
- };
2653
- }
2654
- function syntheticResultBlock(callId) {
2655
- return {
2656
- type: "tool_result",
2657
- callId,
2658
- output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,
2659
- isError: true
2660
- };
2661
- }
2662
- function collectResultIds(content) {
2663
- const ids = /* @__PURE__ */ new Set();
2664
- for (const block of content) if (block.type === "tool_result") ids.add(block.callId);
2665
- return ids;
2666
- }
2667
- function collectIds(content, type, getId) {
2668
- const ids = /* @__PURE__ */ new Set();
2669
- for (const block of content) if (block.type === type) ids.add(getId(block));
2670
- return ids;
2671
- }
2672
- /**
2673
- * Drop ASSISTANT turns whose every `tool_call` block is unresolved
2674
- * (no matching `tool_result` block anywhere later in the transcript).
2675
- * Tool_call blocks with at least one matching tool_result are kept — modes
2676
- * 1/3 of {@link ensureToolResultPairing} handle the partial-pair case at
2677
- * wire-send time.
2678
- *
2679
- * Use case: session resume. A turn that emitted three `tool_use` blocks but
2680
- * never persisted the matching `tool_result` turn (process death, crash,
2681
- * `kill -9`) leaves the transcript with an orphan that Anthropic rejects on
2682
- * the FIRST API call after reload. Dropping the whole assistant turn — not
2683
- * just the orphan blocks — preserves text-only turns that legitimately
2684
- * carry reasoning the model wants to see again.
2685
- *
2686
- * Does NOT mint fresh ids: re-id'ing on every resume would cause
2687
- * exponential transcript growth across repeated resumes of an interrupted
2688
- * session.
2689
- *
2690
- * Pure: returns the input reference unchanged when no turn was dropped.
2691
- */
2692
- function filterUnresolvedToolUses(turns) {
2693
- if (turns.length === 0) return turns;
2694
- const resolvedIds = /* @__PURE__ */ new Set();
2695
- for (const turn of turns) for (const block of turn.content) if (block.type === "tool_result") resolvedIds.add(block.callId);
2696
- let changed = false;
2697
- const out = [];
2698
- for (const turn of turns) {
2699
- if (turn.role !== "assistant") {
2700
- out.push(turn);
2701
- continue;
2702
- }
2703
- const toolCalls = turn.content.filter((b) => b.type === "tool_call");
2704
- if (toolCalls.length === 0) {
2705
- out.push(turn);
2706
- continue;
2707
- }
2708
- if (toolCalls.every((b) => b.type === "tool_call" && !resolvedIds.has(b.id))) {
2709
- changed = true;
2710
- continue;
2711
- }
2712
- out.push(turn);
2713
- }
2714
- return changed ? out : turns;
2715
- }
2716
- /**
2717
- * Inspect a session's trailing turn to classify whether the run was
2718
- * interrupted mid-tool-call. Pure / synchronous — does not modify the input.
2719
- *
2720
- * Pair with {@link filterUnresolvedToolUses} on the resume path: filter
2721
- * first so the result reflects the post-cleanup state (a turn whose orphans
2722
- * were stripped reads as 'completed', not 'interrupted').
2723
- */
2724
- function detectTurnInterruption(turns) {
2725
- if (turns.length === 0) return "clean";
2726
- const last = turns[turns.length - 1];
2727
- if (last.role === "user") return "clean";
2728
- if (last.role !== "assistant") return "clean";
2729
- const resolvedIds = /* @__PURE__ */ new Set();
2730
- for (const turn of turns) for (const block of turn.content) if (block.type === "tool_result") resolvedIds.add(block.callId);
2731
- for (const block of last.content) if (block.type === "tool_call" && !resolvedIds.has(block.id)) return "interrupted";
2732
- return "completed";
2733
- }
2734
- /**
2735
- * Default directive injected by {@link ensureEndsWithUserMessage} when the
2736
- * caller doesn't pass one. Neutral enough to read sensibly in any context —
2737
- * compaction passes its own directive, the agent loop falls back to this.
2738
- *
2739
- * The trailing newline is omitted on purpose; provider `userMessage()`
2740
- * helpers wrap the content in the right block shape and any trailing
2741
- * whitespace would land in the wire payload as-is.
2742
- */
2743
- const DEFAULT_USER_TAIL_DIRECTIVE = "Continue.";
2744
- /**
2745
- * Guarantee a message list ends with a `role: 'user'` message, appending a
2746
- * synthetic directive when it doesn't.
2747
- *
2748
- * Why: Anthropic's API (and several OpenAI-compat reasoning models — opus
2749
- * 4.7, the o-series translation layers on Bedrock/Vertex) rejects requests
2750
- * whose final message is `role: 'assistant'` with
2751
- * "This model does not support assistant message prefill. The conversation
2752
- * must end with a user message."
2753
- *
2754
- * Triggers in zidane:
2755
- * - Compaction over a session whose last turn is the agent's response
2756
- * (the common case — autocompact fires right after `agent.run()`).
2757
- * - Schema enforcement after an assistant text turn (no `tool_use`, so
2758
- * `ensureToolResultPairing` doesn't synthesize a user tail).
2759
- * - `context:transform` hooks that strip the trailing user turn.
2760
- *
2761
- * The pair-repair pass {@link ensureToolResultPairing} already covers the
2762
- * orphan-`tool_use` case via mode 2; this helper closes the plain-text
2763
- * assistant-tail case. They compose: run pair-repair first, then this
2764
- * pass, so an assistant turn with orphan tool_uses gets its synthetic
2765
- * `tool_result` user turn AND this pass is a no-op (the synthetic turn
2766
- * is already user-role).
2767
- *
2768
- * Empty input passes through unchanged — the caller's "nothing to send"
2769
- * guard is responsible for that case; we don't materialize a conversation
2770
- * out of an empty list.
2771
- *
2772
- * Idempotent: returns the input reference unchanged when the list is
2773
- * already user-terminated (or empty).
2774
- *
2775
- * @param messages Wire-ready message list.
2776
- * @param provider Provider whose `userMessage()` shape we use so the
2777
- * synthetic turn matches the wire format the rest of
2778
- * the list already follows.
2779
- * @param directive Text to put in the synthetic user message. Defaults
2780
- * to {@link DEFAULT_USER_TAIL_DIRECTIVE} ("Continue.").
2781
- * Compaction overrides this with a summary-specific cue.
2782
- */
2783
- function ensureEndsWithUserMessage(messages, provider, directive = DEFAULT_USER_TAIL_DIRECTIVE) {
2784
- if (messages.length === 0) return messages;
2785
- if (messages[messages.length - 1].role === "user") return messages;
2786
- return [...messages, provider.userMessage(directive)];
2787
- }
2788
- async function resolveRefContentBlock(block, resolveContentRef) {
2789
- if (isContentRefBlock(block)) {
2790
- if (!resolveContentRef) throw unresolvedContentRefError(block);
2791
- const { data } = await resolveContentRef({
2792
- type: block.type,
2793
- ref: block.ref,
2794
- mediaType: block.mediaType,
2795
- ...block.type === "document" ? { encoding: block.encoding } : {},
2796
- ...block.name ? { name: block.name } : {}
2797
- });
2798
- if (block.type === "image") return {
2799
- block: {
2800
- type: "image",
2801
- mediaType: block.mediaType,
2802
- data,
2803
- ...block.name ? { name: block.name } : {}
2804
- },
2805
- changed: true
2806
- };
2807
- return {
2808
- block: {
2809
- type: "document",
2810
- mediaType: block.mediaType,
2811
- encoding: block.encoding,
2812
- data,
2813
- ...block.name ? { name: block.name } : {}
2814
- },
2815
- changed: true
2816
- };
2817
- }
2818
- if (block.type !== "tool_result" || typeof block.output === "string") return {
2819
- block,
2820
- changed: false
2821
- };
2822
- const resolved = await resolveToolResultContent(block.output, resolveContentRef);
2823
- if (!resolved.changed) return {
2824
- block,
2825
- changed: false
2826
- };
2827
- return {
2828
- block: {
2829
- ...block,
2830
- output: resolved.output
2831
- },
2832
- changed: true
2833
- };
2834
- }
2835
- async function resolveToolResultContent(output, resolveContentRef) {
2836
- let changed = false;
2837
- const next = [];
2838
- for (const part of output) {
2839
- if (!isContentRefBlock(part)) {
2840
- next.push(part);
2841
- continue;
2842
- }
2843
- if (!resolveContentRef) throw unresolvedContentRefError(part);
2844
- const { data } = await resolveContentRef({
2845
- type: part.type,
2846
- ref: part.ref,
2847
- mediaType: part.mediaType,
2848
- ...part.type === "document" ? { encoding: part.encoding } : {},
2849
- ...part.name ? { name: part.name } : {}
2850
- });
2851
- changed = true;
2852
- if (part.type === "image") next.push({
2853
- type: "image",
2854
- mediaType: part.mediaType,
2855
- data,
2856
- ...part.name ? { name: part.name } : {}
2857
- });
2858
- else next.push({
2859
- type: "document",
2860
- mediaType: part.mediaType,
2861
- encoding: part.encoding,
2862
- data,
2863
- ...part.name ? { name: part.name } : {}
2864
- });
2865
- }
2866
- return {
2867
- output: changed ? next : output,
2868
- changed
2869
- };
2870
- }
2871
- /**
2872
- * Resolve image/document ref blocks into data blocks for provider wire use.
2873
- * This is intentionally wire-only: callers must never persist the returned
2874
- * messages back into `session.turns`.
2875
- *
2876
- * Returns the input array reference unchanged when no block carried a ref —
2877
- * the common case (most turns hold no refs, and re-running on already-resolved
2878
- * output is a no-op), so the multi-pass wire pipeline doesn't pay per-message
2879
- * copies for nothing.
2880
- */
2881
- async function resolveContentRefsInMessages(messages, options = {}) {
2882
- let anyChanged = false;
2883
- const out = [];
2884
- for (const msg of messages) {
2885
- let changed = false;
2886
- const content = [];
2887
- for (const block of msg.content) {
2888
- const resolved = await resolveRefContentBlock(block, options.resolveContentRef);
2889
- if (resolved.changed) changed = true;
2890
- content.push(resolved.block);
2891
- }
2892
- if (changed) anyChanged = true;
2893
- out.push(changed ? {
2894
- ...msg,
2895
- content
2896
- } : msg);
2897
- }
2898
- return anyChanged ? out : messages;
2899
- }
2900
- function finalizeWireMessages(messages, options) {
2901
- const paired = ensureToolResultPairing(messages, options.onRepair ? { onRepair: options.onRepair } : {});
2902
- if (!options.provider) return paired;
2903
- return ensureEndsWithUserMessage(paired, options.provider, options.userTailDirective);
2904
- }
2905
- /**
2906
- * Build a wire-ready `SessionMessage[]` from raw persisted `SessionTurn[]`.
2907
- *
2908
- * **The canonical "I want to send these to a provider" projection.** Use
2909
- * this — not raw `session.turns` / `store.getTurns()` — when constructing a
2910
- * provider request outside `agent.run()`. The agent's own wire pipeline
2911
- * (`executeTurn` in `src/loop.ts`) applies the same repair just-in-time;
2912
- * downstream consumers that bypass the loop hit `tool_result must be
2913
- * preceded by a tool_call` 400s precisely because they shipped raw turns.
2914
- *
2915
- * Pipeline:
2916
- * 1. Filter `system`-role turns (the wire only carries user/assistant).
2917
- * 2. {@link ensureToolResultPairing} — repairs all six orphan / dup
2918
- * corruption modes by inserting synthetic placeholders rather than
2919
- * dropping (preserves the model's tool_use shape).
2920
- * 3. {@link ensureEndsWithUserMessage} when `options.provider` is set —
2921
- * guards against assistant-tail prefill rejection (opus 4.7, o-series).
2922
- *
2923
- * Pure + idempotent. Does not mutate the input turns. Re-running on the
2924
- * output is a no-op.
2925
- *
2926
- * **Do not feed the result back into `session.setTurns` / `appendTurns`.**
2927
- * The repair can insert `SYNTHETIC_TOOL_RESULT_PLACEHOLDER` / `[Orphaned
2928
- * tool result removed …]` markers; round-tripping those into persisted
2929
- * state contaminates the session's reasoning history and defeats the
2930
- * "don't rewrite the past" invariant the wire-only repair was designed
2931
- * around.
2932
- */
2933
- async function toWireMessages(turns, options = {}) {
2934
- const messages = [];
2935
- for (const t of turns) {
2936
- if (t.role === "system") continue;
2937
- messages.push({
2938
- role: t.role,
2939
- content: t.content
2940
- });
2941
- }
2942
- return finalizeWireMessages(await resolveContentRefsInMessages(messages, { resolveContentRef: options.resolveContentRef }), options);
2943
- }
2944
- function autoDetectAndConvert(msg) {
2945
- const c = msg.content;
2946
- if (c && typeof c === "object" && !Array.isArray(c)) {
2947
- const tag = c._tag;
2948
- if (typeof tag === "string" && tag.startsWith("__zidane_")) return fromOpenAI(msg);
2949
- }
2950
- if (Array.isArray(c)) {
2951
- for (const block of c) {
2952
- if (!block || typeof block !== "object") continue;
2953
- const b = block;
2954
- if (b.type === "tool_use" || b.type === "tool_result" && "tool_use_id" in b) return fromAnthropic(msg);
2955
- if (b.type === "image_url") return fromOpenAI(msg);
2956
- }
2957
- return fromAnthropic(msg);
2958
- }
2959
- if (typeof c === "string") return fromAnthropic(msg);
2960
- return fromAnthropic(msg);
2961
- }
2962
- //#endregion
2963
- export { joinSystemPrompt as A, sanitizeToolSchema as C, appendDynamicSection as D, SYSTEM_PROMPT_BOUNDARY as E, canonicalizePrompt as F, defaultPromptMessage as I, unsupportedMediaError as L, replaceDynamicSection as M, splitSystemPrompt as N, appendStaticSection as O, buildPromptMessage as P, userMessage as S, fillEstimatedCost as T, assistantMessage as _, detectTurnInterruption as a, openaiCompat as b, filterUnresolvedToolUses as c, remintDuplicateToolCallIds as d, resolveContentRefsInMessages as f, OpenAICompatHttpError as g, toWireMessages as h, autoDetectAndConvert as i, renderSystemForWire as j, hasSystemPromptBoundary as k, fromAnthropic as l, toOpenAI as m, SYNTHETIC_TOOL_RESULT_PLACEHOLDER as n, ensureEndsWithUserMessage as o, toAnthropic as p, TOOL_USE_INTERRUPTED_MARKER as r, ensureToolResultPairing as s, ORPHANED_TOOL_RESULT_MARKER as t, fromOpenAI as u, classifyOpenAICompatError as v, sanitizeToolSpecs as w, toolResultsMessage as x, mapOAIFinishReason as y };
1611
+ export { defaultPromptMessage as C, canonicalizePrompt as S, userMessage as _, applyOAICacheBreakpoints as a, fillEstimatedCost as b, buildAssistantContent as c, formatTools as d, mapOAIFinishReason as f, toolResultsMessage as g, toOAIMessages as h, TOOL_RESULTS_TAG as i, classifyOpenAICompatError as l, openaiCompat as m, OpenAICompatHttpError as n, applyOAIToolCacheBreakpoint as o, openAICompatPromptMessage as p, OpenAICompatStreamError as r, assistantMessage as s, ASSISTANT_TOOL_CALLS_TAG as t, consumeSSE as u, sanitizeToolSchema as v, unsupportedMediaError as w, buildPromptMessage as x, sanitizeToolSpecs as y };
2964
1612
 
2965
- //# sourceMappingURL=messages-Iy_vEycX.js.map
1613
+ //# sourceMappingURL=openai-compat-0Y7jcncn.js.map