zidane 7.2.4 → 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 -27242
- 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-zVSyo6K1.js +0 -331
- package/dist/ipc-zVSyo6K1.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-h5czzllR.js +0 -1549
- package/dist/shell-path-h5czzllR.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,269 +0,0 @@
|
|
|
1
|
-
import { i as statsByModel } from "./stats-DAKBEKjc.js";
|
|
2
|
-
//#region src/run-summary.ts
|
|
3
|
-
/**
|
|
4
|
-
* Build a run-summary collector. State is created fresh inside each
|
|
5
|
-
* `install()` call, so a single collector instance can be installed
|
|
6
|
-
* across multiple agents without attribution cross-talk. `latest()`
|
|
7
|
-
* returns the most-recent summary across **any** install — install
|
|
8
|
-
* per-agent collectors if you need separate post-run snapshots.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const collector = createRunSummaryCollector({
|
|
13
|
-
* onSummary: s => console.log(JSON.stringify(s)),
|
|
14
|
-
* })
|
|
15
|
-
* const uninstall = collector.install(agent.hooks)
|
|
16
|
-
* try { await agent.run({ prompt }) }
|
|
17
|
-
* finally { uninstall() }
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
function createRunSummaryCollector(options = {}) {
|
|
21
|
-
let last;
|
|
22
|
-
return {
|
|
23
|
-
latest: () => last,
|
|
24
|
-
install(hooks) {
|
|
25
|
-
let runId;
|
|
26
|
-
let parentRunId;
|
|
27
|
-
let depth = 0;
|
|
28
|
-
let agentName;
|
|
29
|
-
let startedAt = Date.now();
|
|
30
|
-
let aborted = false;
|
|
31
|
-
const errors = [];
|
|
32
|
-
const blocks = [];
|
|
33
|
-
const validationRejects = [];
|
|
34
|
-
const budgetEvents = [];
|
|
35
|
-
const repeatGuardEvents = [];
|
|
36
|
-
const streamRetries = [];
|
|
37
|
-
const pairingRepairs = {};
|
|
38
|
-
const children = [];
|
|
39
|
-
function resetForNewRun() {
|
|
40
|
-
aborted = false;
|
|
41
|
-
errors.length = 0;
|
|
42
|
-
blocks.length = 0;
|
|
43
|
-
validationRejects.length = 0;
|
|
44
|
-
budgetEvents.length = 0;
|
|
45
|
-
repeatGuardEvents.length = 0;
|
|
46
|
-
streamRetries.length = 0;
|
|
47
|
-
for (const k of Object.keys(pairingRepairs)) delete pairingRepairs[k];
|
|
48
|
-
children.length = 0;
|
|
49
|
-
}
|
|
50
|
-
const unregisters = [];
|
|
51
|
-
unregisters.push(hooks.hook("agent:start", (ctx) => {
|
|
52
|
-
resetForNewRun();
|
|
53
|
-
runId = ctx.runId;
|
|
54
|
-
parentRunId = ctx.parentRunId;
|
|
55
|
-
depth = ctx.depth;
|
|
56
|
-
agentName = ctx.agentName;
|
|
57
|
-
startedAt = ctx.startedAt;
|
|
58
|
-
}));
|
|
59
|
-
unregisters.push(hooks.hook("agent:abort", () => {
|
|
60
|
-
aborted = true;
|
|
61
|
-
}));
|
|
62
|
-
unregisters.push(hooks.hook("stream:retry", (ctx) => {
|
|
63
|
-
const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err);
|
|
64
|
-
streamRetries.push({
|
|
65
|
-
attempt: ctx.attempt,
|
|
66
|
-
delayMs: ctx.delayMs,
|
|
67
|
-
message: msg,
|
|
68
|
-
...ctx.err instanceof Error ? { errorType: ctx.err.name } : {},
|
|
69
|
-
...ctx.statusCode !== void 0 ? { statusCode: ctx.statusCode } : {},
|
|
70
|
-
...ctx.requestId !== void 0 ? { requestId: ctx.requestId } : {},
|
|
71
|
-
turnId: ctx.turnId
|
|
72
|
-
});
|
|
73
|
-
}));
|
|
74
|
-
unregisters.push(hooks.hook("stream:error", (ctx) => {
|
|
75
|
-
const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err);
|
|
76
|
-
const errorType = ctx.err instanceof Error ? ctx.err.name : "unknown";
|
|
77
|
-
errors.push({
|
|
78
|
-
kind: "stream",
|
|
79
|
-
message: msg,
|
|
80
|
-
errorType,
|
|
81
|
-
turnId: ctx.turnId,
|
|
82
|
-
...ctx.statusCode !== void 0 ? { statusCode: ctx.statusCode } : {},
|
|
83
|
-
...ctx.requestId !== void 0 ? { requestId: ctx.requestId } : {}
|
|
84
|
-
});
|
|
85
|
-
}));
|
|
86
|
-
unregisters.push(hooks.hook("tool:error", (ctx) => {
|
|
87
|
-
errors.push({
|
|
88
|
-
kind: "tool",
|
|
89
|
-
message: ctx.error.message,
|
|
90
|
-
errorType: ctx.error.name,
|
|
91
|
-
turnId: ctx.turnId,
|
|
92
|
-
callId: ctx.callId,
|
|
93
|
-
toolName: ctx.name
|
|
94
|
-
});
|
|
95
|
-
}));
|
|
96
|
-
unregisters.push(hooks.hook("mcp:tool:error", (ctx) => {
|
|
97
|
-
errors.push({
|
|
98
|
-
kind: "mcp-tool",
|
|
99
|
-
message: ctx.error.message,
|
|
100
|
-
errorType: ctx.error.name,
|
|
101
|
-
turnId: ctx.turnId,
|
|
102
|
-
callId: ctx.callId,
|
|
103
|
-
server: ctx.server,
|
|
104
|
-
toolName: ctx.displayName
|
|
105
|
-
});
|
|
106
|
-
}));
|
|
107
|
-
unregisters.push(hooks.hook("mcp:error", (ctx) => {
|
|
108
|
-
errors.push({
|
|
109
|
-
kind: "mcp",
|
|
110
|
-
message: ctx.error.message,
|
|
111
|
-
errorType: ctx.error.name,
|
|
112
|
-
server: ctx.name
|
|
113
|
-
});
|
|
114
|
-
}));
|
|
115
|
-
unregisters.push(hooks.hook("spawn:error", (ctx) => {
|
|
116
|
-
errors.push({
|
|
117
|
-
kind: "spawn",
|
|
118
|
-
message: ctx.error.message,
|
|
119
|
-
errorType: ctx.error.name,
|
|
120
|
-
childId: ctx.id
|
|
121
|
-
});
|
|
122
|
-
}));
|
|
123
|
-
unregisters.push(hooks.hook("tool:dispatched", (ctx) => {
|
|
124
|
-
if (ctx.outcome === "gate-block" || ctx.outcome === "unknown" || ctx.outcome === "invalid-input") blocks.push({
|
|
125
|
-
callId: ctx.callId,
|
|
126
|
-
toolName: ctx.name,
|
|
127
|
-
outcome: ctx.outcome,
|
|
128
|
-
...ctx.reason ? { reason: ctx.reason } : {}
|
|
129
|
-
});
|
|
130
|
-
}));
|
|
131
|
-
unregisters.push(hooks.hook("validation:reject", (ctx) => {
|
|
132
|
-
validationRejects.push({
|
|
133
|
-
callId: ctx.callId,
|
|
134
|
-
toolName: ctx.name,
|
|
135
|
-
reason: ctx.reason
|
|
136
|
-
});
|
|
137
|
-
}));
|
|
138
|
-
unregisters.push(hooks.hook("budget:exceeded", (ctx) => {
|
|
139
|
-
budgetEvents.push({
|
|
140
|
-
kind: "bytes",
|
|
141
|
-
observed: ctx.bytes,
|
|
142
|
-
limit: ctx.budget,
|
|
143
|
-
turnId: ctx.turnId
|
|
144
|
-
});
|
|
145
|
-
}));
|
|
146
|
-
unregisters.push(hooks.hook("tool-budget:exceeded", (ctx) => {
|
|
147
|
-
budgetEvents.push({
|
|
148
|
-
kind: "tool-count",
|
|
149
|
-
toolName: ctx.tool,
|
|
150
|
-
mode: ctx.mode,
|
|
151
|
-
observed: ctx.count,
|
|
152
|
-
limit: ctx.max,
|
|
153
|
-
turnId: ctx.turnId
|
|
154
|
-
});
|
|
155
|
-
}));
|
|
156
|
-
unregisters.push(hooks.hook("repeat-guard:exceeded", (ctx) => {
|
|
157
|
-
repeatGuardEvents.push({
|
|
158
|
-
toolName: ctx.tool,
|
|
159
|
-
count: ctx.count,
|
|
160
|
-
threshold: ctx.threshold,
|
|
161
|
-
action: ctx.action,
|
|
162
|
-
turnId: ctx.turnId
|
|
163
|
-
});
|
|
164
|
-
}));
|
|
165
|
-
unregisters.push(hooks.hook("pairing:repair", (ctx) => {
|
|
166
|
-
pairingRepairs[ctx.mode] = (pairingRepairs[ctx.mode] ?? 0) + 1;
|
|
167
|
-
}));
|
|
168
|
-
unregisters.push(hooks.hook("agent:done", (stats) => {
|
|
169
|
-
const endedAt = Date.now();
|
|
170
|
-
const byModel = [];
|
|
171
|
-
for (const [modelId, usage] of statsByModel(stats)) byModel.push({
|
|
172
|
-
modelId,
|
|
173
|
-
input: usage.input,
|
|
174
|
-
output: usage.output,
|
|
175
|
-
cacheRead: usage.cacheRead,
|
|
176
|
-
cacheCreation: usage.cacheCreation,
|
|
177
|
-
cost: usage.cost,
|
|
178
|
-
turns: usage.turns
|
|
179
|
-
});
|
|
180
|
-
for (const c of stats.children ?? []) children.push(minimalSummaryFromStats(c.stats, {
|
|
181
|
-
depth: c.depth ?? depth + 1,
|
|
182
|
-
status: c.status === "aborted" ? "aborted" : "completed"
|
|
183
|
-
}));
|
|
184
|
-
const summary = {
|
|
185
|
-
...runId ? { runId } : {},
|
|
186
|
-
...parentRunId ? { parentRunId } : {},
|
|
187
|
-
depth,
|
|
188
|
-
...agentName ? { agentName } : {},
|
|
189
|
-
startedAt,
|
|
190
|
-
endedAt,
|
|
191
|
-
durationMs: endedAt - startedAt,
|
|
192
|
-
status: aborted ? "aborted" : "completed",
|
|
193
|
-
turns: stats.turns,
|
|
194
|
-
totals: buildTotals(stats),
|
|
195
|
-
byModel,
|
|
196
|
-
errors: errors.slice(),
|
|
197
|
-
blocks: blocks.slice(),
|
|
198
|
-
validationRejects: validationRejects.slice(),
|
|
199
|
-
budgetEvents: budgetEvents.slice(),
|
|
200
|
-
streamRetries: streamRetries.slice(),
|
|
201
|
-
repeatGuardEvents: repeatGuardEvents.slice(),
|
|
202
|
-
pairingRepairs: { ...pairingRepairs },
|
|
203
|
-
...children.length > 0 ? { children: children.slice() } : {}
|
|
204
|
-
};
|
|
205
|
-
last = summary;
|
|
206
|
-
try {
|
|
207
|
-
options.onSummary?.(summary);
|
|
208
|
-
} catch {}
|
|
209
|
-
}));
|
|
210
|
-
let disposed = false;
|
|
211
|
-
return function uninstall() {
|
|
212
|
-
if (disposed) return;
|
|
213
|
-
disposed = true;
|
|
214
|
-
for (const un of unregisters) try {
|
|
215
|
-
un();
|
|
216
|
-
} catch {}
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
function buildTotals(stats) {
|
|
222
|
-
return {
|
|
223
|
-
input: stats.totalIn,
|
|
224
|
-
output: stats.totalOut,
|
|
225
|
-
cacheRead: stats.totalCacheRead,
|
|
226
|
-
cacheCreation: stats.totalCacheCreation,
|
|
227
|
-
...typeof stats.cost === "number" ? { cost: stats.cost } : {},
|
|
228
|
-
...typeof stats.timeTillFirstTokenMs === "number" ? { ttftMs: stats.timeTillFirstTokenMs } : {}
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
function minimalSummaryFromStats(stats, meta) {
|
|
232
|
-
const byModel = [];
|
|
233
|
-
for (const [modelId, usage] of statsByModel(stats)) byModel.push({
|
|
234
|
-
modelId,
|
|
235
|
-
input: usage.input,
|
|
236
|
-
output: usage.output,
|
|
237
|
-
cacheRead: usage.cacheRead,
|
|
238
|
-
cacheCreation: usage.cacheCreation,
|
|
239
|
-
cost: usage.cost,
|
|
240
|
-
turns: usage.turns
|
|
241
|
-
});
|
|
242
|
-
const children = [];
|
|
243
|
-
for (const c of stats.children ?? []) children.push(minimalSummaryFromStats(c.stats, {
|
|
244
|
-
depth: c.depth ?? meta.depth + 1,
|
|
245
|
-
status: c.status === "aborted" ? "aborted" : "completed"
|
|
246
|
-
}));
|
|
247
|
-
return {
|
|
248
|
-
depth: meta.depth,
|
|
249
|
-
startedAt: 0,
|
|
250
|
-
endedAt: 0,
|
|
251
|
-
durationMs: stats.elapsed,
|
|
252
|
-
status: meta.status,
|
|
253
|
-
turns: stats.turns,
|
|
254
|
-
totals: buildTotals(stats),
|
|
255
|
-
byModel,
|
|
256
|
-
errors: [],
|
|
257
|
-
blocks: [],
|
|
258
|
-
validationRejects: [],
|
|
259
|
-
budgetEvents: [],
|
|
260
|
-
streamRetries: [],
|
|
261
|
-
repeatGuardEvents: [],
|
|
262
|
-
pairingRepairs: {},
|
|
263
|
-
...children.length > 0 ? { children } : {}
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
//#endregion
|
|
267
|
-
export { createRunSummaryCollector as t };
|
|
268
|
-
|
|
269
|
-
//# sourceMappingURL=run-summary-BU_NgwUT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-summary-BU_NgwUT.js","names":[],"sources":["../src/run-summary.ts"],"sourcesContent":["/**\n * Run summary collector — one JSON postmortem per `agent.run()`.\n *\n * `AgentStats` is great for billing and per-turn accounting but doesn't\n * carry a record of the *interesting* events that happened during a run\n * (errors, gate blocks, validation rejects, budget hits, MCP failures).\n * Reconstructing that from a hook stream after the fact is exactly the\n * kind of plumbing every consumer ends up re-implementing.\n *\n * This module ships:\n *\n * - {@link RunSummary} — a serializable shape that bundles totals,\n * per-model breakdown, and all the per-run incident lists.\n * - {@link createRunSummaryCollector} — installs hook listeners that\n * build up a {@link RunSummary} during the run and surfaces it on\n * `agent:done`. Calls an optional `onSummary` callback so the host can\n * forward to a log aggregator / DB / postmortem dashboard without\n * touching `agent.run()`'s return value.\n *\n * The collector is purely additive — it doesn't touch the agent loop,\n * doesn't change return shapes, and doesn't suppress any other hook\n * handlers. Drop it in alongside tracing / metrics / logging or use it\n * standalone when you only need the summary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentStats } from './types'\nimport { statsByModel } from './stats'\n\n// ---------------------------------------------------------------------------\n// Public shape\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryTokens {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n /** First observable byte from the provider, ms from run start. */\n ttftMs?: number\n}\n\nexport interface RunSummaryByModel {\n modelId: string\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost: number\n turns: number\n}\n\nexport interface RunSummaryError {\n kind: 'stream' | 'tool' | 'mcp-tool' | 'mcp' | 'spawn'\n message: string\n errorType?: string\n turnId?: string\n callId?: string\n server?: string\n toolName?: string\n childId?: string\n statusCode?: number\n requestId?: string\n}\n\nexport interface RunSummaryBlock {\n callId: string\n toolName: string\n outcome: 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n}\n\nexport interface RunSummaryValidation {\n callId: string\n toolName: string\n reason: string\n}\n\nexport interface RunSummaryBudget {\n kind: 'bytes' | 'tool-count'\n /** Tool name (for `'tool-count'`); absent for byte budgets. */\n toolName?: string\n /** `mode` for `'tool-count'`; absent for byte budgets. */\n mode?: 'steer' | 'block'\n observed: number\n limit: number\n turnId?: string\n}\n\nexport interface RunSummaryRepeatGuard {\n toolName: string\n /** Observed repeat count that tripped the guard. */\n count: number\n threshold: number\n action: 'block' | 'steer' | 'abort'\n turnId?: string\n}\n\n/**\n * One absorbed provider-stream retry. Retries are invisible in the transcript\n * (no turn, no tool result) yet their backoff is real wall time the user\n * waited — measured in production as unexplained 30-90s gaps before trivial\n * tool calls. Recording them makes \"LLM residual\" stalls attributable.\n */\nexport interface RunSummaryRetry {\n /** 1-based attempt number that failed and triggered this retry. */\n attempt: number\n /** Backoff delay taken before the next attempt (ms, after retry-after/cap). */\n delayMs: number\n message: string\n errorType?: string\n statusCode?: number\n requestId?: string\n turnId?: string\n}\n\n/**\n * Postmortem snapshot of one `agent.run()`. Strictly serializable — every\n * field round-trips through `JSON.stringify` / `JSON.parse` without loss\n * so a log aggregator can ingest it as-is.\n */\nexport interface RunSummary {\n runId?: string\n parentRunId?: string\n depth: number\n agentName?: string\n startedAt: number\n endedAt: number\n durationMs: number\n status: 'completed' | 'aborted'\n turns: number\n totals: RunSummaryTokens\n byModel: RunSummaryByModel[]\n errors: RunSummaryError[]\n blocks: RunSummaryBlock[]\n validationRejects: RunSummaryValidation[]\n budgetEvents: RunSummaryBudget[]\n /**\n * Provider-stream retries the loop absorbed. Empty on healthy runs; on\n * incident runs the summed `delayMs` explains wall time that appears\n * nowhere else (not in turns, not in tool durations).\n */\n streamRetries: RunSummaryRetry[]\n /**\n * Repeat-guard escalations. An `action: 'steer'` entry means the tool still\n * executed and got a steering message appended to its result. An\n * `action: 'abort'` entry means the guard terminated the run via the agent's\n * AbortController (the run finalizes as `'aborted'`).\n */\n repeatGuardEvents: RunSummaryRepeatGuard[]\n /** Counts of pairing repairs, keyed by repair mode. */\n pairingRepairs: Record<string, number>\n /**\n * Postmortem snapshots of child runs that bubbled their stats up via\n * `spawn:complete`. Only present when the run actually spawned.\n */\n children?: RunSummary[]\n}\n\n// ---------------------------------------------------------------------------\n// Collector\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryCollectorOptions {\n /**\n * Called with the assembled {@link RunSummary} on every `agent:done`.\n * Synchronous — heavy I/O should be deferred (e.g. via `setImmediate`).\n */\n onSummary?: (summary: RunSummary) => void\n}\n\nexport interface RunSummaryCollector {\n /** Install the collector's hook handlers. Returns an uninstall fn. */\n install: (hooks: Hookable<AgentHooks>) => () => void\n /** Most-recent summary; `undefined` until the first `agent:done` fires. */\n latest: () => RunSummary | undefined\n}\n\n/**\n * Build a run-summary collector. State is created fresh inside each\n * `install()` call, so a single collector instance can be installed\n * across multiple agents without attribution cross-talk. `latest()`\n * returns the most-recent summary across **any** install — install\n * per-agent collectors if you need separate post-run snapshots.\n *\n * @example\n * ```ts\n * const collector = createRunSummaryCollector({\n * onSummary: s => console.log(JSON.stringify(s)),\n * })\n * const uninstall = collector.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createRunSummaryCollector(\n options: RunSummaryCollectorOptions = {},\n): RunSummaryCollector {\n let last: RunSummary | undefined\n\n return {\n latest: () => last,\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-run in-progress accumulators. The hookable bus serializes\n // agent.run lifecycles per agent — we reset between runs on\n // `agent:start` and consume on `agent:done`.\n let runId: string | undefined\n let parentRunId: string | undefined\n let depth = 0\n let agentName: string | undefined\n let startedAt = Date.now()\n let aborted = false\n const errors: RunSummaryError[] = []\n const blocks: RunSummaryBlock[] = []\n const validationRejects: RunSummaryValidation[] = []\n const budgetEvents: RunSummaryBudget[] = []\n const repeatGuardEvents: RunSummaryRepeatGuard[] = []\n const streamRetries: RunSummaryRetry[] = []\n const pairingRepairs: Record<string, number> = {}\n const children: RunSummary[] = []\n\n function resetForNewRun(): void {\n aborted = false\n errors.length = 0\n blocks.length = 0\n validationRejects.length = 0\n budgetEvents.length = 0\n repeatGuardEvents.length = 0\n streamRetries.length = 0\n for (const k of Object.keys(pairingRepairs))\n delete pairingRepairs[k]\n children.length = 0\n }\n\n const unregisters: Array<() => void> = []\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n resetForNewRun()\n runId = ctx.runId\n parentRunId = ctx.parentRunId\n depth = ctx.depth\n agentName = ctx.agentName\n startedAt = ctx.startedAt\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n aborted = true\n }))\n\n unregisters.push(hooks.hook('stream:retry', (ctx) => {\n const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n streamRetries.push({\n attempt: ctx.attempt,\n delayMs: ctx.delayMs,\n message: msg,\n ...(ctx.err instanceof Error ? { errorType: ctx.err.name } : {}),\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n const errorType = ctx.err instanceof Error ? ctx.err.name : 'unknown'\n errors.push({\n kind: 'stream',\n message: msg,\n errorType,\n turnId: ctx.turnId,\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n errors.push({\n kind: 'tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n toolName: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n errors.push({\n kind: 'mcp-tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n server: ctx.server,\n toolName: ctx.displayName,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n errors.push({\n kind: 'mcp',\n message: ctx.error.message,\n errorType: ctx.error.name,\n server: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n errors.push({\n kind: 'spawn',\n message: ctx.error.message,\n errorType: ctx.error.name,\n childId: ctx.id,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block' || ctx.outcome === 'unknown' || ctx.outcome === 'invalid-input') {\n blocks.push({\n callId: ctx.callId,\n toolName: ctx.name,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n validationRejects.push({\n callId: ctx.callId,\n toolName: ctx.name,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'bytes',\n observed: ctx.bytes,\n limit: ctx.budget,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'tool-count',\n toolName: ctx.tool,\n mode: ctx.mode,\n observed: ctx.count,\n limit: ctx.max,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('repeat-guard:exceeded', (ctx) => {\n repeatGuardEvents.push({\n toolName: ctx.tool,\n count: ctx.count,\n threshold: ctx.threshold,\n action: ctx.action,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n pairingRepairs[ctx.mode] = (pairingRepairs[ctx.mode] ?? 0) + 1\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n const endedAt = Date.now()\n\n // Build per-model rollup via the existing `statsByModel` helper\n // so we stay in lockstep with how the rest of the harness\n // attributes cost and tokens by model id.\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n\n // Flatten child stats into nested summaries. We don't have the\n // child's full event lists here (those landed on the child's\n // own hooks), so the nested entry is a minimal totals-only\n // record — enough for a flat per-run audit trail; consumers\n // wanting full per-child event lists install a collector on\n // each subagent.\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n\n const summary: RunSummary = {\n ...(runId ? { runId } : {}),\n ...(parentRunId ? { parentRunId } : {}),\n depth,\n ...(agentName ? { agentName } : {}),\n startedAt,\n endedAt,\n durationMs: endedAt - startedAt,\n status: aborted ? 'aborted' : 'completed',\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: errors.slice(),\n blocks: blocks.slice(),\n validationRejects: validationRejects.slice(),\n budgetEvents: budgetEvents.slice(),\n streamRetries: streamRetries.slice(),\n repeatGuardEvents: repeatGuardEvents.slice(),\n pairingRepairs: { ...pairingRepairs },\n ...(children.length > 0 ? { children: children.slice() } : {}),\n }\n\n last = summary\n try {\n options.onSummary?.(summary)\n }\n catch {\n // Sink errors are not the collector's concern.\n }\n }))\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction buildTotals(stats: AgentStats): RunSummaryTokens {\n return {\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n }\n}\n\nfunction minimalSummaryFromStats(\n stats: AgentStats,\n meta: { depth: number, status: 'completed' | 'aborted' },\n): RunSummary {\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n const children: RunSummary[] = []\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? meta.depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n return {\n depth: meta.depth,\n startedAt: 0,\n endedAt: 0,\n durationMs: stats.elapsed,\n status: meta.status,\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: [],\n blocks: [],\n validationRejects: [],\n budgetEvents: [],\n streamRetries: [],\n repeatGuardEvents: [],\n pairingRepairs: {},\n ...(children.length > 0 ? { children } : {}),\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-BH0WQkXn.d.ts","names":[],"sources":["../src/contexts/sandbox.ts"],"mappings":";;;;UAgBiB,eAAA;EACf,IAAA;EACA,KAAA,GAAQ,MAAA,EAAQ,WAAA,KAAgB,OAAA;IAAU,EAAA;IAAY,GAAA;EAAA;EACtD,IAAA,GAAO,SAAA,UAAmB,OAAA,UAAiB,OAAA;IAAY,GAAA;IAAc,GAAA,GAAM,MAAA;IAAwB,OAAA;EAAA,MAAuB,OAAA,CAAQ,UAAA;EAClI,QAAA,GAAW,SAAA,UAAmB,IAAA,aAAiB,OAAA;EAC/C,SAAA,GAAY,SAAA,UAAmB,IAAA,UAAc,OAAA,WAAkB,MAAA,EAAQ,QAAA,GAAW,cAAA,KAAmB,OAAA;EACrG,SAAA,GAAY,SAAA,UAAmB,IAAA,UAAc,OAAA,GAAU,gBAAA,KAAqB,OAAA;EAC5E,OAAA,GAAU,SAAA,aAAsB,OAAA;EAYpB;;;;;;;EAJZ,UAAA,IACE,SAAA,UACA,OAAA,UACA,OAAA;IAAY,GAAA;IAAc,GAAA,GAAM,MAAA;IAAwB,MAAA,GAAS,WAAA;EAAA,MAC9D,OAAA,CAAQ,YAAA;AAAA;AAAA,iBAOC,oBAAA,CAAqB,QAAA,EAAU,eAAA,GAAkB,gBAAgB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/session/sqlite.ts"],"sourcesContent":["/**\n * SQLite session store using Bun's built-in bun:sqlite.\n *\n * Schema v4 — normalized\n * ----------------------\n *\n * Three tables: `sessions` (one row per session, holds the header fields),\n * `turns` (one row per turn, ordered by `(session_id, seq)`), and `runs`\n * (one row per run, keyed by `(session_id, run_id)`). Replaces the v3\n * \"fat blob\" shape where the entire `SessionData` — including every turn\n * and every run — lived in a single TEXT column.\n *\n * The normalization removes a write-amplification anti-pattern that bit\n * long sessions hard: every `appendTurns` previously re-read the full\n * document, mutated the parsed object in memory, and serialized the\n * whole thing back. At turn N the per-call work was O(N) bytes; a 5000-\n * turn session paid multi-MB JSON.stringify on every assistant turn.\n * The new shape makes:\n *\n * - `appendTurns` a pure `INSERT INTO turns` per turn (O(1) per row,\n * plus one `MAX(seq)` lookup at the start of the call).\n * - `updateRun` an `INSERT OR UPDATE ON CONFLICT` against `runs` —\n * the upsert semantic the v3 code carefully simulated in JS is now\n * the row PK's natural behavior.\n * - `updateStatus` a single-column `UPDATE` against the sessions row\n * instead of a full read-mutate-write cycle.\n * - `getTurns(from, limit)` an indexed `LIMIT/OFFSET` range scan\n * instead of \"parse full doc, slice\".\n *\n * Concurrency model\n * -----------------\n * Mutations still run inside `BEGIN IMMEDIATE` transactions via\n * `db.transaction(fn).immediate(...)` — IMMEDIATE acquires the database-\n * level write lock at `BEGIN`, so two concurrent writers serialize on\n * the transaction boundary. WAL mode (set on connection open) lets\n * readers see committed data without blocking the writer.\n *\n * `delete(sessionId)` relies on `PRAGMA foreign_keys = ON` to cascade\n * `sessions` → `turns` / `runs` via ON DELETE CASCADE. The pragma is\n * per-connection; it's set at connection open.\n *\n * Schema versioning\n * -----------------\n * `PRAGMA user_version` tracks the current shape:\n *\n * 1 — initial schema (sessions table, no version pragma)\n * 2 — `parentRunId` / `depth` on runs; cache-aware `totalUsage` fields\n * 3 — `project_root` column + index\n * 4 — normalized layout (this file). `data` blob column dropped,\n * header fields promoted (`status`, `metadata`), turns + runs\n * split into dedicated tables. v3→v4 migration runs in-place at\n * connection open: existing blobs are parsed and fanned out, then\n * the legacy `data` column is dropped (SQLite 3.35+).\n *\n * Additive, UNVERSIONED enhancement on top of v4: a denormalized\n * `turns.role` column + `(session_id, role, seq)` index. Lets `listMeta`\n * count user turns and seek the title-source turn from the index alone —\n * without it the summary query has to `json_extract` every turn blob,\n * which benches slower than just loading the sessions outright.\n *\n * Deliberately NOT a version bump. `PRAGMA user_version` stays at 4\n * because plain-v4 builds read and write this DB perfectly well (their\n * explicit-column INSERTs leave `role` NULL; the index maintains itself),\n * and bumping would make them refuse the file outright — which bites\n * anyone flipping between a dev build and a released binary against the\n * same `~/.zidane/sessions.db`. Instead the column is feature-detected at\n * open and NULL roles are repaired opportunistically: a partial index\n * makes \"find un-annotated rows\" O(matching rows), so the repair is a\n * no-op read in steady state and heals whatever an older v4 build wrote\n * since the last open. Version bumps are reserved for changes an older\n * build could actually corrupt or misread.\n *\n * ⚠ Version number 5 is BURNED — the next real schema bump must use 6.\n * Short-lived dev builds (2026-07) stamped this role work as version 5\n * before it was made unversioned; the downgrade guard in `migrate()` now\n * accepts-and-re-stamps 5 as plain v4. If a future schema reused 5, those\n * repaired-DB semantics would mis-accept it.\n *\n * Second additive, UNVERSIONED enhancement: the `artifacts` table (see\n * `./artifacts.ts`). Same reasoning as `turns.role` — a plain-v4 build\n * never reads or writes the table, its `DELETE FROM sessions` still\n * cascades into it (the FK lives on the child table and every build sets\n * `PRAGMA foreign_keys = ON`), so there is nothing an older build could\n * corrupt or misread. `CREATE TABLE IF NOT EXISTS` at open is the whole\n * migration.\n *\n * Safety net for v3 → v4\n * ----------------------\n * Before the first destructive change (column drop, data fan-out), the\n * migration copies the source DB file to `<path>.zidane-pre-v4.bak` and\n * checkpoints WAL so the snapshot is point-in-time consistent. A user\n * who hits a regression can restore by copying that file back over\n * `sessions.db` (with the old `zidane` version) without any data loss.\n * The backup is created once and never overwritten — re-runs of the\n * migration (idempotent path) reuse the existing snapshot.\n *\n * Per-row backfill failures (corrupt JSON inside one session's blob)\n * are logged + skipped rather than aborting the whole DB; the .bak\n * file remains the recovery path for those individual rows.\n *\n * Forward-only migrations: we never roll back. Hosts pinning an older\n * `zidane` against a v4 db would fail to read the (now-missing) `data`\n * column — same forward-compat policy as the rest of the project. The\n * `.zidane-pre-v4.bak` snapshot is the explicit escape hatch for that\n * downgrade case.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { JsonValue, SessionTurn } from '../types'\nimport type { ArtifactFilter, SessionArtifact } from './artifacts'\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { Database } from 'bun:sqlite'\nimport { errorMessage } from '../errors'\nimport { mergeArtifact, selectArtifactEvictions } from './artifacts'\n\nconst SCHEMA_VERSION = 4\n\nexport interface SqliteStoreOptions {\n /** Path to the SQLite database file */\n path: string\n}\n\n/**\n * True when `err` is SQLite reporting a damaged file (`SQLITE_CORRUPT`,\n * `SQLITE_NOTADB`). Matched on the message because bun:sqlite's error\n * class exposes the code inconsistently across versions.\n */\nexport function isCorruptDbError(err: unknown): boolean {\n const msg = errorMessage(err)\n return /database disk image is malformed|SQLITE_CORRUPT|SQLITE_NOTADB|file is not a database/i.test(msg)\n}\n\n/**\n * Re-wrap a corruption error with the file path and a recovery recipe.\n * The bare SQLite string (\"database disk image is malformed\") tells the\n * user nothing about WHICH file or what to do; every host (TUI, daemon,\n * headless) surfaces this message instead. Non-corruption errors pass\n * through untouched.\n *\n * Deliberately no auto-repair / quarantine: the file is the only copy of\n * the user's history and `.recover` salvages most of it. Never touch it.\n */\nexport function describeCorruptDb(err: unknown, dbPath: string): unknown {\n if (!isCorruptDbError(err))\n return err\n const wrapped = new Error(\n `[zidane/sqlite] sessions database at \"${dbPath}\" is corrupt (${errorMessage(err)}).\\n`\n + ` Your sessions are still on disk; zidane will not modify the file. To recover:\\n`\n + ` 1. quit zidane (and any running daemon)\\n`\n + ` 2. sqlite3 \"${dbPath}\" \".recover\" | sqlite3 \"${dbPath}.recovered\"\\n`\n + ` 3. mv \"${dbPath}\" \"${dbPath}.corrupt\" && rm -f \"${dbPath}-wal\" \"${dbPath}-shm\" && mv \"${dbPath}.recovered\" \"${dbPath}\"\\n`\n + ` Common causes: git checkout/stash swapping a project .zidane/sessions.db under its -wal, `\n + `a cloud-synced or network folder (iCloud, Dropbox, SMB), or a backup restored without deleting -wal / -shm.`,\n { cause: err },\n )\n return wrapped\n}\n\nexport function createSqliteStore(options: SqliteStoreOptions): SessionStore {\n let store: SessionStore\n try {\n store = openSqliteStore(options)\n }\n catch (err) {\n throw describeCorruptDb(err, options.path)\n }\n // Every async method rethrows corruption with the path + recipe. `close`\n // is sync and can't hit a data page — left as is.\n const wrapped = { ...store } as Record<string, unknown>\n for (const key of Object.keys(store) as (keyof SessionStore)[]) {\n const fn = store[key]\n if (typeof fn !== 'function' || key === 'close')\n continue\n wrapped[key] = async (...args: unknown[]) => {\n try {\n return await (fn as (...a: unknown[]) => Promise<unknown>).apply(store, args)\n }\n catch (err) {\n throw describeCorruptDb(err, options.path)\n }\n }\n }\n return wrapped as unknown as SessionStore\n}\n\nfunction openSqliteStore(options: SqliteStoreOptions): SessionStore {\n const db = new Database(options.path)\n\n // `busy_timeout` BEFORE any other PRAGMA so the very first DDL /\n // mode-change tolerates a brief lock held by another writer rather\n // than throwing \"database is locked\" instantly. Real scenarios this\n // covers:\n //\n // - `bun --watch` dev loops where the prior process hasn't fully\n // released the WAL / -shm sidecars at the moment the restart\n // re-opens the file.\n // - A second zidane TUI booted before the first one finished\n // shutting down (rare, but `bun:sqlite` won't retry on its own).\n // - The v3→v4 migration's `wal_checkpoint(TRUNCATE)` racing a\n // stale background connection.\n //\n // 5s is generous enough that an actual zombie process is obvious to\n // the operator (they can Ctrl+C) while still erring on the side of\n // \"just work\" for the dev-loop case.\n db.run('PRAGMA busy_timeout = 5000')\n\n // WAL gives concurrent reads while a writer holds the lock; `BEGIN\n // IMMEDIATE` below still serializes writers among themselves.\n db.run('PRAGMA journal_mode = WAL')\n // Required for the ON DELETE CASCADE from `sessions` to `turns` / `runs`.\n // Per-connection setting; cheap idempotent call at open.\n db.run('PRAGMA foreign_keys = ON')\n\n // ------------------------------------------------------------------\n // Schema (v4 shape). `CREATE TABLE IF NOT EXISTS` is a no-op on\n // pre-existing v3 databases; the migrate() call below brings them\n // forward via `ALTER TABLE ADD COLUMN` + data backfill.\n // ------------------------------------------------------------------\n db.run(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent_id TEXT,\n project_root TEXT,\n status TEXT NOT NULL DEFAULT 'idle',\n metadata TEXT NOT NULL DEFAULT '{}',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n )\n `)\n db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON sessions(agent_id)`)\n db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_project_root ON sessions(project_root)`)\n\n // Per-turn rows. `(session_id, seq)` PK + `ORDER BY seq` reads give\n // O(log n) ordered access and O(1) appends. Cascade so `delete()` on\n // sessions clears these rows in one statement.\n db.run(`\n CREATE TABLE IF NOT EXISTS turns (\n session_id TEXT NOT NULL,\n seq INTEGER NOT NULL,\n role TEXT,\n data TEXT NOT NULL,\n PRIMARY KEY (session_id, seq),\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n )\n `)\n\n // Per-run rows. `(session_id, run_id)` PK gives natural upsert via\n // `ON CONFLICT(session_id, run_id) DO UPDATE`. `started_at` lifted to\n // a column so reads can `ORDER BY` it cheaply (the wall-clock order of\n // run creation is the order the agent registered them, which is what\n // `eventsFromTurns` expects when reconstructing the run tree).\n db.run(`\n CREATE TABLE IF NOT EXISTS runs (\n session_id TEXT NOT NULL,\n run_id TEXT NOT NULL,\n started_at INTEGER NOT NULL,\n data TEXT NOT NULL,\n PRIMARY KEY (session_id, run_id),\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n )\n `)\n\n migrate(db, options.path)\n\n // Artifacts (unversioned additive table — see header). One row per\n // `(session_id, kind, uri)`; `id` stays stable across upserts so\n // transcript anchors and external references survive re-emission.\n // Runs AFTER `migrate()` so a build that is about to refuse a newer\n // schema never writes to that file — the unversioned argument rests on\n // \"no build touches a DB it doesn't understand\".\n db.run(`\n CREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n run_id TEXT,\n turn_id TEXT,\n kind TEXT NOT NULL,\n uri TEXT NOT NULL,\n label TEXT NOT NULL,\n project_root TEXT,\n meta TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n UNIQUE (session_id, kind, uri),\n FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE\n )\n `)\n db.run(`CREATE INDEX IF NOT EXISTS idx_artifacts_session ON artifacts(session_id, updated_at)`)\n db.run(`CREATE INDEX IF NOT EXISTS idx_artifacts_project_kind ON artifacts(project_root, kind, updated_at)`)\n db.run(`CREATE INDEX IF NOT EXISTS idx_artifacts_kind_uri ON artifacts(kind, uri)`)\n\n // ------------------------------------------------------------------\n // Prepared statements (hot-path queries).\n // ------------------------------------------------------------------\n const stmtLoadHeader = db.prepare(`\n SELECT id, agent_id, project_root, status, metadata, created_at, updated_at\n FROM sessions WHERE id = ?\n `)\n const stmtLoadTurns = db.prepare('SELECT data FROM turns WHERE session_id = ? ORDER BY seq')\n const stmtLoadTurnsRange = db.prepare('SELECT data FROM turns WHERE session_id = ? ORDER BY seq LIMIT ? OFFSET ?')\n const stmtLoadRuns = db.prepare('SELECT data FROM runs WHERE session_id = ? ORDER BY started_at, run_id')\n\n const stmtUpsertHeader = db.prepare(`\n INSERT INTO sessions (id, agent_id, project_root, status, metadata, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n agent_id = excluded.agent_id,\n project_root = excluded.project_root,\n status = excluded.status,\n metadata = excluded.metadata,\n updated_at = excluded.updated_at\n `)\n const stmtDelete = db.prepare('DELETE FROM sessions WHERE id = ?')\n const stmtDeleteTurnsForSession = db.prepare('DELETE FROM turns WHERE session_id = ?')\n const stmtDeleteRunsForSession = db.prepare('DELETE FROM runs WHERE session_id = ?')\n const stmtInsertTurn = db.prepare('INSERT INTO turns (session_id, seq, role, data) VALUES (?, ?, ?, ?)')\n const stmtUpsertRun = db.prepare(`\n INSERT INTO runs (session_id, run_id, started_at, data) VALUES (?, ?, ?, ?)\n ON CONFLICT(session_id, run_id) DO UPDATE SET\n data = excluded.data,\n started_at = excluded.started_at\n `)\n const stmtMaxSeq = db.prepare('SELECT MAX(seq) as m FROM turns WHERE session_id = ?')\n const stmtTouch = db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?')\n const stmtUpdateStatus = db.prepare('UPDATE sessions SET status = ?, updated_at = ? WHERE id = ?')\n // `preserveUpdatedAt` path — pure-read heals write the status column ONLY.\n const stmtUpdateStatusOnly = db.prepare('UPDATE sessions SET status = ? WHERE id = ?')\n\n const ARTIFACT_COLUMNS = 'id, session_id, run_id, turn_id, kind, uri, label, project_root, meta, created_at, updated_at'\n const stmtFindArtifact = db.prepare(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts WHERE session_id = ? AND kind = ? AND uri = ?`)\n // Insertion order (rowid) breaks `created_at` ties so eviction is\n // deterministic when many artifacts land in the same millisecond.\n const stmtSessionArtifacts = db.prepare(`SELECT ${ARTIFACT_COLUMNS} FROM artifacts WHERE session_id = ? ORDER BY created_at, rowid`)\n const stmtInsertArtifact = db.prepare(`\n INSERT INTO artifacts (${ARTIFACT_COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `)\n const stmtRefreshArtifact = db.prepare(`\n UPDATE artifacts SET label = ?, project_root = ?, meta = ?, updated_at = ? WHERE id = ?\n `)\n const stmtDeleteArtifact = db.prepare('DELETE FROM artifacts WHERE session_id = ? AND id = ?')\n\n // ------------------------------------------------------------------\n // Sync helpers used inside transactions. bun:sqlite's `db.transaction(fn)`\n // requires `fn` to be sync; wrapping these as standalone helpers keeps\n // the transactional and non-transactional code paths sharing one\n // implementation.\n // ------------------------------------------------------------------\n interface HeaderRow {\n id: string\n agent_id: string | null\n project_root: string | null\n status: string\n metadata: string\n created_at: number\n updated_at: number\n }\n\n const loadSync = (sessionId: string): SessionData | null => {\n const header = stmtLoadHeader.get(sessionId) as HeaderRow | null\n if (!header)\n return null\n\n const turnRows = stmtLoadTurns.all(sessionId) as { data: string }[]\n const runRows = stmtLoadRuns.all(sessionId) as { data: string }[]\n\n return {\n id: header.id,\n ...(header.agent_id ? { agentId: header.agent_id } : {}),\n ...(header.project_root ? { projectRoot: header.project_root } : {}),\n turns: turnRows\n .map(r => safeParseRow<SessionTurn>(r.data, header.id, 'turn'))\n .filter(t => t !== null),\n runs: runRows\n .map(r => safeParseRow<SessionRun>(r.data, header.id, 'run'))\n .filter(r => r !== null),\n status: header.status as SessionData['status'],\n metadata: safeParseRecord(header.metadata),\n createdAt: header.created_at,\n updatedAt: header.updated_at,\n }\n }\n\n // ------------------------------------------------------------------\n // Mutation transactions. `.immediate(...)` acquires the write lock at\n // `BEGIN IMMEDIATE` time so two concurrent writers serialize on the\n // transaction boundary, not on the (already-too-late) save call.\n // ------------------------------------------------------------------\n const txnSave = db.transaction((session: SessionData) => {\n // Header upsert. `status` + `metadata` are now first-class columns\n // instead of nested fields in a blob; the JSON.stringify on\n // `metadata` is the only serialization we still do at this level.\n stmtUpsertHeader.run(\n session.id,\n session.agentId ?? null,\n session.projectRoot ?? null,\n session.status,\n JSON.stringify(session.metadata ?? {}),\n session.createdAt,\n session.updatedAt,\n )\n // `save()` is the bulk-overwrite path (used by compact / fork /\n // explicit consumer save). Wipe and re-insert turns + runs so the\n // on-disk state mirrors the caller's view exactly — matches the\n // v3 semantic where the whole `data` blob was replaced.\n stmtDeleteTurnsForSession.run(session.id)\n stmtDeleteRunsForSession.run(session.id)\n session.turns.forEach((t, i) => stmtInsertTurn.run(session.id, i, t.role ?? null, JSON.stringify(t)))\n for (const run of session.runs) {\n stmtUpsertRun.run(session.id, run.id, run.startedAt, JSON.stringify(run))\n }\n })\n\n // Header-only persistence for metadata writes. Turns are never touched —\n // `appendTurns` already persisted them incrementally, and a bulk rewrite\n // from a stale snapshot is exactly how a concurrent writer's turns get\n // wiped. Everything else stays last-writer-wins: the header columns are\n // written wholesale and runs are UPSERTED as the snapshot has them (never\n // deleted, so runs recorded by another process since survive).\n const txnSaveMeta = db.transaction((session: SessionData) => {\n stmtUpsertHeader.run(\n session.id,\n session.agentId ?? null,\n session.projectRoot ?? null,\n session.status,\n JSON.stringify(session.metadata ?? {}),\n session.createdAt,\n session.updatedAt,\n )\n for (const run of session.runs) {\n stmtUpsertRun.run(session.id, run.id, run.startedAt, JSON.stringify(run))\n }\n })\n\n // Auto-create a minimal session header when turns arrive for an id the\n // store has never seen. Mirrors the file-map store: `createSession({store})`\n // + `agent.run()` writes turns via `appendTurns` without an explicit\n // `save()` first — silently dropping those writes loses the run's history.\n // Deliberately scoped to `appendTurns` only: a late `updateRun`/\n // `updateStatus` racing a `delete()` must stay a no-op rather than\n // resurrect a ghost header row.\n const ensureHeaderSync = (sessionId: string) => {\n const header = stmtLoadHeader.get(sessionId)\n if (header)\n return\n const now = Date.now()\n stmtUpsertHeader.run(sessionId, null, null, 'idle', '{}', now, now)\n }\n\n const txnAppendTurns = db.transaction((sessionId: string, turns: SessionTurn[]) => {\n ensureHeaderSync(sessionId)\n\n const max = (stmtMaxSeq.get(sessionId) as { m: number | null }).m\n let nextSeq = (max ?? -1) + 1\n for (const turn of turns) {\n stmtInsertTurn.run(sessionId, nextSeq, turn.role ?? null, JSON.stringify(turn))\n nextSeq += 1\n }\n stmtTouch.run(Date.now(), sessionId)\n })\n\n const txnUpdateRun = db.transaction((sessionId: string, run: SessionRun) => {\n // No auto-create here (see ensureHeaderSync) — unknown id is a no-op.\n const header = stmtLoadHeader.get(sessionId)\n if (!header)\n return\n // True upsert via PK constraint — the v3 code had a careful comment\n // explaining why a find-then-update would silently lose run records;\n // that whole class of bug is structurally impossible here. New ids\n // get inserted, existing ids get their JSON + started_at refreshed.\n stmtUpsertRun.run(sessionId, run.id, run.startedAt, JSON.stringify(run))\n stmtTouch.run(Date.now(), sessionId)\n })\n\n const txnUpdateStatus = db.transaction((sessionId: string, status: SessionData['status'], preserveUpdatedAt: boolean) => {\n // UPDATE on an unknown id is naturally a no-op — no auto-create here.\n if (preserveUpdatedAt)\n stmtUpdateStatusOnly.run(status, sessionId)\n else\n stmtUpdateStatus.run(status, Date.now(), sessionId)\n })\n\n interface ArtifactRow {\n id: string\n session_id: string\n run_id: string | null\n turn_id: string | null\n kind: string\n uri: string\n label: string\n project_root: string | null\n meta: string | null\n created_at: number\n updated_at: number\n }\n\n const rowToArtifact = (r: ArtifactRow): SessionArtifact => {\n let meta: JsonValue | undefined\n if (r.meta) {\n try {\n meta = JSON.parse(r.meta) as JsonValue\n }\n catch {\n // Corrupt meta drops the payload, never the artifact (label + uri\n // still render) — same posture as `safeParseRecord`.\n meta = undefined\n }\n }\n return {\n id: r.id,\n sessionId: r.session_id,\n ...(r.run_id ? { runId: r.run_id } : {}),\n ...(r.turn_id ? { turnId: r.turn_id } : {}),\n kind: r.kind,\n uri: r.uri,\n label: r.label,\n ...(r.project_root ? { projectRoot: r.project_root } : {}),\n ...(meta !== undefined ? { meta } : {}),\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n }\n\n const txnUpsertArtifact = db.transaction((artifact: SessionArtifact): SessionArtifact | null => {\n // No auto-create — an artifact for a session the store never saw is\n // dropped (same posture as `updateRun`). The FK would reject it anyway.\n if (!stmtLoadHeader.get(artifact.sessionId))\n return null\n const existingRow = stmtFindArtifact.get(artifact.sessionId, artifact.kind, artifact.uri) as ArtifactRow | null\n if (existingRow) {\n const merged = mergeArtifact(rowToArtifact(existingRow), artifact)\n stmtRefreshArtifact.run(\n merged.label,\n merged.projectRoot ?? null,\n merged.meta !== undefined ? JSON.stringify(merged.meta) : null,\n merged.updatedAt,\n merged.id,\n )\n return merged\n }\n const current = (stmtSessionArtifacts.all(artifact.sessionId) as ArtifactRow[]).map(rowToArtifact)\n for (const id of selectArtifactEvictions(current))\n stmtDeleteArtifact.run(artifact.sessionId, id)\n stmtInsertArtifact.run(\n artifact.id,\n artifact.sessionId,\n artifact.runId ?? null,\n artifact.turnId ?? null,\n artifact.kind,\n artifact.uri,\n artifact.label,\n artifact.projectRoot ?? null,\n artifact.meta !== undefined ? JSON.stringify(artifact.meta) : null,\n artifact.createdAt,\n artifact.updatedAt,\n )\n return artifact\n })\n\n const store: SessionStore = {\n async load(sessionId: string) {\n return loadSync(sessionId)\n },\n\n async save(session: SessionData) {\n txnSave.immediate(session)\n },\n\n async saveMeta(session: SessionData) {\n txnSaveMeta.immediate(session)\n },\n\n async delete(sessionId: string) {\n // FK CASCADE on `turns.session_id` / `runs.session_id` clears the\n // dependent rows. Single statement, single round-trip.\n stmtDelete.run(sessionId)\n },\n\n async list(filter) {\n // Build the SQL on the fly so the filter axes compose cleanly —\n // four prepared statements collapsed into one dynamic query. The\n // shape is always `SELECT id FROM sessions [WHERE …] ORDER BY\n // updated_at DESC [LIMIT N]`; only the WHERE varies (see\n // `buildListFilter` for the axis semantics).\n const { where, limit, params } = buildListFilter(filter)\n const sql = `SELECT id FROM sessions ${where} ORDER BY updated_at DESC ${limit}`.replace(/\\s+/g, ' ').trim()\n const rows = db.query(sql).all(...params) as { id: string }[]\n return rows.map(r => r.id)\n },\n\n async getMeta(sessionId) {\n /*\n * One session's header without touching turn blobs — the hot-path\n * complement to `listMeta` (the cross-process run guard reads\n * `status`/`updatedAt` on every send). Same projection as\n * `listMeta`, scoped to one id.\n */\n const row = db.query(`\n SELECT\n s.id,\n s.project_root,\n s.metadata,\n s.updated_at,\n s.agent_id,\n s.created_at,\n s.status,\n (SELECT COUNT(*) FROM turns t WHERE t.session_id = s.id) AS turn_count,\n (SELECT COUNT(*) FROM turns t WHERE t.session_id = s.id AND t.role = 'user') AS user_count,\n (SELECT COUNT(*) FROM runs r WHERE r.session_id = s.id) AS run_count,\n (SELECT COUNT(*) FROM artifacts a WHERE a.session_id = s.id) AS artifact_count,\n NULL AS first_user_turn\n FROM sessions s WHERE s.id = ?\n `).get(sessionId) as {\n id: string\n project_root: string | null\n metadata: string\n updated_at: number\n agent_id: string | null\n created_at: number\n status: string\n turn_count: number\n user_count: number\n run_count: number\n artifact_count: number\n } | null\n if (!row)\n return null\n return {\n id: row.id,\n metadata: safeParseRecord(row.metadata),\n ...(row.project_root ? { projectRoot: row.project_root } : {}),\n updatedAt: row.updated_at,\n ...(row.agent_id ? { agentId: row.agent_id } : {}),\n createdAt: row.created_at,\n status: row.status as SessionData['status'],\n turnCount: row.turn_count,\n userMessageCount: row.user_count,\n runCount: row.run_count,\n artifactCount: row.artifact_count,\n }\n },\n\n async listMeta(filter) {\n // Single-query summary path for the sessions picker. The generic\n // fallback (`list()` + `load()` per id) parses every turn of every\n // session in JS just to count rows and find the first user message\n // — O(entire database) and the dominant TUI boot cost on large\n // histories. Here every role-scoped aggregate answers from the\n // denormalized `idx_turns_session_role` index (see the \"additive\n // enhancement\" note in the header) without touching a single turn\n // blob; the only turn payload that crosses into JS is the first\n // user turn, and only for sessions with no stored title.\n // Deliberately NOT `json_extract(t.data, ...)`-based: SQLite would\n // have to parse every multi-KB blob, which benches slower than\n // just loading the sessions in JS.\n //\n // Turns appended by a plain-v4 build WHILE this connection is open\n // carry a NULL role until the next open's repair pass — their\n // sessions briefly under-count user turns in the picker. Cosmetic\n // and self-healing; not worth a per-query repair.\n const { where, limit, params } = buildListFilter(filter)\n const sql = `\n SELECT\n s.id,\n s.project_root,\n s.metadata,\n s.updated_at,\n s.agent_id,\n s.created_at,\n s.status,\n (SELECT COUNT(*) FROM turns t WHERE t.session_id = s.id) AS turn_count,\n (SELECT COUNT(*) FROM turns t WHERE t.session_id = s.id AND t.role = 'user') AS user_count,\n (SELECT COUNT(*) FROM runs r WHERE r.session_id = s.id) AS run_count,\n (SELECT COUNT(*) FROM artifacts a WHERE a.session_id = s.id) AS artifact_count,\n CASE WHEN json_valid(s.metadata)\n AND TRIM(COALESCE(json_extract(s.metadata, '$.title'), '')) <> '' THEN NULL\n ELSE (SELECT t.data FROM turns t WHERE t.session_id = s.id AND t.role = 'user'\n ORDER BY t.seq LIMIT 1)\n END AS first_user_turn\n FROM sessions s ${where} ORDER BY s.updated_at DESC ${limit}\n `\n interface MetaQueryRow {\n id: string\n project_root: string | null\n metadata: string\n updated_at: number\n agent_id: string | null\n created_at: number\n status: string\n turn_count: number\n user_count: number\n run_count: number\n artifact_count: number\n first_user_turn: string | null\n }\n const rows = db.query(sql).all(...params) as MetaQueryRow[]\n return rows.map((r) => {\n const firstUserTurn = r.first_user_turn\n ? safeParseRow<SessionTurn>(r.first_user_turn, r.id, 'turn')\n : null\n return {\n id: r.id,\n metadata: safeParseRecord(r.metadata),\n ...(r.project_root ? { projectRoot: r.project_root } : {}),\n updatedAt: r.updated_at,\n ...(r.agent_id ? { agentId: r.agent_id } : {}),\n createdAt: r.created_at,\n status: r.status as SessionData['status'],\n turnCount: r.turn_count,\n userMessageCount: r.user_count,\n runCount: r.run_count,\n artifactCount: r.artifact_count,\n ...(firstUserTurn ? { firstUserTurn } : {}),\n }\n })\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n txnAppendTurns.immediate(sessionId, turns)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n // `LIMIT -1` in SQLite means \"no limit\". Use that as the sentinel\n // for `limit === undefined` so the same prepared statement covers\n // both paged and full-range reads without a second SQL.\n const lim = typeof limit === 'number' ? limit : -1\n const rows = stmtLoadTurnsRange.all(sessionId, lim, from) as { data: string }[]\n return rows\n .map(r => safeParseRow<SessionTurn>(r.data, sessionId, 'turn'))\n .filter(t => t !== null)\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n txnUpdateRun.immediate(sessionId, run)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status'], options?: { preserveUpdatedAt?: boolean }) {\n txnUpdateStatus.immediate(sessionId, status, options?.preserveUpdatedAt === true)\n },\n\n async upsertArtifact(artifact: SessionArtifact) {\n return txnUpsertArtifact.immediate(artifact)\n },\n\n async listArtifacts(filter) {\n const { where, limit, params } = buildArtifactFilter(filter)\n const sql = `SELECT ${ARTIFACT_COLUMNS} FROM artifacts ${where} ORDER BY updated_at DESC, created_at DESC, id ${limit}`\n .replace(/\\s+/g, ' ')\n .trim()\n const rows = db.query(sql).all(...params) as ArtifactRow[]\n return rows.map(rowToArtifact)\n },\n\n async deleteArtifact(sessionId: string, artifactId: string) {\n stmtDeleteArtifact.run(sessionId, artifactId)\n },\n\n close() {\n // bun:sqlite — `false` skips throwing when statements are still\n // alive; finalizers handle them. Idempotent.\n db.close(false)\n },\n }\n\n return store\n}\n\n// ---------------------------------------------------------------------------\n// Migrations\n// ---------------------------------------------------------------------------\n\n/**\n * Forward-only schema migration runner. Idempotent — every step\n * feature-detects rather than relying on the `PRAGMA user_version`\n * number alone, so a partially-migrated DB (e.g. interrupted v3→v4\n * backfill) is brought to a consistent v4 state on the next open.\n *\n * The legacy `data` column is the v3 sentinel: when present we treat\n * the DB as v3-or-older and run the backfill. Before any destructive\n * change we copy the entire DB file to `<path>.zidane-pre-v4.bak` (see\n * {@link backupBeforeV3Migration}) so the user has a guaranteed\n * recovery path if anything goes sideways.\n *\n * After backfill we attempt to drop the column (SQLite 3.35+; Bun\n * ships ≥ 3.43 so this always succeeds in practice). If it does fail,\n * we throw rather than limp along — leaving the column with `NOT NULL`\n * while the new write paths omit it would silently break every future\n * `INSERT`. The `.bak` file lets the user roll back and report the bug.\n */\nfunction migrate(db: Database, dbPath: string) {\n // Downgrade guard: refuse to open a DB written by a newer zidane. The\n // newer schema may have shapes this build doesn't understand; \"limping\n // along\" risks silently corrupting it. Forward-only policy (see header).\n //\n // Exemption: version 5 was stamped by short-lived dev builds (2026-07)\n // whose only change was the additive `turns.role` column — plain v4\n // shape plus an extra nullable column. This build understands it fully\n // and re-stamps it back to 4 at the end of this function.\n const onDiskVersion = (db.query('PRAGMA user_version').get() as { user_version: number } | null)?.user_version ?? 0\n if (onDiskVersion > SCHEMA_VERSION && onDiskVersion !== 5) {\n throw new Error(\n `[zidane/sqlite] \"${dbPath}\" has schema version ${onDiskVersion}, but this zidane build only supports up to ${SCHEMA_VERSION}. `\n + `It was written by a newer zidane — upgrade zidane, or point this build at a different sessions DB.`,\n )\n }\n\n const sessionCols = db.query('PRAGMA table_info(sessions)').all() as { name: string }[]\n const hasCol = (n: string) => sessionCols.some(c => c.name === n)\n\n // v1/v2 → v3: project_root column (idempotent on fresh DBs since\n // CREATE TABLE already has it).\n if (!hasCol('project_root'))\n db.run('ALTER TABLE sessions ADD COLUMN project_root TEXT')\n\n // v3 → v4 header promotion. ADD COLUMN ... DEFAULT applies the\n // default to every existing row, so v3 sessions get a synthetic\n // 'idle' status + empty metadata until the backfill loop below\n // overwrites with the real values from the JSON blob.\n if (!hasCol('status'))\n db.run(`ALTER TABLE sessions ADD COLUMN status TEXT NOT NULL DEFAULT 'idle'`)\n if (!hasCol('metadata'))\n db.run(`ALTER TABLE sessions ADD COLUMN metadata TEXT NOT NULL DEFAULT '{}'`)\n\n // v3 → v4 row fan-out. Skip when the legacy column is gone (already\n // migrated, or fresh v4 DB).\n if (hasCol('data')) {\n // Take a point-in-time snapshot BEFORE the destructive changes\n // below. Aborts the whole migration on backup failure so the user's\n // v3 DB is never modified without a recovery path on disk.\n backupBeforeV3Migration(db, dbPath)\n backfillV3Blobs(db)\n // Drop the legacy column. Bun's SQLite is 3.51+ in practice — DROP\n // COLUMN has been supported since 3.35 (2021). If it ever did fail\n // we'd be left with a `NOT NULL data` column that the new INSERT\n // statements don't populate, making the DB unwritable. Throwing\n // here surfaces the failure immediately and points the user at the\n // backup file we just created.\n try {\n db.run('ALTER TABLE sessions DROP COLUMN data')\n }\n catch (err) {\n throw new Error(\n `[zidane/sqlite] v3→v4 migration could not drop legacy \"data\" column: ${errorMessage(err)}. `\n + `Restore from \"${dbPath}.zidane-pre-v4.bak\" with an older zidane build and report this — `\n + `your sessions are intact in the backup file.`,\n )\n }\n }\n\n // Additive role denormalization (see the header's \"unversioned\n // enhancement\" note for why this never bumps `user_version`). Runs\n // AFTER the v3→v4 fan-out so the repair sees the fanned-out rows.\n //\n // - ALTER is feature-detected (fresh DBs already have the column\n // from CREATE TABLE).\n // - The NULL-role repair is checked on EVERY open: it heals rows\n // written by plain-v4 builds sharing this DB, interrupted\n // first-time backfills, and the initial migration itself — one\n // code path for all three.\n // - The read-only existence probe (cheap via the partial index)\n // gates the UPDATE so the steady-state open performs no write at\n // all. That matters beyond speed: an UPDATE statement acquires the\n // write lock even when it matches nothing, and `createSqliteStore`\n // is synchronous — an unconditional write at open would stall\n // behind any concurrent writer (the exact `bun --watch` restart\n // race busy_timeout exists for).\n // - Rows with corrupt JSON keep a NULL role and are excluded from\n // role-scoped queries — same skip-bad-rows posture as the load\n // path. The probe shares the UPDATE's `json_valid` predicate, so\n // corrupt-only leftovers don't re-trigger the write every open.\n const turnCols = db.query('PRAGMA table_info(turns)').all() as { name: string }[]\n if (!turnCols.some(c => c.name === 'role'))\n db.run('ALTER TABLE turns ADD COLUMN role TEXT')\n db.run('CREATE INDEX IF NOT EXISTS idx_turns_role_null ON turns(session_id) WHERE role IS NULL')\n const needsRepair = db.query(`SELECT 1 FROM turns WHERE role IS NULL AND json_valid(data) LIMIT 1`).get()\n if (needsRepair)\n db.run(`UPDATE turns SET role = json_extract(data, '$.role') WHERE role IS NULL AND json_valid(data)`)\n db.run('CREATE INDEX IF NOT EXISTS idx_turns_session_role ON turns(session_id, role, seq)')\n\n // Repair a DB stamped by the short-lived v5 dev builds (2026-07) — the\n // role work above was briefly shipped as a version bump, which locked\n // released v4-only binaries out of the file. The shape is identical;\n // only the stamp differs. Safe because nothing else ever wrote 5.\n const currentVersion = (db.query('PRAGMA user_version').get() as { user_version: number } | null)?.user_version ?? 0\n if (currentVersion !== SCHEMA_VERSION)\n db.run(`PRAGMA user_version = ${SCHEMA_VERSION}`)\n}\n\n/**\n * Pre-migration safety net for v3 → v4: copy the source DB file to\n * `<path>.zidane-pre-v4.bak` so the user can recover by restoring the\n * snapshot if anything in the migration goes sideways.\n *\n * - Skipped for non-filesystem paths (`:memory:`, empty string) — no\n * file to copy.\n * - Skipped when the file is missing (fresh install, never reached\n * migration territory).\n * - Skipped when the backup already exists — re-runs of the migration\n * (idempotent path) reuse the same snapshot rather than overwrite a\n * potentially-earlier good copy with a later partial state.\n * - WAL is checkpointed first so the .db file contains everything that\n * was committed up to this point (otherwise pending WAL frames would\n * be missing from the snapshot).\n * - On copy failure the whole migration is aborted with a thrown error\n * — we refuse to modify the DB without a recovery path. The user\n * sees a clear diagnostic; their sessions stay intact.\n *\n * Surfaces a one-line stderr message on success so the user knows where\n * the snapshot landed. They can delete it once they've confirmed the\n * post-migration TUI behaves correctly.\n */\nfunction backupBeforeV3Migration(db: Database, dbPath: string) {\n // `:memory:` and similar URI-shaped paths have no filesystem target.\n // bun:sqlite also accepts an empty string as a synonym for in-memory.\n if (dbPath === '' || dbPath === ':memory:' || dbPath.startsWith('file::memory:'))\n return\n\n if (!existsSync(dbPath))\n return\n\n const backupPath = `${dbPath}.zidane-pre-v4.bak`\n // Don't clobber an existing backup. The first interrupted-migration\n // attempt's snapshot is the one closest to the user's pre-zidane-v4\n // state; subsequent retries would only overwrite it with a partial\n // intermediate.\n if (existsSync(backupPath))\n return\n\n // Checkpoint WAL so the .db file contains every committed frame.\n // Without this, a backup of just the .db file would be missing\n // anything sitting in the -wal sidecar. Failure is non-fatal — an\n // empty WAL (the common case for a freshly-opened DB) returns an\n // error from TRUNCATE on some SQLite builds; we still proceed with\n // the file copy.\n let checkpointed = true\n try {\n db.run('PRAGMA wal_checkpoint(TRUNCATE)')\n }\n catch {\n // Best-effort; the copy below still captures whatever's on disk —\n // plus the WAL/SHM sidecars (copied alongside below) so the snapshot\n // stays consistent even with un-checkpointed frames.\n checkpointed = false\n }\n\n try {\n copyFileSync(dbPath, backupPath)\n if (!checkpointed) {\n // Checkpoint failed → committed frames may still live in the -wal\n // sidecar. Copy it (and -shm) next to the backup so a restore of\n // the trio is point-in-time consistent.\n for (const ext of ['-wal', '-shm']) {\n const sidecar = `${dbPath}${ext}`\n if (existsSync(sidecar))\n copyFileSync(sidecar, `${backupPath}${ext}`)\n }\n }\n process.stderr.write(\n `[zidane/sqlite] v3→v4 migration: backed up your sessions DB to \"${backupPath}\".\\n`\n + ` To roll back if anything looks wrong:\\n`\n + ` 1. quit zidane\\n`\n + ` 2. cp \"${backupPath}\" \"${dbPath}\" && rm -f \"${dbPath}-wal\" \"${dbPath}-shm\"\\n`\n + ` 3. pin an older zidane that knows the v3 schema, or upgrade to a fix\\n`\n + ` Delete the .bak once every session loads correctly.\\n`,\n )\n }\n catch (err) {\n throw new Error(\n `[zidane/sqlite] failed to back up \"${dbPath}\" before v3→v4 migration: ${errorMessage(err)}. `\n + `Refusing to modify the DB without a recovery path. Move the file aside or fix the permissions and retry.`,\n )\n }\n}\n\n/**\n * Parse each session's legacy `data` blob and fan its `turns` + `runs`\n * arrays out into the dedicated tables, then overwrite the synthetic\n * `status`/`metadata` defaults with the values from the blob.\n *\n * Skips rows that already have at least one entry in `turns` for them —\n * a previous run of this migration already landed those bytes, and\n * re-inserting them would either duplicate (if seq numbering shifted)\n * or crash on the PK. The \"header already exists, turns table populated\"\n * combination is the idempotency signal.\n *\n * Per-row failures (corrupt JSON, …) log + skip rather than abort the\n * whole migration: one bad row shouldn't prevent the rest of the DB\n * from coming forward to v4.\n */\nfunction backfillV3Blobs(db: Database) {\n // Use a raw SELECT (not a prepared statement) so the legacy column\n // reference doesn't survive in the statement cache after the column\n // is dropped at the end of `migrate()`.\n const rows = db.query('SELECT id, data FROM sessions').all() as { id: string, data: string | null }[]\n if (rows.length === 0)\n return\n\n const insertTurn = db.prepare('INSERT INTO turns (session_id, seq, data) VALUES (?, ?, ?)')\n const upsertRun = db.prepare(`\n INSERT INTO runs (session_id, run_id, started_at, data) VALUES (?, ?, ?, ?)\n ON CONFLICT(session_id, run_id) DO UPDATE SET\n data = excluded.data,\n started_at = excluded.started_at\n `)\n const updateHeader = db.prepare('UPDATE sessions SET status = ?, metadata = ? WHERE id = ?')\n const countTurns = db.prepare('SELECT COUNT(*) as c FROM turns WHERE session_id = ?')\n\n for (const row of rows) {\n if (!row.data)\n continue\n try {\n const data = JSON.parse(row.data) as Partial<SessionData>\n // Skip rows whose turns table already has content — a previous\n // partial migration already fanned this row out.\n const existing = (countTurns.get(row.id) as { c: number }).c\n if (existing > 0)\n continue\n\n const turns = Array.isArray(data.turns) ? data.turns : []\n const runs = Array.isArray(data.runs) ? data.runs : []\n const txn = db.transaction(() => {\n turns.forEach((t, i) => insertTurn.run(row.id, i, JSON.stringify(t)))\n for (const run of runs) {\n // Defensive: a corrupt run record without an id can't go into\n // the (session_id, run_id) PK. Skip rather than crash.\n if (!run || typeof run.id !== 'string')\n continue\n upsertRun.run(row.id, run.id, run.startedAt ?? 0, JSON.stringify(run))\n }\n updateHeader.run(data.status ?? 'idle', JSON.stringify(data.metadata ?? {}), row.id)\n })\n txn.immediate()\n }\n catch (err) {\n process.stderr.write(`[zidane/sqlite] v3→v4 migrate: session \"${row.id}\" skipped: ${errorMessage(err)}\\n`)\n }\n }\n}\n\n/**\n * Compose the WHERE / LIMIT fragments shared by `list` and `listMeta`.\n *\n * `projectRoot: string` → `WHERE project_root = ?` (excludes untagged).\n * `projectRoot: null` → `WHERE project_root IS NULL` (untagged only).\n * `projectRoot: undefined` → axis omitted (untagged + tagged).\n *\n * The `null` case lets a future \"show legacy\" debug toggle query pre-v3\n * sessions explicitly without leaking them into normal per-project lists.\n */\nfunction buildListFilter(\n filter?: { agentId?: string, limit?: number, projectRoot?: string | null },\n): { where: string, limit: string, params: string[] } {\n const conditions: string[] = []\n const params: string[] = []\n if (filter?.agentId) {\n conditions.push('agent_id = ?')\n params.push(filter.agentId)\n }\n if ('projectRoot' in (filter ?? {})) {\n const v = filter!.projectRoot\n if (v === null) {\n conditions.push('project_root IS NULL')\n }\n else if (typeof v === 'string') {\n conditions.push('project_root = ?')\n params.push(v)\n }\n }\n return {\n where: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',\n limit: typeof filter?.limit === 'number' && filter.limit > 0 ? `LIMIT ${filter.limit}` : '',\n params,\n }\n}\n\n/**\n * WHERE / LIMIT fragments for `listArtifacts`. `projectRoot` follows the\n * same tri-state contract as {@link buildListFilter}.\n */\nfunction buildArtifactFilter(\n filter?: ArtifactFilter,\n): { where: string, limit: string, params: (string | number)[] } {\n const conditions: string[] = []\n const params: (string | number)[] = []\n if (filter?.sessionId !== undefined) {\n conditions.push('session_id = ?')\n params.push(filter.sessionId)\n }\n if (filter?.kind !== undefined) {\n conditions.push('kind = ?')\n params.push(filter.kind)\n }\n if (filter?.uri !== undefined) {\n conditions.push('uri = ?')\n params.push(filter.uri)\n }\n if (filter && 'projectRoot' in filter) {\n if (filter.projectRoot === null) {\n conditions.push('project_root IS NULL')\n }\n else if (typeof filter.projectRoot === 'string') {\n conditions.push('project_root = ?')\n params.push(filter.projectRoot)\n }\n }\n if (typeof filter?.since === 'number') {\n conditions.push('updated_at >= ?')\n params.push(filter.since)\n }\n return {\n where: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',\n limit: typeof filter?.limit === 'number' && filter.limit > 0 ? `LIMIT ${filter.limit}` : '',\n params,\n }\n}\n\n/**\n * Guarded per-row parse for `turns.data` / `runs.data`. One corrupt row\n * (truncated write, hand-edited DB, disk bitflip) must not make the whole\n * session unloadable — log + skip it instead, mirroring the migration's\n * per-row backfill policy. The structural check matches the minimal shape\n * both `SessionTurn` and `SessionRun` share (an object with a string `id`).\n */\nfunction safeParseRow<T extends { id: string }>(\n text: string,\n sessionId: string,\n kind: 'turn' | 'run',\n): T | null {\n try {\n const parsed = JSON.parse(text) as unknown\n if (\n !parsed\n || typeof parsed !== 'object'\n || Array.isArray(parsed)\n || typeof (parsed as { id?: unknown }).id !== 'string'\n ) {\n console.error(`[zidane/sqlite] session \"${sessionId}\": skipping malformed ${kind} row (not a ${kind}-shaped object)`)\n return null\n }\n return parsed as T\n }\n catch (err) {\n console.error(`[zidane/sqlite] session \"${sessionId}\": skipping corrupt ${kind} row: ${errorMessage(err)}`)\n return null\n }\n}\n\n/**\n * `JSON.parse` with a fallback to `{}` for the metadata column. v3→v4\n * migration writes well-formed JSON, fresh v4 writes go through\n * `JSON.stringify(metadata ?? {})` — but a hand-edited DB or a botched\n * external migration could leave a row with malformed JSON, and we'd\n * rather surface an empty metadata object than crash the entire load\n * path. Logged under `ZIDANE_DEBUG` so the corruption isn't fully\n * silent.\n */\nfunction safeParseRecord(text: string): Record<string, unknown> {\n try {\n const parsed = JSON.parse(text)\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : {}\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/sqlite] metadata JSON parse failed, defaulting to {}: ${errorMessage(err)}\\n`)\n return {}\n }\n}\n\n/**\n * Convenience: open a SQLite store at `dbPath`, creating its parent\n * directory if it doesn't yet exist. Used by the TUI launcher to spin up\n * the default session store at `~/.zidane/sessions.db`.\n *\n * Lives here (and not in `chat/store.ts`) so the renderer-agnostic\n * `zidane/chat` entry stays free of `bun:sqlite` — non-Bun consumers\n * (a future GUI, an SDK) can import `zidane/chat` without pulling in the\n * Bun-only binding.\n */\nexport function createTuiStore(dbPath: string): SessionStore {\n const dir = dirname(dbPath)\n if (!existsSync(dir)) {\n try {\n mkdirSync(dir, { recursive: true })\n }\n catch (err) {\n throw new Error(\n `Could not create TUI storage directory at \"${dir}\". `\n + `Override the location via \\`runTui({ storageDir, prefix })\\` or the `\n + `\\`ZIDANE_STORAGE_DIR\\` env var. Original error: ${errorMessage(err)}`,\n )\n }\n }\n return createSqliteStore({ path: dbPath })\n}\n"],"mappings":";;;;;;AAoHA,MAAM,iBAAiB;;;;;;AAYvB,SAAgB,iBAAiB,KAAuB;CACtD,MAAM,MAAM,aAAa,GAAG;CAC5B,OAAO,wFAAwF,KAAK,GAAG;AACzG;;;;;;;;;;;AAYA,SAAgB,kBAAkB,KAAc,QAAyB;CACvE,IAAI,CAAC,iBAAiB,GAAG,GACvB,OAAO;CAWT,OAAO,IAVa,MAClB,yCAAyC,OAAO,gBAAgB,aAAa,GAAG,EAAE,oJAG7D,OAAO,0BAA0B,OAAO,0BAC7C,OAAO,KAAK,OAAO,sBAAsB,OAAO,SAAS,OAAO,eAAe,OAAO,eAAe,OAAO,4MAG5H,EAAE,OAAO,IAAI,CAEF;AACf;AAEA,SAAgB,kBAAkB,SAA2C;CAC3E,IAAI;CACJ,IAAI;EACF,QAAQ,gBAAgB,OAAO;CACjC,SACO,KAAK;EACV,MAAM,kBAAkB,KAAK,QAAQ,IAAI;CAC3C;CAGA,MAAM,UAAU,EAAE,GAAG,MAAM;CAC3B,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,GAA6B;EAC9D,MAAM,KAAK,MAAM;EACjB,IAAI,OAAO,OAAO,cAAc,QAAQ,SACtC;EACF,QAAQ,OAAO,OAAO,GAAG,SAAoB;GAC3C,IAAI;IACF,OAAO,MAAO,GAA6C,MAAM,OAAO,IAAI;GAC9E,SACO,KAAK;IACV,MAAM,kBAAkB,KAAK,QAAQ,IAAI;GAC3C;EACF;CACF;CACA,OAAO;AACT;AAEA,SAAS,gBAAgB,SAA2C;CAClE,MAAM,KAAK,IAAI,SAAS,QAAQ,IAAI;CAkBpC,GAAG,IAAI,4BAA4B;CAInC,GAAG,IAAI,2BAA2B;CAGlC,GAAG,IAAI,0BAA0B;CAOjC,GAAG,IAAI;;;;;;;;;;GAUN;CACD,GAAG,IAAI,wEAAwE;CAC/E,GAAG,IAAI,gFAAgF;CAKvF,GAAG,IAAI;;;;;;;;;GASN;CAOD,GAAG,IAAI;;;;;;;;;GASN;CAED,QAAQ,IAAI,QAAQ,IAAI;CAQxB,GAAG,IAAI;;;;;;;;;;;;;;;;GAgBN;CACD,GAAG,IAAI,uFAAuF;CAC9F,GAAG,IAAI,oGAAoG;CAC3G,GAAG,IAAI,2EAA2E;CAKlF,MAAM,iBAAiB,GAAG,QAAQ;;;GAGjC;CACD,MAAM,gBAAgB,GAAG,QAAQ,0DAA0D;CAC3F,MAAM,qBAAqB,GAAG,QAAQ,2EAA2E;CACjH,MAAM,eAAe,GAAG,QAAQ,wEAAwE;CAExG,MAAM,mBAAmB,GAAG,QAAQ;;;;;;;;;GASnC;CACD,MAAM,aAAa,GAAG,QAAQ,mCAAmC;CACjE,MAAM,4BAA4B,GAAG,QAAQ,wCAAwC;CACrF,MAAM,2BAA2B,GAAG,QAAQ,uCAAuC;CACnF,MAAM,iBAAiB,GAAG,QAAQ,qEAAqE;CACvG,MAAM,gBAAgB,GAAG,QAAQ;;;;;GAKhC;CACD,MAAM,aAAa,GAAG,QAAQ,sDAAsD;CACpF,MAAM,YAAY,GAAG,QAAQ,iDAAiD;CAC9E,MAAM,mBAAmB,GAAG,QAAQ,6DAA6D;CAEjG,MAAM,uBAAuB,GAAG,QAAQ,6CAA6C;CAErF,MAAM,mBAAmB;CACzB,MAAM,mBAAmB,GAAG,QAAQ,UAAU,iBAAiB,8DAA8D;CAG7H,MAAM,uBAAuB,GAAG,QAAQ,UAAU,iBAAiB,gEAAgE;CACnI,MAAM,qBAAqB,GAAG,QAAQ;6BACX,iBAAiB;GAC3C;CACD,MAAM,sBAAsB,GAAG,QAAQ;;GAEtC;CACD,MAAM,qBAAqB,GAAG,QAAQ,uDAAuD;CAkB7F,MAAM,YAAY,cAA0C;EAC1D,MAAM,SAAS,eAAe,IAAI,SAAS;EAC3C,IAAI,CAAC,QACH,OAAO;EAET,MAAM,WAAW,cAAc,IAAI,SAAS;EAC5C,MAAM,UAAU,aAAa,IAAI,SAAS;EAE1C,OAAO;GACL,IAAI,OAAO;GACX,GAAI,OAAO,WAAW,EAAE,SAAS,OAAO,SAAS,IAAI,CAAC;GACtD,GAAI,OAAO,eAAe,EAAE,aAAa,OAAO,aAAa,IAAI,CAAC;GAClE,OAAO,SACJ,KAAI,MAAK,aAA0B,EAAE,MAAM,OAAO,IAAI,MAAM,CAAC,CAAC,CAC9D,QAAO,MAAK,MAAM,IAAI;GACzB,MAAM,QACH,KAAI,MAAK,aAAyB,EAAE,MAAM,OAAO,IAAI,KAAK,CAAC,CAAC,CAC5D,QAAO,MAAK,MAAM,IAAI;GACzB,QAAQ,OAAO;GACf,UAAU,gBAAgB,OAAO,QAAQ;GACzC,WAAW,OAAO;GAClB,WAAW,OAAO;EACpB;CACF;CAOA,MAAM,UAAU,GAAG,aAAa,YAAyB;EAIvD,iBAAiB,IACf,QAAQ,IACR,QAAQ,WAAW,MACnB,QAAQ,eAAe,MACvB,QAAQ,QACR,KAAK,UAAU,QAAQ,YAAY,CAAC,CAAC,GACrC,QAAQ,WACR,QAAQ,SACV;EAKA,0BAA0B,IAAI,QAAQ,EAAE;EACxC,yBAAyB,IAAI,QAAQ,EAAE;EACvC,QAAQ,MAAM,SAAS,GAAG,MAAM,eAAe,IAAI,QAAQ,IAAI,GAAG,EAAE,QAAQ,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC;EACpG,KAAK,MAAM,OAAO,QAAQ,MACxB,cAAc,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI,WAAW,KAAK,UAAU,GAAG,CAAC;CAE5E,CAAC;CAQD,MAAM,cAAc,GAAG,aAAa,YAAyB;EAC3D,iBAAiB,IACf,QAAQ,IACR,QAAQ,WAAW,MACnB,QAAQ,eAAe,MACvB,QAAQ,QACR,KAAK,UAAU,QAAQ,YAAY,CAAC,CAAC,GACrC,QAAQ,WACR,QAAQ,SACV;EACA,KAAK,MAAM,OAAO,QAAQ,MACxB,cAAc,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI,WAAW,KAAK,UAAU,GAAG,CAAC;CAE5E,CAAC;CASD,MAAM,oBAAoB,cAAsB;EAE9C,IADe,eAAe,IAAI,SACzB,GACP;EACF,MAAM,MAAM,KAAK,IAAI;EACrB,iBAAiB,IAAI,WAAW,MAAM,MAAM,QAAQ,MAAM,KAAK,GAAG;CACpE;CAEA,MAAM,iBAAiB,GAAG,aAAa,WAAmB,UAAyB;EACjF,iBAAiB,SAAS;EAG1B,IAAI,WADS,WAAW,IAAI,SAAS,CAAC,CAA0B,KAC1C,MAAM;EAC5B,KAAK,MAAM,QAAQ,OAAO;GACxB,eAAe,IAAI,WAAW,SAAS,KAAK,QAAQ,MAAM,KAAK,UAAU,IAAI,CAAC;GAC9E,WAAW;EACb;EACA,UAAU,IAAI,KAAK,IAAI,GAAG,SAAS;CACrC,CAAC;CAED,MAAM,eAAe,GAAG,aAAa,WAAmB,QAAoB;EAG1E,IAAI,CADW,eAAe,IAAI,SACxB,GACR;EAKF,cAAc,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,KAAK,UAAU,GAAG,CAAC;EACvE,UAAU,IAAI,KAAK,IAAI,GAAG,SAAS;CACrC,CAAC;CAED,MAAM,kBAAkB,GAAG,aAAa,WAAmB,QAA+B,sBAA+B;EAEvH,IAAI,mBACF,qBAAqB,IAAI,QAAQ,SAAS;OAE1C,iBAAiB,IAAI,QAAQ,KAAK,IAAI,GAAG,SAAS;CACtD,CAAC;CAgBD,MAAM,iBAAiB,MAAoC;EACzD,IAAI;EACJ,IAAI,EAAE,MACJ,IAAI;GACF,OAAO,KAAK,MAAM,EAAE,IAAI;EAC1B,QACM;GAGJ,OAAO,KAAA;EACT;EAEF,OAAO;GACL,IAAI,EAAE;GACN,WAAW,EAAE;GACb,GAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC;GACtC,GAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,IAAI,CAAC;GACzC,MAAM,EAAE;GACR,KAAK,EAAE;GACP,OAAO,EAAE;GACT,GAAI,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,IAAI,CAAC;GACxD,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;GACrC,WAAW,EAAE;GACb,WAAW,EAAE;EACf;CACF;CAEA,MAAM,oBAAoB,GAAG,aAAa,aAAsD;EAG9F,IAAI,CAAC,eAAe,IAAI,SAAS,SAAS,GACxC,OAAO;EACT,MAAM,cAAc,iBAAiB,IAAI,SAAS,WAAW,SAAS,MAAM,SAAS,GAAG;EACxF,IAAI,aAAa;GACf,MAAM,SAAS,cAAc,cAAc,WAAW,GAAG,QAAQ;GACjE,oBAAoB,IAClB,OAAO,OACP,OAAO,eAAe,MACtB,OAAO,SAAS,KAAA,IAAY,KAAK,UAAU,OAAO,IAAI,IAAI,MAC1D,OAAO,WACP,OAAO,EACT;GACA,OAAO;EACT;EACA,MAAM,UAAW,qBAAqB,IAAI,SAAS,SAAS,CAAC,CAAmB,IAAI,aAAa;EACjG,KAAK,MAAM,MAAM,wBAAwB,OAAO,GAC9C,mBAAmB,IAAI,SAAS,WAAW,EAAE;EAC/C,mBAAmB,IACjB,SAAS,IACT,SAAS,WACT,SAAS,SAAS,MAClB,SAAS,UAAU,MACnB,SAAS,MACT,SAAS,KACT,SAAS,OACT,SAAS,eAAe,MACxB,SAAS,SAAS,KAAA,IAAY,KAAK,UAAU,SAAS,IAAI,IAAI,MAC9D,SAAS,WACT,SAAS,SACX;EACA,OAAO;CACT,CAAC;CA+MD,OAAO;EA5ML,MAAM,KAAK,WAAmB;GAC5B,OAAO,SAAS,SAAS;EAC3B;EAEA,MAAM,KAAK,SAAsB;GAC/B,QAAQ,UAAU,OAAO;EAC3B;EAEA,MAAM,SAAS,SAAsB;GACnC,YAAY,UAAU,OAAO;EAC/B;EAEA,MAAM,OAAO,WAAmB;GAG9B,WAAW,IAAI,SAAS;EAC1B;EAEA,MAAM,KAAK,QAAQ;GAMjB,MAAM,EAAE,OAAO,OAAO,WAAW,gBAAgB,MAAM;GACvD,MAAM,MAAM,2BAA2B,MAAM,4BAA4B,QAAQ,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;GAE3G,OADa,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,MACxB,CAAC,CAAC,KAAI,MAAK,EAAE,EAAE;EAC3B;EAEA,MAAM,QAAQ,WAAW;GAOvB,MAAM,MAAM,GAAG,MAAM;;;;;;;;;;;;;;;OAepB,CAAC,CAAC,IAAI,SAAS;GAahB,IAAI,CAAC,KACH,OAAO;GACT,OAAO;IACL,IAAI,IAAI;IACR,UAAU,gBAAgB,IAAI,QAAQ;IACtC,GAAI,IAAI,eAAe,EAAE,aAAa,IAAI,aAAa,IAAI,CAAC;IAC5D,WAAW,IAAI;IACf,GAAI,IAAI,WAAW,EAAE,SAAS,IAAI,SAAS,IAAI,CAAC;IAChD,WAAW,IAAI;IACf,QAAQ,IAAI;IACZ,WAAW,IAAI;IACf,kBAAkB,IAAI;IACtB,UAAU,IAAI;IACd,eAAe,IAAI;GACrB;EACF;EAEA,MAAM,SAAS,QAAQ;GAkBrB,MAAM,EAAE,OAAO,OAAO,WAAW,gBAAgB,MAAM;GACvD,MAAM,MAAM;;;;;;;;;;;;;;;;;;0BAkBQ,MAAM,8BAA8B,MAAM;;GAiB9D,OADa,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,MACxB,CAAC,CAAC,KAAK,MAAM;IACrB,MAAM,gBAAgB,EAAE,kBACpB,aAA0B,EAAE,iBAAiB,EAAE,IAAI,MAAM,IACzD;IACJ,OAAO;KACL,IAAI,EAAE;KACN,UAAU,gBAAgB,EAAE,QAAQ;KACpC,GAAI,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,IAAI,CAAC;KACxD,WAAW,EAAE;KACb,GAAI,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,IAAI,CAAC;KAC5C,WAAW,EAAE;KACb,QAAQ,EAAE;KACV,WAAW,EAAE;KACb,kBAAkB,EAAE;KACpB,UAAU,EAAE;KACZ,eAAe,EAAE;KACjB,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;IAC3C;GACF,CAAC;EACH;EAEA,MAAM,YAAY,WAAmB,OAAsB;GACzD,eAAe,UAAU,WAAW,KAAK;EAC3C;EAEA,MAAM,SAAS,WAAmB,OAAO,GAAG,OAAgB;GAI1D,MAAM,MAAM,OAAO,UAAU,WAAW,QAAQ;GAEhD,OADa,mBAAmB,IAAI,WAAW,KAAK,IAC1C,CAAC,CACR,KAAI,MAAK,aAA0B,EAAE,MAAM,WAAW,MAAM,CAAC,CAAC,CAC9D,QAAO,MAAK,MAAM,IAAI;EAC3B;EAEA,MAAM,UAAU,WAAmB,KAAiB;GAClD,aAAa,UAAU,WAAW,GAAG;EACvC;EAEA,MAAM,aAAa,WAAmB,QAA+B,SAA2C;GAC9G,gBAAgB,UAAU,WAAW,QAAQ,SAAS,sBAAsB,IAAI;EAClF;EAEA,MAAM,eAAe,UAA2B;GAC9C,OAAO,kBAAkB,UAAU,QAAQ;EAC7C;EAEA,MAAM,cAAc,QAAQ;GAC1B,MAAM,EAAE,OAAO,OAAO,WAAW,oBAAoB,MAAM;GAC3D,MAAM,MAAM,UAAU,iBAAiB,kBAAkB,MAAM,iDAAiD,QAC7G,QAAQ,QAAQ,GAAG,CAAC,CACpB,KAAK;GAER,OADa,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,MACxB,CAAC,CAAC,IAAI,aAAa;EAC/B;EAEA,MAAM,eAAe,WAAmB,YAAoB;GAC1D,mBAAmB,IAAI,WAAW,UAAU;EAC9C;EAEA,QAAQ;GAGN,GAAG,MAAM,KAAK;EAChB;CAGS;AACb;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,QAAQ,IAAc,QAAgB;CAS7C,MAAM,gBAAiB,GAAG,MAAM,qBAAqB,CAAC,CAAC,IAAI,CAAC,EAAsC,gBAAgB;CAClH,IAAI,gBAAgB,kBAAkB,kBAAkB,GACtD,MAAM,IAAI,MACR,oBAAoB,OAAO,uBAAuB,cAAc,8CAA8C,eAAe,qGAE/H;CAGF,MAAM,cAAc,GAAG,MAAM,6BAA6B,CAAC,CAAC,IAAI;CAChE,MAAM,UAAU,MAAc,YAAY,MAAK,MAAK,EAAE,SAAS,CAAC;CAIhE,IAAI,CAAC,OAAO,cAAc,GACxB,GAAG,IAAI,mDAAmD;CAM5D,IAAI,CAAC,OAAO,QAAQ,GAClB,GAAG,IAAI,qEAAqE;CAC9E,IAAI,CAAC,OAAO,UAAU,GACpB,GAAG,IAAI,qEAAqE;CAI9E,IAAI,OAAO,MAAM,GAAG;EAIlB,wBAAwB,IAAI,MAAM;EAClC,gBAAgB,EAAE;EAOlB,IAAI;GACF,GAAG,IAAI,uCAAuC;EAChD,SACO,KAAK;GACV,MAAM,IAAI,MACR,wEAAwE,aAAa,GAAG,EAAE,kBACvE,OAAO,8GAE5B;EACF;CACF;CAwBA,IAAI,CADa,GAAG,MAAM,0BAA0B,CAAC,CAAC,IAC1C,CAAC,CAAC,MAAK,MAAK,EAAE,SAAS,MAAM,GACvC,GAAG,IAAI,wCAAwC;CACjD,GAAG,IAAI,wFAAwF;CAE/F,IADoB,GAAG,MAAM,qEAAqE,CAAC,CAAC,IACtF,GACZ,GAAG,IAAI,8FAA8F;CACvG,GAAG,IAAI,mFAAmF;CAO1F,KADwB,GAAG,MAAM,qBAAqB,CAAC,CAAC,IAAI,CAAC,EAAsC,gBAAgB,OAC5F,gBACrB,GAAG,IAAI,yBAAyB,gBAAgB;AACpD;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAS,wBAAwB,IAAc,QAAgB;CAG7D,IAAI,WAAW,MAAM,WAAW,cAAc,OAAO,WAAW,eAAe,GAC7E;CAEF,IAAI,CAAC,WAAW,MAAM,GACpB;CAEF,MAAM,aAAa,GAAG,OAAO;CAK7B,IAAI,WAAW,UAAU,GACvB;CAQF,IAAI,eAAe;CACnB,IAAI;EACF,GAAG,IAAI,iCAAiC;CAC1C,QACM;EAIJ,eAAe;CACjB;CAEA,IAAI;EACF,aAAa,QAAQ,UAAU;EAC/B,IAAI,CAAC,cAIH,KAAK,MAAM,OAAO,CAAC,QAAQ,MAAM,GAAG;GAClC,MAAM,UAAU,GAAG,SAAS;GAC5B,IAAI,WAAW,OAAO,GACpB,aAAa,SAAS,GAAG,aAAa,KAAK;EAC/C;EAEF,QAAQ,OAAO,MACb,mEAAmE,WAAW,8EAG9D,WAAW,KAAK,OAAO,cAAc,OAAO,SAAS,OAAO,yIAG9E;CACF,SACO,KAAK;EACV,MAAM,IAAI,MACR,sCAAsC,OAAO,4BAA4B,aAAa,GAAG,EAAE,2GAE7F;CACF;AACF;;;;;;;;;;;;;;;;AAiBA,SAAS,gBAAgB,IAAc;CAIrC,MAAM,OAAO,GAAG,MAAM,+BAA+B,CAAC,CAAC,IAAI;CAC3D,IAAI,KAAK,WAAW,GAClB;CAEF,MAAM,aAAa,GAAG,QAAQ,4DAA4D;CAC1F,MAAM,YAAY,GAAG,QAAQ;;;;;GAK5B;CACD,MAAM,eAAe,GAAG,QAAQ,2DAA2D;CAC3F,MAAM,aAAa,GAAG,QAAQ,sDAAsD;CAEpF,KAAK,MAAM,OAAO,MAAM;EACtB,IAAI,CAAC,IAAI,MACP;EACF,IAAI;GACF,MAAM,OAAO,KAAK,MAAM,IAAI,IAAI;GAIhC,IADkB,WAAW,IAAI,IAAI,EAAE,CAAC,CAAmB,IAC5C,GACb;GAEF,MAAM,QAAQ,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC;GACxD,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;GAYrD,GAXe,kBAAkB;IAC/B,MAAM,SAAS,GAAG,MAAM,WAAW,IAAI,IAAI,IAAI,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC;IACpE,KAAK,MAAM,OAAO,MAAM;KAGtB,IAAI,CAAC,OAAO,OAAO,IAAI,OAAO,UAC5B;KACF,UAAU,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,aAAa,GAAG,KAAK,UAAU,GAAG,CAAC;IACvE;IACA,aAAa,IAAI,KAAK,UAAU,QAAQ,KAAK,UAAU,KAAK,YAAY,CAAC,CAAC,GAAG,IAAI,EAAE;GACrF,CACE,CAAC,CAAC,UAAU;EAChB,SACO,KAAK;GACV,QAAQ,OAAO,MAAM,2CAA2C,IAAI,GAAG,aAAa,aAAa,GAAG,EAAE,GAAG;EAC3G;CACF;AACF;;;;;;;;;;;AAYA,SAAS,gBACP,QACoD;CACpD,MAAM,aAAuB,CAAC;CAC9B,MAAM,SAAmB,CAAC;CAC1B,IAAI,QAAQ,SAAS;EACnB,WAAW,KAAK,cAAc;EAC9B,OAAO,KAAK,OAAO,OAAO;CAC5B;CACA,IAAI,kBAAkB,UAAU,CAAC,IAAI;EACnC,MAAM,IAAI,OAAQ;EAClB,IAAI,MAAM,MACR,WAAW,KAAK,sBAAsB;OAEnC,IAAI,OAAO,MAAM,UAAU;GAC9B,WAAW,KAAK,kBAAkB;GAClC,OAAO,KAAK,CAAC;EACf;CACF;CACA,OAAO;EACL,OAAO,WAAW,SAAS,IAAI,SAAS,WAAW,KAAK,OAAO,MAAM;EACrE,OAAO,OAAO,QAAQ,UAAU,YAAY,OAAO,QAAQ,IAAI,SAAS,OAAO,UAAU;EACzF;CACF;AACF;;;;;AAMA,SAAS,oBACP,QAC+D;CAC/D,MAAM,aAAuB,CAAC;CAC9B,MAAM,SAA8B,CAAC;CACrC,IAAI,QAAQ,cAAc,KAAA,GAAW;EACnC,WAAW,KAAK,gBAAgB;EAChC,OAAO,KAAK,OAAO,SAAS;CAC9B;CACA,IAAI,QAAQ,SAAS,KAAA,GAAW;EAC9B,WAAW,KAAK,UAAU;EAC1B,OAAO,KAAK,OAAO,IAAI;CACzB;CACA,IAAI,QAAQ,QAAQ,KAAA,GAAW;EAC7B,WAAW,KAAK,SAAS;EACzB,OAAO,KAAK,OAAO,GAAG;CACxB;CACA,IAAI,UAAU,iBAAiB;MACzB,OAAO,gBAAgB,MACzB,WAAW,KAAK,sBAAsB;OAEnC,IAAI,OAAO,OAAO,gBAAgB,UAAU;GAC/C,WAAW,KAAK,kBAAkB;GAClC,OAAO,KAAK,OAAO,WAAW;EAChC;;CAEF,IAAI,OAAO,QAAQ,UAAU,UAAU;EACrC,WAAW,KAAK,iBAAiB;EACjC,OAAO,KAAK,OAAO,KAAK;CAC1B;CACA,OAAO;EACL,OAAO,WAAW,SAAS,IAAI,SAAS,WAAW,KAAK,OAAO,MAAM;EACrE,OAAO,OAAO,QAAQ,UAAU,YAAY,OAAO,QAAQ,IAAI,SAAS,OAAO,UAAU;EACzF;CACF;AACF;;;;;;;;AASA,SAAS,aACP,MACA,WACA,MACU;CACV,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;EAC9B,IACE,CAAC,UACE,OAAO,WAAW,YAClB,MAAM,QAAQ,MAAM,KACpB,OAAQ,OAA4B,OAAO,UAC9C;GACA,QAAQ,MAAM,4BAA4B,UAAU,wBAAwB,KAAK,cAAc,KAAK,gBAAgB;GACpH,OAAO;EACT;EACA,OAAO;CACT,SACO,KAAK;EACV,QAAQ,MAAM,4BAA4B,UAAU,sBAAsB,KAAK,QAAQ,aAAa,GAAG,GAAG;EAC1G,OAAO;CACT;AACF;;;;;;;;;;AAWA,SAAS,gBAAgB,MAAuC;CAC9D,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;EAC9B,OAAO,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IAChE,SACA,CAAC;CACP,SACO,KAAK;EACV,IAAI,QAAQ,IAAI,cACd,QAAQ,OAAO,MAAM,iEAAiE,aAAa,GAAG,EAAE,GAAG;EAC7G,OAAO,CAAC;CACV;AACF;;;;;;;;;;;AAYA,SAAgB,eAAe,QAA8B;CAC3D,MAAM,MAAM,QAAQ,MAAM;CAC1B,IAAI,CAAC,WAAW,GAAG,GACjB,IAAI;EACF,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;CACpC,SACO,KAAK;EACV,MAAM,IAAI,MACR,8CAA8C,IAAI,yHAEG,aAAa,GAAG,GACvE;CACF;CAEF,OAAO,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAC3C"}
|