@averyyy/pi-coding-agent 0.87.1-piclient.1 → 0.99.1-piclient.1
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/CHANGELOG.md +98 -0
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.js.map +1 -1
- package/dist/bun/runtime-setup.d.ts.map +1 -1
- package/dist/bun/runtime-setup.js +4 -1
- package/dist/bun/runtime-setup.js.map +1 -1
- package/dist/bun/sandbox-env-setup.d.ts.map +1 -1
- package/dist/bundle/chunks/anthropic-messages-A6EXUBG6.js +6 -0
- package/dist/bundle/chunks/anthropic.js +2 -2
- package/dist/bundle/chunks/azure-openai-responses-YZ3Q2WVP.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
- package/dist/bundle/chunks/chunk-3YXHSP6S.js +21 -0
- package/dist/bundle/chunks/chunk-7BINYQ2V.js +1548 -0
- package/dist/bundle/chunks/chunk-7JGR3GZN.js +11 -0
- package/dist/bundle/chunks/chunk-F6ORSP6L.js +187 -0
- package/dist/bundle/chunks/chunk-FJTXMIZI.js +150 -0
- package/dist/bundle/chunks/chunk-H3QXYIF7.js +31 -0
- package/dist/bundle/chunks/{chunk-S7SZN6Z3.js → chunk-HSPCVFST.js} +1 -1
- package/dist/bundle/chunks/chunk-J4CWPC4N.js +2 -0
- package/dist/bundle/chunks/chunk-KYJKN5BJ.js +2 -0
- package/dist/bundle/chunks/chunk-RO6I2QFQ.js +4 -0
- package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-S3GGRYIO.js} +2 -2
- package/dist/bundle/chunks/chunk-UTB4DTYQ.js +2 -0
- package/dist/bundle/chunks/{chunk-UZNGGY6N.js → chunk-V7V43FNB.js} +3 -3
- package/dist/bundle/chunks/{chunk-FUXEF6JQ.js → chunk-W2DOMZSC.js} +1 -1
- package/dist/bundle/chunks/chunk-WVKBMRQX.js +82 -0
- package/dist/bundle/chunks/cli-WF6HDHBO.js +49 -0
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-XJQQYGGU.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +295 -0
- package/dist/bundle/chunks/execute-AVGAUHMT.js +308 -0
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/google-generative-ai-CQAKKJTA.js +2 -0
- package/dist/bundle/chunks/google-vertex-AE36YXJU.js +2 -0
- package/dist/bundle/chunks/{lib-HGQMNYVT.js → lib-TEDK3E5I.js} +2 -2
- package/dist/bundle/chunks/llama-cpp-classify-PJJYKPLD.js +22 -0
- package/dist/bundle/chunks/mistral-conversations-KEYTXJM7.js +5 -0
- package/dist/bundle/chunks/openai-chatgpt.js +75 -0
- package/dist/bundle/chunks/{openai-codex-responses-SG7PGUUZ.js → openai-codex-responses-FUE7WESF.js} +2 -2
- package/dist/bundle/chunks/openai-codex.js +2 -2
- package/dist/bundle/chunks/{openai-completions-OBX42CLD.js → openai-completions-XW2Q5HVC.js} +2 -2
- package/dist/bundle/chunks/openai-responses-ZUXFVYPI.js +3 -0
- package/dist/bundle/chunks/{openrouter-images-N4XVVFSD.js → openrouter-images-TPQ4V6OR.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +2 -2
- package/dist/bundle/chunks/{pi-messages-GFKBWJHZ.js → pi-messages-TM5SOOIW.js} +2 -2
- package/dist/bundle/chunks/radius.js +2 -2
- package/dist/bundle/chunks/runtime-6ZC4MGWD.js +2 -0
- package/dist/bundle/chunks/typesafe-system-one-E7A4HNJI.js +2 -0
- package/dist/bundle/chunks/virtual-modules-H7OCXN6W.js +2 -0
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -3
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-check.d.ts.map +1 -1
- package/dist/cli/auth-check.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -1
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +38 -30
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts +18 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -4
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +1 -1
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +13 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +120 -11
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +619 -289
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/bug-report-upload.d.ts.map +1 -1
- package/dist/core/bug-report-upload.js.map +1 -1
- package/dist/core/bug-report.d.ts +1 -1
- package/dist/core/bug-report.d.ts.map +1 -1
- package/dist/core/bug-report.js +1 -1
- package/dist/core/bug-report.js.map +1 -1
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/cache-warmer.d.ts.map +1 -1
- package/dist/core/cache-warmer.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +10 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +71 -33
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/utils.d.ts +2 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +25 -18
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/crash-log.d.ts.map +1 -1
- package/dist/core/crash-log.js +2 -1
- package/dist/core/crash-log.js.map +1 -1
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.css +10 -0
- package/dist/core/export-html/template.js +66 -17
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/jiti-loader.d.ts.map +1 -1
- package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +55 -5
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +18 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +99 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +296 -22
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +2 -2
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +3 -3
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +1 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/mcp-servers.d.ts +98 -0
- package/dist/core/mcp-servers.d.ts.map +1 -0
- package/dist/core/mcp-servers.js +157 -0
- package/dist/core/mcp-servers.js.map +1 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts +13 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +25 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +2 -2
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +5 -5
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +41 -3
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +167 -17
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/nested-tool-calls.d.ts +102 -0
- package/dist/core/nested-tool-calls.d.ts.map +1 -0
- package/dist/core/nested-tool-calls.js +178 -0
- package/dist/core/nested-tool-calls.js.map +1 -0
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +59 -12
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-client-cli-adapter.d.ts.map +1 -1
- package/dist/core/pi-client-cli-adapter.js.map +1 -1
- package/dist/core/pi-client-runtime-update.d.ts.map +1 -1
- package/dist/core/pi-client-runtime-update.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/pi-server-client.d.ts +5 -0
- package/dist/core/pi-server-client.d.ts.map +1 -1
- package/dist/core/pi-server-client.js +3 -0
- package/dist/core/pi-server-client.js.map +1 -1
- package/dist/core/pi-server-protocol.d.ts +2 -1
- package/dist/core/pi-server-protocol.d.ts.map +1 -1
- package/dist/core/pi-server-protocol.js +16 -0
- package/dist/core/pi-server-protocol.js.map +1 -1
- package/dist/core/pi-server-request.d.ts.map +1 -1
- package/dist/core/pi-server-request.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/provider-composer.d.ts +40 -21
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +92 -40
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/radius.d.ts.map +1 -1
- package/dist/core/radius.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +8 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +18 -3
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +7 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +161 -35
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +24 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts.map +1 -1
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-export.d.ts.map +1 -1
- package/dist/core/session-export.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +22 -22
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts.map +1 -1
- package/dist/core/settings-diagnostics.js.map +1 -1
- package/dist/core/settings-manager.d.ts +31 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +72 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/source-info.d.ts +8 -0
- package/dist/core/source-info.d.ts.map +1 -1
- package/dist/core/source-info.js +16 -0
- package/dist/core/source-info.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts +12 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +35 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +11 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +36 -0
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/powershell.d.ts.map +1 -1
- package/dist/core/tools/powershell.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +6 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +26 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/renderers/bash.d.ts.map +1 -1
- package/dist/core/tools/renderers/bash.js +9 -10
- package/dist/core/tools/renderers/bash.js.map +1 -1
- package/dist/core/tools/renderers/edit.d.ts.map +1 -1
- package/dist/core/tools/renderers/edit.js.map +1 -1
- package/dist/core/tools/renderers/find.d.ts.map +1 -1
- package/dist/core/tools/renderers/find.js.map +1 -1
- package/dist/core/tools/renderers/grep.d.ts.map +1 -1
- package/dist/core/tools/renderers/grep.js.map +1 -1
- package/dist/core/tools/renderers/index.d.ts.map +1 -1
- package/dist/core/tools/renderers/index.js.map +1 -1
- package/dist/core/tools/renderers/ls.d.ts.map +1 -1
- package/dist/core/tools/renderers/ls.js.map +1 -1
- package/dist/core/tools/renderers/read.d.ts.map +1 -1
- package/dist/core/tools/renderers/read.js +3 -2
- package/dist/core/tools/renderers/read.js.map +1 -1
- package/dist/core/tools/renderers/write.d.ts.map +1 -1
- package/dist/core/tools/renderers/write.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts +5 -3
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +3 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/truncate.d.ts +14 -0
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +29 -0
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +1 -0
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/usage-totals.d.ts +2 -0
- package/dist/core/usage-totals.d.ts.map +1 -1
- package/dist/core/usage-totals.js +23 -0
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/core/virtual-models.d.ts +115 -0
- package/dist/core/virtual-models.d.ts.map +1 -0
- package/dist/core/virtual-models.js +117 -0
- package/dist/core/virtual-models.js.map +1 -0
- package/dist/extensions/codemode/execute.d.ts +16 -0
- package/dist/extensions/codemode/execute.d.ts.map +1 -0
- package/dist/extensions/codemode/execute.js +390 -0
- package/dist/extensions/codemode/execute.js.map +1 -0
- package/dist/extensions/codemode/execute.lazy.d.ts +3 -0
- package/dist/extensions/codemode/execute.lazy.d.ts.map +1 -0
- package/dist/extensions/codemode/execute.lazy.js +3 -0
- package/dist/extensions/codemode/execute.lazy.js.map +1 -0
- package/dist/extensions/codemode/index.d.ts +21 -0
- package/dist/extensions/codemode/index.d.ts.map +1 -0
- package/dist/extensions/codemode/index.js +31 -0
- package/dist/extensions/codemode/index.js.map +1 -0
- package/dist/extensions/codemode/renderer.d.ts +12 -0
- package/dist/extensions/codemode/renderer.d.ts.map +1 -0
- package/dist/extensions/codemode/renderer.js +119 -0
- package/dist/extensions/codemode/renderer.js.map +1 -0
- package/dist/extensions/codemode/tool.d.ts +122 -0
- package/dist/extensions/codemode/tool.d.ts.map +1 -0
- package/dist/extensions/codemode/tool.js +324 -0
- package/dist/extensions/codemode/tool.js.map +1 -0
- package/dist/extensions/codemode/worker.d.ts +7 -0
- package/dist/extensions/codemode/worker.d.ts.map +1 -0
- package/dist/extensions/codemode/worker.js +7 -0
- package/dist/extensions/codemode/worker.js.map +1 -0
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +11 -1
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/huggingface.d.ts.map +1 -1
- package/dist/extensions/llama/huggingface.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +65 -13
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/llama/ui.d.ts.map +1 -1
- package/dist/extensions/llama/ui.js.map +1 -1
- package/dist/extensions/mcp/cli.d.ts +21 -0
- package/dist/extensions/mcp/cli.d.ts.map +1 -0
- package/dist/extensions/mcp/cli.js +485 -0
- package/dist/extensions/mcp/cli.js.map +1 -0
- package/dist/extensions/mcp/cli.lazy.d.ts +3 -0
- package/dist/extensions/mcp/cli.lazy.d.ts.map +1 -0
- package/dist/extensions/mcp/cli.lazy.js +3 -0
- package/dist/extensions/mcp/cli.lazy.js.map +1 -0
- package/dist/extensions/mcp/config.d.ts +70 -0
- package/dist/extensions/mcp/config.d.ts.map +1 -0
- package/dist/extensions/mcp/config.js +146 -0
- package/dist/extensions/mcp/config.js.map +1 -0
- package/dist/extensions/mcp/index.d.ts +52 -0
- package/dist/extensions/mcp/index.d.ts.map +1 -0
- package/dist/extensions/mcp/index.js +902 -0
- package/dist/extensions/mcp/index.js.map +1 -0
- package/dist/extensions/mcp/log.d.ts +16 -0
- package/dist/extensions/mcp/log.d.ts.map +1 -0
- package/dist/extensions/mcp/log.js +66 -0
- package/dist/extensions/mcp/log.js.map +1 -0
- package/dist/extensions/mcp/oauth.d.ts +94 -0
- package/dist/extensions/mcp/oauth.d.ts.map +1 -0
- package/dist/extensions/mcp/oauth.js +333 -0
- package/dist/extensions/mcp/oauth.js.map +1 -0
- package/dist/extensions/mcp/resources.d.ts +43 -0
- package/dist/extensions/mcp/resources.d.ts.map +1 -0
- package/dist/extensions/mcp/resources.js +262 -0
- package/dist/extensions/mcp/resources.js.map +1 -0
- package/dist/extensions/mcp/runtime.d.ts +96 -0
- package/dist/extensions/mcp/runtime.d.ts.map +1 -0
- package/dist/extensions/mcp/runtime.js +388 -0
- package/dist/extensions/mcp/runtime.js.map +1 -0
- package/dist/extensions/mcp/runtime.lazy.d.ts +3 -0
- package/dist/extensions/mcp/runtime.lazy.d.ts.map +1 -0
- package/dist/extensions/mcp/runtime.lazy.js +3 -0
- package/dist/extensions/mcp/runtime.lazy.js.map +1 -0
- package/dist/extensions/mcp/tools.d.ts +83 -0
- package/dist/extensions/mcp/tools.d.ts.map +1 -0
- package/dist/extensions/mcp/tools.js +233 -0
- package/dist/extensions/mcp/tools.js.map +1 -0
- package/dist/extensions/mcp/ui.d.ts +60 -0
- package/dist/extensions/mcp/ui.d.ts.map +1 -0
- package/dist/extensions/mcp/ui.js +172 -0
- package/dist/extensions/mcp/ui.js.map +1 -0
- package/dist/extensions/tool-search/index.d.ts +12 -0
- package/dist/extensions/tool-search/index.d.ts.map +1 -0
- package/dist/extensions/tool-search/index.js +15 -0
- package/dist/extensions/tool-search/index.js.map +1 -0
- package/dist/extensions/tool-search/tool.d.ts +72 -0
- package/dist/extensions/tool-search/tool.d.ts.map +1 -0
- package/dist/extensions/tool-search/tool.js +210 -0
- package/dist/extensions/tool-search/tool.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -0
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/interactive/bug-report.d.ts.map +1 -1
- package/dist/modes/interactive/bug-report.js.map +1 -1
- package/dist/modes/interactive/chat-viewport.d.ts.map +1 -1
- package/dist/modes/interactive/chat-viewport.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +13 -3
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +4 -4
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +8 -2
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +7 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +43 -3
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -1
- package/dist/modes/interactive/components/markdown-transform.js.map +1 -1
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -1
- package/dist/modes/interactive/components/mermaid.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/pi-logo.d.ts +12 -0
- package/dist/modes/interactive/components/pi-logo.d.ts.map +1 -0
- package/dist/modes/interactive/components/pi-logo.js +25 -0
- package/dist/modes/interactive/components/pi-logo.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +28 -6
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/themed-text.d.ts +17 -0
- package/dist/modes/interactive/components/themed-text.d.ts.map +1 -0
- package/dist/modes/interactive/components/themed-text.js +29 -0
- package/dist/modes/interactive/components/themed-text.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +2 -2
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/trust-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +11 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +199 -161
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -1
- package/dist/modes/interactive/model-search.d.ts.map +1 -1
- package/dist/modes/interactive/model-search.js.map +1 -1
- package/dist/modes/interactive/session-share.d.ts.map +1 -1
- package/dist/modes/interactive/session-share.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +59 -72
- package/dist/modes/interactive/theme/light.json +57 -69
- package/dist/modes/interactive/theme/system-theme.d.ts +54 -0
- package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/system-theme.js +535 -0
- package/dist/modes/interactive/theme/system-theme.js.map +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +46 -6
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +127 -47
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-json.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-json.js +3 -1
- package/dist/modes/interactive/theme/theme-json.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +7 -2
- package/dist/modes/interactive/theme/theme.d.ts +66 -39
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +238 -317
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/tui-renderer.d.ts +2 -1
- package/dist/modes/interactive/tui-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/tui-renderer.js +1 -0
- package/dist/modes/interactive/tui-renderer.js.map +1 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/dist/modes/rpc/jsonl.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +6 -6
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +12 -8
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +7 -9
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +10 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +6 -2
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/pi-client-cli.d.ts.map +1 -1
- package/dist/pi-client-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js.map +1 -1
- package/dist/utils/abort.d.ts.map +1 -1
- package/dist/utils/abort.js.map +1 -1
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-command.d.ts.map +1 -1
- package/dist/utils/clipboard-command.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +14 -17
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +5 -0
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/deprecation.d.ts.map +1 -1
- package/dist/utils/deprecation.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/html.d.ts.map +1 -1
- package/dist/utils/html.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts.map +1 -1
- package/dist/utils/image-process.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/image-resize-worker.d.ts.map +1 -1
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/open-browser.d.ts.map +1 -1
- package/dist/utils/open-browser.js.map +1 -1
- package/dist/utils/paths.d.ts +3 -3
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +5 -4
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts.map +1 -1
- package/dist/utils/pi-user-agent.js.map +1 -1
- package/dist/utils/shell.d.ts +1 -3
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +3 -30
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +2 -0
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts.map +1 -1
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/tool-result-images.d.ts.map +1 -1
- package/dist/utils/tool-result-images.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/dist/utils/wsl.d.ts.map +1 -1
- package/dist/utils/wsl.js.map +1 -1
- package/dist/utils/zip.d.ts.map +1 -1
- package/dist/utils/zip.js.map +1 -1
- package/docs/cli.md +64 -3
- package/docs/configuration.md +2 -0
- package/docs/custom-provider.md +43 -4
- package/docs/docs.json +8 -0
- package/docs/extensions.md +55 -0
- package/docs/keybindings.md +1 -1
- package/docs/llama-cpp.md +13 -0
- package/docs/mcp.md +188 -0
- package/docs/models.md +35 -0
- package/docs/packages.md +4 -2
- package/docs/providers.md +1 -0
- package/docs/rpc-commands.md +9 -3
- package/docs/rpc.md +3 -1
- package/docs/sdk.md +7 -2
- package/docs/security.md +2 -0
- package/docs/session-format.md +5 -1
- package/docs/settings.md +20 -3
- package/docs/themes.md +33 -7
- package/docs/tui.md +18 -0
- package/docs/virtual-models.md +114 -0
- package/docs/windows.md +2 -0
- package/examples/README.md +1 -1
- package/examples/extensions/README.md +2 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
- package/examples/extensions/debug-provider.ts +90 -0
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/jev-router.ts +113 -0
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-client.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/14-codemode-mcp.ts +61 -0
- package/examples/sdk/README.md +2 -1
- package/package.json +14 -12
- package/dist/bundle/chunks/anthropic-messages-J5WXPPPC.js +0 -6
- package/dist/bundle/chunks/azure-openai-responses-QJ7SPP5M.js +0 -2
- package/dist/bundle/chunks/chunk-6R7UI4IY.js +0 -2
- package/dist/bundle/chunks/chunk-CGLIERPR.js +0 -2
- package/dist/bundle/chunks/chunk-G4PLSCJV.js +0 -1669
- package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
- package/dist/bundle/chunks/chunk-RCZIEVGO.js +0 -11
- package/dist/bundle/chunks/google-generative-ai-IMS5EK2Y.js +0 -2
- package/dist/bundle/chunks/google-vertex-O4YJEQUW.js +0 -2
- package/dist/bundle/chunks/mistral-conversations-NOSQHBEY.js +0 -5
- package/dist/bundle/chunks/openai-responses-AH7QTG23.js +0 -2
- package/dist/bundle/chunks/virtual-modules-LRIRMGSI.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-stats.js","sourceRoot":"","sources":["../../src/core/cache-stats.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport type { SessionEntry } from \"./session-manager.ts\";\n\n/**\n * Prompt-cache TTL: idle gaps longer than this are worth mentioning as the\n * likely cause of a miss. Anthropic's default cache TTL is 5 minutes.\n */\nexport const CACHE_TTL_MS = 5 * 60 * 1000;\n\n/** Per-turn misses at or below this are cache breakpoint granularity noise. */\nconst NOISE_FLOOR_TOKENS = 1024;\n\n/** A counted cache miss on a single assistant message. */\nexport interface CacheMiss {\n\t/** Prompt tokens that were in the previous turn's prompt but not read from cache. */\n\tmissedTokens: number;\n\t/** Extra dollars paid vs. a full cache hit; 0 when pricing is unknown. */\n\tmissedCost: number;\n\t/** Milliseconds since the previous request (which last refreshed the cache). */\n\tidleMs: number;\n\t/** True when the model changed relative to the previous request. */\n\tmodelChanged: boolean;\n}\n\nexport interface CacheWasteTotals {\n\tmissedTokens: number;\n\tmissedCost: number;\n\t/** Number of counted misses (turns above the noise floor). */\n\tmissCount: number;\n}\n\n/** Minimal pricing lookup, satisfied by ModelRuntime. Cost is $/million tokens. */\nexport interface ModelPriceSource {\n\tgetModel(provider: string, modelId: string): { cost: { cacheRead: number } } | undefined;\n}\n\n/** The last request seen by the scan; everything in its prompt should be cached. */\ninterface PreviousRequest {\n\tpromptTokens: number;\n\tmodelKey: string;\n\ttimestamp: number;\n\t/**\n\t * Sticky: some earlier request in this scan segment reported cache activity.\n\t * Distinguishes a total miss on a cache-read-only provider (OpenAI-style,\n\t * writes unreported) from a provider that never reports caching at all.\n\t */\n\treportedCache: boolean;\n}\n\n/**\n * Compute the cache miss for one assistant message relative to the previous\n * request. Returns undefined when nothing is counted: first turn, after a\n * reset, no cache activity ever reported (provider without cache support), or\n * miss below the noise floor.\n */\nfunction detectMiss(\n\tprev: PreviousRequest | undefined,\n\tmessage: AssistantMessage,\n\tmodels: ModelPriceSource,\n): CacheMiss | undefined {\n\tconst usage = message.usage;\n\tconst promptTokens = usage.input + usage.cacheRead + usage.cacheWrite;\n\t// A zero-cache turn only counts when cache activity was reported before:\n\t// on cache-read-only providers that is a total miss, while on providers\n\t// that never report caching it means nothing.\n\tif (!prev || promptTokens <= 0 || (usage.cacheRead + usage.cacheWrite === 0 && !prev.reportedCache)) {\n\t\treturn undefined;\n\t}\n\n\tconst missedTokens = Math.min(prev.promptTokens, promptTokens) - usage.cacheRead;\n\tif (missedTokens <= NOISE_FLOOR_TOKENS) return undefined;\n\n\t// Extra cost = missed tokens billed at the actual paid rate (input/cacheWrite,\n\t// incl. write premium) instead of the cache-read rate. Missed tokens can only\n\t// land in the input or cacheWrite buckets, so the paid rate comes straight\n\t// from this message's own cost breakdown.\n\tconst paidTokens = usage.input + usage.cacheWrite;\n\tconst paidPerToken = paidTokens > 0 ? (usage.cost.input + usage.cost.cacheWrite) / paidTokens : 0;\n\tconst readPerToken =\n\t\tusage.cacheRead > 0\n\t\t\t? usage.cost.cacheRead / usage.cacheRead\n\t\t\t: (models.getModel(message.provider, message.model)?.cost.cacheRead ?? 0) / 1_000_000;\n\n\treturn {\n\t\tmissedTokens,\n\t\tmissedCost: missedTokens * Math.max(0, paidPerToken - readPerToken),\n\t\tidleMs: Math.max(0, message.timestamp - prev.timestamp),\n\t\tmodelChanged: `${message.provider}/${message.model}` !== prev.modelKey,\n\t};\n}\n\nfunction asPreviousRequest(message: AssistantMessage, reportedCache: boolean): PreviousRequest | undefined {\n\tconst usage = message.usage;\n\tconst promptTokens = usage.input + usage.cacheRead + usage.cacheWrite;\n\tif (promptTokens <= 0) return undefined;\n\treturn {\n\t\tpromptTokens,\n\t\tmodelKey: `${message.provider}/${message.model}`,\n\t\ttimestamp: message.timestamp,\n\t\treportedCache: reportedCache || usage.cacheRead + usage.cacheWrite > 0,\n\t};\n}\n\nfunction scan(\n\tentries: SessionEntry[],\n\tmodels: ModelPriceSource,\n): { prev: PreviousRequest | undefined; totals: CacheWasteTotals; misses: Map<AssistantMessage, CacheMiss> } {\n\tlet prev: PreviousRequest | undefined;\n\tconst totals: CacheWasteTotals = { missedTokens: 0, missedCost: 0, missCount: 0 };\n\tconst misses = new Map<AssistantMessage, CacheMiss>();\n\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t// The context legitimately changed; the next turn's prompt is new content,\n\t\t\t// not re-billed content. Model switches are NOT exempt: they re-bill the\n\t\t\t// full prompt and should be counted.\n\t\t\tprev = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.type === \"usage\" && entry.kind === \"cache_warm\") {\n\t\t\tconst promptTokens = entry.usage.input + entry.usage.cacheRead + entry.usage.cacheWrite;\n\t\t\tif (promptTokens > 0) {\n\t\t\t\tprev = {\n\t\t\t\t\tpromptTokens,\n\t\t\t\t\tmodelKey: `${entry.provider}/${entry.model}`,\n\t\t\t\t\ttimestamp: Date.parse(entry.timestamp),\n\t\t\t\t\treportedCache: true,\n\t\t\t\t};\n\t\t\t}\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst miss = detectMiss(prev, entry.message, models);\n\t\t\tif (miss) {\n\t\t\t\ttotals.missedTokens += miss.missedTokens;\n\t\t\t\ttotals.missedCost += miss.missedCost;\n\t\t\t\ttotals.missCount += 1;\n\t\t\t\tmisses.set(entry.message, miss);\n\t\t\t}\n\t\t\tprev = asPreviousRequest(entry.message, prev?.reportedCache ?? false) ?? prev;\n\t\t}\n\t}\n\treturn { prev, totals, misses };\n}\n\n/**\n * Cumulative cache waste across a session: prompt tokens that should have been\n * cache reads (they were in the previous turn's prompt) but were re-billed.\n */\nexport function computeCacheWaste(entries: SessionEntry[], models: ModelPriceSource): CacheWasteTotals {\n\treturn scan(entries, models).totals;\n}\n\n/**\n * All counted cache misses across a session, keyed by the assistant message\n * (by reference) that paid for them. Used to re-derive transcript notices when\n * rebuilding the chat from entries (resume, post-compaction rebuild).\n */\nexport function collectCacheMisses(\n\tentries: SessionEntry[],\n\tmodels: ModelPriceSource,\n): Map<AssistantMessage, CacheMiss> {\n\treturn scan(entries, models).misses;\n}\n\n/**\n * Detect a cache miss on a just-completed assistant message.\n * `entries` must not yet contain `message` (message_end fires before persistence).\n */\nexport function detectCacheMiss(\n\tentries: SessionEntry[],\n\tmessage: AssistantMessage,\n\tmodels: ModelPriceSource,\n): CacheMiss | undefined {\n\treturn detectMiss(scan(entries, models).prev, message, models);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cache-stats.js","sourceRoot":"","sources":["../../src/core/cache-stats.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport type { SessionEntry } from \"./session-manager.ts\";\n\n/**\n * Prompt-cache TTL: idle gaps longer than this are worth mentioning as the\n * likely cause of a miss. Anthropic's default cache TTL is 5 minutes.\n */\nexport const CACHE_TTL_MS = 5 * 60 * 1000;\n\n/** Per-turn misses at or below this are cache breakpoint granularity noise. */\nconst NOISE_FLOOR_TOKENS = 1024;\n\n/** A counted cache miss on a single assistant message. */\nexport interface CacheMiss {\n\t/** Prompt tokens that were in the previous turn's prompt but not read from cache. */\n\tmissedTokens: number;\n\t/** Extra dollars paid vs. a full cache hit; 0 when pricing is unknown. */\n\tmissedCost: number;\n\t/** Milliseconds since the previous request (which last refreshed the cache). */\n\tidleMs: number;\n\t/** True when the model changed relative to the previous request. */\n\tmodelChanged: boolean;\n}\n\nexport interface CacheWasteTotals {\n\tmissedTokens: number;\n\tmissedCost: number;\n\t/** Number of counted misses (turns above the noise floor). */\n\tmissCount: number;\n}\n\n/** Minimal pricing lookup, satisfied by ModelRuntime. Cost is $/million tokens. */\nexport interface ModelPriceSource {\n\tgetModel(provider: string, modelId: string): { cost: { cacheRead: number } } | undefined;\n}\n\n/** The last request seen by the scan; everything in its prompt should be cached. */\ninterface PreviousRequest {\n\tpromptTokens: number;\n\tmodelKey: string;\n\ttimestamp: number;\n\t/**\n\t * Sticky: some earlier request in this scan segment reported cache activity.\n\t * Distinguishes a total miss on a cache-read-only provider (OpenAI-style,\n\t * writes unreported) from a provider that never reports caching at all.\n\t */\n\treportedCache: boolean;\n}\n\n/**\n * Compute the cache miss for one assistant message relative to the previous\n * request. Returns undefined when nothing is counted: first turn, after a\n * reset, no cache activity ever reported (provider without cache support), or\n * miss below the noise floor.\n */\nfunction detectMiss(\n\tprev: PreviousRequest | undefined,\n\tmessage: AssistantMessage,\n\tmodels: ModelPriceSource,\n): CacheMiss | undefined {\n\tconst usage = message.usage;\n\tconst promptTokens = usage.input + usage.cacheRead + usage.cacheWrite;\n\t// A zero-cache turn only counts when cache activity was reported before:\n\t// on cache-read-only providers that is a total miss, while on providers\n\t// that never report caching it means nothing.\n\tif (!prev || promptTokens <= 0 || (usage.cacheRead + usage.cacheWrite === 0 && !prev.reportedCache)) {\n\t\treturn undefined;\n\t}\n\n\tconst missedTokens = Math.min(prev.promptTokens, promptTokens) - usage.cacheRead;\n\tif (missedTokens <= NOISE_FLOOR_TOKENS) return undefined;\n\n\t// Extra cost = missed tokens billed at the actual paid rate (input/cacheWrite,\n\t// incl. write premium) instead of the cache-read rate. Missed tokens can only\n\t// land in the input or cacheWrite buckets, so the paid rate comes straight\n\t// from this message's own cost breakdown.\n\tconst paidTokens = usage.input + usage.cacheWrite;\n\tconst paidPerToken = paidTokens > 0 ? (usage.cost.input + usage.cost.cacheWrite) / paidTokens : 0;\n\tconst readPerToken =\n\t\tusage.cacheRead > 0\n\t\t\t? usage.cost.cacheRead / usage.cacheRead\n\t\t\t: (models.getModel(message.provider, message.model)?.cost.cacheRead ?? 0) / 1_000_000;\n\n\treturn {\n\t\tmissedTokens,\n\t\tmissedCost: missedTokens * Math.max(0, paidPerToken - readPerToken),\n\t\tidleMs: Math.max(0, message.timestamp - prev.timestamp),\n\t\tmodelChanged: `${message.provider}/${message.model}` !== prev.modelKey,\n\t};\n}\n\nfunction asPreviousRequest(message: AssistantMessage, reportedCache: boolean): PreviousRequest | undefined {\n\tconst usage = message.usage;\n\tconst promptTokens = usage.input + usage.cacheRead + usage.cacheWrite;\n\tif (promptTokens <= 0) return undefined;\n\treturn {\n\t\tpromptTokens,\n\t\tmodelKey: `${message.provider}/${message.model}`,\n\t\ttimestamp: message.timestamp,\n\t\treportedCache: reportedCache || usage.cacheRead + usage.cacheWrite > 0,\n\t};\n}\n\nfunction scan(\n\tentries: SessionEntry[],\n\tmodels: ModelPriceSource,\n): { prev: PreviousRequest | undefined; totals: CacheWasteTotals; misses: Map<AssistantMessage, CacheMiss> } {\n\tlet prev: PreviousRequest | undefined;\n\tconst totals: CacheWasteTotals = { missedTokens: 0, missedCost: 0, missCount: 0 };\n\tconst misses = new Map<AssistantMessage, CacheMiss>();\n\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t// The context legitimately changed; the next turn's prompt is new content,\n\t\t\t// not re-billed content. Model switches are NOT exempt: they re-bill the\n\t\t\t// full prompt and should be counted.\n\t\t\tprev = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.type === \"usage\" && entry.kind === \"cache_warm\") {\n\t\t\tconst promptTokens = entry.usage.input + entry.usage.cacheRead + entry.usage.cacheWrite;\n\t\t\tif (promptTokens > 0) {\n\t\t\t\tprev = {\n\t\t\t\t\tpromptTokens,\n\t\t\t\t\tmodelKey: `${entry.provider}/${entry.model}`,\n\t\t\t\t\ttimestamp: Date.parse(entry.timestamp),\n\t\t\t\t\treportedCache: true,\n\t\t\t\t};\n\t\t\t}\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst miss = detectMiss(prev, entry.message, models);\n\t\t\tif (miss) {\n\t\t\t\ttotals.missedTokens += miss.missedTokens;\n\t\t\t\ttotals.missedCost += miss.missedCost;\n\t\t\t\ttotals.missCount += 1;\n\t\t\t\tmisses.set(entry.message, miss);\n\t\t\t}\n\t\t\tprev = asPreviousRequest(entry.message, prev?.reportedCache ?? false) ?? prev;\n\t\t}\n\t}\n\treturn { prev, totals, misses };\n}\n\n/**\n * Cumulative cache waste across a session: prompt tokens that should have been\n * cache reads (they were in the previous turn's prompt) but were re-billed.\n */\nexport function computeCacheWaste(entries: SessionEntry[], models: ModelPriceSource): CacheWasteTotals {\n\treturn scan(entries, models).totals;\n}\n\n/**\n * All counted cache misses across a session, keyed by the assistant message\n * (by reference) that paid for them. Used to re-derive transcript notices when\n * rebuilding the chat from entries (resume, post-compaction rebuild).\n */\nexport function collectCacheMisses(\n\tentries: SessionEntry[],\n\tmodels: ModelPriceSource,\n): Map<AssistantMessage, CacheMiss> {\n\treturn scan(entries, models).misses;\n}\n\n/**\n * Detect a cache miss on a just-completed assistant message.\n * `entries` must not yet contain `message` (message_end fires before persistence).\n */\nexport function detectCacheMiss(\n\tentries: SessionEntry[],\n\tmessage: AssistantMessage,\n\tmodels: ModelPriceSource,\n): CacheMiss | undefined {\n\treturn detectMiss(scan(entries, models).prev, message, models);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-warmer.d.ts","sourceRoot":"","sources":["../../src/core/cache-warmer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype Context,\n\tcalculateCost,\n\ttype Model,\n\ttype ModelsSimpleStreamOptions,\n\ttype SimpleStreamOptions,\n\ttype Usage,\n} from \"@earendil-works/pi-ai\";\nimport { getProviderEnvValue } from \"@earendil-works/pi-ai/utils/provider-env\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport type { SessionEntry, SessionManager, UsageEntry } from \"./session-manager.ts\";\nimport type { CacheWarmingMode } from \"./settings-manager.ts\";\n\n/** Streaming warming never continues past this long after the real request that started it. */\nconst MAX_WARMING_AGE_MS = 60 * 60_000;\n/** Idle warming uses a shorter horizon because continuation estimates become less reliable with age. */\nconst MAX_IDLE_WARMING_AGE_MS = 30 * 60_000;\n/** A refresh is sent only when it is expected to save at least this many dollars. */\nconst CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS = 0.05;\n/**\n * Chance that a real request arrives before the cache entry expires while the\n * agent sits idle. Measured from our own usage; per-session estimates were not\n * better than this constant.\n */\nconst IDLE_CONTINUATION_PROBABILITY = 0.15;\n\n/** Refresh at 90% of the TTL while preserving at least ten seconds of margin. */\nexport function getCacheWarmingDelayMs(ttlMs: number): number | undefined {\n\tif (ttlMs <= 10_000) return undefined;\n\treturn Math.max(1, Math.floor(Math.min(ttlMs * 0.9, ttlMs - 10_000)));\n}\n\n/**\n * Lifetime of the prompt cache entry a request writes, from the model's\n * `promptCache` tier for the retention the request used. Undefined when the\n * model has no lifetime for that tier or caching is off.\n */\nexport function getPromptCacheTtlMs(model: Model<Api>, options: SimpleStreamOptions | undefined): number | undefined {\n\tconst retention =\n\t\toptions?.cacheRetention ??\n\t\t(getProviderEnvValue(\"PI_CACHE_RETENTION\", options?.env) === \"long\" ? \"long\" : \"short\");\n\tif (retention === \"none\") return undefined;\n\tconst seconds = model.promptCache?.[retention];\n\treturn seconds === undefined ? undefined : seconds * 1000;\n}\n\n/**\n * Whether replaying the request with a one-token output cap leaves its cache\n * entry untouched. Anthropic's budget-based thinking (Claude models without\n * adaptive thinking) derives `budget_tokens` from `max_tokens`; the replay\n * would get a different budget, which Anthropic keys the message cache on,\n * and the model could still think for thousands of tokens.\n */\nexport function isReplayable(model: Model<Api>, options: SimpleStreamOptions | undefined): boolean {\n\tif (!options?.reasoning || model.api !== \"anthropic-messages\") return true;\n\treturn (model as Model<\"anthropic-messages\">).compat?.forceAdaptiveThinking === true;\n}\n\n/** Prompt size of the most recent real request on the branch, as reported by the provider. */\nfunction lastPromptTokens(entries: SessionEntry[]): number {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst usage = entry.message.usage;\n\t\t\treturn usage.input + usage.cacheRead + usage.cacheWrite;\n\t\t}\n\t}\n\treturn 0;\n}\n\nfunction price(\n\tmodel: Model<Api>,\n\ttokens: Partial<Pick<Usage, \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\">>,\n): number {\n\tconst usage: Usage = {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t...tokens,\n\t};\n\treturn calculateCost(model, usage).total;\n}\n\nexport type CacheWarmingAction = \"warm\" | \"stop\";\n\n/** Inputs and outcome of one warm-or-stop decision, as shown by `/session`. */\nexport interface CacheWarmingDecision {\n\t/** \"streaming\" while the agent run that sent the request is still active. */\n\tphase: \"streaming\" | \"idle\";\n\t/** Price of this refresh: a cache read of the prompt plus one output token. */\n\twarmCost: number;\n\t/** Extra price of the next real request if the cache entry is lost. */\n\tmissCost: number;\n\t/** Estimated chance that a real request arrives before the entry expires. */\n\tcontinuationProbability: number;\n\t/** `continuationProbability * missCost - warmCost`. */\n\texpectedSavings: number;\n\t/** False when the prompt size or the model's prices are unknown. */\n\teconomicsAvailable: boolean;\n\t/** Pi's decision: \"warm\" when `expectedSavings` is at least $0.05. */\n\taction: CacheWarmingAction;\n}\n\n/**\n * Fired before each refresh with pi's decision filled in. Everything else an\n * extension might want (model, idle state, context size) is on the context.\n */\nexport interface CacheWarmingDecisionEvent\n\textends Pick<CacheWarmingDecision, \"warmCost\" | \"missCost\" | \"continuationProbability\" | \"action\"> {\n\ttype: \"cache_warming_decision\";\n}\n\nexport interface CacheWarmingDecisionEventResult {\n\t/** Override whether this refresh is sent. \"stop\" ends warming until the next real request. */\n\taction?: CacheWarmingAction;\n}\n\nexport interface CacheWarmingStatus {\n\t/** \"scheduled\": a refresh timer is armed; \"refreshing\": a warm request is in flight. */\n\tstate: \"inactive\" | \"scheduled\" | \"refreshing\";\n\t/** Why nothing is scheduled. */\n\treason?: string;\n\tnextWarmAt?: number;\n\t/** The pending decision, or the decision that stopped warming. */\n\tdecision?: CacheWarmingDecision;\n\t/** True when an extension changed `decision.action`. */\n\textensionOverride?: boolean;\n}\n\n/** The request whose prompt cache entry should be kept warm, exactly as it was sent. */\nexport interface CacheWarmRequest {\n\tmodel: Model<Api>;\n\tcontext: Context;\n\toptions: ModelsSimpleStreamOptions;\n}\n\ninterface ActiveRun extends CacheWarmRequest {\n\t/** False once the session's model or messages no longer match the request. */\n\tisCurrent: () => boolean;\n\tttlMs: number;\n\tdelayMs: number;\n\t/** Latest safe time to send this refresh, leaving half the original expiry margin. */\n\trefreshDeadlineAt: number;\n\tstartedAt: number;\n\tcontroller: AbortController;\n\tphase: \"streaming\" | \"idle\";\n\tnextWarmAt: number;\n\t/** Set while a refresh that an extension forced is in flight. */\n\textensionOverride: boolean;\n\ttimer?: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Keeps one prompt cache entry alive by re-sending its request with a\n * one-token output cap before the entry expires. `start` replaces any\n * previous run; warm requests never extend the fixed safety windows.\n */\nexport class CacheWarmer {\n\tprivate run?: ActiveRun;\n\tprivate inactive: CacheWarmingStatus;\n\tprivate readonly models: Pick<ModelRuntime, \"streamSimple\">;\n\tprivate readonly sessionManager: Pick<SessionManager, \"appendUsage\" | \"getBranch\">;\n\tprivate readonly getMode: () => CacheWarmingMode;\n\t/** Lets extensions override `event.action`; failures fall back to pi's decision. */\n\tprivate readonly decide: (event: CacheWarmingDecisionEvent) => Promise<CacheWarmingAction>;\n\t/** Called with the persisted usage entry after each successful refresh. */\n\tonWarmed?: (entry: UsageEntry) => void;\n\n\tconstructor(\n\t\tmodels: Pick<ModelRuntime, \"streamSimple\">,\n\t\tsessionManager: Pick<SessionManager, \"appendUsage\" | \"getBranch\">,\n\t\tgetMode: () => CacheWarmingMode,\n\t\tdecide: (event: CacheWarmingDecisionEvent) => Promise<CacheWarmingAction> = async (event) => event.action,\n\t) {\n\t\tthis.models = models;\n\t\tthis.sessionManager = sessionManager;\n\t\tthis.getMode = getMode;\n\t\tthis.decide = decide;\n\t\tthis.inactive = { state: \"inactive\", reason: \"waiting for first request\" };\n\t}\n\n\tget status(): CacheWarmingStatus {\n\t\tif (this.getMode() === \"off\") return { state: \"inactive\", reason: \"cache warming disabled\" };\n\t\tconst run = this.run;\n\t\tif (!run) return this.inactive;\n\t\tif (!run.isCurrent()) return { state: \"inactive\", reason: \"conversation context changed\" };\n\t\tconst decision = this.evaluate(run);\n\t\tconst refreshing = run.timer === undefined;\n\t\tif (!decision.economicsAvailable && !refreshing) {\n\t\t\treturn { state: \"inactive\", reason: \"cache economics unavailable\" };\n\t\t}\n\t\treturn {\n\t\t\tstate: refreshing ? \"refreshing\" : \"scheduled\",\n\t\t\tnextWarmAt: run.nextWarmAt,\n\t\t\tdecision,\n\t\t\textensionOverride: run.extensionOverride,\n\t\t};\n\t}\n\n\t/** Keep the prompt cache entry written by `request` warm while `isCurrent` holds. */\n\tstart(request: CacheWarmRequest, isCurrent: () => boolean): void {\n\t\tthis.clearRun();\n\t\tconst mode = this.getMode();\n\t\tif (mode === \"off\") {\n\t\t\tthis.stop(\"cache warming disabled\");\n\t\t\treturn;\n\t\t}\n\t\tif (!isReplayable(request.model, request.options)) {\n\t\t\tthis.stop(\"request cannot be replayed safely\");\n\t\t\treturn;\n\t\t}\n\t\tconst ttlMs = getPromptCacheTtlMs(request.model, request.options);\n\t\tif (ttlMs === undefined) {\n\t\t\tthis.stop(\n\t\t\t\trequest.options.cacheRetention === \"none\"\n\t\t\t\t\t? \"request disabled prompt caching\"\n\t\t\t\t\t: \"cache lifetime unavailable\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = getCacheWarmingDelayMs(ttlMs);\n\t\tif (delayMs === undefined) {\n\t\t\tthis.stop(\"cache lifetime unavailable\");\n\t\t\treturn;\n\t\t}\n\t\tthis.run = {\n\t\t\t...request,\n\t\t\tisCurrent,\n\t\t\tttlMs,\n\t\t\tdelayMs,\n\t\t\trefreshDeadlineAt: 0,\n\t\t\tstartedAt: Date.now(),\n\t\t\tcontroller: new AbortController(),\n\t\t\tphase: \"streaming\",\n\t\t\tnextWarmAt: 0,\n\t\t\textensionOverride: false,\n\t\t};\n\t\tthis.schedule(this.run);\n\t}\n\n\tonAgentSettled(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tif (this.getMode() === \"streaming\") {\n\t\t\tthis.stop(\"agent run settled\");\n\t\t\treturn;\n\t\t}\n\t\trun.phase = \"idle\";\n\t\tconst deadline = run.startedAt + MAX_IDLE_WARMING_AGE_MS;\n\t\tif (run.nextWarmAt > deadline || Date.now() >= deadline) {\n\t\t\tthis.stop(\"30-minute idle safety limit reached\");\n\t\t}\n\t}\n\n\t/** Reconcile an active run after the persisted warming mode changes. */\n\tonModeChanged(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tconst reason = this.getModeStopReason(run);\n\t\tif (reason) this.stop(reason);\n\t}\n\n\tcancel(): void {\n\t\tthis.stop(\"inactive\");\n\t}\n\n\tprivate clearRun(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tthis.run = undefined;\n\t\tif (run.timer) clearTimeout(run.timer);\n\t\trun.controller.abort();\n\t}\n\n\tprivate stop(reason: string, stopped?: Pick<CacheWarmingStatus, \"decision\" | \"extensionOverride\">): void {\n\t\tthis.clearRun();\n\t\tthis.inactive = { state: \"inactive\", reason, ...stopped };\n\t}\n\n\tprivate schedule(run: ActiveRun): void {\n\t\trun.extensionOverride = false;\n\t\trun.nextWarmAt = Date.now() + run.delayMs;\n\t\t// A timer can run late after sleep or event-loop blockage. Keep half of\n\t\t// the planned pre-expiry margin for that delay and request dispatch; a\n\t\t// late refresh is likely a full-price cache write, not a cache warm.\n\t\trun.refreshDeadlineAt = run.nextWarmAt + Math.floor((run.ttlMs - run.delayMs) / 2);\n\t\tconst deadline = run.startedAt + (run.phase === \"idle\" ? MAX_IDLE_WARMING_AGE_MS : MAX_WARMING_AGE_MS);\n\t\tif (run.nextWarmAt > deadline || Date.now() >= deadline) {\n\t\t\tthis.stop(run.phase === \"idle\" ? \"30-minute idle safety limit reached\" : \"one-hour safety limit reached\");\n\t\t\treturn;\n\t\t}\n\t\trun.timer = setTimeout(() => void this.refresh(run), Math.max(0, run.nextWarmAt - Date.now()));\n\t\trun.timer.unref?.();\n\t}\n\n\tprivate async refresh(run: ActiveRun): Promise<void> {\n\t\trun.timer = undefined;\n\t\tif (!this.validateRun(run)) return;\n\t\tif (this.refreshDeadlineMissed(run)) return;\n\t\tconst decision = this.evaluate(run);\n\t\tconst { warmCost, missCost, continuationProbability } = decision;\n\t\tlet action = decision.action;\n\t\ttry {\n\t\t\taction = await this.decide({\n\t\t\t\ttype: \"cache_warming_decision\",\n\t\t\t\twarmCost,\n\t\t\t\tmissCost,\n\t\t\t\tcontinuationProbability,\n\t\t\t\taction,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Extension failures fall back to pi's own decision.\n\t\t}\n\t\tif (!this.validateRun(run) || this.refreshDeadlineMissed(run)) return;\n\t\tconst extensionOverride = action !== decision.action;\n\t\tif (action === \"stop\") {\n\t\t\tconst reason = extensionOverride\n\t\t\t\t? \"stopped by extension\"\n\t\t\t\t: decision.economicsAvailable\n\t\t\t\t\t? \"expected savings below threshold\"\n\t\t\t\t\t: \"cache economics unavailable\";\n\t\t\tthis.stop(reason, { decision, extensionOverride });\n\t\t\treturn;\n\t\t}\n\n\t\trun.extensionOverride = extensionOverride;\n\t\ttry {\n\t\t\tconst message = await this.models\n\t\t\t\t.streamSimple(run.model, run.context, {\n\t\t\t\t\t...run.options,\n\t\t\t\t\tmaxTokens: 1,\n\t\t\t\t\tmaxRetries: 0,\n\t\t\t\t\tsignal: run.controller.signal,\n\t\t\t\t})\n\t\t\t\t.result();\n\t\t\tif (!this.validateRun(run)) return;\n\t\t\tif (message.stopReason !== \"error\" && message.stopReason !== \"aborted\") {\n\t\t\t\tconst entry = this.sessionManager.appendUsage(\n\t\t\t\t\t\"cache_warm\",\n\t\t\t\t\tmessage.provider,\n\t\t\t\t\tmessage.responseModel ?? message.model,\n\t\t\t\t\tmessage.usage,\n\t\t\t\t\textensionOverride ? \"extension override\" : undefined,\n\t\t\t\t);\n\t\t\t\tthis.onWarmed?.(entry);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Cache warming is best-effort and must not affect the active agent run.\n\t\t}\n\t\tif (this.run === run) this.schedule(run);\n\t}\n\n\tprivate refreshDeadlineMissed(run: ActiveRun): boolean {\n\t\tif (Date.now() <= run.refreshDeadlineAt) return false;\n\t\tthis.stop(\"cache refresh deadline missed\");\n\t\treturn true;\n\t}\n\n\tprivate validateRun(run: ActiveRun): boolean {\n\t\tif (this.run !== run) return false;\n\t\tconst reason = this.getModeStopReason(run) ?? (!run.isCurrent() ? \"conversation context changed\" : undefined);\n\t\tif (!reason) return true;\n\t\tthis.stop(reason);\n\t\treturn false;\n\t}\n\n\tprivate getModeStopReason(run: ActiveRun): string | undefined {\n\t\tconst mode = this.getMode();\n\t\tif (mode === \"off\") return \"cache warming disabled\";\n\t\tif (mode === \"streaming\" && run.phase === \"idle\") return \"agent run settled\";\n\t\treturn undefined;\n\t}\n\n\tprivate evaluate(run: ActiveRun): CacheWarmingDecision {\n\t\tconst model = run.model;\n\t\tconst promptTokens = lastPromptTokens(this.sessionManager.getBranch());\n\t\tconst cacheHitCost = price(model, { cacheRead: promptTokens });\n\t\tconst cacheMissCost = price(\n\t\t\tmodel,\n\t\t\tmodel.cost.cacheWrite > 0 ? { cacheWrite: promptTokens } : { input: promptTokens },\n\t\t);\n\t\tconst warmCost = price(model, { cacheRead: promptTokens, output: 1 });\n\t\tconst missCost = Math.max(0, cacheMissCost - cacheHitCost);\n\t\tconst continuationProbability = run.phase === \"idle\" ? IDLE_CONTINUATION_PROBABILITY : 1;\n\t\tconst economicsAvailable = promptTokens > 0 && (cacheHitCost > 0 || cacheMissCost > 0);\n\t\tconst expectedSavings = continuationProbability * missCost - warmCost;\n\t\treturn {\n\t\t\tphase: run.phase,\n\t\t\twarmCost,\n\t\t\tmissCost,\n\t\t\tcontinuationProbability,\n\t\t\texpectedSavings,\n\t\t\teconomicsAvailable,\n\t\t\taction: expectedSavings >= CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS ? \"warm\" : \"stop\",\n\t\t};\n\t}\n}\n\nfunction formatDollars(value: number): string {\n\treturn value < 0 ? `-$${Math.abs(value).toFixed(3)}` : `$${value.toFixed(3)}`;\n}\n\nfunction formatCacheWarmingEconomics(decision: CacheWarmingDecision): string {\n\tif (!decision.economicsAvailable) return \"cache economics unavailable\";\n\tconst probability = Math.round(decision.continuationProbability * 100);\n\tconst probabilityText =\n\t\tdecision.phase === \"streaming\"\n\t\t\t? `${probability}% continuation probability while agent is running`\n\t\t\t: `${probability}% continuation probability`;\n\tconst comparison = decision.action === \"warm\" ? \">=\" : \"<\";\n\treturn `${probabilityText}, expected savings ${formatDollars(decision.expectedSavings)} ${comparison} $${CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS.toFixed(3)}`;\n}\n\nfunction formatCacheWarmingDecisionTime(nextWarmAt: number | undefined, now: number): string {\n\tif (nextWarmAt === undefined || nextWarmAt <= now) return \"Decision now\";\n\tlet remainingSeconds = Math.ceil((nextWarmAt - now) / 1000);\n\tconst hours = Math.floor(remainingSeconds / 3600);\n\tremainingSeconds %= 3600;\n\tconst minutes = Math.floor(remainingSeconds / 60);\n\tconst seconds = remainingSeconds % 60;\n\tconst parts: string[] = [];\n\tif (hours > 0) parts.push(`${hours}h`);\n\tif (minutes > 0) parts.push(`${minutes}m`);\n\tif (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);\n\treturn `Decision in ${parts.join(\" \")}`;\n}\n\n/** One-line status for `/session`. */\nexport function formatCacheWarmingStatus(status: CacheWarmingStatus, now = Date.now()): string {\n\tconst decision = status.decision;\n\t// A decision is attached once pi (or an extension) acted on it; \"inactive\"\n\t// without one never got that far.\n\tif (!decision || (status.state === \"inactive\" && !decision.economicsAvailable && !status.extensionOverride)) {\n\t\treturn `Inactive (${status.reason ?? \"unknown reason\"})`;\n\t}\n\tconst details = status.extensionOverride\n\t\t? `extension override, ${formatCacheWarmingEconomics(decision)}`\n\t\t: `${formatCacheWarmingEconomics(decision)} -> ${decision.action}`;\n\tif (status.state === \"inactive\") return `Stopped (${details})`;\n\tif (status.state === \"refreshing\") return `Warming cache (${details})`;\n\treturn `${formatCacheWarmingDecisionTime(status.nextWarmAt, now)} (${details})`;\n}\n\n/** One-line transcript text for persisted cache-warming usage. */\nexport function formatCacheWarmingUsage(entry: UsageEntry): string {\n\tconst note = entry.note ? ` (${entry.note})` : \"\";\n\tconst cost = entry.usage.cost.total.toFixed(6).replace(/(\\.\\d{3}\\d*?)0+$/, \"$1\");\n\treturn `Cache warmed${note}: $${cost}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cache-warmer.d.ts","sourceRoot":"","sources":["../../src/core/cache-warmer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-warmer.js","sourceRoot":"","sources":["../../src/core/cache-warmer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype Context,\n\tcalculateCost,\n\ttype Model,\n\ttype ModelsSimpleStreamOptions,\n\ttype SimpleStreamOptions,\n\ttype Usage,\n} from \"@earendil-works/pi-ai\";\nimport { getProviderEnvValue } from \"@earendil-works/pi-ai/utils/provider-env\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport type { SessionEntry, SessionManager, UsageEntry } from \"./session-manager.ts\";\nimport type { CacheWarmingMode } from \"./settings-manager.ts\";\n\n/** Streaming warming never continues past this long after the real request that started it. */\nconst MAX_WARMING_AGE_MS = 60 * 60_000;\n/** Idle warming uses a shorter horizon because continuation estimates become less reliable with age. */\nconst MAX_IDLE_WARMING_AGE_MS = 30 * 60_000;\n/** A refresh is sent only when it is expected to save at least this many dollars. */\nconst CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS = 0.05;\n/**\n * Chance that a real request arrives before the cache entry expires while the\n * agent sits idle. Measured from our own usage; per-session estimates were not\n * better than this constant.\n */\nconst IDLE_CONTINUATION_PROBABILITY = 0.15;\n\n/** Refresh at 90% of the TTL while preserving at least ten seconds of margin. */\nexport function getCacheWarmingDelayMs(ttlMs: number): number | undefined {\n\tif (ttlMs <= 10_000) return undefined;\n\treturn Math.max(1, Math.floor(Math.min(ttlMs * 0.9, ttlMs - 10_000)));\n}\n\n/**\n * Lifetime of the prompt cache entry a request writes, from the model's\n * `promptCache` tier for the retention the request used. Undefined when the\n * model has no lifetime for that tier or caching is off.\n */\nexport function getPromptCacheTtlMs(model: Model<Api>, options: SimpleStreamOptions | undefined): number | undefined {\n\tconst retention =\n\t\toptions?.cacheRetention ??\n\t\t(getProviderEnvValue(\"PI_CACHE_RETENTION\", options?.env) === \"long\" ? \"long\" : \"short\");\n\tif (retention === \"none\") return undefined;\n\tconst seconds = model.promptCache?.[retention];\n\treturn seconds === undefined ? undefined : seconds * 1000;\n}\n\n/**\n * Whether replaying the request with a one-token output cap leaves its cache\n * entry untouched. Anthropic's budget-based thinking (Claude models without\n * adaptive thinking) derives `budget_tokens` from `max_tokens`; the replay\n * would get a different budget, which Anthropic keys the message cache on,\n * and the model could still think for thousands of tokens.\n */\nexport function isReplayable(model: Model<Api>, options: SimpleStreamOptions | undefined): boolean {\n\tif (!options?.reasoning || model.api !== \"anthropic-messages\") return true;\n\treturn (model as Model<\"anthropic-messages\">).compat?.forceAdaptiveThinking === true;\n}\n\n/** Prompt size of the most recent real request on the branch, as reported by the provider. */\nfunction lastPromptTokens(entries: SessionEntry[]): number {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst usage = entry.message.usage;\n\t\t\treturn usage.input + usage.cacheRead + usage.cacheWrite;\n\t\t}\n\t}\n\treturn 0;\n}\n\nfunction price(\n\tmodel: Model<Api>,\n\ttokens: Partial<Pick<Usage, \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\">>,\n): number {\n\tconst usage: Usage = {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t...tokens,\n\t};\n\treturn calculateCost(model, usage).total;\n}\n\nexport type CacheWarmingAction = \"warm\" | \"stop\";\n\n/** Inputs and outcome of one warm-or-stop decision, as shown by `/session`. */\nexport interface CacheWarmingDecision {\n\t/** \"streaming\" while the agent run that sent the request is still active. */\n\tphase: \"streaming\" | \"idle\";\n\t/** Price of this refresh: a cache read of the prompt plus one output token. */\n\twarmCost: number;\n\t/** Extra price of the next real request if the cache entry is lost. */\n\tmissCost: number;\n\t/** Estimated chance that a real request arrives before the entry expires. */\n\tcontinuationProbability: number;\n\t/** `continuationProbability * missCost - warmCost`. */\n\texpectedSavings: number;\n\t/** False when the prompt size or the model's prices are unknown. */\n\teconomicsAvailable: boolean;\n\t/** Pi's decision: \"warm\" when `expectedSavings` is at least $0.05. */\n\taction: CacheWarmingAction;\n}\n\n/**\n * Fired before each refresh with pi's decision filled in. Everything else an\n * extension might want (model, idle state, context size) is on the context.\n */\nexport interface CacheWarmingDecisionEvent\n\textends Pick<CacheWarmingDecision, \"warmCost\" | \"missCost\" | \"continuationProbability\" | \"action\"> {\n\ttype: \"cache_warming_decision\";\n}\n\nexport interface CacheWarmingDecisionEventResult {\n\t/** Override whether this refresh is sent. \"stop\" ends warming until the next real request. */\n\taction?: CacheWarmingAction;\n}\n\nexport interface CacheWarmingStatus {\n\t/** \"scheduled\": a refresh timer is armed; \"refreshing\": a warm request is in flight. */\n\tstate: \"inactive\" | \"scheduled\" | \"refreshing\";\n\t/** Why nothing is scheduled. */\n\treason?: string;\n\tnextWarmAt?: number;\n\t/** The pending decision, or the decision that stopped warming. */\n\tdecision?: CacheWarmingDecision;\n\t/** True when an extension changed `decision.action`. */\n\textensionOverride?: boolean;\n}\n\n/** The request whose prompt cache entry should be kept warm, exactly as it was sent. */\nexport interface CacheWarmRequest {\n\tmodel: Model<Api>;\n\tcontext: Context;\n\toptions: ModelsSimpleStreamOptions;\n}\n\ninterface ActiveRun extends CacheWarmRequest {\n\t/** False once the session's model or messages no longer match the request. */\n\tisCurrent: () => boolean;\n\tttlMs: number;\n\tdelayMs: number;\n\t/** Latest safe time to send this refresh, leaving half the original expiry margin. */\n\trefreshDeadlineAt: number;\n\tstartedAt: number;\n\tcontroller: AbortController;\n\tphase: \"streaming\" | \"idle\";\n\tnextWarmAt: number;\n\t/** Set while a refresh that an extension forced is in flight. */\n\textensionOverride: boolean;\n\ttimer?: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Keeps one prompt cache entry alive by re-sending its request with a\n * one-token output cap before the entry expires. `start` replaces any\n * previous run; warm requests never extend the fixed safety windows.\n */\nexport class CacheWarmer {\n\tprivate run?: ActiveRun;\n\tprivate inactive: CacheWarmingStatus;\n\tprivate readonly models: Pick<ModelRuntime, \"streamSimple\">;\n\tprivate readonly sessionManager: Pick<SessionManager, \"appendUsage\" | \"getBranch\">;\n\tprivate readonly getMode: () => CacheWarmingMode;\n\t/** Lets extensions override `event.action`; failures fall back to pi's decision. */\n\tprivate readonly decide: (event: CacheWarmingDecisionEvent) => Promise<CacheWarmingAction>;\n\t/** Called with the persisted usage entry after each successful refresh. */\n\tonWarmed?: (entry: UsageEntry) => void;\n\n\tconstructor(\n\t\tmodels: Pick<ModelRuntime, \"streamSimple\">,\n\t\tsessionManager: Pick<SessionManager, \"appendUsage\" | \"getBranch\">,\n\t\tgetMode: () => CacheWarmingMode,\n\t\tdecide: (event: CacheWarmingDecisionEvent) => Promise<CacheWarmingAction> = async (event) => event.action,\n\t) {\n\t\tthis.models = models;\n\t\tthis.sessionManager = sessionManager;\n\t\tthis.getMode = getMode;\n\t\tthis.decide = decide;\n\t\tthis.inactive = { state: \"inactive\", reason: \"waiting for first request\" };\n\t}\n\n\tget status(): CacheWarmingStatus {\n\t\tif (this.getMode() === \"off\") return { state: \"inactive\", reason: \"cache warming disabled\" };\n\t\tconst run = this.run;\n\t\tif (!run) return this.inactive;\n\t\tif (!run.isCurrent()) return { state: \"inactive\", reason: \"conversation context changed\" };\n\t\tconst decision = this.evaluate(run);\n\t\tconst refreshing = run.timer === undefined;\n\t\tif (!decision.economicsAvailable && !refreshing) {\n\t\t\treturn { state: \"inactive\", reason: \"cache economics unavailable\" };\n\t\t}\n\t\treturn {\n\t\t\tstate: refreshing ? \"refreshing\" : \"scheduled\",\n\t\t\tnextWarmAt: run.nextWarmAt,\n\t\t\tdecision,\n\t\t\textensionOverride: run.extensionOverride,\n\t\t};\n\t}\n\n\t/** Keep the prompt cache entry written by `request` warm while `isCurrent` holds. */\n\tstart(request: CacheWarmRequest, isCurrent: () => boolean): void {\n\t\tthis.clearRun();\n\t\tconst mode = this.getMode();\n\t\tif (mode === \"off\") {\n\t\t\tthis.stop(\"cache warming disabled\");\n\t\t\treturn;\n\t\t}\n\t\tif (!isReplayable(request.model, request.options)) {\n\t\t\tthis.stop(\"request cannot be replayed safely\");\n\t\t\treturn;\n\t\t}\n\t\tconst ttlMs = getPromptCacheTtlMs(request.model, request.options);\n\t\tif (ttlMs === undefined) {\n\t\t\tthis.stop(\n\t\t\t\trequest.options.cacheRetention === \"none\"\n\t\t\t\t\t? \"request disabled prompt caching\"\n\t\t\t\t\t: \"cache lifetime unavailable\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = getCacheWarmingDelayMs(ttlMs);\n\t\tif (delayMs === undefined) {\n\t\t\tthis.stop(\"cache lifetime unavailable\");\n\t\t\treturn;\n\t\t}\n\t\tthis.run = {\n\t\t\t...request,\n\t\t\tisCurrent,\n\t\t\tttlMs,\n\t\t\tdelayMs,\n\t\t\trefreshDeadlineAt: 0,\n\t\t\tstartedAt: Date.now(),\n\t\t\tcontroller: new AbortController(),\n\t\t\tphase: \"streaming\",\n\t\t\tnextWarmAt: 0,\n\t\t\textensionOverride: false,\n\t\t};\n\t\tthis.schedule(this.run);\n\t}\n\n\tonAgentSettled(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tif (this.getMode() === \"streaming\") {\n\t\t\tthis.stop(\"agent run settled\");\n\t\t\treturn;\n\t\t}\n\t\trun.phase = \"idle\";\n\t\tconst deadline = run.startedAt + MAX_IDLE_WARMING_AGE_MS;\n\t\tif (run.nextWarmAt > deadline || Date.now() >= deadline) {\n\t\t\tthis.stop(\"30-minute idle safety limit reached\");\n\t\t}\n\t}\n\n\t/** Reconcile an active run after the persisted warming mode changes. */\n\tonModeChanged(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tconst reason = this.getModeStopReason(run);\n\t\tif (reason) this.stop(reason);\n\t}\n\n\tcancel(): void {\n\t\tthis.stop(\"inactive\");\n\t}\n\n\tprivate clearRun(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tthis.run = undefined;\n\t\tif (run.timer) clearTimeout(run.timer);\n\t\trun.controller.abort();\n\t}\n\n\tprivate stop(reason: string, stopped?: Pick<CacheWarmingStatus, \"decision\" | \"extensionOverride\">): void {\n\t\tthis.clearRun();\n\t\tthis.inactive = { state: \"inactive\", reason, ...stopped };\n\t}\n\n\tprivate schedule(run: ActiveRun): void {\n\t\trun.extensionOverride = false;\n\t\trun.nextWarmAt = Date.now() + run.delayMs;\n\t\t// A timer can run late after sleep or event-loop blockage. Keep half of\n\t\t// the planned pre-expiry margin for that delay and request dispatch; a\n\t\t// late refresh is likely a full-price cache write, not a cache warm.\n\t\trun.refreshDeadlineAt = run.nextWarmAt + Math.floor((run.ttlMs - run.delayMs) / 2);\n\t\tconst deadline = run.startedAt + (run.phase === \"idle\" ? MAX_IDLE_WARMING_AGE_MS : MAX_WARMING_AGE_MS);\n\t\tif (run.nextWarmAt > deadline || Date.now() >= deadline) {\n\t\t\tthis.stop(run.phase === \"idle\" ? \"30-minute idle safety limit reached\" : \"one-hour safety limit reached\");\n\t\t\treturn;\n\t\t}\n\t\trun.timer = setTimeout(() => void this.refresh(run), Math.max(0, run.nextWarmAt - Date.now()));\n\t\trun.timer.unref?.();\n\t}\n\n\tprivate async refresh(run: ActiveRun): Promise<void> {\n\t\trun.timer = undefined;\n\t\tif (!this.validateRun(run)) return;\n\t\tif (this.refreshDeadlineMissed(run)) return;\n\t\tconst decision = this.evaluate(run);\n\t\tconst { warmCost, missCost, continuationProbability } = decision;\n\t\tlet action = decision.action;\n\t\ttry {\n\t\t\taction = await this.decide({\n\t\t\t\ttype: \"cache_warming_decision\",\n\t\t\t\twarmCost,\n\t\t\t\tmissCost,\n\t\t\t\tcontinuationProbability,\n\t\t\t\taction,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Extension failures fall back to pi's own decision.\n\t\t}\n\t\tif (!this.validateRun(run) || this.refreshDeadlineMissed(run)) return;\n\t\tconst extensionOverride = action !== decision.action;\n\t\tif (action === \"stop\") {\n\t\t\tconst reason = extensionOverride\n\t\t\t\t? \"stopped by extension\"\n\t\t\t\t: decision.economicsAvailable\n\t\t\t\t\t? \"expected savings below threshold\"\n\t\t\t\t\t: \"cache economics unavailable\";\n\t\t\tthis.stop(reason, { decision, extensionOverride });\n\t\t\treturn;\n\t\t}\n\n\t\trun.extensionOverride = extensionOverride;\n\t\ttry {\n\t\t\tconst message = await this.models\n\t\t\t\t.streamSimple(run.model, run.context, {\n\t\t\t\t\t...run.options,\n\t\t\t\t\tmaxTokens: 1,\n\t\t\t\t\tmaxRetries: 0,\n\t\t\t\t\tsignal: run.controller.signal,\n\t\t\t\t})\n\t\t\t\t.result();\n\t\t\tif (!this.validateRun(run)) return;\n\t\t\tif (message.stopReason !== \"error\" && message.stopReason !== \"aborted\") {\n\t\t\t\tconst entry = this.sessionManager.appendUsage(\n\t\t\t\t\t\"cache_warm\",\n\t\t\t\t\tmessage.provider,\n\t\t\t\t\tmessage.responseModel ?? message.model,\n\t\t\t\t\tmessage.usage,\n\t\t\t\t\textensionOverride ? \"extension override\" : undefined,\n\t\t\t\t);\n\t\t\t\tthis.onWarmed?.(entry);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Cache warming is best-effort and must not affect the active agent run.\n\t\t}\n\t\tif (this.run === run) this.schedule(run);\n\t}\n\n\tprivate refreshDeadlineMissed(run: ActiveRun): boolean {\n\t\tif (Date.now() <= run.refreshDeadlineAt) return false;\n\t\tthis.stop(\"cache refresh deadline missed\");\n\t\treturn true;\n\t}\n\n\tprivate validateRun(run: ActiveRun): boolean {\n\t\tif (this.run !== run) return false;\n\t\tconst reason = this.getModeStopReason(run) ?? (!run.isCurrent() ? \"conversation context changed\" : undefined);\n\t\tif (!reason) return true;\n\t\tthis.stop(reason);\n\t\treturn false;\n\t}\n\n\tprivate getModeStopReason(run: ActiveRun): string | undefined {\n\t\tconst mode = this.getMode();\n\t\tif (mode === \"off\") return \"cache warming disabled\";\n\t\tif (mode === \"streaming\" && run.phase === \"idle\") return \"agent run settled\";\n\t\treturn undefined;\n\t}\n\n\tprivate evaluate(run: ActiveRun): CacheWarmingDecision {\n\t\tconst model = run.model;\n\t\tconst promptTokens = lastPromptTokens(this.sessionManager.getBranch());\n\t\tconst cacheHitCost = price(model, { cacheRead: promptTokens });\n\t\tconst cacheMissCost = price(\n\t\t\tmodel,\n\t\t\tmodel.cost.cacheWrite > 0 ? { cacheWrite: promptTokens } : { input: promptTokens },\n\t\t);\n\t\tconst warmCost = price(model, { cacheRead: promptTokens, output: 1 });\n\t\tconst missCost = Math.max(0, cacheMissCost - cacheHitCost);\n\t\tconst continuationProbability = run.phase === \"idle\" ? IDLE_CONTINUATION_PROBABILITY : 1;\n\t\tconst economicsAvailable = promptTokens > 0 && (cacheHitCost > 0 || cacheMissCost > 0);\n\t\tconst expectedSavings = continuationProbability * missCost - warmCost;\n\t\treturn {\n\t\t\tphase: run.phase,\n\t\t\twarmCost,\n\t\t\tmissCost,\n\t\t\tcontinuationProbability,\n\t\t\texpectedSavings,\n\t\t\teconomicsAvailable,\n\t\t\taction: expectedSavings >= CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS ? \"warm\" : \"stop\",\n\t\t};\n\t}\n}\n\nfunction formatDollars(value: number): string {\n\treturn value < 0 ? `-$${Math.abs(value).toFixed(3)}` : `$${value.toFixed(3)}`;\n}\n\nfunction formatCacheWarmingEconomics(decision: CacheWarmingDecision): string {\n\tif (!decision.economicsAvailable) return \"cache economics unavailable\";\n\tconst probability = Math.round(decision.continuationProbability * 100);\n\tconst probabilityText =\n\t\tdecision.phase === \"streaming\"\n\t\t\t? `${probability}% continuation probability while agent is running`\n\t\t\t: `${probability}% continuation probability`;\n\tconst comparison = decision.action === \"warm\" ? \">=\" : \"<\";\n\treturn `${probabilityText}, expected savings ${formatDollars(decision.expectedSavings)} ${comparison} $${CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS.toFixed(3)}`;\n}\n\nfunction formatCacheWarmingDecisionTime(nextWarmAt: number | undefined, now: number): string {\n\tif (nextWarmAt === undefined || nextWarmAt <= now) return \"Decision now\";\n\tlet remainingSeconds = Math.ceil((nextWarmAt - now) / 1000);\n\tconst hours = Math.floor(remainingSeconds / 3600);\n\tremainingSeconds %= 3600;\n\tconst minutes = Math.floor(remainingSeconds / 60);\n\tconst seconds = remainingSeconds % 60;\n\tconst parts: string[] = [];\n\tif (hours > 0) parts.push(`${hours}h`);\n\tif (minutes > 0) parts.push(`${minutes}m`);\n\tif (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);\n\treturn `Decision in ${parts.join(\" \")}`;\n}\n\n/** One-line status for `/session`. */\nexport function formatCacheWarmingStatus(status: CacheWarmingStatus, now = Date.now()): string {\n\tconst decision = status.decision;\n\t// A decision is attached once pi (or an extension) acted on it; \"inactive\"\n\t// without one never got that far.\n\tif (!decision || (status.state === \"inactive\" && !decision.economicsAvailable && !status.extensionOverride)) {\n\t\treturn `Inactive (${status.reason ?? \"unknown reason\"})`;\n\t}\n\tconst details = status.extensionOverride\n\t\t? `extension override, ${formatCacheWarmingEconomics(decision)}`\n\t\t: `${formatCacheWarmingEconomics(decision)} -> ${decision.action}`;\n\tif (status.state === \"inactive\") return `Stopped (${details})`;\n\tif (status.state === \"refreshing\") return `Warming cache (${details})`;\n\treturn `${formatCacheWarmingDecisionTime(status.nextWarmAt, now)} (${details})`;\n}\n\n/** One-line transcript text for persisted cache-warming usage. */\nexport function formatCacheWarmingUsage(entry: UsageEntry): string {\n\tconst note = entry.note ? ` (${entry.note})` : \"\";\n\tconst cost = entry.usage.cost.total.toFixed(6).replace(/(\\.\\d{3}\\d*?)0+$/, \"$1\");\n\treturn `Cache warmed${note}: $${cost}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cache-warmer.js","sourceRoot":"","sources":["../../src/core/cache-warmer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Api,\n\ttype Context,\n\tcalculateCost,\n\ttype Model,\n\ttype ModelsSimpleStreamOptions,\n\ttype SimpleStreamOptions,\n\ttype Usage,\n} from \"@earendil-works/pi-ai\";\nimport { getProviderEnvValue } from \"@earendil-works/pi-ai/utils/provider-env\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\nimport type { SessionEntry, SessionManager, UsageEntry } from \"./session-manager.ts\";\nimport type { CacheWarmingMode } from \"./settings-manager.ts\";\n\n/** Streaming warming never continues past this long after the real request that started it. */\nconst MAX_WARMING_AGE_MS = 60 * 60_000;\n/** Idle warming uses a shorter horizon because continuation estimates become less reliable with age. */\nconst MAX_IDLE_WARMING_AGE_MS = 30 * 60_000;\n/** A refresh is sent only when it is expected to save at least this many dollars. */\nconst CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS = 0.05;\n/**\n * Chance that a real request arrives before the cache entry expires while the\n * agent sits idle. Measured from our own usage; per-session estimates were not\n * better than this constant.\n */\nconst IDLE_CONTINUATION_PROBABILITY = 0.15;\n\n/** Refresh at 90% of the TTL while preserving at least ten seconds of margin. */\nexport function getCacheWarmingDelayMs(ttlMs: number): number | undefined {\n\tif (ttlMs <= 10_000) return undefined;\n\treturn Math.max(1, Math.floor(Math.min(ttlMs * 0.9, ttlMs - 10_000)));\n}\n\n/**\n * Lifetime of the prompt cache entry a request writes, from the model's\n * `promptCache` tier for the retention the request used. Undefined when the\n * model has no lifetime for that tier or caching is off.\n */\nexport function getPromptCacheTtlMs(model: Model<Api>, options: SimpleStreamOptions | undefined): number | undefined {\n\tconst retention =\n\t\toptions?.cacheRetention ??\n\t\t(getProviderEnvValue(\"PI_CACHE_RETENTION\", options?.env) === \"long\" ? \"long\" : \"short\");\n\tif (retention === \"none\") return undefined;\n\tconst seconds = model.promptCache?.[retention];\n\treturn seconds === undefined ? undefined : seconds * 1000;\n}\n\n/**\n * Whether replaying the request with a one-token output cap leaves its cache\n * entry untouched. Anthropic's budget-based thinking (Claude models without\n * adaptive thinking) derives `budget_tokens` from `max_tokens`; the replay\n * would get a different budget, which Anthropic keys the message cache on,\n * and the model could still think for thousands of tokens.\n */\nexport function isReplayable(model: Model<Api>, options: SimpleStreamOptions | undefined): boolean {\n\tif (!options?.reasoning || model.api !== \"anthropic-messages\") return true;\n\treturn (model as Model<\"anthropic-messages\">).compat?.forceAdaptiveThinking === true;\n}\n\n/** Prompt size of the most recent real request on the branch, as reported by the provider. */\nfunction lastPromptTokens(entries: SessionEntry[]): number {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tconst usage = entry.message.usage;\n\t\t\treturn usage.input + usage.cacheRead + usage.cacheWrite;\n\t\t}\n\t}\n\treturn 0;\n}\n\nfunction price(\n\tmodel: Model<Api>,\n\ttokens: Partial<Pick<Usage, \"input\" | \"output\" | \"cacheRead\" | \"cacheWrite\">>,\n): number {\n\tconst usage: Usage = {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t...tokens,\n\t};\n\treturn calculateCost(model, usage).total;\n}\n\nexport type CacheWarmingAction = \"warm\" | \"stop\";\n\n/** Inputs and outcome of one warm-or-stop decision, as shown by `/session`. */\nexport interface CacheWarmingDecision {\n\t/** \"streaming\" while the agent run that sent the request is still active. */\n\tphase: \"streaming\" | \"idle\";\n\t/** Price of this refresh: a cache read of the prompt plus one output token. */\n\twarmCost: number;\n\t/** Extra price of the next real request if the cache entry is lost. */\n\tmissCost: number;\n\t/** Estimated chance that a real request arrives before the entry expires. */\n\tcontinuationProbability: number;\n\t/** `continuationProbability * missCost - warmCost`. */\n\texpectedSavings: number;\n\t/** False when the prompt size or the model's prices are unknown. */\n\teconomicsAvailable: boolean;\n\t/** Pi's decision: \"warm\" when `expectedSavings` is at least $0.05. */\n\taction: CacheWarmingAction;\n}\n\n/**\n * Fired before each refresh with pi's decision filled in. Everything else an\n * extension might want (model, idle state, context size) is on the context.\n */\nexport interface CacheWarmingDecisionEvent\n\textends Pick<CacheWarmingDecision, \"warmCost\" | \"missCost\" | \"continuationProbability\" | \"action\"> {\n\ttype: \"cache_warming_decision\";\n}\n\nexport interface CacheWarmingDecisionEventResult {\n\t/** Override whether this refresh is sent. \"stop\" ends warming until the next real request. */\n\taction?: CacheWarmingAction;\n}\n\nexport interface CacheWarmingStatus {\n\t/** \"scheduled\": a refresh timer is armed; \"refreshing\": a warm request is in flight. */\n\tstate: \"inactive\" | \"scheduled\" | \"refreshing\";\n\t/** Why nothing is scheduled. */\n\treason?: string;\n\tnextWarmAt?: number;\n\t/** The pending decision, or the decision that stopped warming. */\n\tdecision?: CacheWarmingDecision;\n\t/** True when an extension changed `decision.action`. */\n\textensionOverride?: boolean;\n}\n\n/** The request whose prompt cache entry should be kept warm, exactly as it was sent. */\nexport interface CacheWarmRequest {\n\tmodel: Model<Api>;\n\tcontext: Context;\n\toptions: ModelsSimpleStreamOptions;\n}\n\ninterface ActiveRun extends CacheWarmRequest {\n\t/** False once the session's model or messages no longer match the request. */\n\tisCurrent: () => boolean;\n\tttlMs: number;\n\tdelayMs: number;\n\t/** Latest safe time to send this refresh, leaving half the original expiry margin. */\n\trefreshDeadlineAt: number;\n\tstartedAt: number;\n\tcontroller: AbortController;\n\tphase: \"streaming\" | \"idle\";\n\tnextWarmAt: number;\n\t/** Set while a refresh that an extension forced is in flight. */\n\textensionOverride: boolean;\n\ttimer?: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Keeps one prompt cache entry alive by re-sending its request with a\n * one-token output cap before the entry expires. `start` replaces any\n * previous run; warm requests never extend the fixed safety windows.\n */\nexport class CacheWarmer {\n\tprivate run?: ActiveRun;\n\tprivate inactive: CacheWarmingStatus;\n\tprivate readonly models: Pick<ModelRuntime, \"streamSimple\">;\n\tprivate readonly sessionManager: Pick<SessionManager, \"appendUsage\" | \"getBranch\">;\n\tprivate readonly getMode: () => CacheWarmingMode;\n\t/** Lets extensions override `event.action`; failures fall back to pi's decision. */\n\tprivate readonly decide: (event: CacheWarmingDecisionEvent) => Promise<CacheWarmingAction>;\n\t/** Called with the persisted usage entry after each successful refresh. */\n\tonWarmed?: (entry: UsageEntry) => void;\n\n\tconstructor(\n\t\tmodels: Pick<ModelRuntime, \"streamSimple\">,\n\t\tsessionManager: Pick<SessionManager, \"appendUsage\" | \"getBranch\">,\n\t\tgetMode: () => CacheWarmingMode,\n\t\tdecide: (event: CacheWarmingDecisionEvent) => Promise<CacheWarmingAction> = async (event) => event.action,\n\t) {\n\t\tthis.models = models;\n\t\tthis.sessionManager = sessionManager;\n\t\tthis.getMode = getMode;\n\t\tthis.decide = decide;\n\t\tthis.inactive = { state: \"inactive\", reason: \"waiting for first request\" };\n\t}\n\n\tget status(): CacheWarmingStatus {\n\t\tif (this.getMode() === \"off\") return { state: \"inactive\", reason: \"cache warming disabled\" };\n\t\tconst run = this.run;\n\t\tif (!run) return this.inactive;\n\t\tif (!run.isCurrent()) return { state: \"inactive\", reason: \"conversation context changed\" };\n\t\tconst decision = this.evaluate(run);\n\t\tconst refreshing = run.timer === undefined;\n\t\tif (!decision.economicsAvailable && !refreshing) {\n\t\t\treturn { state: \"inactive\", reason: \"cache economics unavailable\" };\n\t\t}\n\t\treturn {\n\t\t\tstate: refreshing ? \"refreshing\" : \"scheduled\",\n\t\t\tnextWarmAt: run.nextWarmAt,\n\t\t\tdecision,\n\t\t\textensionOverride: run.extensionOverride,\n\t\t};\n\t}\n\n\t/** Keep the prompt cache entry written by `request` warm while `isCurrent` holds. */\n\tstart(request: CacheWarmRequest, isCurrent: () => boolean): void {\n\t\tthis.clearRun();\n\t\tconst mode = this.getMode();\n\t\tif (mode === \"off\") {\n\t\t\tthis.stop(\"cache warming disabled\");\n\t\t\treturn;\n\t\t}\n\t\tif (!isReplayable(request.model, request.options)) {\n\t\t\tthis.stop(\"request cannot be replayed safely\");\n\t\t\treturn;\n\t\t}\n\t\tconst ttlMs = getPromptCacheTtlMs(request.model, request.options);\n\t\tif (ttlMs === undefined) {\n\t\t\tthis.stop(\n\t\t\t\trequest.options.cacheRetention === \"none\"\n\t\t\t\t\t? \"request disabled prompt caching\"\n\t\t\t\t\t: \"cache lifetime unavailable\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = getCacheWarmingDelayMs(ttlMs);\n\t\tif (delayMs === undefined) {\n\t\t\tthis.stop(\"cache lifetime unavailable\");\n\t\t\treturn;\n\t\t}\n\t\tthis.run = {\n\t\t\t...request,\n\t\t\tisCurrent,\n\t\t\tttlMs,\n\t\t\tdelayMs,\n\t\t\trefreshDeadlineAt: 0,\n\t\t\tstartedAt: Date.now(),\n\t\t\tcontroller: new AbortController(),\n\t\t\tphase: \"streaming\",\n\t\t\tnextWarmAt: 0,\n\t\t\textensionOverride: false,\n\t\t};\n\t\tthis.schedule(this.run);\n\t}\n\n\tonAgentSettled(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tif (this.getMode() === \"streaming\") {\n\t\t\tthis.stop(\"agent run settled\");\n\t\t\treturn;\n\t\t}\n\t\trun.phase = \"idle\";\n\t\tconst deadline = run.startedAt + MAX_IDLE_WARMING_AGE_MS;\n\t\tif (run.nextWarmAt > deadline || Date.now() >= deadline) {\n\t\t\tthis.stop(\"30-minute idle safety limit reached\");\n\t\t}\n\t}\n\n\t/** Reconcile an active run after the persisted warming mode changes. */\n\tonModeChanged(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tconst reason = this.getModeStopReason(run);\n\t\tif (reason) this.stop(reason);\n\t}\n\n\tcancel(): void {\n\t\tthis.stop(\"inactive\");\n\t}\n\n\tprivate clearRun(): void {\n\t\tconst run = this.run;\n\t\tif (!run) return;\n\t\tthis.run = undefined;\n\t\tif (run.timer) clearTimeout(run.timer);\n\t\trun.controller.abort();\n\t}\n\n\tprivate stop(reason: string, stopped?: Pick<CacheWarmingStatus, \"decision\" | \"extensionOverride\">): void {\n\t\tthis.clearRun();\n\t\tthis.inactive = { state: \"inactive\", reason, ...stopped };\n\t}\n\n\tprivate schedule(run: ActiveRun): void {\n\t\trun.extensionOverride = false;\n\t\trun.nextWarmAt = Date.now() + run.delayMs;\n\t\t// A timer can run late after sleep or event-loop blockage. Keep half of\n\t\t// the planned pre-expiry margin for that delay and request dispatch; a\n\t\t// late refresh is likely a full-price cache write, not a cache warm.\n\t\trun.refreshDeadlineAt = run.nextWarmAt + Math.floor((run.ttlMs - run.delayMs) / 2);\n\t\tconst deadline = run.startedAt + (run.phase === \"idle\" ? MAX_IDLE_WARMING_AGE_MS : MAX_WARMING_AGE_MS);\n\t\tif (run.nextWarmAt > deadline || Date.now() >= deadline) {\n\t\t\tthis.stop(run.phase === \"idle\" ? \"30-minute idle safety limit reached\" : \"one-hour safety limit reached\");\n\t\t\treturn;\n\t\t}\n\t\trun.timer = setTimeout(() => void this.refresh(run), Math.max(0, run.nextWarmAt - Date.now()));\n\t\trun.timer.unref?.();\n\t}\n\n\tprivate async refresh(run: ActiveRun): Promise<void> {\n\t\trun.timer = undefined;\n\t\tif (!this.validateRun(run)) return;\n\t\tif (this.refreshDeadlineMissed(run)) return;\n\t\tconst decision = this.evaluate(run);\n\t\tconst { warmCost, missCost, continuationProbability } = decision;\n\t\tlet action = decision.action;\n\t\ttry {\n\t\t\taction = await this.decide({\n\t\t\t\ttype: \"cache_warming_decision\",\n\t\t\t\twarmCost,\n\t\t\t\tmissCost,\n\t\t\t\tcontinuationProbability,\n\t\t\t\taction,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Extension failures fall back to pi's own decision.\n\t\t}\n\t\tif (!this.validateRun(run) || this.refreshDeadlineMissed(run)) return;\n\t\tconst extensionOverride = action !== decision.action;\n\t\tif (action === \"stop\") {\n\t\t\tconst reason = extensionOverride\n\t\t\t\t? \"stopped by extension\"\n\t\t\t\t: decision.economicsAvailable\n\t\t\t\t\t? \"expected savings below threshold\"\n\t\t\t\t\t: \"cache economics unavailable\";\n\t\t\tthis.stop(reason, { decision, extensionOverride });\n\t\t\treturn;\n\t\t}\n\n\t\trun.extensionOverride = extensionOverride;\n\t\ttry {\n\t\t\tconst message = await this.models\n\t\t\t\t.streamSimple(run.model, run.context, {\n\t\t\t\t\t...run.options,\n\t\t\t\t\tmaxTokens: 1,\n\t\t\t\t\tmaxRetries: 0,\n\t\t\t\t\tsignal: run.controller.signal,\n\t\t\t\t})\n\t\t\t\t.result();\n\t\t\tif (!this.validateRun(run)) return;\n\t\t\tif (message.stopReason !== \"error\" && message.stopReason !== \"aborted\") {\n\t\t\t\tconst entry = this.sessionManager.appendUsage(\n\t\t\t\t\t\"cache_warm\",\n\t\t\t\t\tmessage.provider,\n\t\t\t\t\tmessage.responseModel ?? message.model,\n\t\t\t\t\tmessage.usage,\n\t\t\t\t\textensionOverride ? \"extension override\" : undefined,\n\t\t\t\t);\n\t\t\t\tthis.onWarmed?.(entry);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Cache warming is best-effort and must not affect the active agent run.\n\t\t}\n\t\tif (this.run === run) this.schedule(run);\n\t}\n\n\tprivate refreshDeadlineMissed(run: ActiveRun): boolean {\n\t\tif (Date.now() <= run.refreshDeadlineAt) return false;\n\t\tthis.stop(\"cache refresh deadline missed\");\n\t\treturn true;\n\t}\n\n\tprivate validateRun(run: ActiveRun): boolean {\n\t\tif (this.run !== run) return false;\n\t\tconst reason = this.getModeStopReason(run) ?? (!run.isCurrent() ? \"conversation context changed\" : undefined);\n\t\tif (!reason) return true;\n\t\tthis.stop(reason);\n\t\treturn false;\n\t}\n\n\tprivate getModeStopReason(run: ActiveRun): string | undefined {\n\t\tconst mode = this.getMode();\n\t\tif (mode === \"off\") return \"cache warming disabled\";\n\t\tif (mode === \"streaming\" && run.phase === \"idle\") return \"agent run settled\";\n\t\treturn undefined;\n\t}\n\n\tprivate evaluate(run: ActiveRun): CacheWarmingDecision {\n\t\tconst model = run.model;\n\t\tconst promptTokens = lastPromptTokens(this.sessionManager.getBranch());\n\t\tconst cacheHitCost = price(model, { cacheRead: promptTokens });\n\t\tconst cacheMissCost = price(\n\t\t\tmodel,\n\t\t\tmodel.cost.cacheWrite > 0 ? { cacheWrite: promptTokens } : { input: promptTokens },\n\t\t);\n\t\tconst warmCost = price(model, { cacheRead: promptTokens, output: 1 });\n\t\tconst missCost = Math.max(0, cacheMissCost - cacheHitCost);\n\t\tconst continuationProbability = run.phase === \"idle\" ? IDLE_CONTINUATION_PROBABILITY : 1;\n\t\tconst economicsAvailable = promptTokens > 0 && (cacheHitCost > 0 || cacheMissCost > 0);\n\t\tconst expectedSavings = continuationProbability * missCost - warmCost;\n\t\treturn {\n\t\t\tphase: run.phase,\n\t\t\twarmCost,\n\t\t\tmissCost,\n\t\t\tcontinuationProbability,\n\t\t\texpectedSavings,\n\t\t\teconomicsAvailable,\n\t\t\taction: expectedSavings >= CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS ? \"warm\" : \"stop\",\n\t\t};\n\t}\n}\n\nfunction formatDollars(value: number): string {\n\treturn value < 0 ? `-$${Math.abs(value).toFixed(3)}` : `$${value.toFixed(3)}`;\n}\n\nfunction formatCacheWarmingEconomics(decision: CacheWarmingDecision): string {\n\tif (!decision.economicsAvailable) return \"cache economics unavailable\";\n\tconst probability = Math.round(decision.continuationProbability * 100);\n\tconst probabilityText =\n\t\tdecision.phase === \"streaming\"\n\t\t\t? `${probability}% continuation probability while agent is running`\n\t\t\t: `${probability}% continuation probability`;\n\tconst comparison = decision.action === \"warm\" ? \">=\" : \"<\";\n\treturn `${probabilityText}, expected savings ${formatDollars(decision.expectedSavings)} ${comparison} $${CACHE_WARMING_MINIMUM_EXPECTED_SAVINGS.toFixed(3)}`;\n}\n\nfunction formatCacheWarmingDecisionTime(nextWarmAt: number | undefined, now: number): string {\n\tif (nextWarmAt === undefined || nextWarmAt <= now) return \"Decision now\";\n\tlet remainingSeconds = Math.ceil((nextWarmAt - now) / 1000);\n\tconst hours = Math.floor(remainingSeconds / 3600);\n\tremainingSeconds %= 3600;\n\tconst minutes = Math.floor(remainingSeconds / 60);\n\tconst seconds = remainingSeconds % 60;\n\tconst parts: string[] = [];\n\tif (hours > 0) parts.push(`${hours}h`);\n\tif (minutes > 0) parts.push(`${minutes}m`);\n\tif (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);\n\treturn `Decision in ${parts.join(\" \")}`;\n}\n\n/** One-line status for `/session`. */\nexport function formatCacheWarmingStatus(status: CacheWarmingStatus, now = Date.now()): string {\n\tconst decision = status.decision;\n\t// A decision is attached once pi (or an extension) acted on it; \"inactive\"\n\t// without one never got that far.\n\tif (!decision || (status.state === \"inactive\" && !decision.economicsAvailable && !status.extensionOverride)) {\n\t\treturn `Inactive (${status.reason ?? \"unknown reason\"})`;\n\t}\n\tconst details = status.extensionOverride\n\t\t? `extension override, ${formatCacheWarmingEconomics(decision)}`\n\t\t: `${formatCacheWarmingEconomics(decision)} -> ${decision.action}`;\n\tif (status.state === \"inactive\") return `Stopped (${details})`;\n\tif (status.state === \"refreshing\") return `Warming cache (${details})`;\n\treturn `${formatCacheWarmingDecisionTime(status.nextWarmAt, now)} (${details})`;\n}\n\n/** One-line transcript text for persisted cache-warming usage. */\nexport function formatCacheWarmingUsage(entry: UsageEntry): string {\n\tconst note = entry.note ? ` (${entry.note})` : \"\";\n\tconst cost = entry.usage.cost.total.toFixed(6).replace(/(\\.\\d{3}\\d*?)0+$/, \"$1\");\n\treturn `Cache warmed${note}: $${cost}`;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-summarization.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/branch-summarization.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAElF,OAAO,EAIN,KAAK,cAAc,EAEnB,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IACjC,mEAAmE;IACnE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,gDAAgD;IAChD,OAAO,EAAE,cAAc,CAAC;IACxB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACpC,mDAAmD;IACnD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,4BAA4B;IAC5C,qCAAqC;IACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wFAAwF;IACxF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,cAAc,CAAC;CAC3B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,sBAAsB,EAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,MAAM,GACd,oBAAoB,CAkCtB;AAoCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,WAAW,GAAE,MAAU,GAAG,iBAAiB,CAoDxG;AAoDD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CA0F9B","sourcesContent":["/**\n * Branch summarization for tree navigation.\n *\n * When navigating to a different point in the session tree, this generates\n * a summary of the branch being left so context isn't lost.\n */\n\nimport type { AgentMessage, StreamFn } from \"@earendil-works/pi-agent-core\";\nimport type { RetryCallbacks, RetryPolicy } from \"@earendil-works/pi-ai\";\nimport type { Model, Usage } from \"@earendil-works/pi-ai/compat\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.ts\";\nimport type { ReadonlySessionManager, SessionEntry } from \"../session-manager.ts\";\nimport { estimateTokens, summarizeMessages } from \"./compaction.ts\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n} from \"./utils.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BranchSummaryResult {\n\tsummary?: string;\n\tusage?: Usage;\n\treadFiles?: string[];\n\tmodifiedFiles?: string[];\n\taborted?: boolean;\n\terror?: string;\n}\n\n/** Details stored in BranchSummaryEntry.details for file tracking */\nexport interface BranchSummaryDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.ts\";\n\nexport interface BranchPreparation {\n\t/** Messages extracted for summarization, in chronological order */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from tool calls */\n\tfileOps: FileOperations;\n\t/** Total estimated tokens in messages */\n\ttotalTokens: number;\n}\n\nexport interface CollectEntriesResult {\n\t/** Entries to summarize, in chronological order */\n\tentries: SessionEntry[];\n\t/** Common ancestor between old and new position, if any */\n\tcommonAncestorId: string | null;\n}\n\nexport interface GenerateBranchSummaryOptions {\n\t/** Model to use for summarization */\n\tmodel: Model<any>;\n\t/** API key for the model */\n\tapiKey?: string;\n\t/** Request headers for the model */\n\theaders?: Record<string, string>;\n\t/** Provider-scoped environment values for the model */\n\tenv?: Record<string, string>;\n\t/** Abort signal for cancellation */\n\tsignal: AbortSignal;\n\t/** Optional custom instructions for summarization */\n\tcustomInstructions?: string;\n\t/** If true, customInstructions replaces the default prompt instead of being appended */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved when selecting branch history (default 16384) */\n\treserveTokens?: number;\n\t/** Optional session stream function. Used to preserve SDK request behavior without mutating agent state. */\n\tstreamFn?: StreamFn;\n\t/** Retry policy for transient summarization errors. Reuses coding-agent's `settings.retry`. */\n\tretry?: RetryPolicy;\n\t/** Optional callbacks for retry reporting (e.g. TUI retry indicators). */\n\tcallbacks?: RetryCallbacks;\n}\n\n// ============================================================================\n// Entry Collection\n// ============================================================================\n\n/**\n * Collect entries that should be summarized when navigating from one position to another.\n *\n * Walks from oldLeafId back to the common ancestor with targetId, collecting entries\n * along the way. Does NOT stop at compaction boundaries - those are included and their\n * summaries become context.\n *\n * @param session - Session manager (read-only access)\n * @param oldLeafId - Current position (where we're navigating from)\n * @param targetId - Target position (where we're navigating to)\n * @returns Entries to summarize and the common ancestor\n */\nexport function collectEntriesForBranchSummary(\n\tsession: ReadonlySessionManager,\n\toldLeafId: string | null,\n\ttargetId: string,\n): CollectEntriesResult {\n\t// If no old position, nothing to summarize\n\tif (!oldLeafId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\n\t// Find common ancestor (deepest node that's on both paths)\n\tconst oldPath = new Set(session.getBranch(oldLeafId).map((e) => e.id));\n\tconst targetPath = session.getBranch(targetId);\n\n\t// targetPath is root-first, so iterate backwards to find deepest common ancestor\n\tlet commonAncestorId: string | null = null;\n\tfor (let i = targetPath.length - 1; i >= 0; i--) {\n\t\tif (oldPath.has(targetPath[i].id)) {\n\t\t\tcommonAncestorId = targetPath[i].id;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Collect entries from old leaf back to common ancestor\n\tconst entries: SessionEntry[] = [];\n\tlet current: string | null = oldLeafId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = session.getEntry(current);\n\t\tif (!entry) break;\n\t\tentries.push(entry);\n\t\tcurrent = entry.parentId;\n\t}\n\n\t// Reverse to get chronological order\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\n\n// ============================================================================\n// Entry to Message Conversion\n// ============================================================================\n\n/**\n * Extract AgentMessage from a session entry.\n * Similar to getMessageFromEntry in compaction.ts but also handles compaction entries.\n */\nfunction getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\t// Skip tool results - context is in assistant's tool call\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message;\n\n\t\tcase \"custom_message\":\n\t\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\n\t\t// These don't contribute to conversation content\n\t\tcase \"thinking_level_change\":\n\t\tcase \"model_change\":\n\t\tcase \"custom\":\n\t\tcase \"label\":\n\t\tcase \"session_info\":\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Prepare entries for summarization with token budget.\n *\n * Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.\n * This ensures we keep the most recent context when the branch is too long.\n *\n * Also collects file operations from:\n * - Tool calls in assistant messages\n * - Existing branch_summary entries' details (for cumulative tracking)\n *\n * @param entries - Entries in chronological order\n * @param tokenBudget - Maximum tokens to include (0 = no limit)\n */\nexport function prepareBranchEntries(entries: SessionEntry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\n\t// First pass: collect file ops from ALL entries (even if they don't fit in token budget)\n\t// This ensures we capture cumulative file tracking from nested branch summaries\n\t// Only extract from pi-generated summaries (fromHook !== true), not extension-generated ones\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"branch_summary\" && !entry.fromHook && entry.details) {\n\t\t\tconst details = entry.details as BranchSummaryDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\t// Modified files go into both edited and written for proper deduplication\n\t\t\t\tfor (const f of details.modifiedFiles) {\n\t\t\t\t\tfileOps.edited.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: walk from newest to oldest, adding messages until token budget\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\n\t\t// Extract file ops from assistant messages (tool calls)\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\n\t\t// Check budget before adding\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\t// If this is a summary entry, try to fit it anyway as it's important context\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Stop - we've hit the budget\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\n// ============================================================================\n// Summary Generation\n// ============================================================================\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\nconst UPDATE_BRANCH_SUMMARY_PROMPT = `The messages above are NEW messages from the same abandoned branch.\n\nUpdate the existing branch summary in <previous-summary> tags with the new information. Keep the same sections:\n\n## Goal\n## Constraints & Preferences\n## Progress\n## Key Decisions\n## Next Steps\n\nBe concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Generate a summary of abandoned branch entries.\n *\n * @param entries - Session entries to summarize (chronological order)\n * @param options - Generation options\n */\nexport async function generateBranchSummary(\n\tentries: SessionEntry[],\n\toptions: GenerateBranchSummaryOptions,\n): Promise<BranchSummaryResult> {\n\tconst {\n\t\tmodel,\n\t\tapiKey,\n\t\theaders,\n\t\tenv,\n\t\tsignal,\n\t\tcustomInstructions,\n\t\treplaceInstructions,\n\t\treserveTokens = 16384,\n\t\tstreamFn,\n\t\tretry,\n\t\tcallbacks,\n\t} = options;\n\n\tconst { messages, fileOps } = prepareBranchEntries(entries);\n\n\tif (messages.length === 0) {\n\t\treturn { summary: \"No content to summarize\" };\n\t}\n\n\tlet instructions: string;\n\tlet updateInstructions = UPDATE_BRANCH_SUMMARY_PROMPT;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t\tupdateInstructions = `${UPDATE_BRANCH_SUMMARY_PROMPT}\\n\\nUse these replacement instructions while updating:\\n\\n${customInstructions}`;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t\tupdateInstructions = `${UPDATE_BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\n\tconst maxTokens = Math.min(\n\t\t4096,\n\t\tMath.floor(0.8 * reserveTokens),\n\t\tmodel.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,\n\t);\n\n\tlet summary: string;\n\tlet summaryUsage: Usage;\n\ttry {\n\t\tconst result = await summarizeMessages(\n\t\t\tmessages,\n\t\t\tmodel,\n\t\t\tmaxTokens,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tenv,\n\t\t\tsignal,\n\t\t\tundefined,\n\t\t\tstreamFn,\n\t\t\tinstructions,\n\t\t\tupdateInstructions,\n\t\t\tundefined,\n\t\t\tretry,\n\t\t\tcallbacks,\n\t\t);\n\t\tsummary = result.text;\n\t\tsummaryUsage = result.usage;\n\t} catch (error) {\n\t\tif (signal.aborted || (error instanceof Error && error.name === \"AbortError\")) {\n\t\t\treturn { aborted: true };\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message === \"Summarization attempted to call a tool\") {\n\t\t\treturn { error: \"Branch summarization attempted to call a tool\" };\n\t\t}\n\t\tif (message.startsWith(\"Summarization failed: \")) {\n\t\t\treturn { error: `Branch summarization failed: ${message.slice(\"Summarization failed: \".length)}` };\n\t\t}\n\t\treturn { error: message };\n\t}\n\n\tif (signal.aborted) {\n\t\treturn { aborted: true };\n\t}\n\t// Prepend preamble to provide context about the branch summary\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\n\t// Compute file lists and append to summary\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn {\n\t\tsummary: summary || \"No summary generated\",\n\t\tusage: summaryUsage,\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"branch-summarization.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/branch-summarization.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAElF,OAAO,EAIN,KAAK,cAAc,EAEnB,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IACjC,mEAAmE;IACnE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,gDAAgD;IAChD,OAAO,EAAE,cAAc,CAAC;IACxB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACpC,mDAAmD;IACnD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,4BAA4B;IAC5C,qCAAqC;IACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wFAAwF;IACxF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,cAAc,CAAC;CAC3B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,sBAAsB,EAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,MAAM,GACd,oBAAoB,CAkCtB;AAoCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,WAAW,GAAE,MAAU,GAAG,iBAAiB,CAoDxG;AAoDD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CA0F9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-summarization.js","sourceRoot":"","sources":["../../../src/core/compaction/branch-summarization.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Branch summarization for tree navigation.\n *\n * When navigating to a different point in the session tree, this generates\n * a summary of the branch being left so context isn't lost.\n */\n\nimport type { AgentMessage, StreamFn } from \"@earendil-works/pi-agent-core\";\nimport type { RetryCallbacks, RetryPolicy } from \"@earendil-works/pi-ai\";\nimport type { Model, Usage } from \"@earendil-works/pi-ai/compat\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.ts\";\nimport type { ReadonlySessionManager, SessionEntry } from \"../session-manager.ts\";\nimport { estimateTokens, summarizeMessages } from \"./compaction.ts\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n} from \"./utils.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BranchSummaryResult {\n\tsummary?: string;\n\tusage?: Usage;\n\treadFiles?: string[];\n\tmodifiedFiles?: string[];\n\taborted?: boolean;\n\terror?: string;\n}\n\n/** Details stored in BranchSummaryEntry.details for file tracking */\nexport interface BranchSummaryDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.ts\";\n\nexport interface BranchPreparation {\n\t/** Messages extracted for summarization, in chronological order */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from tool calls */\n\tfileOps: FileOperations;\n\t/** Total estimated tokens in messages */\n\ttotalTokens: number;\n}\n\nexport interface CollectEntriesResult {\n\t/** Entries to summarize, in chronological order */\n\tentries: SessionEntry[];\n\t/** Common ancestor between old and new position, if any */\n\tcommonAncestorId: string | null;\n}\n\nexport interface GenerateBranchSummaryOptions {\n\t/** Model to use for summarization */\n\tmodel: Model<any>;\n\t/** API key for the model */\n\tapiKey?: string;\n\t/** Request headers for the model */\n\theaders?: Record<string, string>;\n\t/** Provider-scoped environment values for the model */\n\tenv?: Record<string, string>;\n\t/** Abort signal for cancellation */\n\tsignal: AbortSignal;\n\t/** Optional custom instructions for summarization */\n\tcustomInstructions?: string;\n\t/** If true, customInstructions replaces the default prompt instead of being appended */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved when selecting branch history (default 16384) */\n\treserveTokens?: number;\n\t/** Optional session stream function. Used to preserve SDK request behavior without mutating agent state. */\n\tstreamFn?: StreamFn;\n\t/** Retry policy for transient summarization errors. Reuses coding-agent's `settings.retry`. */\n\tretry?: RetryPolicy;\n\t/** Optional callbacks for retry reporting (e.g. TUI retry indicators). */\n\tcallbacks?: RetryCallbacks;\n}\n\n// ============================================================================\n// Entry Collection\n// ============================================================================\n\n/**\n * Collect entries that should be summarized when navigating from one position to another.\n *\n * Walks from oldLeafId back to the common ancestor with targetId, collecting entries\n * along the way. Does NOT stop at compaction boundaries - those are included and their\n * summaries become context.\n *\n * @param session - Session manager (read-only access)\n * @param oldLeafId - Current position (where we're navigating from)\n * @param targetId - Target position (where we're navigating to)\n * @returns Entries to summarize and the common ancestor\n */\nexport function collectEntriesForBranchSummary(\n\tsession: ReadonlySessionManager,\n\toldLeafId: string | null,\n\ttargetId: string,\n): CollectEntriesResult {\n\t// If no old position, nothing to summarize\n\tif (!oldLeafId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\n\t// Find common ancestor (deepest node that's on both paths)\n\tconst oldPath = new Set(session.getBranch(oldLeafId).map((e) => e.id));\n\tconst targetPath = session.getBranch(targetId);\n\n\t// targetPath is root-first, so iterate backwards to find deepest common ancestor\n\tlet commonAncestorId: string | null = null;\n\tfor (let i = targetPath.length - 1; i >= 0; i--) {\n\t\tif (oldPath.has(targetPath[i].id)) {\n\t\t\tcommonAncestorId = targetPath[i].id;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Collect entries from old leaf back to common ancestor\n\tconst entries: SessionEntry[] = [];\n\tlet current: string | null = oldLeafId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = session.getEntry(current);\n\t\tif (!entry) break;\n\t\tentries.push(entry);\n\t\tcurrent = entry.parentId;\n\t}\n\n\t// Reverse to get chronological order\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\n\n// ============================================================================\n// Entry to Message Conversion\n// ============================================================================\n\n/**\n * Extract AgentMessage from a session entry.\n * Similar to getMessageFromEntry in compaction.ts but also handles compaction entries.\n */\nfunction getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\t// Skip tool results - context is in assistant's tool call\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message;\n\n\t\tcase \"custom_message\":\n\t\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\n\t\t// These don't contribute to conversation content\n\t\tcase \"thinking_level_change\":\n\t\tcase \"model_change\":\n\t\tcase \"custom\":\n\t\tcase \"label\":\n\t\tcase \"session_info\":\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Prepare entries for summarization with token budget.\n *\n * Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.\n * This ensures we keep the most recent context when the branch is too long.\n *\n * Also collects file operations from:\n * - Tool calls in assistant messages\n * - Existing branch_summary entries' details (for cumulative tracking)\n *\n * @param entries - Entries in chronological order\n * @param tokenBudget - Maximum tokens to include (0 = no limit)\n */\nexport function prepareBranchEntries(entries: SessionEntry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\n\t// First pass: collect file ops from ALL entries (even if they don't fit in token budget)\n\t// This ensures we capture cumulative file tracking from nested branch summaries\n\t// Only extract from pi-generated summaries (fromHook !== true), not extension-generated ones\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"branch_summary\" && !entry.fromHook && entry.details) {\n\t\t\tconst details = entry.details as BranchSummaryDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\t// Modified files go into both edited and written for proper deduplication\n\t\t\t\tfor (const f of details.modifiedFiles) {\n\t\t\t\t\tfileOps.edited.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: walk from newest to oldest, adding messages until token budget\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\n\t\t// Extract file ops from assistant messages (tool calls)\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\n\t\t// Check budget before adding\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\t// If this is a summary entry, try to fit it anyway as it's important context\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Stop - we've hit the budget\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\n// ============================================================================\n// Summary Generation\n// ============================================================================\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\nconst UPDATE_BRANCH_SUMMARY_PROMPT = `The messages above are NEW messages from the same abandoned branch.\n\nUpdate the existing branch summary in <previous-summary> tags with the new information. Keep the same sections:\n\n## Goal\n## Constraints & Preferences\n## Progress\n## Key Decisions\n## Next Steps\n\nBe concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Generate a summary of abandoned branch entries.\n *\n * @param entries - Session entries to summarize (chronological order)\n * @param options - Generation options\n */\nexport async function generateBranchSummary(\n\tentries: SessionEntry[],\n\toptions: GenerateBranchSummaryOptions,\n): Promise<BranchSummaryResult> {\n\tconst {\n\t\tmodel,\n\t\tapiKey,\n\t\theaders,\n\t\tenv,\n\t\tsignal,\n\t\tcustomInstructions,\n\t\treplaceInstructions,\n\t\treserveTokens = 16384,\n\t\tstreamFn,\n\t\tretry,\n\t\tcallbacks,\n\t} = options;\n\n\tconst { messages, fileOps } = prepareBranchEntries(entries);\n\n\tif (messages.length === 0) {\n\t\treturn { summary: \"No content to summarize\" };\n\t}\n\n\tlet instructions: string;\n\tlet updateInstructions = UPDATE_BRANCH_SUMMARY_PROMPT;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t\tupdateInstructions = `${UPDATE_BRANCH_SUMMARY_PROMPT}\\n\\nUse these replacement instructions while updating:\\n\\n${customInstructions}`;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t\tupdateInstructions = `${UPDATE_BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\n\tconst maxTokens = Math.min(\n\t\t4096,\n\t\tMath.floor(0.8 * reserveTokens),\n\t\tmodel.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,\n\t);\n\n\tlet summary: string;\n\tlet summaryUsage: Usage;\n\ttry {\n\t\tconst result = await summarizeMessages(\n\t\t\tmessages,\n\t\t\tmodel,\n\t\t\tmaxTokens,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tenv,\n\t\t\tsignal,\n\t\t\tundefined,\n\t\t\tstreamFn,\n\t\t\tinstructions,\n\t\t\tupdateInstructions,\n\t\t\tundefined,\n\t\t\tretry,\n\t\t\tcallbacks,\n\t\t);\n\t\tsummary = result.text;\n\t\tsummaryUsage = result.usage;\n\t} catch (error) {\n\t\tif (signal.aborted || (error instanceof Error && error.name === \"AbortError\")) {\n\t\t\treturn { aborted: true };\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message === \"Summarization attempted to call a tool\") {\n\t\t\treturn { error: \"Branch summarization attempted to call a tool\" };\n\t\t}\n\t\tif (message.startsWith(\"Summarization failed: \")) {\n\t\t\treturn { error: `Branch summarization failed: ${message.slice(\"Summarization failed: \".length)}` };\n\t\t}\n\t\treturn { error: message };\n\t}\n\n\tif (signal.aborted) {\n\t\treturn { aborted: true };\n\t}\n\t// Prepend preamble to provide context about the branch summary\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\n\t// Compute file lists and append to summary\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn {\n\t\tsummary: summary || \"No summary generated\",\n\t\tusage: summaryUsage,\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"branch-summarization.js","sourceRoot":"","sources":["../../../src/core/compaction/branch-summarization.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Branch summarization for tree navigation.\n *\n * When navigating to a different point in the session tree, this generates\n * a summary of the branch being left so context isn't lost.\n */\n\nimport type { AgentMessage, StreamFn } from \"@earendil-works/pi-agent-core\";\nimport type { RetryCallbacks, RetryPolicy } from \"@earendil-works/pi-ai\";\nimport type { Model, Usage } from \"@earendil-works/pi-ai/compat\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from \"../messages.ts\";\nimport type { ReadonlySessionManager, SessionEntry } from \"../session-manager.ts\";\nimport { estimateTokens, summarizeMessages } from \"./compaction.ts\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n} from \"./utils.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BranchSummaryResult {\n\tsummary?: string;\n\tusage?: Usage;\n\treadFiles?: string[];\n\tmodifiedFiles?: string[];\n\taborted?: boolean;\n\terror?: string;\n}\n\n/** Details stored in BranchSummaryEntry.details for file tracking */\nexport interface BranchSummaryDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport type { FileOperations } from \"./utils.ts\";\n\nexport interface BranchPreparation {\n\t/** Messages extracted for summarization, in chronological order */\n\tmessages: AgentMessage[];\n\t/** File operations extracted from tool calls */\n\tfileOps: FileOperations;\n\t/** Total estimated tokens in messages */\n\ttotalTokens: number;\n}\n\nexport interface CollectEntriesResult {\n\t/** Entries to summarize, in chronological order */\n\tentries: SessionEntry[];\n\t/** Common ancestor between old and new position, if any */\n\tcommonAncestorId: string | null;\n}\n\nexport interface GenerateBranchSummaryOptions {\n\t/** Model to use for summarization */\n\tmodel: Model<any>;\n\t/** API key for the model */\n\tapiKey?: string;\n\t/** Request headers for the model */\n\theaders?: Record<string, string>;\n\t/** Provider-scoped environment values for the model */\n\tenv?: Record<string, string>;\n\t/** Abort signal for cancellation */\n\tsignal: AbortSignal;\n\t/** Optional custom instructions for summarization */\n\tcustomInstructions?: string;\n\t/** If true, customInstructions replaces the default prompt instead of being appended */\n\treplaceInstructions?: boolean;\n\t/** Tokens reserved when selecting branch history (default 16384) */\n\treserveTokens?: number;\n\t/** Optional session stream function. Used to preserve SDK request behavior without mutating agent state. */\n\tstreamFn?: StreamFn;\n\t/** Retry policy for transient summarization errors. Reuses coding-agent's `settings.retry`. */\n\tretry?: RetryPolicy;\n\t/** Optional callbacks for retry reporting (e.g. TUI retry indicators). */\n\tcallbacks?: RetryCallbacks;\n}\n\n// ============================================================================\n// Entry Collection\n// ============================================================================\n\n/**\n * Collect entries that should be summarized when navigating from one position to another.\n *\n * Walks from oldLeafId back to the common ancestor with targetId, collecting entries\n * along the way. Does NOT stop at compaction boundaries - those are included and their\n * summaries become context.\n *\n * @param session - Session manager (read-only access)\n * @param oldLeafId - Current position (where we're navigating from)\n * @param targetId - Target position (where we're navigating to)\n * @returns Entries to summarize and the common ancestor\n */\nexport function collectEntriesForBranchSummary(\n\tsession: ReadonlySessionManager,\n\toldLeafId: string | null,\n\ttargetId: string,\n): CollectEntriesResult {\n\t// If no old position, nothing to summarize\n\tif (!oldLeafId) {\n\t\treturn { entries: [], commonAncestorId: null };\n\t}\n\n\t// Find common ancestor (deepest node that's on both paths)\n\tconst oldPath = new Set(session.getBranch(oldLeafId).map((e) => e.id));\n\tconst targetPath = session.getBranch(targetId);\n\n\t// targetPath is root-first, so iterate backwards to find deepest common ancestor\n\tlet commonAncestorId: string | null = null;\n\tfor (let i = targetPath.length - 1; i >= 0; i--) {\n\t\tif (oldPath.has(targetPath[i].id)) {\n\t\t\tcommonAncestorId = targetPath[i].id;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Collect entries from old leaf back to common ancestor\n\tconst entries: SessionEntry[] = [];\n\tlet current: string | null = oldLeafId;\n\n\twhile (current && current !== commonAncestorId) {\n\t\tconst entry = session.getEntry(current);\n\t\tif (!entry) break;\n\t\tentries.push(entry);\n\t\tcurrent = entry.parentId;\n\t}\n\n\t// Reverse to get chronological order\n\tentries.reverse();\n\n\treturn { entries, commonAncestorId };\n}\n\n// ============================================================================\n// Entry to Message Conversion\n// ============================================================================\n\n/**\n * Extract AgentMessage from a session entry.\n * Similar to getMessageFromEntry in compaction.ts but also handles compaction entries.\n */\nfunction getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\t// Skip tool results - context is in assistant's tool call\n\t\t\tif (entry.message.role === \"toolResult\") return undefined;\n\t\t\treturn entry.message;\n\n\t\tcase \"custom_message\":\n\t\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\n\t\tcase \"branch_summary\":\n\t\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\n\t\tcase \"compaction\":\n\t\t\treturn createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);\n\n\t\t// These don't contribute to conversation content\n\t\tcase \"thinking_level_change\":\n\t\tcase \"model_change\":\n\t\tcase \"custom\":\n\t\tcase \"label\":\n\t\tcase \"session_info\":\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Prepare entries for summarization with token budget.\n *\n * Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.\n * This ensures we keep the most recent context when the branch is too long.\n *\n * Also collects file operations from:\n * - Tool calls in assistant messages\n * - Existing branch_summary entries' details (for cumulative tracking)\n *\n * @param entries - Entries in chronological order\n * @param tokenBudget - Maximum tokens to include (0 = no limit)\n */\nexport function prepareBranchEntries(entries: SessionEntry[], tokenBudget: number = 0): BranchPreparation {\n\tconst messages: AgentMessage[] = [];\n\tconst fileOps = createFileOps();\n\tlet totalTokens = 0;\n\n\t// First pass: collect file ops from ALL entries (even if they don't fit in token budget)\n\t// This ensures we capture cumulative file tracking from nested branch summaries\n\t// Only extract from pi-generated summaries (fromHook !== true), not extension-generated ones\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"branch_summary\" && !entry.fromHook && entry.details) {\n\t\t\tconst details = entry.details as BranchSummaryDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\t// Modified files go into both edited and written for proper deduplication\n\t\t\t\tfor (const f of details.modifiedFiles) {\n\t\t\t\t\tfileOps.edited.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second pass: walk from newest to oldest, adding messages until token budget\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tconst message = getMessageFromEntry(entry);\n\t\tif (!message) continue;\n\n\t\t// Extract file ops from assistant messages (tool calls)\n\t\textractFileOpsFromMessage(message, fileOps);\n\n\t\tconst tokens = estimateTokens(message);\n\n\t\t// Check budget before adding\n\t\tif (tokenBudget > 0 && totalTokens + tokens > tokenBudget) {\n\t\t\t// If this is a summary entry, try to fit it anyway as it's important context\n\t\t\tif (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\t\tif (totalTokens < tokenBudget * 0.9) {\n\t\t\t\t\tmessages.unshift(message);\n\t\t\t\t\ttotalTokens += tokens;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Stop - we've hit the budget\n\t\t\tbreak;\n\t\t}\n\n\t\tmessages.unshift(message);\n\t\ttotalTokens += tokens;\n\t}\n\n\treturn { messages, fileOps, totalTokens };\n}\n\n// ============================================================================\n// Summary Generation\n// ============================================================================\n\nconst BRANCH_SUMMARY_PREAMBLE = `The user explored a different conversation branch before returning here.\nSummary of that exploration:\n\n`;\n\nconst BRANCH_SUMMARY_PROMPT = `Create a structured summary of this conversation branch for context when returning later.\n\nUse this EXACT format:\n\n## Goal\n[What was the user trying to accomplish in this branch?]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Work that was started but not finished]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [What should happen next to continue this work]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\nconst UPDATE_BRANCH_SUMMARY_PROMPT = `The messages above are NEW messages from the same abandoned branch.\n\nUpdate the existing branch summary in <previous-summary> tags with the new information. Keep the same sections:\n\n## Goal\n## Constraints & Preferences\n## Progress\n## Key Decisions\n## Next Steps\n\nBe concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Generate a summary of abandoned branch entries.\n *\n * @param entries - Session entries to summarize (chronological order)\n * @param options - Generation options\n */\nexport async function generateBranchSummary(\n\tentries: SessionEntry[],\n\toptions: GenerateBranchSummaryOptions,\n): Promise<BranchSummaryResult> {\n\tconst {\n\t\tmodel,\n\t\tapiKey,\n\t\theaders,\n\t\tenv,\n\t\tsignal,\n\t\tcustomInstructions,\n\t\treplaceInstructions,\n\t\treserveTokens = 16384,\n\t\tstreamFn,\n\t\tretry,\n\t\tcallbacks,\n\t} = options;\n\n\tconst { messages, fileOps } = prepareBranchEntries(entries);\n\n\tif (messages.length === 0) {\n\t\treturn { summary: \"No content to summarize\" };\n\t}\n\n\tlet instructions: string;\n\tlet updateInstructions = UPDATE_BRANCH_SUMMARY_PROMPT;\n\tif (replaceInstructions && customInstructions) {\n\t\tinstructions = customInstructions;\n\t\tupdateInstructions = `${UPDATE_BRANCH_SUMMARY_PROMPT}\\n\\nUse these replacement instructions while updating:\\n\\n${customInstructions}`;\n\t} else if (customInstructions) {\n\t\tinstructions = `${BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t\tupdateInstructions = `${UPDATE_BRANCH_SUMMARY_PROMPT}\\n\\nAdditional focus: ${customInstructions}`;\n\t} else {\n\t\tinstructions = BRANCH_SUMMARY_PROMPT;\n\t}\n\n\tconst maxTokens = Math.min(\n\t\t4096,\n\t\tMath.floor(0.8 * reserveTokens),\n\t\tmodel.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,\n\t);\n\n\tlet summary: string;\n\tlet summaryUsage: Usage;\n\ttry {\n\t\tconst result = await summarizeMessages(\n\t\t\tmessages,\n\t\t\tmodel,\n\t\t\tmaxTokens,\n\t\t\tapiKey,\n\t\t\theaders,\n\t\t\tenv,\n\t\t\tsignal,\n\t\t\tundefined,\n\t\t\tstreamFn,\n\t\t\tinstructions,\n\t\t\tupdateInstructions,\n\t\t\tundefined,\n\t\t\tretry,\n\t\t\tcallbacks,\n\t\t);\n\t\tsummary = result.text;\n\t\tsummaryUsage = result.usage;\n\t} catch (error) {\n\t\tif (signal.aborted || (error instanceof Error && error.name === \"AbortError\")) {\n\t\t\treturn { aborted: true };\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message === \"Summarization attempted to call a tool\") {\n\t\t\treturn { error: \"Branch summarization attempted to call a tool\" };\n\t\t}\n\t\tif (message.startsWith(\"Summarization failed: \")) {\n\t\t\treturn { error: `Branch summarization failed: ${message.slice(\"Summarization failed: \".length)}` };\n\t\t}\n\t\treturn { error: message };\n\t}\n\n\tif (signal.aborted) {\n\t\treturn { aborted: true };\n\t}\n\t// Prepend preamble to provide context about the branch summary\n\tsummary = BRANCH_SUMMARY_PREAMBLE + summary;\n\n\t// Compute file lists and append to summary\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tsummary += formatFileOperations(readFiles, modifiedFiles);\n\n\treturn {\n\t\tsummary: summary || \"No summary generated\",\n\t\tusage: summaryUsage,\n\t\treadFiles,\n\t\tmodifiedFiles,\n\t};\n}\n"]}
|
|
@@ -13,7 +13,12 @@ import { type FileOperations } from "./utils.ts";
|
|
|
13
13
|
export interface CompactionDetails {
|
|
14
14
|
readFiles: string[];
|
|
15
15
|
modifiedFiles: string[];
|
|
16
|
+
cachePath?: {
|
|
17
|
+
mode: "exact_prefix" | "chunked";
|
|
18
|
+
reason: string;
|
|
19
|
+
};
|
|
16
20
|
}
|
|
21
|
+
export type CompactionCacheRequestOptions = Pick<SimpleStreamOptions, "cacheRetention" | "sessionId" | "transport" | "thinkingBudgets" | "maxRetryDelayMs">;
|
|
17
22
|
/** Result from compact() - SessionManager adds uuid/parentUuid when saving */
|
|
18
23
|
export interface CompactionResult<T = unknown> {
|
|
19
24
|
summary: string;
|
|
@@ -98,13 +103,13 @@ export declare function findCutPoint(entries: SessionEntry[], startIndex: number
|
|
|
98
103
|
*/
|
|
99
104
|
export declare function getSummarizationFailure(response: AssistantMessage, label: string): string | undefined;
|
|
100
105
|
/**
|
|
101
|
-
* Shared choke point for
|
|
106
|
+
* Shared choke point for compaction and branch-summary summarization calls. Wraps the
|
|
102
107
|
* single LLM call in {@link retryAssistantCall} so transient stream drops (e.g.
|
|
103
108
|
* `terminated`, socket close) honor the configured retry policy instead of failing
|
|
104
109
|
* the whole compaction on the first attempt. Deterministic errors and aborts return
|
|
105
110
|
* immediately (see {@link retryAssistantCall}).
|
|
106
111
|
*/
|
|
107
|
-
export declare function completeSummarization(model: Model<any>, context: TranscriptContext, options: SimpleStreamOptions, streamFn?: StreamFn, retry?: RetryPolicy, callbacks?: RetryCallbacks): Promise<AssistantMessage>;
|
|
112
|
+
export declare function completeSummarization(model: Model<any>, context: TranscriptContext, options: SimpleStreamOptions, streamFn?: StreamFn, retry?: RetryPolicy, callbacks?: RetryCallbacks, preservePromptCache?: boolean): Promise<AssistantMessage>;
|
|
108
113
|
export declare function summarizeMessages(messages: AgentMessage[], model: Model<any>, maxTokens: number, apiKey: string | undefined, headers: Record<string, string> | undefined, env: Record<string, string> | undefined, signal: AbortSignal | undefined, thinkingLevel: ThinkingLevel | undefined, streamFn: StreamFn | undefined, initialPrompt: string, updatePrompt: string, previousSummary: string | undefined, retry?: RetryPolicy, callbacks?: RetryCallbacks, sessionId?: string): Promise<{
|
|
109
114
|
text: string;
|
|
110
115
|
usage: Usage;
|
|
@@ -122,6 +127,8 @@ export declare function generateSummaryWithUsage(currentMessages: AgentMessage[]
|
|
|
122
127
|
export interface CompactionPreparation {
|
|
123
128
|
/** UUID of first entry to keep */
|
|
124
129
|
firstKeptEntryId: string;
|
|
130
|
+
/** Canonical model-visible session prefix before the current cut point. */
|
|
131
|
+
cachePrefixMessages: AgentMessage[];
|
|
125
132
|
/** Messages that will be summarized and discarded */
|
|
126
133
|
messagesToSummarize: AgentMessage[];
|
|
127
134
|
/** Messages that will be turned into turn prefix summary (if splitting) */
|
|
@@ -147,5 +154,5 @@ export declare function prepareCompaction(pathEntries: SessionEntry[], settings:
|
|
|
147
154
|
* @param preparation - Pre-calculated preparation from prepareCompaction()
|
|
148
155
|
* @param customInstructions - Optional custom focus for the summary
|
|
149
156
|
*/
|
|
150
|
-
export declare function compact(preparation: CompactionPreparation, model: Model<any>, apiKey: string | undefined, headers?: Record<string, string>, customInstructions?: string, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, env?: Record<string, string>, retry?: RetryPolicy, callbacks?: RetryCallbacks, sessionId?: string): Promise<CompactionResult>;
|
|
157
|
+
export declare function compact(preparation: CompactionPreparation, model: Model<any>, apiKey: string | undefined, headers?: Record<string, string>, customInstructions?: string, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, env?: Record<string, string>, retry?: RetryPolicy, callbacks?: RetryCallbacks, sessionId?: string, cachePrefixContext?: TranscriptContext, cacheOptions?: CompactionCacheRequestOptions, cacheFallbackReason?: string): Promise<CompactionResult>;
|
|
151
158
|
//# sourceMappingURL=compaction.d.ts.map
|