zidane 7.2.5 → 8.0.4
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/LICENSE +21 -0
- package/README.md +34 -6
- package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
- package/dist/acp-DQDhi2dj.js.map +1 -0
- package/dist/acp-cli.d.ts +1 -1
- package/dist/acp-cli.js +9 -66
- package/dist/acp-cli.js.map +1 -1
- package/dist/acp.d.ts +10 -590
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +3 -3
- package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
- package/dist/agent-DSLNmBp0.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +3 -2
- package/dist/aliasing-lVFbm81n.js +217 -0
- package/dist/aliasing-lVFbm81n.js.map +1 -0
- package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
- package/dist/anthropic-CD00LhUP.js.map +1 -0
- package/dist/artifacts-CV2hUKnL.js.map +1 -1
- package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
- package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
- package/dist/call-tool-dHlbGOIY.js +70 -0
- package/dist/call-tool-dHlbGOIY.js.map +1 -0
- package/dist/chat/pure.d.ts +372 -6
- package/dist/chat/pure.d.ts.map +1 -1
- package/dist/chat/pure.js +35 -244
- package/dist/chat/pure.js.map +1 -1
- package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
- package/dist/chat-JPJJ8uu5.js.map +1 -0
- package/dist/chat.d.ts +4 -6
- package/dist/chat.js +23 -12
- package/dist/cli-shared-C-YCaglL.js +159 -0
- package/dist/cli-shared-C-YCaglL.js.map +1 -0
- package/dist/client-core-BQSyDALT.d.ts +2115 -0
- package/dist/client-core-BQSyDALT.d.ts.map +1 -0
- package/dist/codemode-worker.d.ts +1 -0
- package/dist/codemode-worker.js +155 -0
- package/dist/codemode-worker.js.map +1 -0
- package/dist/codemode.d.ts +352 -0
- package/dist/codemode.d.ts.map +1 -0
- package/dist/codemode.js +5 -0
- package/dist/connection-Dr-Js4yZ.d.ts +28 -0
- package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
- package/dist/connection-loop-gdh22LTI.js +112 -0
- package/dist/connection-loop-gdh22LTI.js.map +1 -0
- package/dist/context-segments-BHKlagL6.js +1016 -0
- package/dist/context-segments-BHKlagL6.js.map +1 -0
- package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
- package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +19 -8
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +1 -440
- package/dist/contexts/docker.d.ts +36 -7
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +1 -456
- package/dist/contexts/e2b.d.ts +68 -13
- package/dist/contexts/e2b.d.ts.map +1 -1
- package/dist/contexts/e2b.js +1 -382
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/contexts/sandbox.js +5 -0
- package/dist/contexts/sandbox.js.map +1 -1
- package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
- package/dist/contexts-d7UolYot.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/cred-keys-GpBg71cP.js +105 -0
- package/dist/cred-keys-GpBg71cP.js.map +1 -0
- package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
- package/dist/credentials-JttEuRON.js.map +1 -0
- package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
- package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
- package/dist/daemon-cli.d.ts +1 -1
- package/dist/daemon-cli.js +3 -2
- package/dist/daemon-cli.js.map +1 -1
- package/dist/daytona-DG3omipZ.js +513 -0
- package/dist/daytona-DG3omipZ.js.map +1 -0
- package/dist/defaults-Cx3zvDOV.js +23 -0
- package/dist/defaults-Cx3zvDOV.js.map +1 -0
- package/dist/dispatcher-BMlu2BmZ.js +4941 -0
- package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
- package/dist/docker-BZhMMJUp.js +727 -0
- package/dist/docker-BZhMMJUp.js.map +1 -0
- package/dist/e2b-CEVOdRib.js +480 -0
- package/dist/e2b-CEVOdRib.js.map +1 -0
- package/dist/edit-utils-EGosADZq.js +234 -0
- package/dist/edit-utils-EGosADZq.js.map +1 -0
- package/dist/environment-8wsKE2Ln.js +214 -0
- package/dist/environment-8wsKE2Ln.js.map +1 -0
- package/dist/eval-BNcvle_J.js +1595 -0
- package/dist/eval-BNcvle_J.js.map +1 -0
- package/dist/eval.d.ts +2 -2
- package/dist/eval.js +1 -1489
- package/dist/executor-Cx5-8yqi.js +621 -0
- package/dist/executor-Cx5-8yqi.js.map +1 -0
- package/dist/extension-authoring-DyGGmq9V.js +385 -0
- package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +3 -3
- package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
- package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
- package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
- package/dist/format-C2NQEVUH.js.map +1 -0
- package/dist/format-bytes-C7tfc3Zm.js +55 -0
- package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
- package/dist/headless-C_rNUuYC.js +960 -0
- package/dist/headless-C_rNUuYC.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -413
- package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
- package/dist/history-search-CcYzF8-0.js.map +1 -0
- package/dist/history-search-worker.d.ts +1 -1
- package/dist/history-search-worker.js +1 -1
- package/dist/host-D7NrI03V.js +378 -0
- package/dist/host-D7NrI03V.js.map +1 -0
- package/dist/host-signals-BcP5QKUD.js +118 -0
- package/dist/host-signals-BcP5QKUD.js.map +1 -0
- package/dist/identity-CwfO8K-t.js +26 -0
- package/dist/identity-CwfO8K-t.js.map +1 -0
- package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
- package/dist/index-BGe1fQT7.d.ts.map +1 -0
- package/dist/index-BVQsIKGn.d.ts +448 -0
- package/dist/index-BVQsIKGn.d.ts.map +1 -0
- package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
- package/dist/index-CHABJd-L.d.ts.map +1 -0
- package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
- package/dist/index-D6a7qXpg.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +27 -23
- package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
- package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
- package/dist/ipc/remote.d.ts +313 -0
- package/dist/ipc/remote.d.ts.map +1 -0
- package/dist/ipc/remote.js +4 -0
- package/dist/ipc.d.ts +6 -964
- package/dist/ipc.js +403 -4
- package/dist/ipc.js.map +1 -0
- package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
- package/dist/isolation-D9L650W5.d.ts.map +1 -0
- package/dist/json-rpc--HXQti1E.js +350 -0
- package/dist/json-rpc--HXQti1E.js.map +1 -0
- package/dist/links-j8rED1sR.js +603 -0
- package/dist/links-j8rED1sR.js.map +1 -0
- package/dist/local-terminal-D8QThwo_.js +252 -0
- package/dist/local-terminal-D8QThwo_.js.map +1 -0
- package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
- package/dist/logger-Gtqlayx8.js.map +1 -0
- package/dist/logs-view-CWG8aUAG.js.map +1 -1
- package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
- package/dist/mcp-_wecH9Qe.js.map +1 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +2 -2
- package/dist/media-sniff-UKijEaiQ.js.map +1 -1
- package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
- package/dist/messages-BqiwcD2Q.js.map +1 -0
- package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
- package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
- package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
- package/dist/openai-compat-CFrpH7uq.js.map +1 -0
- package/dist/orchestration-BCGmB8Ps.js +511 -0
- package/dist/orchestration-BCGmB8Ps.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -2
- package/dist/output/stream-json.d.ts.map +1 -1
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +1 -2
- package/dist/output/terminal.d.ts.map +1 -1
- package/dist/output/terminal.js +13 -7
- package/dist/output/terminal.js.map +1 -1
- package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
- package/dist/plugins-DS7mahzZ.js.map +1 -0
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +1 -2
- package/dist/prelude-Cxo4t74B.js +517 -0
- package/dist/prelude-Cxo4t74B.js.map +1 -0
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -2
- package/dist/project-root-B8zADr4o.js +167 -0
- package/dist/project-root-B8zADr4o.js.map +1 -0
- package/dist/project-user-paths-BSgtrRxl.js +58 -0
- package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
- package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
- package/dist/prompts-DYcvqShf.d.ts.map +1 -0
- package/dist/protocol-DnhqqSmE.js +13 -0
- package/dist/protocol-DnhqqSmE.js.map +1 -0
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +1 -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 +3 -2
- package/dist/providers/cerebras.js.map +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 -2
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +15 -12
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +3 -2
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +3 -3
- package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
- package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/registry-D6ShBWJU.js +5234 -0
- package/dist/registry-D6ShBWJU.js.map +1 -0
- package/dist/remote-hosts-BtA_9ZGp.js +97 -0
- package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
- package/dist/restate.d.ts +2 -3
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +631 -572
- package/dist/restate.js.map +1 -1
- package/dist/run-summary-CklLiZ4y.js +288 -0
- package/dist/run-summary-CklLiZ4y.js.map +1 -0
- package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
- package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
- package/dist/session/sqlite.d.ts +1 -2
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +1 -616
- package/dist/session-factory-BUGY5P40.js +1576 -0
- package/dist/session-factory-BUGY5P40.js.map +1 -0
- package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
- package/dist/session-xh9JXu6i.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/settings-keys-Ckf29NeM.js +677 -0
- package/dist/settings-keys-Ckf29NeM.js.map +1 -0
- package/dist/shell-path-Dqaoow2J.js +412 -0
- package/dist/shell-path-Dqaoow2J.js.map +1 -0
- package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
- package/dist/skills-DKqZJm4X.js.map +1 -0
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -2
- package/dist/source-mPExaUm8.js +381 -0
- package/dist/source-mPExaUm8.js.map +1 -0
- package/dist/sqlite-DV1DQzN2.js +698 -0
- package/dist/sqlite-DV1DQzN2.js.map +1 -0
- package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
- package/dist/src-BRMYRJib.js.map +1 -0
- package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
- package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
- package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
- package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
- package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
- package/dist/timeout-DEaqslLO.d.ts.map +1 -1
- package/dist/todo-edits-Dn251YUP.js +1790 -0
- package/dist/todo-edits-Dn251YUP.js.map +1 -0
- package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
- package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
- package/dist/tool-state-Dku6YqAB.js +989 -0
- package/dist/tool-state-Dku6YqAB.js.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -2
- package/dist/tools/fetch-url.d.ts.map +1 -1
- package/dist/tools/fetch-url.js +1 -2
- package/dist/tools/prompts.d.ts +1 -1
- package/dist/tools/prompts.js +7 -7
- package/dist/tools/prompts.js.map +1 -1
- package/dist/tools/web-search.d.ts +1 -2
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +2 -2
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools-_DnT3AGM.js +578 -0
- package/dist/tools-_DnT3AGM.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +4 -2
- package/dist/tui.d.ts +534 -517
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +34933 -27243
- package/dist/tui.js.map +1 -1
- package/dist/turn-operations-B8sokwi5.js +333 -0
- package/dist/turn-operations-B8sokwi5.js.map +1 -0
- package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
- package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
- package/dist/types-C7ZFOztE.d.ts +132 -0
- package/dist/types-C7ZFOztE.d.ts.map +1 -0
- package/dist/types-CH5XpIHw.js +114 -0
- package/dist/types-CH5XpIHw.js.map +1 -0
- package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
- package/dist/types-Cu0ZGDu9.js.map +1 -0
- package/dist/types-IqegbJEE.d.ts +624 -0
- package/dist/types-IqegbJEE.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.js +1 -1
- package/dist/url-D8HCl65b.js +1259 -0
- package/dist/url-D8HCl65b.js.map +1 -0
- package/dist/wasm-BVT-j5cl.js +33 -0
- package/dist/wasm-BVT-j5cl.js.map +1 -0
- package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
- package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
- package/dist/ws-CIHMvlSh.js +952 -0
- package/dist/ws-CIHMvlSh.js.map +1 -0
- package/dist/ws-client-N7NxzvQp.js +230 -0
- package/dist/ws-client-N7NxzvQp.js.map +1 -0
- package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
- package/dist/xai-CvJTeLrI.js.map +1 -0
- package/dist/xdg-zweed0-t.js +117 -0
- package/dist/xdg-zweed0-t.js.map +1 -0
- package/docs/ACP.md +52 -0
- package/docs/ARCHITECTURE.md +103 -6
- package/docs/ARTIFACTS.md +8 -4
- package/docs/CHAT.md +165 -14
- package/docs/EXECUTION_CONTEXT.md +8 -0
- package/docs/EXTENSIONS.md +123 -2
- package/docs/HEADLESS.md +2 -0
- package/docs/IPC.md +775 -64
- package/docs/MACHINES.md +456 -0
- package/docs/PANELS.md +3 -0
- package/docs/README.md +4 -0
- package/docs/RESTATE.md +3 -2
- package/docs/RUN_IN_BACKGROUND.md +10 -2
- package/docs/SERVER.md +1085 -0
- package/docs/SKILL.md +6 -1
- package/docs/TERMINAL.md +75 -0
- package/docs/TUI.md +65 -14
- package/package.json +58 -26
- package/scripts/eval.ts +4 -0
- package/dist/acp-D-tVyUjw.js.map +0 -1
- package/dist/agent-D0zZnGbt.js.map +0 -1
- package/dist/anthropic-C2aole0G.js.map +0 -1
- package/dist/approvals-DKaJZYlm.js.map +0 -1
- package/dist/atomic-write-B7zr1ddF.js.map +0 -1
- package/dist/auth-BG2rVKSt.js.map +0 -1
- package/dist/completion-core-BRo86R_L.js.map +0 -1
- package/dist/contexts/daytona.js.map +0 -1
- package/dist/contexts/docker.js.map +0 -1
- package/dist/contexts/e2b.js.map +0 -1
- package/dist/contexts-CRE9pw07.js.map +0 -1
- package/dist/eval.js.map +0 -1
- package/dist/format-BF745pLL.js.map +0 -1
- package/dist/glob-DluQFSw1.js +0 -270
- package/dist/glob-DluQFSw1.js.map +0 -1
- package/dist/headless.js.map +0 -1
- package/dist/history-search-C4bk1Wz_.js.map +0 -1
- package/dist/index-BgN1T3lk.d.ts.map +0 -1
- package/dist/index-C37_ppWv.d.ts.map +0 -1
- package/dist/index-C6NEd5y_.d.ts.map +0 -1
- package/dist/ipc-BKVIIOqH.js +0 -331
- package/dist/ipc-BKVIIOqH.js.map +0 -1
- package/dist/ipc.d.ts.map +0 -1
- package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
- package/dist/json-rpc-rSWMUh9b.js +0 -192
- package/dist/json-rpc-rSWMUh9b.js.map +0 -1
- package/dist/logger--C1VQjwJ.js.map +0 -1
- package/dist/mcp-CgNd-f3F.js.map +0 -1
- package/dist/messages-2jcOA_5R.js.map +0 -1
- package/dist/openai-compat-Dy56weK8.js.map +0 -1
- package/dist/output-cap-C9zukcCj.js +0 -100
- package/dist/output-cap-C9zukcCj.js.map +0 -1
- package/dist/panels-B6gRk4bf.js +0 -789
- package/dist/panels-B6gRk4bf.js.map +0 -1
- package/dist/plugins-gbI7SBA6.js.map +0 -1
- package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
- package/dist/run-summary-BU_NgwUT.js +0 -269
- package/dist/run-summary-BU_NgwUT.js.map +0 -1
- package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
- package/dist/session/sqlite.js.map +0 -1
- package/dist/session-BqRP1hS7.js.map +0 -1
- package/dist/shell-path-DfdXrGg9.js +0 -1549
- package/dist/shell-path-DfdXrGg9.js.map +0 -1
- package/dist/skills-BC3dodWp.js.map +0 -1
- package/dist/src-CkNQKFKr.js.map +0 -1
- package/dist/todo-edits-UOwgxGKu.js +0 -528
- package/dist/todo-edits-UOwgxGKu.js.map +0 -1
- package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
- package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
- package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
- package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
- package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
- package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
- package/dist/turn-operations-DrbJlpq9.js.map +0 -1
- package/dist/types-CWgENVLY.js.map +0 -1
- package/dist/xai-CazjStOK.js.map +0 -1
|
@@ -1,1119 +1,1122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as documentBlockMarker, o as isWireTurn, r as assertResolvedMediaBlock } from "./types-CWgENVLY.js";
|
|
1
|
+
import { a as documentBlockMarker, r as assertResolvedMediaBlock, s as isWireTurn } from "./types-Cu0ZGDu9.js";
|
|
3
2
|
import { n as reconcileImageMediaType, t as audioFormatFromMediaType } from "./media-sniff-UKijEaiQ.js";
|
|
4
|
-
|
|
3
|
+
import { c as fnv1aBase36 } from "./aliasing-lVFbm81n.js";
|
|
4
|
+
import { r as TOOL_RESULTS_TAG, t as ASSISTANT_TOOL_CALLS_TAG } from "./openai-compat-CFrpH7uq.js";
|
|
5
|
+
//#region src/session/messages-pairing.ts
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* block is text to keep simple cases readable.
|
|
7
|
+
* Placeholder content inserted into a synthetic `tool_result` when the harness
|
|
8
|
+
* has to repair an orphan `tool_use`. Exported so downstream consumers
|
|
9
|
+
* (training-data collectors, HFI submission) can reject any payload containing
|
|
10
|
+
* it — the marker satisfies the wire-level pairing contract structurally but
|
|
11
|
+
* the content itself is fake and would poison fine-tuning data.
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
if (typeof content === "string") return content;
|
|
15
|
-
if (!Array.isArray(content)) return JSON.stringify(content);
|
|
16
|
-
const blocks = [];
|
|
17
|
-
for (const raw of content) {
|
|
18
|
-
if (!raw || typeof raw !== "object") continue;
|
|
19
|
-
const b = raw;
|
|
20
|
-
if (b.type === "text" && typeof b.text === "string") {
|
|
21
|
-
blocks.push({
|
|
22
|
-
type: "text",
|
|
23
|
-
text: b.text
|
|
24
|
-
});
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (b.type === "image" && b.source && typeof b.source === "object") {
|
|
28
|
-
const src = b.source;
|
|
29
|
-
if (src.type === "base64" && typeof src.data === "string" && typeof src.media_type === "string") {
|
|
30
|
-
blocks.push({
|
|
31
|
-
type: "image",
|
|
32
|
-
mediaType: src.media_type,
|
|
33
|
-
data: src.data
|
|
34
|
-
});
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
blocks.push({
|
|
39
|
-
type: "text",
|
|
40
|
-
text: JSON.stringify(raw)
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
if (blocks.length === 0) return "";
|
|
44
|
-
if (!blocks.some((b) => b.type !== "text")) return blocks.map((b) => b.text).join("\n");
|
|
45
|
-
return blocks;
|
|
46
|
-
}
|
|
13
|
+
const SYNTHETIC_TOOL_RESULT_PLACEHOLDER = "[Tool result missing due to internal error]";
|
|
47
14
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* was specified using the X media type, but the image appears to be a
|
|
54
|
-
* Y image`). The upstream sources of `{type:'image'}` blocks each sniff
|
|
55
|
-
* before producing the block (read_file / MCP / TUI paste), but this
|
|
56
|
-
* wire-boundary reconciliation is a final safety net for any future code
|
|
57
|
-
* path or host-supplied prompt part that slips through without sniffing.
|
|
58
|
-
*
|
|
59
|
-
* The reconciler is conservative: it overrides only when the magic bytes
|
|
60
|
-
* positively identify a *different* known format. Unknown / too-short
|
|
61
|
-
* payloads pass `mediaType` through unchanged.
|
|
15
|
+
* Replacement text for an assistant message whose every content block was
|
|
16
|
+
* stripped during pairing repair (e.g. the only blocks were orphan
|
|
17
|
+
* `tool_call`s). Providers reject empty `content` arrays — the marker keeps
|
|
18
|
+
* the turn shape valid while signaling "this assistant turn lost its
|
|
19
|
+
* outputs" to the model so it can recover.
|
|
62
20
|
*/
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
type: "base64",
|
|
66
|
-
media_type: reconcileImageMediaType(mediaType, data),
|
|
67
|
-
data
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function anthropicDocumentBlocks(doc) {
|
|
71
|
-
if (doc.encoding === "base64" && doc.mediaType === "application/pdf") return [{
|
|
72
|
-
type: "document",
|
|
73
|
-
source: {
|
|
74
|
-
type: "base64",
|
|
75
|
-
media_type: "application/pdf",
|
|
76
|
-
data: doc.data
|
|
77
|
-
},
|
|
78
|
-
...doc.name ? { title: doc.name } : {}
|
|
79
|
-
}];
|
|
80
|
-
return [{
|
|
81
|
-
type: "text",
|
|
82
|
-
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>`
|
|
83
|
-
}];
|
|
84
|
-
}
|
|
85
|
-
function documentMarker(doc) {
|
|
86
|
-
return documentBlockMarker(doc, "document attached");
|
|
87
|
-
}
|
|
88
|
-
function isContentRefBlock(block) {
|
|
89
|
-
return (block.type === "image" || block.type === "document") && typeof block.ref === "string";
|
|
90
|
-
}
|
|
91
|
-
function unresolvedContentRefError(block) {
|
|
92
|
-
return /* @__PURE__ */ new Error(`Cannot project content ref "${block.ref}" to provider wire messages without resolveContentRef.`);
|
|
93
|
-
}
|
|
21
|
+
const TOOL_USE_INTERRUPTED_MARKER = "[Tool use interrupted]";
|
|
94
22
|
/**
|
|
95
|
-
*
|
|
23
|
+
* Replacement text for a user message whose every content block was a
|
|
24
|
+
* `tool_result` with no matching upstream `tool_call` (e.g. the assistant
|
|
25
|
+
* pair was stripped by an earlier compaction). Same role as
|
|
26
|
+
* {@link TOOL_USE_INTERRUPTED_MARKER} on the user side.
|
|
27
|
+
*/
|
|
28
|
+
const ORPHANED_TOOL_RESULT_MARKER = "[Orphaned tool result removed due to conversation resume]";
|
|
29
|
+
/**
|
|
30
|
+
* Defensive repair pass that rewrites a message list so it satisfies the
|
|
31
|
+
* wire-level `tool_use` ↔ `tool_result` adjacency contract every modern
|
|
32
|
+
* provider enforces.
|
|
96
33
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* match pattern '^[a-zA-Z0-9_-]+$'`).
|
|
34
|
+
* Anthropic 400s on orphans with `'tool_use' ids were found without
|
|
35
|
+
* 'tool_result' blocks immediately after` (and its inverse, `tool_result
|
|
36
|
+
* must be preceded by a tool_call with the same toolCallId`). OpenAI's
|
|
37
|
+
* Chat Completions API rejects most mismatches with a 400 too.
|
|
102
38
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* `
|
|
109
|
-
*
|
|
39
|
+
* Six repair modes — modeled after Anthropic's Claude Code defenses:
|
|
40
|
+
*
|
|
41
|
+
* | # | Corruption | Repair |
|
|
42
|
+
* |---|------------|--------|
|
|
43
|
+
* | 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` |
|
|
44
|
+
* | 2 | assistant `tool_use` followed by nothing (or by a non-user msg) | **Insert** a new synthetic user message with the same placeholder |
|
|
45
|
+
* | 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 |
|
|
46
|
+
* | 4 | duplicate `tool_call.id` across assistant messages (CC-1212) | **Strip** later instances + their matching tool_results |
|
|
47
|
+
* | 5 | duplicate `tool_result.callId` within a single user message | **Dedupe** by `callId`, keep first |
|
|
48
|
+
* | 6 | assistant message emptied by mode-4 stripping | **Replace** content with {@link TOOL_USE_INTERRUPTED_MARKER} text block |
|
|
49
|
+
*
|
|
50
|
+
* **Repair, not drop.** Earlier versions of this pass simply dropped orphan
|
|
51
|
+
* blocks, which (a) silently rewrote history the model had reasoned over and
|
|
52
|
+
* (b) cascaded — dropping an assistant tool_call would orphan its
|
|
53
|
+
* tool_result, which would then get dropped too, removing any trace of
|
|
54
|
+
* "what the model tried to do" from the transcript. The repair-based pass
|
|
55
|
+
* preserves the model's tool_use shape and patches the dangling result with
|
|
56
|
+
* an `is_error` placeholder so the model sees "I tried X, the result was
|
|
57
|
+
* lost" and can retry intelligently.
|
|
58
|
+
*
|
|
59
|
+
* Adjacency contract: `tool_result` blocks must live in the user message
|
|
60
|
+
* IMMEDIATELY following the assistant message that emitted the matching
|
|
61
|
+
* `tool_use`. The pass enforces strict adjacency — a tool_result two
|
|
62
|
+
* messages downstream of its tool_call is still an orphan.
|
|
63
|
+
*
|
|
64
|
+
* Idempotent: returns the input reference unchanged when no repairs were
|
|
65
|
+
* necessary. Re-running on already-repaired output is a no-op.
|
|
66
|
+
*
|
|
67
|
+
* Pure: does not mutate input messages or their content arrays — every
|
|
68
|
+
* repair allocates a fresh array / object.
|
|
110
69
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
70
|
+
function ensureToolResultPairing(messages, options = {}) {
|
|
71
|
+
if (messages.length === 0) return messages;
|
|
72
|
+
const fireRepair = options.onRepair ?? (() => {});
|
|
73
|
+
const firstSeenAt = /* @__PURE__ */ new Map();
|
|
74
|
+
for (let i = 0; i < messages.length; i++) {
|
|
75
|
+
const msg = messages[i];
|
|
76
|
+
if (msg.role !== "assistant") continue;
|
|
77
|
+
for (const block of msg.content) if (block.type === "tool_call" && !firstSeenAt.has(block.id)) firstSeenAt.set(block.id, i);
|
|
78
|
+
}
|
|
79
|
+
const pendingOrphansByUserIndex = /* @__PURE__ */ new Map();
|
|
80
|
+
let changed = false;
|
|
81
|
+
const markChanged = () => {
|
|
82
|
+
changed = true;
|
|
83
|
+
};
|
|
84
|
+
const out = [];
|
|
85
|
+
for (let i = 0; i < messages.length; i++) {
|
|
86
|
+
const msg = messages[i];
|
|
87
|
+
if (msg.role === "assistant") processAssistantMessage({
|
|
88
|
+
index: i,
|
|
89
|
+
msg,
|
|
90
|
+
messages,
|
|
91
|
+
firstSeenAt,
|
|
92
|
+
pendingOrphansByUserIndex,
|
|
93
|
+
out,
|
|
94
|
+
fireRepair,
|
|
95
|
+
markChanged
|
|
96
|
+
});
|
|
97
|
+
else processUserMessage({
|
|
98
|
+
index: i,
|
|
99
|
+
msg,
|
|
100
|
+
pendingOrphansByUserIndex,
|
|
101
|
+
out,
|
|
102
|
+
fireRepair,
|
|
103
|
+
markChanged
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return changed ? out : messages;
|
|
117
107
|
}
|
|
118
108
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
109
|
+
* Process one assistant message: strip mode-4 duplicates, queue mode-1 / 2
|
|
110
|
+
* orphan-tool-use repairs for the next user message (or insert a synthetic
|
|
111
|
+
* one), and emit a mode-6 marker when dedup empties the message.
|
|
121
112
|
*/
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
113
|
+
function processAssistantMessage(args) {
|
|
114
|
+
const { index, msg, messages, firstSeenAt, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args;
|
|
115
|
+
const dedupedContent = [];
|
|
116
|
+
const seenHere = /* @__PURE__ */ new Set();
|
|
117
|
+
let dedupedChanged = false;
|
|
118
|
+
for (const block of msg.content) {
|
|
119
|
+
if (block.type === "tool_call") {
|
|
120
|
+
const isCrossMsgDup = firstSeenAt.get(block.id) !== index;
|
|
121
|
+
const isIntraMsgDup = seenHere.has(block.id);
|
|
122
|
+
if (isCrossMsgDup || isIntraMsgDup) {
|
|
123
|
+
dedupedChanged = true;
|
|
124
|
+
fireRepair({
|
|
125
|
+
mode: "duplicate-tool-use-strip",
|
|
126
|
+
callId: block.id,
|
|
127
|
+
messageIndex: index
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
seenHere.add(block.id);
|
|
135
132
|
}
|
|
136
|
-
|
|
137
|
-
type: "text",
|
|
138
|
-
text: `[audio omitted: ${b.mediaType}]`
|
|
139
|
-
};
|
|
140
|
-
if (b.type === "video") return {
|
|
141
|
-
type: "text",
|
|
142
|
-
text: `[video omitted: ${b.mediaType}]`
|
|
143
|
-
};
|
|
144
|
-
assertResolvedMediaBlock(b, "Anthropic wire messages");
|
|
145
|
-
return {
|
|
146
|
-
type: "image",
|
|
147
|
-
source: anthropicImageSource(b.mediaType, b.data)
|
|
148
|
-
};
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
function fromAnthropic(msg) {
|
|
152
|
-
const role = msg.role;
|
|
153
|
-
const content = [];
|
|
154
|
-
if (typeof msg.content === "string") {
|
|
155
|
-
content.push({
|
|
156
|
-
type: "text",
|
|
157
|
-
text: msg.content
|
|
158
|
-
});
|
|
159
|
-
return {
|
|
160
|
-
role,
|
|
161
|
-
content
|
|
162
|
-
};
|
|
133
|
+
dedupedContent.push(block);
|
|
163
134
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
else if (source?.type === "text" && typeof source.media_type === "string" && typeof source.data === "string") content.push({
|
|
188
|
-
type: "document",
|
|
189
|
-
mediaType: source.media_type,
|
|
190
|
-
data: source.data,
|
|
191
|
-
encoding: "text",
|
|
192
|
-
...typeof b.title === "string" ? { name: b.title } : {}
|
|
193
|
-
});
|
|
194
|
-
} else if (b.type === "tool_use") content.push({
|
|
195
|
-
type: "tool_call",
|
|
196
|
-
id: b.id,
|
|
197
|
-
name: b.name,
|
|
198
|
-
input: b.input
|
|
199
|
-
});
|
|
200
|
-
else if (b.type === "tool_result") {
|
|
201
|
-
const output = decodeAnthropicToolResultContent(b.content);
|
|
202
|
-
const block = {
|
|
203
|
-
type: "tool_result",
|
|
204
|
-
callId: b.tool_use_id,
|
|
205
|
-
output
|
|
206
|
-
};
|
|
207
|
-
if (b.is_error === true) block.isError = true;
|
|
208
|
-
content.push(block);
|
|
209
|
-
} else if (b.type === "thinking") {
|
|
210
|
-
const block = {
|
|
211
|
-
type: "thinking",
|
|
212
|
-
text: b.thinking ?? ""
|
|
213
|
-
};
|
|
214
|
-
if (typeof b.signature === "string") {
|
|
215
|
-
block.signature = b.signature;
|
|
216
|
-
block.signatureProducer = "anthropic";
|
|
217
|
-
}
|
|
218
|
-
content.push(block);
|
|
219
|
-
} else if (b.type === "redacted_thinking") content.push({
|
|
220
|
-
type: "redacted_thinking",
|
|
221
|
-
data: b.data ?? ""
|
|
222
|
-
});
|
|
223
|
-
else if (b.type === "server_tool_use") content.push({
|
|
224
|
-
type: "server_tool_use",
|
|
225
|
-
producer: "anthropic",
|
|
226
|
-
id: b.id ?? "",
|
|
227
|
-
name: b.name ?? "",
|
|
228
|
-
input: b.input ?? {}
|
|
229
|
-
});
|
|
230
|
-
else if (b.type === "web_search_tool_result") content.push({
|
|
231
|
-
type: "server_tool_result",
|
|
232
|
-
producer: "anthropic",
|
|
233
|
-
toolUseId: b.tool_use_id ?? "",
|
|
234
|
-
toolName: "web_search",
|
|
235
|
-
content: b.content
|
|
135
|
+
const surviving = collectIds(dedupedContent, "tool_call", (b) => b.id);
|
|
136
|
+
const next = messages[index + 1];
|
|
137
|
+
const nextIsUser = next && next.role === "user";
|
|
138
|
+
const nextResultIds = nextIsUser ? collectResultIds(next.content) : /* @__PURE__ */ new Set();
|
|
139
|
+
const orphans = [];
|
|
140
|
+
for (const id of surviving) if (!nextResultIds.has(id)) orphans.push(id);
|
|
141
|
+
emitAssistantAfterDedup({
|
|
142
|
+
index,
|
|
143
|
+
msg,
|
|
144
|
+
dedupedContent,
|
|
145
|
+
dedupedChanged,
|
|
146
|
+
out,
|
|
147
|
+
fireRepair,
|
|
148
|
+
markChanged
|
|
149
|
+
});
|
|
150
|
+
if (orphans.length === 0) return;
|
|
151
|
+
if (nextIsUser) {
|
|
152
|
+
markChanged();
|
|
153
|
+
pendingOrphansByUserIndex.set(index + 1, orphans);
|
|
154
|
+
for (const callId of orphans) fireRepair({
|
|
155
|
+
mode: "orphan-tool-use-prepend",
|
|
156
|
+
callId,
|
|
157
|
+
messageIndex: index
|
|
236
158
|
});
|
|
159
|
+
return;
|
|
237
160
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
161
|
+
markChanged();
|
|
162
|
+
out.push({
|
|
163
|
+
role: "user",
|
|
164
|
+
content: orphans.map((callId) => syntheticResultBlock(callId))
|
|
165
|
+
});
|
|
166
|
+
for (const callId of orphans) fireRepair({
|
|
167
|
+
mode: "orphan-tool-use-append",
|
|
168
|
+
callId,
|
|
169
|
+
messageIndex: index
|
|
170
|
+
});
|
|
242
171
|
}
|
|
243
|
-
function
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
role,
|
|
249
|
-
content
|
|
250
|
-
};
|
|
251
|
-
if (typeof c === "string") {
|
|
252
|
-
content.push({
|
|
253
|
-
type: "text",
|
|
254
|
-
text: c
|
|
255
|
-
});
|
|
256
|
-
return {
|
|
257
|
-
role,
|
|
258
|
-
content
|
|
259
|
-
};
|
|
172
|
+
function emitAssistantAfterDedup(args) {
|
|
173
|
+
const { index, msg, dedupedContent, dedupedChanged, out, fireRepair, markChanged } = args;
|
|
174
|
+
if (!dedupedChanged) {
|
|
175
|
+
out.push(msg);
|
|
176
|
+
return;
|
|
260
177
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
178
|
+
markChanged();
|
|
179
|
+
if (dedupedContent.length === 0) {
|
|
180
|
+
out.push({
|
|
181
|
+
...msg,
|
|
182
|
+
content: [{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: TOOL_USE_INTERRUPTED_MARKER
|
|
185
|
+
}]
|
|
266
186
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
187
|
+
fireRepair({
|
|
188
|
+
mode: "empty-assistant-marker",
|
|
189
|
+
messageIndex: index
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
out.push({
|
|
194
|
+
...msg,
|
|
195
|
+
content: dedupedContent
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Process one user message: dedup mode-5 duplicate tool_results, strip
|
|
200
|
+
* mode-3 orphans whose tool_call is missing from the previously-emitted
|
|
201
|
+
* assistant message, and prepend any synthetic results queued by the prior
|
|
202
|
+
* assistant's mode-1 repair.
|
|
203
|
+
*/
|
|
204
|
+
function processUserMessage(args) {
|
|
205
|
+
const { index, msg, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args;
|
|
206
|
+
const queuedOrphans = pendingOrphansByUserIndex.get(index);
|
|
207
|
+
pendingOrphansByUserIndex.delete(index);
|
|
208
|
+
const prev = out.length > 0 ? out[out.length - 1] : null;
|
|
209
|
+
const validIds = prev && prev.role === "assistant" ? collectIds(prev.content, "tool_call", (b) => b.id) : /* @__PURE__ */ new Set();
|
|
210
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
211
|
+
let modifiedHere = false;
|
|
212
|
+
const afterDedup = [];
|
|
213
|
+
for (const block of msg.content) {
|
|
214
|
+
if (block.type !== "tool_result") {
|
|
215
|
+
afterDedup.push(block);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (seenCallIds.has(block.callId)) {
|
|
219
|
+
modifiedHere = true;
|
|
220
|
+
fireRepair({
|
|
221
|
+
mode: "duplicate-tool-result-strip",
|
|
222
|
+
callId: block.callId,
|
|
223
|
+
messageIndex: index
|
|
283
224
|
});
|
|
225
|
+
continue;
|
|
284
226
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
content
|
|
288
|
-
};
|
|
227
|
+
seenCallIds.add(block.callId);
|
|
228
|
+
afterDedup.push(block);
|
|
289
229
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
output: r.content ?? ""
|
|
230
|
+
const afterStrip = [];
|
|
231
|
+
for (const block of afterDedup) {
|
|
232
|
+
if (block.type === "tool_result" && !validIds.has(block.callId)) {
|
|
233
|
+
modifiedHere = true;
|
|
234
|
+
fireRepair({
|
|
235
|
+
mode: "orphan-tool-result-strip",
|
|
236
|
+
callId: block.callId,
|
|
237
|
+
messageIndex: index
|
|
299
238
|
});
|
|
239
|
+
continue;
|
|
300
240
|
}
|
|
301
|
-
|
|
302
|
-
type: "text",
|
|
303
|
-
text: tagged.text
|
|
304
|
-
});
|
|
305
|
-
return {
|
|
306
|
-
role,
|
|
307
|
-
content
|
|
308
|
-
};
|
|
241
|
+
afterStrip.push(block);
|
|
309
242
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
243
|
+
const finalContent = queuedOrphans ? [...queuedOrphans.map(syntheticResultBlock), ...afterStrip] : afterStrip;
|
|
244
|
+
if (!modifiedHere && queuedOrphans === void 0) {
|
|
245
|
+
out.push(msg);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
markChanged();
|
|
249
|
+
if (finalContent.length === 0) {
|
|
250
|
+
out.push({
|
|
251
|
+
...msg,
|
|
252
|
+
content: [{
|
|
315
253
|
type: "text",
|
|
316
|
-
text:
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (imageUrl?.startsWith("data:")) {
|
|
321
|
-
const [meta, data] = imageUrl.slice(5).split(",", 2);
|
|
322
|
-
const mediaType = meta.replace(";base64", "");
|
|
323
|
-
content.push({
|
|
324
|
-
type: "image",
|
|
325
|
-
mediaType,
|
|
326
|
-
data
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
role,
|
|
333
|
-
content
|
|
334
|
-
};
|
|
254
|
+
text: ORPHANED_TOOL_RESULT_MARKER
|
|
255
|
+
}]
|
|
256
|
+
});
|
|
257
|
+
return;
|
|
335
258
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
content
|
|
339
|
-
};
|
|
259
|
+
out.push({
|
|
260
|
+
...msg,
|
|
261
|
+
content: finalContent
|
|
262
|
+
});
|
|
340
263
|
}
|
|
341
264
|
/**
|
|
342
|
-
*
|
|
343
|
-
* (
|
|
344
|
-
*
|
|
345
|
-
* and the host-facing {@link sanitizeHistoryForProvider} apply this same
|
|
346
|
-
* policy — keeping them one function means the sanitizer can never drift
|
|
347
|
-
* from what the wire actually sends.
|
|
265
|
+
* Re-mint tool-call ids that collide with ids already present in the
|
|
266
|
+
* transcript (or with an earlier sibling in the same turn), BEFORE the turn
|
|
267
|
+
* is persisted and its tools dispatched.
|
|
348
268
|
*
|
|
349
|
-
*
|
|
269
|
+
* Why this exists: some providers/serving stacks emit non-globally-unique
|
|
270
|
+
* tool_use ids (per-turn counters like `call_0`, retry-replay duplicates,
|
|
271
|
+
* Kimi-K2.6 on Fireworks). The wire-time pairing pass
|
|
272
|
+
* ({@link ensureToolResultPairing}) can only STRIP such duplicates — which
|
|
273
|
+
* erases the model's tool calls and results from the wire view, stalls
|
|
274
|
+
* progress, and re-accumulates repairs quadratically because the persisted
|
|
275
|
+
* session keeps the corrupted ids. Re-minting at persist time keeps the
|
|
276
|
+
* pairing intact instead: the call, its execution, and its tool_result all
|
|
277
|
+
* carry the fresh id, so the pairing pass has nothing to strip on any later
|
|
278
|
+
* turn.
|
|
350
279
|
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* a valid Anthropic HMAC; 400s).
|
|
355
|
-
* - unsigned `thinking` → drop (`ThinkingBlockParam` requires a
|
|
356
|
-
* signature; unsigned blocks come from non-signing producers).
|
|
357
|
-
* - signed but UNTAGGED `thinking` (pre-tagging sessions) → keep: it is
|
|
358
|
-
* most plausibly legacy Anthropic output, and the signature-400
|
|
359
|
-
* recovery converges if it is not.
|
|
280
|
+
* Fresh ids are `${oldId}_r{n}` with `n` bumped until unique — deterministic
|
|
281
|
+
* shape, Anthropic-wire-safe charset (given a safe input id), and visibly
|
|
282
|
+
* traceable back to the original.
|
|
360
283
|
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* unrecoverable 400 — the reasoning text survives, only the opaque
|
|
369
|
-
* signature is withheld.
|
|
284
|
+
* Alignment contract: `toolCalls` and the `tool_call` blocks inside
|
|
285
|
+
* `content` are two views of the same emission and MUST be in the same
|
|
286
|
+
* order (true for every provider — both are built from one source array).
|
|
287
|
+
* Occurrences are matched positionally per old id so intra-turn duplicates
|
|
288
|
+
* map to distinct fresh ids on both views.
|
|
289
|
+
*
|
|
290
|
+
* Pure: returns the input references unchanged when no id collides.
|
|
370
291
|
*/
|
|
371
|
-
function
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
292
|
+
function remintDuplicateToolCallIds(toolCalls, content, priorIds) {
|
|
293
|
+
const seen = new Set(priorIds);
|
|
294
|
+
const reminted = [];
|
|
295
|
+
const finalIdQueues = /* @__PURE__ */ new Map();
|
|
296
|
+
let changed = false;
|
|
297
|
+
const outCalls = toolCalls.map((tc) => {
|
|
298
|
+
let finalId = tc.id;
|
|
299
|
+
if (seen.has(tc.id)) {
|
|
300
|
+
let n = 1;
|
|
301
|
+
while (seen.has(`${tc.id}_r${n}`)) n++;
|
|
302
|
+
finalId = `${tc.id}_r${n}`;
|
|
303
|
+
reminted.push({
|
|
304
|
+
oldId: tc.id,
|
|
305
|
+
newId: finalId
|
|
306
|
+
});
|
|
307
|
+
changed = true;
|
|
308
|
+
}
|
|
309
|
+
seen.add(finalId);
|
|
310
|
+
const queue = finalIdQueues.get(tc.id);
|
|
311
|
+
if (queue) queue.push(finalId);
|
|
312
|
+
else finalIdQueues.set(tc.id, [finalId]);
|
|
313
|
+
return finalId === tc.id ? tc : {
|
|
314
|
+
...tc,
|
|
315
|
+
id: finalId
|
|
316
|
+
};
|
|
317
|
+
});
|
|
318
|
+
if (!changed) return {
|
|
319
|
+
toolCalls,
|
|
320
|
+
content,
|
|
321
|
+
reminted
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
toolCalls: outCalls,
|
|
325
|
+
content: content.map((block) => {
|
|
326
|
+
if (block.type !== "tool_call") return block;
|
|
327
|
+
const finalId = finalIdQueues.get(block.id)?.shift();
|
|
328
|
+
if (finalId === void 0 || finalId === block.id) return block;
|
|
329
|
+
return {
|
|
330
|
+
...block,
|
|
331
|
+
id: finalId
|
|
332
|
+
};
|
|
333
|
+
}),
|
|
334
|
+
reminted
|
|
335
|
+
};
|
|
381
336
|
}
|
|
382
337
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* hosts that seed history from an external durable store (or hydrate
|
|
387
|
-
* pre-provenance-tagging sessions) and want the poison removed from their
|
|
388
|
-
* source of truth in one call instead of re-deriving block semantics.
|
|
338
|
+
* Drop-only normalization of persisted history: delete structurally invalid
|
|
339
|
+
* tool blocks so the transcript never re-triggers wire-time pairing repairs
|
|
340
|
+
* turn after turn.
|
|
389
341
|
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
342
|
+
* Unlike {@link ensureToolResultPairing} — which SYNTHESIZES placeholder
|
|
343
|
+
* content and must therefore stay wire-only (fabricated results would poison
|
|
344
|
+
* persisted history and training data) — this pass performs pure deletions of
|
|
345
|
+
* blocks that are provably invalid regardless of interpretation:
|
|
346
|
+
*
|
|
347
|
+
* | Corruption | Action |
|
|
348
|
+
* |------------|--------|
|
|
349
|
+
* | duplicate `tool_call` id within one run lane (across or within turns) | drop the later instance |
|
|
350
|
+
* | duplicate `tool_result.callId` within one turn | drop the later instance |
|
|
351
|
+
* | `tool_result` with no matching `tool_call` in the immediately preceding same-lane assistant turn | drop the block |
|
|
352
|
+
* | turn emptied by the drops above (reasoning-only remainders count as empty) | drop the whole turn |
|
|
353
|
+
*
|
|
354
|
+
* Duplicate detection is scoped to the WIRE-VIEW lane: tool_call ids only
|
|
355
|
+
* collide when both instances reach one model request. By default a turn's
|
|
356
|
+
* lane is its raw `runId` — correct for parent/subagent interleaving, where
|
|
357
|
+
* providers minting per-conversation counter ids (`call_0`, `call_1`)
|
|
358
|
+
* legitimately reuse the same id across lanes. Callers that know the run
|
|
359
|
+
* topology (see `createSession`) pass `laneForRun` to widen lanes to true
|
|
360
|
+
* wire views: all depth-0 runs share the main conversation view, and a
|
|
361
|
+
* resumed subagent shares its resume-chain ancestors' view — duplicates
|
|
362
|
+
* across those runIds DO reach a single request (wire-time
|
|
363
|
+
* `ensureToolResultPairing` mode 4 would strip them on every send) and are
|
|
364
|
+
* healed here.
|
|
365
|
+
*
|
|
366
|
+
* Orphan `tool_use` blocks (a call whose result was never persisted —
|
|
367
|
+
* interrupted run) are deliberately KEPT: they are authentic model output,
|
|
368
|
+
* and the wire-time pass patches them with a synthetic result on every send
|
|
369
|
+
* without touching history.
|
|
370
|
+
*
|
|
371
|
+
* Deletions cascade by design: dropping a duplicate `tool_call` orphans its
|
|
372
|
+
* `tool_result`, which is then dropped too; a turn emptied by either is
|
|
373
|
+
* removed entirely.
|
|
374
|
+
*
|
|
375
|
+
* Safe to persist: deterministic, idempotent (re-running on healed output is
|
|
376
|
+
* a no-op returning the same reference), and fabricates nothing.
|
|
377
|
+
*
|
|
378
|
+
* Pure: does not mutate input turns or their content arrays.
|
|
393
379
|
*/
|
|
394
|
-
function
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
380
|
+
function dropInvalidToolPairings(turns, options = {}) {
|
|
381
|
+
if (turns.length === 0) return turns;
|
|
382
|
+
const onDrop = options.onDrop ?? (() => {});
|
|
383
|
+
const laneForRun = options.laneForRun ?? ((runId) => runId ?? "");
|
|
384
|
+
const laneOf = (turn) => laneForRun(turn.runId);
|
|
385
|
+
const laneKey = (turn, id) => `${laneOf(turn)}\u0000${id}`;
|
|
386
|
+
const firstSeenAt = /* @__PURE__ */ new Map();
|
|
387
|
+
for (let i = 0; i < turns.length; i++) {
|
|
388
|
+
const turn = turns[i];
|
|
389
|
+
if (turn.role !== "assistant") continue;
|
|
390
|
+
for (const block of turn.content) if (block.type === "tool_call" && !firstSeenAt.has(laneKey(turn, block.id))) firstSeenAt.set(laneKey(turn, block.id), i);
|
|
391
|
+
}
|
|
392
|
+
let changed = false;
|
|
393
|
+
const out = [];
|
|
394
|
+
for (let i = 0; i < turns.length; i++) {
|
|
395
|
+
const turn = turns[i];
|
|
396
|
+
if (turn.role === "assistant") {
|
|
397
|
+
const kept = [];
|
|
398
|
+
const seenHere = /* @__PURE__ */ new Set();
|
|
399
|
+
let dropped = false;
|
|
401
400
|
for (const block of turn.content) {
|
|
402
|
-
if (block.type
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const { signature: _sig, signatureProducer: _producer, ...rest } = block;
|
|
414
|
-
content.push(rest);
|
|
415
|
-
stripped += 1;
|
|
416
|
-
changed = true;
|
|
417
|
-
continue;
|
|
401
|
+
if (block.type === "tool_call") {
|
|
402
|
+
if (firstSeenAt.get(laneKey(turn, block.id)) !== i || seenHere.has(block.id)) {
|
|
403
|
+
dropped = true;
|
|
404
|
+
onDrop({
|
|
405
|
+
kind: "duplicate-tool-call",
|
|
406
|
+
callId: block.id,
|
|
407
|
+
index: i
|
|
408
|
+
});
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
seenHere.add(block.id);
|
|
418
412
|
}
|
|
419
|
-
|
|
413
|
+
kept.push(block);
|
|
420
414
|
}
|
|
421
|
-
if (!
|
|
422
|
-
|
|
415
|
+
if (!dropped) {
|
|
416
|
+
out.push(turn);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
changed = true;
|
|
420
|
+
const onlyReasoning = kept.every((b) => b.type === "thinking" || b.type === "redacted_thinking" || b.type === "provider_reasoning");
|
|
421
|
+
if (kept.length === 0 || onlyReasoning) {
|
|
422
|
+
onDrop({
|
|
423
|
+
kind: "empty-turn",
|
|
424
|
+
index: i
|
|
425
|
+
});
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
out.push({
|
|
423
429
|
...turn,
|
|
424
|
-
content:
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}]
|
|
428
|
-
};
|
|
429
|
-
}),
|
|
430
|
-
removed,
|
|
431
|
-
stripped
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
function toAnthropic(msg) {
|
|
435
|
-
const nonEmpty = msg.content.filter((b) => {
|
|
436
|
-
if (b.type === "provider_reasoning") return false;
|
|
437
|
-
if (b.type !== "thinking" && b.type !== "redacted_thinking") return true;
|
|
438
|
-
return reasoningWireAction(b, "anthropic") === "keep";
|
|
439
|
-
}).flatMap((block) => {
|
|
440
|
-
switch (block.type) {
|
|
441
|
-
case "text": return [{
|
|
442
|
-
type: "text",
|
|
443
|
-
text: block.text
|
|
444
|
-
}];
|
|
445
|
-
case "image":
|
|
446
|
-
assertResolvedMediaBlock(block, "Anthropic wire messages");
|
|
447
|
-
return [{
|
|
448
|
-
type: "image",
|
|
449
|
-
source: anthropicImageSource(block.mediaType, block.data)
|
|
450
|
-
}];
|
|
451
|
-
case "audio": return [{
|
|
452
|
-
type: "text",
|
|
453
|
-
text: `[audio omitted: ${block.mediaType}]`
|
|
454
|
-
}];
|
|
455
|
-
case "video": return [{
|
|
456
|
-
type: "text",
|
|
457
|
-
text: `[video omitted: ${block.mediaType}]`
|
|
458
|
-
}];
|
|
459
|
-
case "document":
|
|
460
|
-
assertResolvedMediaBlock(block, "Anthropic wire messages");
|
|
461
|
-
return anthropicDocumentBlocks(block);
|
|
462
|
-
case "tool_call": return [{
|
|
463
|
-
type: "tool_use",
|
|
464
|
-
id: sanitizeAnthropicToolId(block.id),
|
|
465
|
-
name: block.name,
|
|
466
|
-
input: block.input
|
|
467
|
-
}];
|
|
468
|
-
case "tool_result": {
|
|
469
|
-
const out = {
|
|
470
|
-
type: "tool_result",
|
|
471
|
-
tool_use_id: sanitizeAnthropicToolId(block.callId),
|
|
472
|
-
content: encodeAnthropicToolResultContent(block.output)
|
|
473
|
-
};
|
|
474
|
-
if (block.isError) out.is_error = true;
|
|
475
|
-
return [out, ...(typeof block.output === "string" ? [] : block.output.filter((part) => part.type === "document" && typeof part.data === "string")).flatMap(anthropicDocumentBlocks)];
|
|
476
|
-
}
|
|
477
|
-
case "thinking": return [{
|
|
478
|
-
type: "thinking",
|
|
479
|
-
thinking: block.text,
|
|
480
|
-
signature: block.signature ?? ""
|
|
481
|
-
}];
|
|
482
|
-
case "redacted_thinking": return [{
|
|
483
|
-
type: "redacted_thinking",
|
|
484
|
-
data: block.data
|
|
485
|
-
}];
|
|
486
|
-
case "server_tool_use": return [{
|
|
487
|
-
type: "server_tool_use",
|
|
488
|
-
id: sanitizeAnthropicToolId(block.id),
|
|
489
|
-
name: block.name,
|
|
490
|
-
input: block.input
|
|
491
|
-
}];
|
|
492
|
-
case "server_tool_result": return [{
|
|
493
|
-
type: "web_search_tool_result",
|
|
494
|
-
tool_use_id: sanitizeAnthropicToolId(block.toolUseId),
|
|
495
|
-
content: block.content
|
|
496
|
-
}];
|
|
497
|
-
default: return [{
|
|
498
|
-
type: "text",
|
|
499
|
-
text: ""
|
|
500
|
-
}];
|
|
430
|
+
content: kept
|
|
431
|
+
});
|
|
432
|
+
continue;
|
|
501
433
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const videoBlocks = msg.content.filter((b) => b.type === "video");
|
|
524
|
-
const documentBlocks = msg.content.filter((b) => b.type === "document");
|
|
525
|
-
if (toolResults.length > 0) {
|
|
526
|
-
const siblingText = textBlocks.map((b) => b.text).join("\n");
|
|
527
|
-
return {
|
|
528
|
-
role: msg.role,
|
|
529
|
-
content: {
|
|
530
|
-
_tag: TOOL_RESULTS_TAG,
|
|
531
|
-
results: toolResults.map((tr) => ({
|
|
532
|
-
tool_call_id: tr.callId,
|
|
533
|
-
content: tr.output
|
|
534
|
-
})),
|
|
535
|
-
...siblingText ? { text: siblingText } : {}
|
|
434
|
+
if (turn.role !== "user") {
|
|
435
|
+
out.push(turn);
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const lane = laneOf(turn);
|
|
439
|
+
let prev = null;
|
|
440
|
+
for (let j = out.length - 1; j >= 0; j--) {
|
|
441
|
+
const candidate = out[j];
|
|
442
|
+
if (candidate.role === "system" || candidate.role === "command") continue;
|
|
443
|
+
if (laneOf(candidate) !== lane) continue;
|
|
444
|
+
prev = candidate;
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
const validIds = prev && prev.role === "assistant" ? collectIds(prev.content, "tool_call", (b) => b.id) : /* @__PURE__ */ new Set();
|
|
448
|
+
const kept = [];
|
|
449
|
+
const seenCallIds = /* @__PURE__ */ new Set();
|
|
450
|
+
let dropped = false;
|
|
451
|
+
for (const block of turn.content) {
|
|
452
|
+
if (block.type !== "tool_result") {
|
|
453
|
+
kept.push(block);
|
|
454
|
+
continue;
|
|
536
455
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
text: textContent,
|
|
546
|
-
tool_calls: toolCalls.map((tc) => ({
|
|
547
|
-
id: tc.id,
|
|
548
|
-
type: "function",
|
|
549
|
-
function: {
|
|
550
|
-
name: tc.name,
|
|
551
|
-
arguments: JSON.stringify(tc.input)
|
|
552
|
-
}
|
|
553
|
-
}))
|
|
456
|
+
if (seenCallIds.has(block.callId)) {
|
|
457
|
+
dropped = true;
|
|
458
|
+
onDrop({
|
|
459
|
+
kind: "duplicate-tool-result",
|
|
460
|
+
callId: block.callId,
|
|
461
|
+
index: i
|
|
462
|
+
});
|
|
463
|
+
continue;
|
|
554
464
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
for (const b of audioBlocks) parts.push({
|
|
567
|
-
type: "input_audio",
|
|
568
|
-
input_audio: {
|
|
569
|
-
data: b.data,
|
|
570
|
-
format: audioFormatFromMediaType(b.mediaType)
|
|
465
|
+
seenCallIds.add(block.callId);
|
|
466
|
+
if (!validIds.has(block.callId)) {
|
|
467
|
+
dropped = true;
|
|
468
|
+
onDrop({
|
|
469
|
+
kind: "orphan-tool-result",
|
|
470
|
+
callId: block.callId,
|
|
471
|
+
index: i
|
|
472
|
+
});
|
|
473
|
+
continue;
|
|
571
474
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
475
|
+
kept.push(block);
|
|
476
|
+
}
|
|
477
|
+
if (!dropped) {
|
|
478
|
+
out.push(turn);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
changed = true;
|
|
482
|
+
if (kept.length === 0) {
|
|
483
|
+
onDrop({
|
|
484
|
+
kind: "empty-turn",
|
|
485
|
+
index: i
|
|
582
486
|
});
|
|
487
|
+
continue;
|
|
583
488
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
489
|
+
out.push({
|
|
490
|
+
...turn,
|
|
491
|
+
content: kept
|
|
587
492
|
});
|
|
588
|
-
return {
|
|
589
|
-
role: msg.role,
|
|
590
|
-
content: parts
|
|
591
|
-
};
|
|
592
493
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
};
|
|
597
|
-
if (textBlocks.length > 1) return {
|
|
598
|
-
role: msg.role,
|
|
599
|
-
content: textBlocks.map((b) => ({
|
|
600
|
-
type: "text",
|
|
601
|
-
text: b.text
|
|
602
|
-
}))
|
|
603
|
-
};
|
|
494
|
+
return changed ? out : turns;
|
|
495
|
+
}
|
|
496
|
+
function syntheticResultBlock(callId) {
|
|
604
497
|
return {
|
|
605
|
-
|
|
606
|
-
|
|
498
|
+
type: "tool_result",
|
|
499
|
+
callId,
|
|
500
|
+
output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,
|
|
501
|
+
isError: true
|
|
607
502
|
};
|
|
608
503
|
}
|
|
504
|
+
function collectResultIds(content) {
|
|
505
|
+
const ids = /* @__PURE__ */ new Set();
|
|
506
|
+
for (const block of content) if (block.type === "tool_result") ids.add(block.callId);
|
|
507
|
+
return ids;
|
|
508
|
+
}
|
|
509
|
+
function collectIds(content, type, getId) {
|
|
510
|
+
const ids = /* @__PURE__ */ new Set();
|
|
511
|
+
for (const block of content) if (block.type === type) ids.add(getId(block));
|
|
512
|
+
return ids;
|
|
513
|
+
}
|
|
514
|
+
//#endregion
|
|
515
|
+
//#region src/session/messages.ts
|
|
609
516
|
/**
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
517
|
+
* Decode an Anthropic `tool_result.content` wire value back to zidane's canonical
|
|
518
|
+
* `string | ToolResultContent[]` shape.
|
|
519
|
+
*
|
|
520
|
+
* Anthropic accepts three shapes in the wire: `string`, `Array<{type:'text',text}>`,
|
|
521
|
+
* or `Array<{type:'text'|'image', ...}>`. We collapse to `string` only when every
|
|
522
|
+
* block is text to keep simple cases readable.
|
|
615
523
|
*/
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
const
|
|
524
|
+
function decodeAnthropicToolResultContent(content) {
|
|
525
|
+
if (typeof content === "string") return content;
|
|
526
|
+
if (!Array.isArray(content)) return JSON.stringify(content);
|
|
527
|
+
const blocks = [];
|
|
528
|
+
for (const raw of content) {
|
|
529
|
+
if (!raw || typeof raw !== "object") continue;
|
|
530
|
+
const b = raw;
|
|
531
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
532
|
+
blocks.push({
|
|
533
|
+
type: "text",
|
|
534
|
+
text: b.text
|
|
535
|
+
});
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
if (b.type === "image" && b.source && typeof b.source === "object") {
|
|
539
|
+
const src = b.source;
|
|
540
|
+
if (src.type === "base64" && typeof src.data === "string" && typeof src.media_type === "string") {
|
|
541
|
+
blocks.push({
|
|
542
|
+
type: "image",
|
|
543
|
+
mediaType: src.media_type,
|
|
544
|
+
data: src.data
|
|
545
|
+
});
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
blocks.push({
|
|
550
|
+
type: "text",
|
|
551
|
+
text: JSON.stringify(raw)
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
if (blocks.length === 0) return "";
|
|
555
|
+
if (!blocks.some((b) => b.type !== "text")) return blocks.map((b) => b.text).join("\n");
|
|
556
|
+
return blocks;
|
|
557
|
+
}
|
|
632
558
|
/**
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
* provider enforces.
|
|
636
|
-
*
|
|
637
|
-
* Anthropic 400s on orphans with `'tool_use' ids were found without
|
|
638
|
-
* 'tool_result' blocks immediately after` (and its inverse, `tool_result
|
|
639
|
-
* must be preceded by a tool_call with the same toolCallId`). OpenAI's
|
|
640
|
-
* Chat Completions API rejects most mismatches with a 400 too.
|
|
641
|
-
*
|
|
642
|
-
* Six repair modes — modeled after Anthropic's Claude Code defenses:
|
|
643
|
-
*
|
|
644
|
-
* | # | Corruption | Repair |
|
|
645
|
-
* |---|------------|--------|
|
|
646
|
-
* | 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` |
|
|
647
|
-
* | 2 | assistant `tool_use` followed by nothing (or by a non-user msg) | **Insert** a new synthetic user message with the same placeholder |
|
|
648
|
-
* | 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 |
|
|
649
|
-
* | 4 | duplicate `tool_call.id` across assistant messages (CC-1212) | **Strip** later instances + their matching tool_results |
|
|
650
|
-
* | 5 | duplicate `tool_result.callId` within a single user message | **Dedupe** by `callId`, keep first |
|
|
651
|
-
* | 6 | assistant message emptied by mode-4 stripping | **Replace** content with {@link TOOL_USE_INTERRUPTED_MARKER} text block |
|
|
559
|
+
* Build an Anthropic `image.source` block, reconciling the declared media
|
|
560
|
+
* type against the actual magic bytes.
|
|
652
561
|
*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
660
|
-
* lost" and can retry intelligently.
|
|
562
|
+
* Anthropic's API rejects the entire request when `media_type` disagrees
|
|
563
|
+
* with the payload (`messages.N.content.K.image.source.base64: The image
|
|
564
|
+
* was specified using the X media type, but the image appears to be a
|
|
565
|
+
* Y image`). The upstream sources of `{type:'image'}` blocks each sniff
|
|
566
|
+
* before producing the block (read_file / MCP / TUI paste), but this
|
|
567
|
+
* wire-boundary reconciliation is a final safety net for any future code
|
|
568
|
+
* path or host-supplied prompt part that slips through without sniffing.
|
|
661
569
|
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
|
|
570
|
+
* The reconciler is conservative: it overrides only when the magic bytes
|
|
571
|
+
* positively identify a *different* known format. Unknown / too-short
|
|
572
|
+
* payloads pass `mediaType` through unchanged.
|
|
573
|
+
*/
|
|
574
|
+
function anthropicImageSource(mediaType, data) {
|
|
575
|
+
return {
|
|
576
|
+
type: "base64",
|
|
577
|
+
media_type: reconcileImageMediaType(mediaType, data),
|
|
578
|
+
data
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function anthropicDocumentBlocks(doc) {
|
|
582
|
+
if (doc.encoding === "base64" && doc.mediaType === "application/pdf") return [{
|
|
583
|
+
type: "document",
|
|
584
|
+
source: {
|
|
585
|
+
type: "base64",
|
|
586
|
+
media_type: "application/pdf",
|
|
587
|
+
data: doc.data
|
|
588
|
+
},
|
|
589
|
+
...doc.name ? { title: doc.name } : {}
|
|
590
|
+
}];
|
|
591
|
+
return [{
|
|
592
|
+
type: "text",
|
|
593
|
+
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>`
|
|
594
|
+
}];
|
|
595
|
+
}
|
|
596
|
+
function documentMarker(doc) {
|
|
597
|
+
return documentBlockMarker(doc, "document attached");
|
|
598
|
+
}
|
|
599
|
+
function isContentRefBlock(block) {
|
|
600
|
+
return (block.type === "image" || block.type === "document") && typeof block.ref === "string";
|
|
601
|
+
}
|
|
602
|
+
function unresolvedContentRefError(block) {
|
|
603
|
+
return /* @__PURE__ */ new Error(`Cannot project content ref "${block.ref}" to provider wire messages without resolveContentRef.`);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Coerce a tool-call id to Anthropic's wire constraint `^[a-zA-Z0-9_-]+$`.
|
|
666
607
|
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
608
|
+
* Sessions started under another provider (OpenAI-compat / OpenRouter /
|
|
609
|
+
* Cerebras) or carrying MCP-namespaced calls can hold ids with `.`, `:`,
|
|
610
|
+
* `/` or other characters that 400 the request the moment the user switches
|
|
611
|
+
* to an Anthropic model (`messages.N.content.M.tool_use.id: String should
|
|
612
|
+
* match pattern '^[a-zA-Z0-9_-]+$'`).
|
|
669
613
|
*
|
|
670
|
-
*
|
|
671
|
-
*
|
|
614
|
+
* The mapping is deterministic — every disallowed char becomes `_` — so a
|
|
615
|
+
* `tool_use` and its paired `tool_result` (sanitized identically below) keep
|
|
616
|
+
* matching ids and the pairing survives. When sanitization actually changed
|
|
617
|
+
* the id, a short hash of the ORIGINAL id is appended so distinct inputs
|
|
618
|
+
* that would collapse to the same replacement (`call.1` vs `call_1` vs
|
|
619
|
+
* `call:1`) stay distinct on the wire. Empty ids get a stable placeholder
|
|
620
|
+
* so the block stays well-formed.
|
|
672
621
|
*/
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if (msg.role !== "assistant") continue;
|
|
680
|
-
for (const block of msg.content) if (block.type === "tool_call" && !firstSeenAt.has(block.id)) firstSeenAt.set(block.id, i);
|
|
681
|
-
}
|
|
682
|
-
const pendingOrphansByUserIndex = /* @__PURE__ */ new Map();
|
|
683
|
-
let changed = false;
|
|
684
|
-
const markChanged = () => {
|
|
685
|
-
changed = true;
|
|
686
|
-
};
|
|
687
|
-
const out = [];
|
|
688
|
-
for (let i = 0; i < messages.length; i++) {
|
|
689
|
-
const msg = messages[i];
|
|
690
|
-
if (msg.role === "assistant") processAssistantMessage({
|
|
691
|
-
index: i,
|
|
692
|
-
msg,
|
|
693
|
-
messages,
|
|
694
|
-
firstSeenAt,
|
|
695
|
-
pendingOrphansByUserIndex,
|
|
696
|
-
out,
|
|
697
|
-
fireRepair,
|
|
698
|
-
markChanged
|
|
699
|
-
});
|
|
700
|
-
else processUserMessage({
|
|
701
|
-
index: i,
|
|
702
|
-
msg,
|
|
703
|
-
pendingOrphansByUserIndex,
|
|
704
|
-
out,
|
|
705
|
-
fireRepair,
|
|
706
|
-
markChanged
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
return changed ? out : messages;
|
|
622
|
+
const ANTHROPIC_TOOL_ID_INVALID = /[^\w-]/g;
|
|
623
|
+
function sanitizeAnthropicToolId(id) {
|
|
624
|
+
if (!id) return "tool_0";
|
|
625
|
+
const sanitized = id.replace(ANTHROPIC_TOOL_ID_INVALID, "_");
|
|
626
|
+
if (sanitized === id) return id;
|
|
627
|
+
return `${sanitized}_${fnv1aBase36(id)}`;
|
|
710
628
|
}
|
|
711
629
|
/**
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
* one), and emit a mode-6 marker when dedup empties the message.
|
|
630
|
+
* Encode zidane's canonical `string | ToolResultContent[]` shape to Anthropic's
|
|
631
|
+
* wire format for `tool_result.content`.
|
|
715
632
|
*/
|
|
716
|
-
function
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
callId: block.id,
|
|
730
|
-
messageIndex: index
|
|
731
|
-
});
|
|
732
|
-
continue;
|
|
733
|
-
}
|
|
734
|
-
seenHere.add(block.id);
|
|
633
|
+
function encodeAnthropicToolResultContent(output) {
|
|
634
|
+
if (typeof output === "string") return output;
|
|
635
|
+
return output.map((b) => {
|
|
636
|
+
if (b.type === "text") return {
|
|
637
|
+
type: "text",
|
|
638
|
+
text: b.text
|
|
639
|
+
};
|
|
640
|
+
if (b.type === "document") {
|
|
641
|
+
assertResolvedMediaBlock(b, "Anthropic wire messages");
|
|
642
|
+
return {
|
|
643
|
+
type: "text",
|
|
644
|
+
text: documentMarker(b)
|
|
645
|
+
};
|
|
735
646
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
out,
|
|
750
|
-
fireRepair,
|
|
751
|
-
markChanged
|
|
752
|
-
});
|
|
753
|
-
if (orphans.length === 0) return;
|
|
754
|
-
if (nextIsUser) {
|
|
755
|
-
markChanged();
|
|
756
|
-
pendingOrphansByUserIndex.set(index + 1, orphans);
|
|
757
|
-
for (const callId of orphans) fireRepair({
|
|
758
|
-
mode: "orphan-tool-use-prepend",
|
|
759
|
-
callId,
|
|
760
|
-
messageIndex: index
|
|
761
|
-
});
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
markChanged();
|
|
765
|
-
out.push({
|
|
766
|
-
role: "user",
|
|
767
|
-
content: orphans.map((callId) => syntheticResultBlock(callId))
|
|
768
|
-
});
|
|
769
|
-
for (const callId of orphans) fireRepair({
|
|
770
|
-
mode: "orphan-tool-use-append",
|
|
771
|
-
callId,
|
|
772
|
-
messageIndex: index
|
|
647
|
+
if (b.type === "audio") return {
|
|
648
|
+
type: "text",
|
|
649
|
+
text: `[audio omitted: ${b.mediaType}]`
|
|
650
|
+
};
|
|
651
|
+
if (b.type === "video") return {
|
|
652
|
+
type: "text",
|
|
653
|
+
text: `[video omitted: ${b.mediaType}]`
|
|
654
|
+
};
|
|
655
|
+
assertResolvedMediaBlock(b, "Anthropic wire messages");
|
|
656
|
+
return {
|
|
657
|
+
type: "image",
|
|
658
|
+
source: anthropicImageSource(b.mediaType, b.data)
|
|
659
|
+
};
|
|
773
660
|
});
|
|
774
661
|
}
|
|
775
|
-
function
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
662
|
+
function fromAnthropic(msg) {
|
|
663
|
+
const role = msg.role;
|
|
664
|
+
const content = [];
|
|
665
|
+
if (typeof msg.content === "string") {
|
|
666
|
+
content.push({
|
|
667
|
+
type: "text",
|
|
668
|
+
text: msg.content
|
|
669
|
+
});
|
|
670
|
+
return {
|
|
671
|
+
role,
|
|
672
|
+
content
|
|
673
|
+
};
|
|
780
674
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
text: TOOL_USE_INTERRUPTED_MARKER
|
|
788
|
-
}]
|
|
675
|
+
if (Array.isArray(msg.content)) for (const block of msg.content) {
|
|
676
|
+
if (!block || typeof block !== "object") continue;
|
|
677
|
+
const b = block;
|
|
678
|
+
if (b.type === "text") content.push({
|
|
679
|
+
type: "text",
|
|
680
|
+
text: b.text
|
|
789
681
|
});
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
682
|
+
else if (b.type === "image") {
|
|
683
|
+
const source = b.source;
|
|
684
|
+
if (source?.type === "base64") content.push({
|
|
685
|
+
type: "image",
|
|
686
|
+
mediaType: source.media_type,
|
|
687
|
+
data: source.data
|
|
688
|
+
});
|
|
689
|
+
} else if (b.type === "document") {
|
|
690
|
+
const source = b.source;
|
|
691
|
+
if (source?.type === "base64" && typeof source.media_type === "string" && typeof source.data === "string") content.push({
|
|
692
|
+
type: "document",
|
|
693
|
+
mediaType: source.media_type,
|
|
694
|
+
data: source.data,
|
|
695
|
+
encoding: "base64",
|
|
696
|
+
...typeof b.title === "string" ? { name: b.title } : {}
|
|
697
|
+
});
|
|
698
|
+
else if (source?.type === "text" && typeof source.media_type === "string" && typeof source.data === "string") content.push({
|
|
699
|
+
type: "document",
|
|
700
|
+
mediaType: source.media_type,
|
|
701
|
+
data: source.data,
|
|
702
|
+
encoding: "text",
|
|
703
|
+
...typeof b.title === "string" ? { name: b.title } : {}
|
|
704
|
+
});
|
|
705
|
+
} else if (b.type === "tool_use") content.push({
|
|
706
|
+
type: "tool_call",
|
|
707
|
+
id: b.id,
|
|
708
|
+
name: b.name,
|
|
709
|
+
input: b.input
|
|
710
|
+
});
|
|
711
|
+
else if (b.type === "tool_result") {
|
|
712
|
+
const output = decodeAnthropicToolResultContent(b.content);
|
|
713
|
+
const block = {
|
|
714
|
+
type: "tool_result",
|
|
715
|
+
callId: b.tool_use_id,
|
|
716
|
+
output
|
|
717
|
+
};
|
|
718
|
+
if (b.is_error === true) block.isError = true;
|
|
719
|
+
content.push(block);
|
|
720
|
+
} else if (b.type === "thinking") {
|
|
721
|
+
const block = {
|
|
722
|
+
type: "thinking",
|
|
723
|
+
text: b.thinking ?? ""
|
|
724
|
+
};
|
|
725
|
+
if (typeof b.signature === "string") {
|
|
726
|
+
block.signature = b.signature;
|
|
727
|
+
block.signatureProducer = "anthropic";
|
|
728
|
+
}
|
|
729
|
+
content.push(block);
|
|
730
|
+
} else if (b.type === "redacted_thinking") content.push({
|
|
731
|
+
type: "redacted_thinking",
|
|
732
|
+
data: b.data ?? ""
|
|
733
|
+
});
|
|
734
|
+
else if (b.type === "server_tool_use") content.push({
|
|
735
|
+
type: "server_tool_use",
|
|
736
|
+
producer: "anthropic",
|
|
737
|
+
id: b.id ?? "",
|
|
738
|
+
name: b.name ?? "",
|
|
739
|
+
input: b.input ?? {}
|
|
740
|
+
});
|
|
741
|
+
else if (b.type === "web_search_tool_result") content.push({
|
|
742
|
+
type: "server_tool_result",
|
|
743
|
+
producer: "anthropic",
|
|
744
|
+
toolUseId: b.tool_use_id ?? "",
|
|
745
|
+
toolName: "web_search",
|
|
746
|
+
content: b.content
|
|
793
747
|
});
|
|
794
|
-
return;
|
|
795
748
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
content
|
|
799
|
-
}
|
|
749
|
+
return {
|
|
750
|
+
role,
|
|
751
|
+
content
|
|
752
|
+
};
|
|
800
753
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
if (
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
754
|
+
function fromOpenAI(msg) {
|
|
755
|
+
const role = msg.role;
|
|
756
|
+
const content = [];
|
|
757
|
+
const c = msg.content;
|
|
758
|
+
if (c == null) return {
|
|
759
|
+
role,
|
|
760
|
+
content
|
|
761
|
+
};
|
|
762
|
+
if (typeof c === "string") {
|
|
763
|
+
content.push({
|
|
764
|
+
type: "text",
|
|
765
|
+
text: c
|
|
766
|
+
});
|
|
767
|
+
return {
|
|
768
|
+
role,
|
|
769
|
+
content
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
if (typeof c === "object" && !Array.isArray(c) && c._tag === "__zidane_assistant_tc__") {
|
|
773
|
+
const tagged = c;
|
|
774
|
+
if (typeof tagged.text === "string" && tagged.text) content.push({
|
|
775
|
+
type: "text",
|
|
776
|
+
text: tagged.text
|
|
777
|
+
});
|
|
778
|
+
if (Array.isArray(tagged.tool_calls)) for (const raw of tagged.tool_calls) {
|
|
779
|
+
if (!raw || typeof raw !== "object") continue;
|
|
780
|
+
const tc = raw;
|
|
781
|
+
const rawArgs = tc.function?.arguments;
|
|
782
|
+
let input = {};
|
|
783
|
+
if (typeof rawArgs === "string") try {
|
|
784
|
+
input = JSON.parse(rawArgs);
|
|
785
|
+
} catch {
|
|
786
|
+
input = {};
|
|
787
|
+
}
|
|
788
|
+
else if (rawArgs && typeof rawArgs === "object") input = rawArgs;
|
|
789
|
+
content.push({
|
|
790
|
+
type: "tool_call",
|
|
791
|
+
id: tc.id ?? "",
|
|
792
|
+
name: tc.function?.name ?? "",
|
|
793
|
+
input
|
|
827
794
|
});
|
|
828
|
-
continue;
|
|
829
795
|
}
|
|
830
|
-
|
|
831
|
-
|
|
796
|
+
return {
|
|
797
|
+
role,
|
|
798
|
+
content
|
|
799
|
+
};
|
|
832
800
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
if (
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
801
|
+
if (typeof c === "object" && !Array.isArray(c) && c._tag === "__zidane_tool_results__") {
|
|
802
|
+
const tagged = c;
|
|
803
|
+
if (Array.isArray(tagged.results)) for (const raw of tagged.results) {
|
|
804
|
+
if (!raw || typeof raw !== "object") continue;
|
|
805
|
+
const r = raw;
|
|
806
|
+
content.push({
|
|
807
|
+
type: "tool_result",
|
|
808
|
+
callId: r.tool_call_id ?? "",
|
|
809
|
+
output: r.content ?? ""
|
|
841
810
|
});
|
|
842
|
-
continue;
|
|
843
811
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
812
|
+
if (typeof tagged.text === "string" && tagged.text) content.push({
|
|
813
|
+
type: "text",
|
|
814
|
+
text: tagged.text
|
|
815
|
+
});
|
|
816
|
+
return {
|
|
817
|
+
role,
|
|
818
|
+
content
|
|
819
|
+
};
|
|
850
820
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
content
|
|
821
|
+
if (Array.isArray(c)) {
|
|
822
|
+
for (const block of c) {
|
|
823
|
+
if (!block || typeof block !== "object") continue;
|
|
824
|
+
const b = block;
|
|
825
|
+
if (b.type === "text") content.push({
|
|
856
826
|
type: "text",
|
|
857
|
-
text:
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
|
|
827
|
+
text: b.text
|
|
828
|
+
});
|
|
829
|
+
else if (b.type === "image_url") {
|
|
830
|
+
const imageUrl = b.image_url?.url;
|
|
831
|
+
if (imageUrl?.startsWith("data:")) {
|
|
832
|
+
const [meta, data] = imageUrl.slice(5).split(",", 2);
|
|
833
|
+
const mediaType = meta.replace(";base64", "");
|
|
834
|
+
content.push({
|
|
835
|
+
type: "image",
|
|
836
|
+
mediaType,
|
|
837
|
+
data
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
role,
|
|
844
|
+
content
|
|
845
|
+
};
|
|
861
846
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
content
|
|
865
|
-
}
|
|
847
|
+
return {
|
|
848
|
+
role,
|
|
849
|
+
content
|
|
850
|
+
};
|
|
866
851
|
}
|
|
867
852
|
/**
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
853
|
+
* Single source of truth for whether a signature-bearing reasoning block
|
|
854
|
+
* (`thinking` / `redacted_thinking`) may be forwarded to a provider. Both
|
|
855
|
+
* wire builders ({@link toAnthropic}, the OpenAI assistant-message shaper)
|
|
856
|
+
* and the host-facing {@link sanitizeHistoryForProvider} apply this same
|
|
857
|
+
* policy — keeping them one function means the sanitizer can never drift
|
|
858
|
+
* from what the wire actually sends.
|
|
871
859
|
*
|
|
872
|
-
*
|
|
873
|
-
* tool_use ids (per-turn counters like `call_0`, retry-replay duplicates,
|
|
874
|
-
* Kimi-K2.6 on Fireworks). The wire-time pairing pass
|
|
875
|
-
* ({@link ensureToolResultPairing}) can only STRIP such duplicates — which
|
|
876
|
-
* erases the model's tool calls and results from the wire view, stalls
|
|
877
|
-
* progress, and re-accumulates repairs quadratically because the persisted
|
|
878
|
-
* session keeps the corrupted ids. Re-minting at persist time keeps the
|
|
879
|
-
* pairing intact instead: the call, its execution, and its tool_result all
|
|
880
|
-
* carry the fresh id, so the pairing pass has nothing to strip on any later
|
|
881
|
-
* turn.
|
|
860
|
+
* Rules, each driven by an API constraint hit in production:
|
|
882
861
|
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
862
|
+
* - target `anthropic`:
|
|
863
|
+
* - `redacted_thinking` → keep (native Anthropic block; must be echoed).
|
|
864
|
+
* - `thinking` tagged `openai` → drop (OpenAI `encrypted_content` is not
|
|
865
|
+
* a valid Anthropic HMAC; 400s).
|
|
866
|
+
* - unsigned `thinking` → drop (`ThinkingBlockParam` requires a
|
|
867
|
+
* signature; unsigned blocks come from non-signing producers).
|
|
868
|
+
* - signed but UNTAGGED `thinking` (pre-tagging sessions) → keep: it is
|
|
869
|
+
* most plausibly legacy Anthropic output, and the signature-400
|
|
870
|
+
* recovery converges if it is not.
|
|
886
871
|
*
|
|
887
|
-
*
|
|
888
|
-
* `
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
872
|
+
* - target `openai`:
|
|
873
|
+
* - `redacted_thinking` → drop (Anthropic-only block type).
|
|
874
|
+
* - `thinking` tagged `anthropic` → drop (an Anthropic HMAC is not
|
|
875
|
+
* valid `encrypted_content`).
|
|
876
|
+
* - signed but UNTAGGED `thinking` → strip-signature: unlike the
|
|
877
|
+
* Anthropic direction there is no signature-error recovery on this
|
|
878
|
+
* path, so forwarding a possibly-foreign signature risks an
|
|
879
|
+
* unrecoverable 400 — the reasoning text survives, only the opaque
|
|
880
|
+
* signature is withheld.
|
|
881
|
+
*/
|
|
882
|
+
function reasoningWireAction(block, target) {
|
|
883
|
+
if (block.type === "redacted_thinking") return target === "anthropic" ? "keep" : "drop";
|
|
884
|
+
if (target === "anthropic") {
|
|
885
|
+
if (block.signatureProducer === "openai") return "drop";
|
|
886
|
+
if (!block.signature) return "drop";
|
|
887
|
+
return "keep";
|
|
888
|
+
}
|
|
889
|
+
if (block.signatureProducer === "anthropic") return "drop";
|
|
890
|
+
if (block.signature && block.signatureProducer !== "openai") return "strip-signature";
|
|
891
|
+
return "keep";
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Host-facing history sanitizer — applies {@link reasoningWireAction} to a
|
|
895
|
+
* turn set BEFORE it is handed to an agent targeting `target`. The in-loop
|
|
896
|
+
* wire builders already enforce the same policy per call, so this is for
|
|
897
|
+
* hosts that seed history from an external durable store (or hydrate
|
|
898
|
+
* pre-provenance-tagging sessions) and want the poison removed from their
|
|
899
|
+
* source of truth in one call instead of re-deriving block semantics.
|
|
892
900
|
*
|
|
893
|
-
*
|
|
901
|
+
* Returns new turn objects only where something changed (unchanged turns
|
|
902
|
+
* pass through by reference). A turn whose content empties keeps the same
|
|
903
|
+
* neutral marker the wire repair uses, so role boundaries stay intact.
|
|
894
904
|
*/
|
|
895
|
-
function
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
905
|
+
function sanitizeHistoryForProvider(turns, target) {
|
|
906
|
+
let removed = 0;
|
|
907
|
+
let stripped = 0;
|
|
908
|
+
return {
|
|
909
|
+
turns: turns.map((turn) => {
|
|
910
|
+
let changed = false;
|
|
911
|
+
const content = [];
|
|
912
|
+
for (const block of turn.content) {
|
|
913
|
+
if (block.type !== "thinking" && block.type !== "redacted_thinking") {
|
|
914
|
+
content.push(block);
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
const action = reasoningWireAction(block, target);
|
|
918
|
+
if (action === "drop") {
|
|
919
|
+
removed += 1;
|
|
920
|
+
changed = true;
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (action === "strip-signature" && block.type === "thinking") {
|
|
924
|
+
const { signature: _sig, signatureProducer: _producer, ...rest } = block;
|
|
925
|
+
content.push(rest);
|
|
926
|
+
stripped += 1;
|
|
927
|
+
changed = true;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
content.push(block);
|
|
931
|
+
}
|
|
932
|
+
if (!changed) return turn;
|
|
933
|
+
return {
|
|
934
|
+
...turn,
|
|
935
|
+
content: content.length > 0 ? content : [{
|
|
936
|
+
type: "text",
|
|
937
|
+
text: "[historical reasoning omitted]"
|
|
938
|
+
}]
|
|
939
|
+
};
|
|
940
|
+
}),
|
|
941
|
+
removed,
|
|
942
|
+
stripped
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
function toAnthropic(msg) {
|
|
946
|
+
const nonEmpty = msg.content.filter((b) => {
|
|
947
|
+
if (b.type === "provider_reasoning") return false;
|
|
948
|
+
if (b.type !== "thinking" && b.type !== "redacted_thinking") return true;
|
|
949
|
+
return reasoningWireAction(b, "anthropic") === "keep";
|
|
950
|
+
}).flatMap((block) => {
|
|
951
|
+
switch (block.type) {
|
|
952
|
+
case "text": return [{
|
|
953
|
+
type: "text",
|
|
954
|
+
text: block.text
|
|
955
|
+
}];
|
|
956
|
+
case "image":
|
|
957
|
+
assertResolvedMediaBlock(block, "Anthropic wire messages");
|
|
958
|
+
return [{
|
|
959
|
+
type: "image",
|
|
960
|
+
source: anthropicImageSource(block.mediaType, block.data)
|
|
961
|
+
}];
|
|
962
|
+
case "audio": return [{
|
|
963
|
+
type: "text",
|
|
964
|
+
text: `[audio omitted: ${block.mediaType}]`
|
|
965
|
+
}];
|
|
966
|
+
case "video": return [{
|
|
967
|
+
type: "text",
|
|
968
|
+
text: `[video omitted: ${block.mediaType}]`
|
|
969
|
+
}];
|
|
970
|
+
case "document":
|
|
971
|
+
assertResolvedMediaBlock(block, "Anthropic wire messages");
|
|
972
|
+
return anthropicDocumentBlocks(block);
|
|
973
|
+
case "tool_call": return [{
|
|
974
|
+
type: "tool_use",
|
|
975
|
+
id: sanitizeAnthropicToolId(block.id),
|
|
976
|
+
name: block.name,
|
|
977
|
+
input: block.input
|
|
978
|
+
}];
|
|
979
|
+
case "tool_result": {
|
|
980
|
+
const out = {
|
|
981
|
+
type: "tool_result",
|
|
982
|
+
tool_use_id: sanitizeAnthropicToolId(block.callId),
|
|
983
|
+
content: encodeAnthropicToolResultContent(block.output)
|
|
984
|
+
};
|
|
985
|
+
if (block.isError) out.is_error = true;
|
|
986
|
+
return [out, ...(typeof block.output === "string" ? [] : block.output.filter((part) => part.type === "document" && typeof part.data === "string")).flatMap(anthropicDocumentBlocks)];
|
|
987
|
+
}
|
|
988
|
+
case "thinking": return [{
|
|
989
|
+
type: "thinking",
|
|
990
|
+
thinking: block.text,
|
|
991
|
+
signature: block.signature ?? ""
|
|
992
|
+
}];
|
|
993
|
+
case "redacted_thinking": return [{
|
|
994
|
+
type: "redacted_thinking",
|
|
995
|
+
data: block.data
|
|
996
|
+
}];
|
|
997
|
+
case "server_tool_use": return [{
|
|
998
|
+
type: "server_tool_use",
|
|
999
|
+
id: sanitizeAnthropicToolId(block.id),
|
|
1000
|
+
name: block.name,
|
|
1001
|
+
input: block.input
|
|
1002
|
+
}];
|
|
1003
|
+
case "server_tool_result": return [{
|
|
1004
|
+
type: "web_search_tool_result",
|
|
1005
|
+
tool_use_id: sanitizeAnthropicToolId(block.toolUseId),
|
|
1006
|
+
content: block.content
|
|
1007
|
+
}];
|
|
1008
|
+
default: return [{
|
|
1009
|
+
type: "text",
|
|
1010
|
+
text: ""
|
|
1011
|
+
}];
|
|
911
1012
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
if (!changed) return {
|
|
922
|
-
toolCalls,
|
|
923
|
-
content,
|
|
924
|
-
reminted
|
|
1013
|
+
}).filter((b) => !(b.type === "text" && b.text.length === 0));
|
|
1014
|
+
if (nonEmpty.length === 0) return {
|
|
1015
|
+
role: msg.role,
|
|
1016
|
+
content: ""
|
|
1017
|
+
};
|
|
1018
|
+
const first = nonEmpty[0];
|
|
1019
|
+
if (nonEmpty.length === 1 && first.type === "text") return {
|
|
1020
|
+
role: msg.role,
|
|
1021
|
+
content: first.text
|
|
925
1022
|
};
|
|
926
1023
|
return {
|
|
927
|
-
|
|
928
|
-
content:
|
|
929
|
-
if (block.type !== "tool_call") return block;
|
|
930
|
-
const finalId = finalIdQueues.get(block.id)?.shift();
|
|
931
|
-
if (finalId === void 0 || finalId === block.id) return block;
|
|
932
|
-
return {
|
|
933
|
-
...block,
|
|
934
|
-
id: finalId
|
|
935
|
-
};
|
|
936
|
-
}),
|
|
937
|
-
reminted
|
|
1024
|
+
role: msg.role,
|
|
1025
|
+
content: nonEmpty
|
|
938
1026
|
};
|
|
939
1027
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
* legitimately reuse the same id across lanes. Callers that know the run
|
|
962
|
-
* topology (see `createSession`) pass `laneForRun` to widen lanes to true
|
|
963
|
-
* wire views: all depth-0 runs share the main conversation view, and a
|
|
964
|
-
* resumed subagent shares its resume-chain ancestors' view — duplicates
|
|
965
|
-
* across those runIds DO reach a single request (wire-time
|
|
966
|
-
* `ensureToolResultPairing` mode 4 would strip them on every send) and are
|
|
967
|
-
* healed here.
|
|
968
|
-
*
|
|
969
|
-
* Orphan `tool_use` blocks (a call whose result was never persisted —
|
|
970
|
-
* interrupted run) are deliberately KEPT: they are authentic model output,
|
|
971
|
-
* and the wire-time pass patches them with a synthetic result on every send
|
|
972
|
-
* without touching history.
|
|
973
|
-
*
|
|
974
|
-
* Deletions cascade by design: dropping a duplicate `tool_call` orphans its
|
|
975
|
-
* `tool_result`, which is then dropped too; a turn emptied by either is
|
|
976
|
-
* removed entirely.
|
|
977
|
-
*
|
|
978
|
-
* Safe to persist: deterministic, idempotent (re-running on healed output is
|
|
979
|
-
* a no-op returning the same reference), and fabricates nothing.
|
|
980
|
-
*
|
|
981
|
-
* Pure: does not mutate input turns or their content arrays.
|
|
982
|
-
*/
|
|
983
|
-
function dropInvalidToolPairings(turns, options = {}) {
|
|
984
|
-
if (turns.length === 0) return turns;
|
|
985
|
-
const onDrop = options.onDrop ?? (() => {});
|
|
986
|
-
const laneForRun = options.laneForRun ?? ((runId) => runId ?? "");
|
|
987
|
-
const laneOf = (turn) => laneForRun(turn.runId);
|
|
988
|
-
const laneKey = (turn, id) => `${laneOf(turn)}\u0000${id}`;
|
|
989
|
-
const firstSeenAt = /* @__PURE__ */ new Map();
|
|
990
|
-
for (let i = 0; i < turns.length; i++) {
|
|
991
|
-
const turn = turns[i];
|
|
992
|
-
if (turn.role !== "assistant") continue;
|
|
993
|
-
for (const block of turn.content) if (block.type === "tool_call" && !firstSeenAt.has(laneKey(turn, block.id))) firstSeenAt.set(laneKey(turn, block.id), i);
|
|
1028
|
+
function toOpenAI(msg) {
|
|
1029
|
+
const toolCalls = msg.content.filter((b) => b.type === "tool_call");
|
|
1030
|
+
const toolResults = msg.content.filter((b) => b.type === "tool_result");
|
|
1031
|
+
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
1032
|
+
const imageBlocks = msg.content.filter((b) => b.type === "image");
|
|
1033
|
+
const audioBlocks = msg.content.filter((b) => b.type === "audio");
|
|
1034
|
+
const videoBlocks = msg.content.filter((b) => b.type === "video");
|
|
1035
|
+
const documentBlocks = msg.content.filter((b) => b.type === "document");
|
|
1036
|
+
if (toolResults.length > 0) {
|
|
1037
|
+
const siblingText = textBlocks.map((b) => b.text).join("\n");
|
|
1038
|
+
return {
|
|
1039
|
+
role: msg.role,
|
|
1040
|
+
content: {
|
|
1041
|
+
_tag: TOOL_RESULTS_TAG,
|
|
1042
|
+
results: toolResults.map((tr) => ({
|
|
1043
|
+
tool_call_id: tr.callId,
|
|
1044
|
+
content: tr.output
|
|
1045
|
+
})),
|
|
1046
|
+
...siblingText ? { text: siblingText } : {}
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
994
1049
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
kind: "duplicate-tool-call",
|
|
1009
|
-
callId: block.id,
|
|
1010
|
-
index: i
|
|
1011
|
-
});
|
|
1012
|
-
continue;
|
|
1050
|
+
if (toolCalls.length > 0) {
|
|
1051
|
+
const textContent = textBlocks.length > 0 ? textBlocks[0].text : null;
|
|
1052
|
+
return {
|
|
1053
|
+
role: msg.role,
|
|
1054
|
+
content: {
|
|
1055
|
+
_tag: ASSISTANT_TOOL_CALLS_TAG,
|
|
1056
|
+
text: textContent,
|
|
1057
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
1058
|
+
id: tc.id,
|
|
1059
|
+
type: "function",
|
|
1060
|
+
function: {
|
|
1061
|
+
name: tc.name,
|
|
1062
|
+
arguments: JSON.stringify(tc.input)
|
|
1013
1063
|
}
|
|
1014
|
-
|
|
1015
|
-
}
|
|
1016
|
-
kept.push(block);
|
|
1017
|
-
}
|
|
1018
|
-
if (!dropped) {
|
|
1019
|
-
out.push(turn);
|
|
1020
|
-
continue;
|
|
1021
|
-
}
|
|
1022
|
-
changed = true;
|
|
1023
|
-
const onlyReasoning = kept.every((b) => b.type === "thinking" || b.type === "redacted_thinking" || b.type === "provider_reasoning");
|
|
1024
|
-
if (kept.length === 0 || onlyReasoning) {
|
|
1025
|
-
onDrop({
|
|
1026
|
-
kind: "empty-turn",
|
|
1027
|
-
index: i
|
|
1028
|
-
});
|
|
1029
|
-
continue;
|
|
1064
|
+
}))
|
|
1030
1065
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
if (imageBlocks.length > 0 || audioBlocks.length > 0 || videoBlocks.length > 0 || documentBlocks.length > 0) {
|
|
1069
|
+
const parts = [];
|
|
1070
|
+
for (const img of imageBlocks) {
|
|
1071
|
+
assertResolvedMediaBlock(img, "OpenAI-compatible wire messages");
|
|
1072
|
+
parts.push({
|
|
1073
|
+
type: "image_url",
|
|
1074
|
+
image_url: { url: `data:${img.mediaType};base64,${img.data}` }
|
|
1034
1075
|
});
|
|
1035
|
-
continue;
|
|
1036
|
-
}
|
|
1037
|
-
if (turn.role !== "user") {
|
|
1038
|
-
out.push(turn);
|
|
1039
|
-
continue;
|
|
1040
|
-
}
|
|
1041
|
-
const lane = laneOf(turn);
|
|
1042
|
-
let prev = null;
|
|
1043
|
-
for (let j = out.length - 1; j >= 0; j--) {
|
|
1044
|
-
const candidate = out[j];
|
|
1045
|
-
if (candidate.role === "system" || candidate.role === "command") continue;
|
|
1046
|
-
if (laneOf(candidate) !== lane) continue;
|
|
1047
|
-
prev = candidate;
|
|
1048
|
-
break;
|
|
1049
1076
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
if (block.type !== "tool_result") {
|
|
1056
|
-
kept.push(block);
|
|
1057
|
-
continue;
|
|
1058
|
-
}
|
|
1059
|
-
if (seenCallIds.has(block.callId)) {
|
|
1060
|
-
dropped = true;
|
|
1061
|
-
onDrop({
|
|
1062
|
-
kind: "duplicate-tool-result",
|
|
1063
|
-
callId: block.callId,
|
|
1064
|
-
index: i
|
|
1065
|
-
});
|
|
1066
|
-
continue;
|
|
1067
|
-
}
|
|
1068
|
-
seenCallIds.add(block.callId);
|
|
1069
|
-
if (!validIds.has(block.callId)) {
|
|
1070
|
-
dropped = true;
|
|
1071
|
-
onDrop({
|
|
1072
|
-
kind: "orphan-tool-result",
|
|
1073
|
-
callId: block.callId,
|
|
1074
|
-
index: i
|
|
1075
|
-
});
|
|
1076
|
-
continue;
|
|
1077
|
+
for (const b of audioBlocks) parts.push({
|
|
1078
|
+
type: "input_audio",
|
|
1079
|
+
input_audio: {
|
|
1080
|
+
data: b.data,
|
|
1081
|
+
format: audioFormatFromMediaType(b.mediaType)
|
|
1077
1082
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
index: i
|
|
1083
|
+
});
|
|
1084
|
+
for (const b of videoBlocks) parts.push({
|
|
1085
|
+
type: "video_url",
|
|
1086
|
+
video_url: { url: `data:${b.mediaType};base64,${b.data}` }
|
|
1087
|
+
});
|
|
1088
|
+
for (const b of documentBlocks) {
|
|
1089
|
+
assertResolvedMediaBlock(b, "OpenAI-compatible wire messages");
|
|
1090
|
+
parts.push({
|
|
1091
|
+
type: "text",
|
|
1092
|
+
text: documentMarker(b)
|
|
1089
1093
|
});
|
|
1090
|
-
continue;
|
|
1091
1094
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
+
for (const b of textBlocks) parts.push({
|
|
1096
|
+
type: "text",
|
|
1097
|
+
text: b.text
|
|
1095
1098
|
});
|
|
1099
|
+
return {
|
|
1100
|
+
role: msg.role,
|
|
1101
|
+
content: parts
|
|
1102
|
+
};
|
|
1096
1103
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1104
|
+
if (textBlocks.length === 1) return {
|
|
1105
|
+
role: msg.role,
|
|
1106
|
+
content: textBlocks[0].text
|
|
1107
|
+
};
|
|
1108
|
+
if (textBlocks.length > 1) return {
|
|
1109
|
+
role: msg.role,
|
|
1110
|
+
content: textBlocks.map((b) => ({
|
|
1111
|
+
type: "text",
|
|
1112
|
+
text: b.text
|
|
1113
|
+
}))
|
|
1114
|
+
};
|
|
1100
1115
|
return {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,
|
|
1104
|
-
isError: true
|
|
1116
|
+
role: msg.role,
|
|
1117
|
+
content: null
|
|
1105
1118
|
};
|
|
1106
1119
|
}
|
|
1107
|
-
function collectResultIds(content) {
|
|
1108
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1109
|
-
for (const block of content) if (block.type === "tool_result") ids.add(block.callId);
|
|
1110
|
-
return ids;
|
|
1111
|
-
}
|
|
1112
|
-
function collectIds(content, type, getId) {
|
|
1113
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1114
|
-
for (const block of content) if (block.type === type) ids.add(getId(block));
|
|
1115
|
-
return ids;
|
|
1116
|
-
}
|
|
1117
1120
|
/**
|
|
1118
1121
|
* Drop ASSISTANT turns whose every `tool_call` block is unresolved
|
|
1119
1122
|
* (no matching `tool_result` block anywhere later in the transcript).
|
|
@@ -1405,6 +1408,6 @@ function autoDetectAndConvert(msg) {
|
|
|
1405
1408
|
return fromAnthropic(msg);
|
|
1406
1409
|
}
|
|
1407
1410
|
//#endregion
|
|
1408
|
-
export {
|
|
1411
|
+
export { ensureToolResultPairing as _, fromAnthropic as a, resolveContentRefsInMessages as c, toOpenAI as d, toWireMessages as f, dropInvalidToolPairings as g, TOOL_USE_INTERRUPTED_MARKER as h, filterUnresolvedToolUses as i, sanitizeHistoryForProvider as l, SYNTHETIC_TOOL_RESULT_PLACEHOLDER as m, detectTurnInterruption as n, fromOpenAI as o, ORPHANED_TOOL_RESULT_MARKER as p, ensureEndsWithUserMessage as r, reasoningWireAction as s, autoDetectAndConvert as t, toAnthropic as u, remintDuplicateToolCallIds as v };
|
|
1409
1412
|
|
|
1410
|
-
//# sourceMappingURL=messages-
|
|
1413
|
+
//# sourceMappingURL=messages-BqiwcD2Q.js.map
|