@handy_wote/pi-coding-agent 0.81.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 +5107 -0
- package/README.md +694 -0
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +11 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/bun/restore-sandbox-env.d.ts +17 -0
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
- package/dist/bun/restore-sandbox-env.js +36 -0
- package/dist/bun/restore-sandbox-env.js.map +1 -0
- package/dist/cli/args.d.ts +57 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +381 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/config-selector.d.ts +16 -0
- package/dist/cli/config-selector.d.ts.map +1 -0
- package/dist/cli/config-selector.js +31 -0
- package/dist/cli/config-selector.js.map +1 -0
- package/dist/cli/file-processor.d.ts +15 -0
- package/dist/cli/file-processor.d.ts.map +1 -0
- package/dist/cli/file-processor.js +70 -0
- package/dist/cli/file-processor.js.map +1 -0
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/list-models.d.ts +9 -0
- package/dist/cli/list-models.d.ts.map +1 -0
- package/dist/cli/list-models.js +95 -0
- package/dist/cli/list-models.js.map +1 -0
- package/dist/cli/project-trust.d.ts +10 -0
- package/dist/cli/project-trust.d.ts.map +1 -0
- package/dist/cli/project-trust.js +48 -0
- package/dist/cli/project-trust.js.map +1 -0
- package/dist/cli/session-picker.d.ts +10 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +36 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/startup-ui.d.ts +20 -0
- package/dist/cli/startup-ui.d.ts.map +1 -0
- package/dist/cli/startup-ui.js +184 -0
- package/dist/cli/startup-ui.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +98 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +459 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts +119 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +306 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +85 -0
- package/dist/core/agent-session-services.d.ts.map +1 -0
- package/dist/core/agent-session-services.js +133 -0
- package/dist/core/agent-session-services.js.map +1 -0
- package/dist/core/agent-session.d.ts +642 -0
- package/dist/core/agent-session.d.ts.map +1 -0
- package/dist/core/agent-session.js +2684 -0
- package/dist/core/agent-session.js.map +1 -0
- package/dist/core/auth-storage.d.ts +56 -0
- package/dist/core/auth-storage.d.ts.map +1 -0
- package/dist/core/auth-storage.js +232 -0
- package/dist/core/auth-storage.js.map +1 -0
- package/dist/core/bash-executor.d.ts +32 -0
- package/dist/core/bash-executor.d.ts.map +1 -0
- package/dist/core/bash-executor.js +111 -0
- package/dist/core/bash-executor.js.map +1 -0
- package/dist/core/cache-stats.d.ts +49 -0
- package/dist/core/cache-stats.d.ts.map +1 -0
- package/dist/core/cache-stats.js +101 -0
- package/dist/core/cache-stats.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts +98 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +246 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +137 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +637 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +38 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +142 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/defaults.d.ts +3 -0
- package/dist/core/defaults.d.ts.map +1 -0
- package/dist/core/defaults.js +2 -0
- package/dist/core/defaults.js.map +1 -0
- package/dist/core/diagnostics.d.ts +15 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +75 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/experimental.d.ts +2 -0
- package/dist/core/experimental.d.ts.map +1 -0
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -0
- package/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
- package/dist/core/export-html/ansi-to-html.js +249 -0
- package/dist/core/export-html/ansi-to-html.js.map +1 -0
- package/dist/core/export-html/index.d.ts +37 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +226 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +1066 -0
- package/dist/core/export-html/template.html +55 -0
- package/dist/core/export-html/template.js +1864 -0
- package/dist/core/export-html/tool-renderer.d.ts +34 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
- package/dist/core/export-html/tool-renderer.js +108 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +78 -0
- package/dist/core/extensions/index.d.ts +12 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/index.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +23 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +586 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +171 -0
- package/dist/core/extensions/runner.d.ts.map +1 -0
- package/dist/core/extensions/runner.js +953 -0
- package/dist/core/extensions/runner.js.map +1 -0
- package/dist/core/extensions/types.d.ts +1259 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/extensions/types.js +45 -0
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts +20 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -0
- package/dist/core/extensions/wrapper.js +41 -0
- package/dist/core/extensions/wrapper.js.map +1 -0
- package/dist/core/footer-data-provider.d.ts +54 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -0
- package/dist/core/footer-data-provider.js +338 -0
- package/dist/core/footer-data-provider.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +22 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +91 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/keybindings.d.ts +390 -0
- package/dist/core/keybindings.d.ts.map +1 -0
- package/dist/core/keybindings.js +296 -0
- package/dist/core/keybindings.js.map +1 -0
- package/dist/core/messages.d.ts +77 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +123 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/model-metadata.d.ts +30 -0
- package/dist/core/model-metadata.d.ts.map +1 -0
- package/dist/core/model-metadata.js +139 -0
- package/dist/core/model-metadata.js.map +1 -0
- package/dist/core/model-registry.d.ts +48 -0
- package/dist/core/model-registry.d.ts.map +1 -0
- package/dist/core/model-registry.js +105 -0
- package/dist/core/model-registry.js.map +1 -0
- package/dist/core/model-resolver.d.ts +121 -0
- package/dist/core/model-resolver.d.ts.map +1 -0
- package/dist/core/model-resolver.js +534 -0
- package/dist/core/model-resolver.js.map +1 -0
- package/dist/core/model-runtime.d.ts +84 -0
- package/dist/core/model-runtime.d.ts.map +1 -0
- package/dist/core/model-runtime.js +453 -0
- package/dist/core/model-runtime.js.map +1 -0
- package/dist/core/models-store.d.ts +17 -0
- package/dist/core/models-store.d.ts.map +1 -0
- package/dist/core/models-store.js +45 -0
- package/dist/core/models-store.js.map +1 -0
- package/dist/core/output-guard.d.ts +7 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +89 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-manager.d.ts +210 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +2135 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/profile-runtime.d.ts +4 -0
- package/dist/core/profile-runtime.d.ts.map +1 -0
- package/dist/core/profile-runtime.js +57 -0
- package/dist/core/profile-runtime.js.map +1 -0
- package/dist/core/profiles-store.d.ts +17 -0
- package/dist/core/profiles-store.d.ts.map +1 -0
- package/dist/core/profiles-store.js +100 -0
- package/dist/core/profiles-store.js.map +1 -0
- package/dist/core/profiles-types.d.ts +31 -0
- package/dist/core/profiles-types.d.ts.map +1 -0
- package/dist/core/profiles-types.js +4 -0
- package/dist/core/profiles-types.js.map +1 -0
- package/dist/core/project-trust.d.ts +15 -0
- package/dist/core/project-trust.d.ts.map +1 -0
- package/dist/core/project-trust.js +59 -0
- package/dist/core/project-trust.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +54 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/prompt-templates.js +236 -0
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/provider-attribution.d.ts +4 -0
- package/dist/core/provider-attribution.d.ts.map +1 -0
- package/dist/core/provider-attribution.js +72 -0
- package/dist/core/provider-attribution.js.map +1 -0
- package/dist/core/radius.d.ts +2 -0
- package/dist/core/radius.d.ts.map +1 -0
- package/dist/core/radius.js +2 -0
- package/dist/core/radius.js.map +1 -0
- package/dist/core/resource-loader.d.ts +206 -0
- package/dist/core/resource-loader.d.ts.map +1 -0
- package/dist/core/resource-loader.js +817 -0
- package/dist/core/resource-loader.js.map +1 -0
- package/dist/core/sdk.d.ts +106 -0
- package/dist/core/sdk.d.ts.map +1 -0
- package/dist/core/sdk.js +269 -0
- package/dist/core/sdk.js.map +1 -0
- package/dist/core/session-cwd.d.ts +19 -0
- package/dist/core/session-cwd.d.ts.map +1 -0
- package/dist/core/session-cwd.js +38 -0
- package/dist/core/session-cwd.js.map +1 -0
- package/dist/core/session-manager.d.ts +356 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +1338 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/settings-manager.d.ts +297 -0
- package/dist/core/settings-manager.d.ts.map +1 -0
- package/dist/core/settings-manager.js +902 -0
- package/dist/core/settings-manager.js.map +1 -0
- package/dist/core/skills.d.ts +60 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +387 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/slash-commands.d.ts +15 -0
- package/dist/core/slash-commands.d.ts.map +1 -0
- package/dist/core/slash-commands.js +25 -0
- package/dist/core/slash-commands.js.map +1 -0
- package/dist/core/source-info.d.ts +18 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +19 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts +28 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +110 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/telemetry.d.ts +3 -0
- package/dist/core/telemetry.d.ts.map +1 -0
- package/dist/core/telemetry.js +9 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/timings.d.ts +10 -0
- package/dist/core/timings.d.ts.map +1 -0
- package/dist/core/timings.js +41 -0
- package/dist/core/timings.js.map +1 -0
- package/dist/core/tools/bash.d.ts +68 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +361 -0
- package/dist/core/tools/bash.js.map +1 -0
- package/dist/core/tools/edit-diff.d.ts +106 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +424 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +51 -0
- package/dist/core/tools/edit.d.ts.map +1 -0
- package/dist/core/tools/edit.js +284 -0
- package/dist/core/tools/edit.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +52 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +35 -0
- package/dist/core/tools/find.d.ts.map +1 -0
- package/dist/core/tools/find.js +305 -0
- package/dist/core/tools/find.js.map +1 -0
- package/dist/core/tools/grep.d.ts +37 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +304 -0
- package/dist/core/tools/grep.js.map +1 -0
- package/dist/core/tools/index.d.ts +40 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +112 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/ls.d.ts +37 -0
- package/dist/core/tools/ls.d.ts.map +1 -0
- package/dist/core/tools/ls.js +167 -0
- package/dist/core/tools/ls.js.map +1 -0
- package/dist/core/tools/output-accumulator.d.ts +52 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -0
- package/dist/core/tools/output-accumulator.js +184 -0
- package/dist/core/tools/output-accumulator.js.map +1 -0
- package/dist/core/tools/path-utils.d.ts +10 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -0
- package/dist/core/tools/path-utils.js +99 -0
- package/dist/core/tools/path-utils.js.map +1 -0
- package/dist/core/tools/read.d.ts +35 -0
- package/dist/core/tools/read.d.ts.map +1 -0
- package/dist/core/tools/read.js +276 -0
- package/dist/core/tools/read.js.map +1 -0
- package/dist/core/tools/render-utils.d.ts +24 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +65 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +34 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/truncate.d.ts +70 -0
- package/dist/core/tools/truncate.d.ts.map +1 -0
- package/dist/core/tools/truncate.js +215 -0
- package/dist/core/tools/truncate.js.map +1 -0
- package/dist/core/tools/write.d.ts +26 -0
- package/dist/core/tools/write.d.ts.map +1 -0
- package/dist/core/tools/write.js +197 -0
- package/dist/core/tools/write.js.map +1 -0
- package/dist/core/trust-manager.d.ts +36 -0
- package/dist/core/trust-manager.d.ts.map +1 -0
- package/dist/core/trust-manager.js +202 -0
- package/dist/core/trust-manager.js.map +1 -0
- package/dist/core/usage-totals.d.ts +19 -0
- package/dist/core/usage-totals.d.ts.map +1 -0
- package/dist/core/usage-totals.js +52 -0
- package/dist/core/usage-totals.js.map +1 -0
- package/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +3 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/llama/client.d.ts +61 -0
- package/dist/extensions/llama/client.d.ts.map +1 -0
- package/dist/extensions/llama/client.js +302 -0
- package/dist/extensions/llama/client.js.map +1 -0
- package/dist/extensions/llama/huggingface.d.ts +23 -0
- package/dist/extensions/llama/huggingface.d.ts.map +1 -0
- package/dist/extensions/llama/huggingface.js +141 -0
- package/dist/extensions/llama/huggingface.js.map +1 -0
- package/dist/extensions/llama/index.d.ts +3 -0
- package/dist/extensions/llama/index.d.ts.map +1 -0
- package/dist/extensions/llama/index.js +208 -0
- package/dist/extensions/llama/index.js.map +1 -0
- package/dist/extensions/llama/provider.d.ts +10 -0
- package/dist/extensions/llama/provider.d.ts.map +1 -0
- package/dist/extensions/llama/provider.js +102 -0
- package/dist/extensions/llama/provider.js.map +1 -0
- package/dist/extensions/llama/ui.d.ts +42 -0
- package/dist/extensions/llama/ui.d.ts.map +1 -0
- package/dist/extensions/llama/ui.js +416 -0
- package/dist/extensions/llama/ui.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +12 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +706 -0
- package/dist/main.js.map +1 -0
- package/dist/migrations.d.ts +33 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +281 -0
- package/dist/migrations.js.map +1 -0
- package/dist/modes/index.d.ts +9 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +8 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/interactive/assets/clankolas.png +0 -0
- package/dist/modes/interactive/components/armin.d.ts +34 -0
- package/dist/modes/interactive/components/armin.d.ts.map +1 -0
- package/dist/modes/interactive/components/armin.js +333 -0
- package/dist/modes/interactive/components/armin.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +22 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/assistant-message.js +141 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts +34 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/bash-execution.js +175 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +54 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +44 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts +102 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/config-selector.js +761 -0
- package/dist/modes/interactive/components/config-selector.js.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.js +33 -0
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts +21 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-editor.js +70 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -0
- package/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-entry.js +52 -0
- package/dist/modes/interactive/components/custom-entry.js.map +1 -0
- package/dist/modes/interactive/components/custom-message.d.ts +20 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-message.js +79 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
- package/dist/modes/interactive/components/daxnuts.js +140 -0
- package/dist/modes/interactive/components/daxnuts.js.map +1 -0
- package/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/modes/interactive/components/diff.d.ts.map +1 -0
- package/dist/modes/interactive/components/diff.js +133 -0
- package/dist/modes/interactive/components/diff.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.js +21 -0
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
- package/dist/modes/interactive/components/earendil-announcement.js +40 -0
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
- package/dist/modes/interactive/components/entity-list-theme.d.ts +3 -0
- package/dist/modes/interactive/components/entity-list-theme.d.ts.map +1 -0
- package/dist/modes/interactive/components/entity-list-theme.js +17 -0
- package/dist/modes/interactive/components/entity-list-theme.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +22 -0
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-editor.js +128 -0
- package/dist/modes/interactive/components/extension-editor.js.map +1 -0
- package/dist/modes/interactive/components/extension-input.d.ts +23 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-input.js +61 -0
- package/dist/modes/interactive/components/extension-input.js.map +1 -0
- package/dist/modes/interactive/components/extension-selector.d.ts +22 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-selector.js +61 -0
- package/dist/modes/interactive/components/extension-selector.js.map +1 -0
- package/dist/modes/interactive/components/first-time-setup.d.ts +24 -0
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
- package/dist/modes/interactive/components/first-time-setup.js +92 -0
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +32 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -0
- package/dist/modes/interactive/components/footer.js +222 -0
- package/dist/modes/interactive/components/footer.js.map +1 -0
- package/dist/modes/interactive/components/index.d.ts +32 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -0
- package/dist/modes/interactive/components/index.js +33 -0
- package/dist/modes/interactive/components/index.js.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.js +36 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +50 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/model-selector.js +240 -0
- package/dist/modes/interactive/components/model-selector.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +36 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.js +222 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.js +155 -0
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts +95 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector.js +779 -0
- package/dist/modes/interactive/components/session-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +77 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-selector.js +593 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.js +39 -0
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
- package/dist/modes/interactive/components/status-indicator.js +60 -0
- package/dist/modes/interactive/components/status-indicator.js.map +1 -0
- package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/theme-selector.js +46 -0
- package/dist/modes/interactive/components/theme-selector.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.js +47 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +63 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution.js +317 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +94 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +1245 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/trust-selector.d.ts +21 -0
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/trust-selector.js +72 -0
- package/dist/modes/interactive/components/trust-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +16 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.js +53 -0
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +14 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message.js +42 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.js +33 -0
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +391 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-mode.js +4996 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -0
- package/dist/modes/interactive/model-search.d.ts +12 -0
- package/dist/modes/interactive/model-search.d.ts.map +1 -0
- package/dist/modes/interactive/model-search.js +15 -0
- package/dist/modes/interactive/model-search.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +87 -0
- package/dist/modes/interactive/theme/light.json +86 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +102 -0
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.json +340 -0
- package/dist/modes/interactive/theme/theme.d.ts +120 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme.js +1063 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -0
- package/dist/modes/print-mode.d.ts +28 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +132 -0
- package/dist/modes/print-mode.js.map +1 -0
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +246 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-client.js +488 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts +20 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-mode.js +639 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -0
- package/dist/modes/rpc/rpc-types.d.ts +457 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-types.js +8 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -0
- package/dist/package-manager-cli.d.ts +8 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +775 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +10 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -0
- package/dist/utils/ansi.js +52 -0
- package/dist/utils/ansi.js.map +1 -0
- package/dist/utils/changelog.d.ts +22 -0
- package/dist/utils/changelog.d.ts.map +1 -0
- package/dist/utils/changelog.js +165 -0
- package/dist/utils/changelog.js.map +1 -0
- package/dist/utils/child-process.d.ts +18 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +106 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts +11 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -0
- package/dist/utils/clipboard-image.js +245 -0
- package/dist/utils/clipboard-image.js.map +1 -0
- package/dist/utils/clipboard-native.d.ts +11 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -0
- package/dist/utils/clipboard-native.js +20 -0
- package/dist/utils/clipboard-native.js.map +1 -0
- package/dist/utils/clipboard.d.ts +4 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +130 -0
- package/dist/utils/clipboard.js.map +1 -0
- package/dist/utils/deprecation.d.ts +4 -0
- package/dist/utils/deprecation.d.ts.map +1 -0
- package/dist/utils/deprecation.js +13 -0
- package/dist/utils/deprecation.js.map +1 -0
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +8 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +26 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/fs-watch.d.ts +5 -0
- package/dist/utils/fs-watch.d.ts.map +1 -0
- package/dist/utils/fs-watch.js +25 -0
- package/dist/utils/fs-watch.js.map +1 -0
- package/dist/utils/git.d.ts +26 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +195 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +40 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/image-convert.d.ts +10 -0
- package/dist/utils/image-convert.d.ts.map +1 -0
- package/dist/utils/image-convert.js +45 -0
- package/dist/utils/image-convert.js.map +1 -0
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +124 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +16 -0
- package/dist/utils/image-resize.d.ts.map +1 -0
- package/dist/utils/image-resize.js +97 -0
- package/dist/utils/image-resize.js.map +1 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +7 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/mime.d.ts +3 -0
- package/dist/utils/mime.d.ts.map +1 -0
- package/dist/utils/mime.js +110 -0
- package/dist/utils/mime.js.map +1 -0
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +22 -0
- package/dist/utils/open-browser.js.map +1 -0
- package/dist/utils/paths.d.ts +31 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +92 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/photon.d.ts +21 -0
- package/dist/utils/photon.d.ts.map +1 -0
- package/dist/utils/photon.js +121 -0
- package/dist/utils/photon.js.map +1 -0
- package/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/utils/pi-user-agent.d.ts.map +1 -0
- package/dist/utils/pi-user-agent.js +5 -0
- package/dist/utils/pi-user-agent.js.map +1 -0
- package/dist/utils/shell.d.ts +31 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +202 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/sleep.d.ts +5 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +118 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +3 -0
- package/dist/utils/tools-manager.d.ts.map +1 -0
- package/dist/utils/tools-manager.js +328 -0
- package/dist/utils/tools-manager.js.map +1 -0
- package/dist/utils/version-check.d.ts +15 -0
- package/dist/utils/version-check.d.ts.map +1 -0
- package/dist/utils/version-check.js +59 -0
- package/dist/utils/version-check.js.map +1 -0
- package/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/utils/windows-self-update.d.ts.map +1 -0
- package/dist/utils/windows-self-update.js +77 -0
- package/dist/utils/windows-self-update.js.map +1 -0
- package/docs/compaction.md +401 -0
- package/docs/containerization.md +111 -0
- package/docs/custom-provider.md +763 -0
- package/docs/development.md +71 -0
- package/docs/docs.json +156 -0
- package/docs/extensions.md +2953 -0
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/exy.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/index.md +83 -0
- package/docs/json.md +86 -0
- package/docs/keybindings.md +198 -0
- package/docs/llama-cpp.md +99 -0
- package/docs/models.md +540 -0
- package/docs/packages.md +228 -0
- package/docs/prompt-templates.md +96 -0
- package/docs/providers.md +301 -0
- package/docs/quickstart.md +165 -0
- package/docs/rpc.md +1559 -0
- package/docs/sdk.md +1186 -0
- package/docs/security.md +59 -0
- package/docs/session-format.md +436 -0
- package/docs/sessions.md +145 -0
- package/docs/settings.md +319 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +231 -0
- package/docs/terminal-setup.md +142 -0
- package/docs/termux.md +127 -0
- package/docs/themes.md +297 -0
- package/docs/tmux.md +63 -0
- package/docs/tui.md +942 -0
- package/docs/usage.md +311 -0
- package/docs/windows.md +17 -0
- package/examples/README.md +25 -0
- package/examples/extensions/README.md +213 -0
- package/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/border-status-editor.ts +150 -0
- package/examples/extensions/built-in-tool-renderer.ts +249 -0
- package/examples/extensions/claude-rules.ts +86 -0
- package/examples/extensions/commands.ts +72 -0
- package/examples/extensions/confirm-destructive.ts +59 -0
- package/examples/extensions/custom-compaction.ts +129 -0
- package/examples/extensions/custom-footer.ts +64 -0
- package/examples/extensions/custom-header.ts +73 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/examples/extensions/dirty-repo-guard.ts +56 -0
- package/examples/extensions/doom-overlay/README.md +46 -0
- package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/examples/extensions/doom-overlay/doom-component.ts +132 -0
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/examples/extensions/doom-overlay/index.ts +74 -0
- package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/examples/extensions/dynamic-resources/index.ts +15 -0
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/entry-renderer.ts +41 -0
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/file-trigger.ts +41 -0
- package/examples/extensions/git-checkpoint.ts +53 -0
- package/examples/extensions/git-merge-and-resolve.ts +115 -0
- package/examples/extensions/github-issue-autocomplete.ts +185 -0
- package/examples/extensions/gondolin/index.ts +531 -0
- package/examples/extensions/gondolin/package-lock.json +185 -0
- package/examples/extensions/gondolin/package.json +19 -0
- package/examples/extensions/handoff.ts +191 -0
- package/examples/extensions/hello.ts +26 -0
- package/examples/extensions/hidden-thinking-label.ts +53 -0
- package/examples/extensions/inline-bash.ts +94 -0
- package/examples/extensions/input-transform-streaming.ts +39 -0
- package/examples/extensions/input-transform.ts +43 -0
- package/examples/extensions/interactive-shell.ts +196 -0
- package/examples/extensions/kimi-deferred-tools.ts +61 -0
- package/examples/extensions/mac-system-theme.ts +47 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/minimal-mode.ts +426 -0
- package/examples/extensions/modal-editor.ts +85 -0
- package/examples/extensions/model-status.ts +31 -0
- package/examples/extensions/notify.ts +55 -0
- package/examples/extensions/overlay-qa-tests.ts +1450 -0
- package/examples/extensions/overlay-test.ts +153 -0
- package/examples/extensions/permission-gate.ts +34 -0
- package/examples/extensions/pirate.ts +47 -0
- package/examples/extensions/plan-mode/README.md +66 -0
- package/examples/extensions/plan-mode/index.ts +390 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/preset.ts +436 -0
- package/examples/extensions/project-trust.ts +64 -0
- package/examples/extensions/prompt-customizer.ts +97 -0
- package/examples/extensions/protected-paths.ts +30 -0
- package/examples/extensions/provider-payload.ts +18 -0
- package/examples/extensions/qna.ts +122 -0
- package/examples/extensions/question.ts +278 -0
- package/examples/extensions/questionnaire.ts +440 -0
- package/examples/extensions/rainbow-editor.ts +88 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/rpc-demo.ts +118 -0
- package/examples/extensions/sandbox/index.ts +321 -0
- package/examples/extensions/sandbox/package-lock.json +92 -0
- package/examples/extensions/sandbox/package.json +19 -0
- package/examples/extensions/send-user-message.ts +97 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/shutdown-command.ts +63 -0
- package/examples/extensions/snake.ts +343 -0
- package/examples/extensions/space-invaders.ts +560 -0
- package/examples/extensions/ssh.ts +220 -0
- package/examples/extensions/status-line.ts +32 -0
- package/examples/extensions/structured-output.ts +65 -0
- package/examples/extensions/subagent/README.md +175 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +126 -0
- package/examples/extensions/subagent/index.ts +1015 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/summarize.ts +207 -0
- package/examples/extensions/system-prompt-header.ts +17 -0
- package/examples/extensions/tic-tac-toe.ts +1008 -0
- package/examples/extensions/timed-confirm.ts +70 -0
- package/examples/extensions/titlebar-spinner.ts +58 -0
- package/examples/extensions/todo.ts +297 -0
- package/examples/extensions/tool-override.ts +144 -0
- package/examples/extensions/tools.ts +146 -0
- package/examples/extensions/trigger-compact.ts +50 -0
- package/examples/extensions/truncated-tool.ts +195 -0
- package/examples/extensions/widget-placement.ts +9 -0
- package/examples/extensions/with-deps/index.ts +32 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/extensions/working-indicator.ts +123 -0
- package/examples/extensions/working-message-test.ts +25 -0
- package/examples/rpc-extension-ui.ts +632 -0
- package/examples/sdk/01-minimal.ts +26 -0
- package/examples/sdk/02-custom-model.ts +50 -0
- package/examples/sdk/03-custom-prompt.ts +70 -0
- package/examples/sdk/04-skills.ts +55 -0
- package/examples/sdk/05-tools.ts +48 -0
- package/examples/sdk/06-extensions.ts +94 -0
- package/examples/sdk/07-context-files.ts +42 -0
- package/examples/sdk/08-prompt-templates.ts +51 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +34 -0
- package/examples/sdk/10-settings.ts +53 -0
- package/examples/sdk/11-sessions.ts +52 -0
- package/examples/sdk/12-full-control.ts +72 -0
- package/examples/sdk/13-session-runtime.ts +67 -0
- package/examples/sdk/README.md +140 -0
- package/npm-shrinkwrap.json +1824 -0
- package/package.json +100 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CredentialStore implementation backed by auth.json.
|
|
3
|
+
* Provider auth orchestration belongs to ModelRuntime and pi-ai Models.
|
|
4
|
+
*/
|
|
5
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import lockfile from "proper-lockfile";
|
|
8
|
+
import { getAgentDir } from "../config.js";
|
|
9
|
+
import { normalizePath } from "../utils/paths.js";
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
|
|
11
|
+
function resolveConfigValue(value, _env) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
const AUTH_FILE_WRITE_OPTIONS = { encoding: "utf-8", mode: 0o600 };
|
|
15
|
+
export class FileAuthStorageBackend {
|
|
16
|
+
authPath;
|
|
17
|
+
constructor(authPath = join(getAgentDir(), "auth.json")) {
|
|
18
|
+
this.authPath = normalizePath(authPath);
|
|
19
|
+
}
|
|
20
|
+
ensureParentDir() {
|
|
21
|
+
const dir = dirname(this.authPath);
|
|
22
|
+
if (!existsSync(dir)) {
|
|
23
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
ensureFileExists() {
|
|
27
|
+
if (!existsSync(this.authPath)) {
|
|
28
|
+
writeFileSync(this.authPath, "{}", AUTH_FILE_WRITE_OPTIONS);
|
|
29
|
+
chmodSync(this.authPath, 0o600);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
acquireLockSyncWithRetry(path) {
|
|
33
|
+
const maxAttempts = 10;
|
|
34
|
+
const delayMs = 20;
|
|
35
|
+
let lastError;
|
|
36
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
37
|
+
try {
|
|
38
|
+
return lockfile.lockSync(path, { realpath: false });
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
42
|
+
? String(error.code)
|
|
43
|
+
: undefined;
|
|
44
|
+
if (code !== "ELOCKED" || attempt === maxAttempts) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
lastError = error;
|
|
48
|
+
const start = Date.now();
|
|
49
|
+
while (Date.now() - start < delayMs) {
|
|
50
|
+
// Sleep synchronously to avoid changing callers to async.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw lastError ?? new Error("Failed to acquire auth storage lock");
|
|
55
|
+
}
|
|
56
|
+
withLock(fn) {
|
|
57
|
+
this.ensureParentDir();
|
|
58
|
+
this.ensureFileExists();
|
|
59
|
+
let release;
|
|
60
|
+
try {
|
|
61
|
+
release = this.acquireLockSyncWithRetry(this.authPath);
|
|
62
|
+
const current = existsSync(this.authPath) ? readFileSync(this.authPath, "utf-8") : undefined;
|
|
63
|
+
const { result, next } = fn(current);
|
|
64
|
+
if (next !== undefined) {
|
|
65
|
+
writeFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);
|
|
66
|
+
chmodSync(this.authPath, 0o600);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
if (release) {
|
|
72
|
+
release();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async withLockAsync(fn) {
|
|
77
|
+
this.ensureParentDir();
|
|
78
|
+
this.ensureFileExists();
|
|
79
|
+
let release;
|
|
80
|
+
let lockCompromised = false;
|
|
81
|
+
let lockCompromisedError;
|
|
82
|
+
const throwIfCompromised = () => {
|
|
83
|
+
if (lockCompromised) {
|
|
84
|
+
throw lockCompromisedError ?? new Error("Auth storage lock was compromised");
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
release = await lockfile.lock(this.authPath, {
|
|
89
|
+
retries: {
|
|
90
|
+
retries: 10,
|
|
91
|
+
factor: 2,
|
|
92
|
+
minTimeout: 100,
|
|
93
|
+
maxTimeout: 10000,
|
|
94
|
+
randomize: true,
|
|
95
|
+
},
|
|
96
|
+
stale: 30000,
|
|
97
|
+
onCompromised: (err) => {
|
|
98
|
+
lockCompromised = true;
|
|
99
|
+
lockCompromisedError = err;
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
throwIfCompromised();
|
|
103
|
+
const current = existsSync(this.authPath) ? readFileSync(this.authPath, "utf-8") : undefined;
|
|
104
|
+
const { result, next } = await fn(current);
|
|
105
|
+
throwIfCompromised();
|
|
106
|
+
if (next !== undefined) {
|
|
107
|
+
writeFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);
|
|
108
|
+
chmodSync(this.authPath, 0o600);
|
|
109
|
+
}
|
|
110
|
+
throwIfCompromised();
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
if (release) {
|
|
115
|
+
try {
|
|
116
|
+
await release();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Ignore unlock errors when lock is compromised.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export class InMemoryAuthStorageBackend {
|
|
126
|
+
value;
|
|
127
|
+
withLock(fn) {
|
|
128
|
+
const { result, next } = fn(this.value);
|
|
129
|
+
if (next !== undefined) {
|
|
130
|
+
this.value = next;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
async withLockAsync(fn) {
|
|
135
|
+
const { result, next } = await fn(this.value);
|
|
136
|
+
if (next !== undefined) {
|
|
137
|
+
this.value = next;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Credential storage backed by a JSON file.
|
|
144
|
+
*/
|
|
145
|
+
export class AuthStorage {
|
|
146
|
+
data = {};
|
|
147
|
+
storage;
|
|
148
|
+
constructor(storage) {
|
|
149
|
+
this.storage = storage;
|
|
150
|
+
this.reload();
|
|
151
|
+
}
|
|
152
|
+
static create(authPath) {
|
|
153
|
+
return new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), "auth.json")));
|
|
154
|
+
}
|
|
155
|
+
static fromStorage(storage) {
|
|
156
|
+
return new AuthStorage(storage);
|
|
157
|
+
}
|
|
158
|
+
static inMemory(data = {}) {
|
|
159
|
+
const storage = new InMemoryAuthStorageBackend();
|
|
160
|
+
storage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));
|
|
161
|
+
return AuthStorage.fromStorage(storage);
|
|
162
|
+
}
|
|
163
|
+
parseStorageData(content) {
|
|
164
|
+
if (!content) {
|
|
165
|
+
return {};
|
|
166
|
+
}
|
|
167
|
+
return JSON.parse(content);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Reload credentials from storage.
|
|
171
|
+
*/
|
|
172
|
+
reload() {
|
|
173
|
+
let content;
|
|
174
|
+
try {
|
|
175
|
+
this.storage.withLock((current) => {
|
|
176
|
+
content = current;
|
|
177
|
+
return { result: undefined };
|
|
178
|
+
});
|
|
179
|
+
this.data = this.parseStorageData(content);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Preserve the last valid in-memory snapshot.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async read(provider) {
|
|
186
|
+
const credential = this.data[provider];
|
|
187
|
+
if (credential?.type !== "api_key")
|
|
188
|
+
return credential;
|
|
189
|
+
if (credential.key === undefined)
|
|
190
|
+
return credential;
|
|
191
|
+
return { ...credential, key: resolveConfigValue(credential.key, credential.env) };
|
|
192
|
+
}
|
|
193
|
+
async modify(provider, fn) {
|
|
194
|
+
return this.storage.withLockAsync(async (content) => {
|
|
195
|
+
const currentData = this.parseStorageData(content);
|
|
196
|
+
const next = await fn(currentData[provider]);
|
|
197
|
+
if (next === undefined) {
|
|
198
|
+
this.data = currentData;
|
|
199
|
+
return { result: currentData[provider] };
|
|
200
|
+
}
|
|
201
|
+
const merged = { ...currentData, [provider]: next };
|
|
202
|
+
this.data = merged;
|
|
203
|
+
return { result: next, next: JSON.stringify(merged, null, 2) };
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async delete(provider) {
|
|
207
|
+
await this.storage.withLockAsync(async (content) => {
|
|
208
|
+
const currentData = this.parseStorageData(content);
|
|
209
|
+
delete currentData[provider];
|
|
210
|
+
this.data = currentData;
|
|
211
|
+
return { result: undefined, next: JSON.stringify(currentData, null, 2) };
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/** List credential metadata without resolving configured key values. */
|
|
215
|
+
async list() {
|
|
216
|
+
return Object.entries(this.data).map(([providerId, credential]) => ({ providerId, type: credential.type }));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* One-off synchronous read of a stored credential from an auth.json file,
|
|
221
|
+
* without instantiating a store or resolving configured key values.
|
|
222
|
+
*/
|
|
223
|
+
export function readStoredCredential(providerId, authPath = join(getAgentDir(), "auth.json")) {
|
|
224
|
+
try {
|
|
225
|
+
const data = JSON.parse(readFileSync(normalizePath(authPath), "utf-8"));
|
|
226
|
+
return data[providerId];
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=auth-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-storage.js","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CredentialStore implementation backed by auth.json.\n * Provider auth orchestration belongs to ModelRuntime and pi-ai Models.\n */\n\nimport type { Credential, CredentialInfo, CredentialStore } from \"@handy_wote/pi-ai\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars\nfunction resolveConfigValue(value: string, _env?: Record<string, string>): string {\n\treturn value;\n}\n\ntype AuthStorageData = Record<string, Credential>;\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage implements CredentialStore {\n\tprivate data: AuthStorageData = {};\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t} catch {\n\t\t\t// Preserve the last valid in-memory snapshot.\n\t\t}\n\t}\n\n\tasync read(provider: string): Promise<Credential | undefined> {\n\t\tconst credential = this.data[provider];\n\t\tif (credential?.type !== \"api_key\") return credential;\n\t\tif (credential.key === undefined) return credential;\n\t\treturn { ...credential, key: resolveConfigValue(credential.key, credential.env) };\n\t}\n\n\tasync modify(\n\t\tprovider: string,\n\t\tfn: (current: Credential | undefined) => Promise<Credential | undefined>,\n\t): Promise<Credential | undefined> {\n\t\treturn this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tconst next = await fn(currentData[provider]);\n\t\t\tif (next === undefined) {\n\t\t\t\tthis.data = currentData;\n\t\t\t\treturn { result: currentData[provider] };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = { ...currentData, [provider]: next };\n\t\t\tthis.data = merged;\n\t\t\treturn { result: next, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\t}\n\n\tasync delete(provider: string): Promise<void> {\n\t\tawait this.storage.withLockAsync(async (content) => {\n\t\t\tconst currentData = this.parseStorageData(content);\n\t\t\tdelete currentData[provider];\n\t\t\tthis.data = currentData;\n\t\t\treturn { result: undefined, next: JSON.stringify(currentData, null, 2) };\n\t\t});\n\t}\n\n\t/** List credential metadata without resolving configured key values. */\n\tasync list(): Promise<readonly CredentialInfo[]> {\n\t\treturn Object.entries(this.data).map(([providerId, credential]) => ({ providerId, type: credential.type }));\n\t}\n}\n\n/**\n * One-off synchronous read of a stored credential from an auth.json file,\n * without instantiating a store or resolving configured key values.\n */\nexport function readStoredCredential(\n\tproviderId: string,\n\tauthPath: string = join(getAgentDir(), \"auth.json\"),\n): Credential | undefined {\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(normalizePath(authPath), \"utf-8\")) as AuthStorageData;\n\t\treturn data[providerId];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command execution with streaming support and cancellation.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a unified bash execution implementation used by:
|
|
5
|
+
* - AgentSession.executeBash() for interactive and RPC modes
|
|
6
|
+
* - Direct calls from modes that need bash execution
|
|
7
|
+
*/
|
|
8
|
+
import type { BashOperations } from "./tools/bash.ts";
|
|
9
|
+
export interface BashExecutorOptions {
|
|
10
|
+
/** Callback for streaming output chunks (already sanitized) */
|
|
11
|
+
onChunk?: (chunk: string) => void;
|
|
12
|
+
/** AbortSignal for cancellation */
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export interface BashResult {
|
|
16
|
+
/** Combined stdout + stderr output (sanitized, possibly truncated) */
|
|
17
|
+
output: string;
|
|
18
|
+
/** Process exit code (undefined if killed/cancelled) */
|
|
19
|
+
exitCode: number | undefined;
|
|
20
|
+
/** Whether the command was cancelled via signal */
|
|
21
|
+
cancelled: boolean;
|
|
22
|
+
/** Whether the output was truncated */
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
/** Path to temp file containing full output (if output exceeded truncation threshold) */
|
|
25
|
+
fullOutputPath?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Execute a bash command using custom BashOperations.
|
|
29
|
+
* Used for remote execution (SSH, containers, etc.).
|
|
30
|
+
*/
|
|
31
|
+
export declare function executeBashWithOperations(command: string, cwd: string, operations: BashOperations, options?: BashExecutorOptions): Promise<BashResult>;
|
|
32
|
+
//# sourceMappingURL=bash-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAqGrB","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\tfor (const chunk of outputChunks) {\n\t\t\ttempFileStream.write(chunk);\n\t\t}\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tif (truncationResult.truncated) {\n\t\t\tensureTempFile();\n\t\t}\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command execution with streaming support and cancellation.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a unified bash execution implementation used by:
|
|
5
|
+
* - AgentSession.executeBash() for interactive and RPC modes
|
|
6
|
+
* - Direct calls from modes that need bash execution
|
|
7
|
+
*/
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
|
+
import { createWriteStream } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { stripAnsi } from "../utils/ansi.js";
|
|
13
|
+
import { sanitizeBinaryOutput } from "../utils/shell.js";
|
|
14
|
+
import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Implementation
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Execute a bash command using custom BashOperations.
|
|
20
|
+
* Used for remote execution (SSH, containers, etc.).
|
|
21
|
+
*/
|
|
22
|
+
export async function executeBashWithOperations(command, cwd, operations, options) {
|
|
23
|
+
const outputChunks = [];
|
|
24
|
+
let outputBytes = 0;
|
|
25
|
+
const maxOutputBytes = DEFAULT_MAX_BYTES * 2;
|
|
26
|
+
let tempFilePath;
|
|
27
|
+
let tempFileStream;
|
|
28
|
+
let totalBytes = 0;
|
|
29
|
+
const ensureTempFile = () => {
|
|
30
|
+
if (tempFilePath) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const id = randomBytes(8).toString("hex");
|
|
34
|
+
tempFilePath = join(tmpdir(), `pi-bash-${id}.log`);
|
|
35
|
+
tempFileStream = createWriteStream(tempFilePath);
|
|
36
|
+
for (const chunk of outputChunks) {
|
|
37
|
+
tempFileStream.write(chunk);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const decoder = new TextDecoder();
|
|
41
|
+
const onData = (data) => {
|
|
42
|
+
totalBytes += data.length;
|
|
43
|
+
// Sanitize: strip ANSI, replace binary garbage, normalize newlines
|
|
44
|
+
const text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\r/g, "");
|
|
45
|
+
// Start writing to temp file if exceeds threshold
|
|
46
|
+
if (totalBytes > DEFAULT_MAX_BYTES) {
|
|
47
|
+
ensureTempFile();
|
|
48
|
+
}
|
|
49
|
+
if (tempFileStream) {
|
|
50
|
+
tempFileStream.write(text);
|
|
51
|
+
}
|
|
52
|
+
// Keep rolling buffer
|
|
53
|
+
outputChunks.push(text);
|
|
54
|
+
outputBytes += text.length;
|
|
55
|
+
while (outputBytes > maxOutputBytes && outputChunks.length > 1) {
|
|
56
|
+
const removed = outputChunks.shift();
|
|
57
|
+
outputBytes -= removed.length;
|
|
58
|
+
}
|
|
59
|
+
// Stream to callback
|
|
60
|
+
if (options?.onChunk) {
|
|
61
|
+
options.onChunk(text);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
const result = await operations.exec(command, cwd, {
|
|
66
|
+
onData,
|
|
67
|
+
signal: options?.signal,
|
|
68
|
+
});
|
|
69
|
+
const fullOutput = outputChunks.join("");
|
|
70
|
+
const truncationResult = truncateTail(fullOutput);
|
|
71
|
+
if (truncationResult.truncated) {
|
|
72
|
+
ensureTempFile();
|
|
73
|
+
}
|
|
74
|
+
if (tempFileStream) {
|
|
75
|
+
tempFileStream.end();
|
|
76
|
+
}
|
|
77
|
+
const cancelled = options?.signal?.aborted ?? false;
|
|
78
|
+
return {
|
|
79
|
+
output: truncationResult.truncated ? truncationResult.content : fullOutput,
|
|
80
|
+
exitCode: cancelled ? undefined : (result.exitCode ?? undefined),
|
|
81
|
+
cancelled,
|
|
82
|
+
truncated: truncationResult.truncated,
|
|
83
|
+
fullOutputPath: tempFilePath,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
// Check if it was an abort
|
|
88
|
+
if (options?.signal?.aborted) {
|
|
89
|
+
const fullOutput = outputChunks.join("");
|
|
90
|
+
const truncationResult = truncateTail(fullOutput);
|
|
91
|
+
if (truncationResult.truncated) {
|
|
92
|
+
ensureTempFile();
|
|
93
|
+
}
|
|
94
|
+
if (tempFileStream) {
|
|
95
|
+
tempFileStream.end();
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
output: truncationResult.truncated ? truncationResult.content : fullOutput,
|
|
99
|
+
exitCode: undefined,
|
|
100
|
+
cancelled: true,
|
|
101
|
+
truncated: truncationResult.truncated,
|
|
102
|
+
fullOutputPath: tempFilePath,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (tempFileStream) {
|
|
106
|
+
tempFileStream.end();
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=bash-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripAnsi } from \"../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../utils/shell.ts\";\nimport type { BashOperations } from \"./tools/bash.ts\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.ts\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst ensureTempFile = () => {\n\t\tif (tempFilePath) {\n\t\t\treturn;\n\t\t}\n\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\ttempFilePath = join(tmpdir(), `pi-bash-${id}.log`);\n\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\tfor (const chunk of outputChunks) {\n\t\t\ttempFileStream.write(chunk);\n\t\t}\n\t};\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureTempFile();\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tif (truncationResult.truncated) {\n\t\t\tensureTempFile();\n\t\t}\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\tif (truncationResult.truncated) {\n\t\t\t\tensureTempFile();\n\t\t\t}\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AssistantMessage } from "@handy_wote/pi-ai";
|
|
2
|
+
import type { SessionEntry } from "./session-manager.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Prompt-cache TTL: idle gaps longer than this are worth mentioning as the
|
|
5
|
+
* likely cause of a miss. Anthropic's default cache TTL is 5 minutes.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CACHE_TTL_MS: number;
|
|
8
|
+
/** A counted cache miss on a single assistant message. */
|
|
9
|
+
export interface CacheMiss {
|
|
10
|
+
/** Prompt tokens that were in the previous turn's prompt but not read from cache. */
|
|
11
|
+
missedTokens: number;
|
|
12
|
+
/** Extra dollars paid vs. a full cache hit; 0 when pricing is unknown. */
|
|
13
|
+
missedCost: number;
|
|
14
|
+
/** Milliseconds since the previous request (which last refreshed the cache). */
|
|
15
|
+
idleMs: number;
|
|
16
|
+
/** True when the model changed relative to the previous request. */
|
|
17
|
+
modelChanged: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface CacheWasteTotals {
|
|
20
|
+
missedTokens: number;
|
|
21
|
+
missedCost: number;
|
|
22
|
+
/** Number of counted misses (turns above the noise floor). */
|
|
23
|
+
missCount: number;
|
|
24
|
+
}
|
|
25
|
+
/** Minimal pricing lookup, satisfied by ModelRuntime. Cost is $/million tokens. */
|
|
26
|
+
export interface ModelPriceSource {
|
|
27
|
+
getModel(provider: string, modelId: string): {
|
|
28
|
+
cost: {
|
|
29
|
+
cacheRead: number;
|
|
30
|
+
};
|
|
31
|
+
} | undefined;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cumulative cache waste across a session: prompt tokens that should have been
|
|
35
|
+
* cache reads (they were in the previous turn's prompt) but were re-billed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function computeCacheWaste(entries: SessionEntry[], models: ModelPriceSource): CacheWasteTotals;
|
|
38
|
+
/**
|
|
39
|
+
* All counted cache misses across a session, keyed by the assistant message
|
|
40
|
+
* (by reference) that paid for them. Used to re-derive transcript notices when
|
|
41
|
+
* rebuilding the chat from entries (resume, post-compaction rebuild).
|
|
42
|
+
*/
|
|
43
|
+
export declare function collectCacheMisses(entries: SessionEntry[], models: ModelPriceSource): Map<AssistantMessage, CacheMiss>;
|
|
44
|
+
/**
|
|
45
|
+
* Detect a cache miss on a just-completed assistant message.
|
|
46
|
+
* `entries` must not yet contain `message` (message_end fires before persistence).
|
|
47
|
+
*/
|
|
48
|
+
export declare function detectCacheMiss(entries: SessionEntry[], message: AssistantMessage, models: ModelPriceSource): CacheMiss | undefined;
|
|
49
|
+
//# sourceMappingURL=cache-stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-stats.d.ts","sourceRoot":"","sources":["../../src/core/cache-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,YAAY,QAAgB,CAAC;AAK1C,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACzB,qFAAqF;IACrF,YAAY,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,SAAS,CAAC;CACzF;AAmGD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAErG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,YAAY,EAAE,EACvB,MAAM,EAAE,gBAAgB,GACtB,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAElC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,gBAAgB,GACtB,SAAS,GAAG,SAAS,CAEvB","sourcesContent":["import type { AssistantMessage } from \"@handy_wote/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 === \"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"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-cache TTL: idle gaps longer than this are worth mentioning as the
|
|
3
|
+
* likely cause of a miss. Anthropic's default cache TTL is 5 minutes.
|
|
4
|
+
*/
|
|
5
|
+
export const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
6
|
+
/** Per-turn misses at or below this are cache breakpoint granularity noise. */
|
|
7
|
+
const NOISE_FLOOR_TOKENS = 1024;
|
|
8
|
+
/**
|
|
9
|
+
* Compute the cache miss for one assistant message relative to the previous
|
|
10
|
+
* request. Returns undefined when nothing is counted: first turn, after a
|
|
11
|
+
* reset, no cache activity ever reported (provider without cache support), or
|
|
12
|
+
* miss below the noise floor.
|
|
13
|
+
*/
|
|
14
|
+
function detectMiss(prev, message, models) {
|
|
15
|
+
const usage = message.usage;
|
|
16
|
+
const promptTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
17
|
+
// A zero-cache turn only counts when cache activity was reported before:
|
|
18
|
+
// on cache-read-only providers that is a total miss, while on providers
|
|
19
|
+
// that never report caching it means nothing.
|
|
20
|
+
if (!prev || promptTokens <= 0 || (usage.cacheRead + usage.cacheWrite === 0 && !prev.reportedCache)) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const missedTokens = Math.min(prev.promptTokens, promptTokens) - usage.cacheRead;
|
|
24
|
+
if (missedTokens <= NOISE_FLOOR_TOKENS)
|
|
25
|
+
return undefined;
|
|
26
|
+
// Extra cost = missed tokens billed at the actual paid rate (input/cacheWrite,
|
|
27
|
+
// incl. write premium) instead of the cache-read rate. Missed tokens can only
|
|
28
|
+
// land in the input or cacheWrite buckets, so the paid rate comes straight
|
|
29
|
+
// from this message's own cost breakdown.
|
|
30
|
+
const paidTokens = usage.input + usage.cacheWrite;
|
|
31
|
+
const paidPerToken = paidTokens > 0 ? (usage.cost.input + usage.cost.cacheWrite) / paidTokens : 0;
|
|
32
|
+
const readPerToken = usage.cacheRead > 0
|
|
33
|
+
? usage.cost.cacheRead / usage.cacheRead
|
|
34
|
+
: (models.getModel(message.provider, message.model)?.cost.cacheRead ?? 0) / 1_000_000;
|
|
35
|
+
return {
|
|
36
|
+
missedTokens,
|
|
37
|
+
missedCost: missedTokens * Math.max(0, paidPerToken - readPerToken),
|
|
38
|
+
idleMs: Math.max(0, message.timestamp - prev.timestamp),
|
|
39
|
+
modelChanged: `${message.provider}/${message.model}` !== prev.modelKey,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function asPreviousRequest(message, reportedCache) {
|
|
43
|
+
const usage = message.usage;
|
|
44
|
+
const promptTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
45
|
+
if (promptTokens <= 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
return {
|
|
48
|
+
promptTokens,
|
|
49
|
+
modelKey: `${message.provider}/${message.model}`,
|
|
50
|
+
timestamp: message.timestamp,
|
|
51
|
+
reportedCache: reportedCache || usage.cacheRead + usage.cacheWrite > 0,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function scan(entries, models) {
|
|
55
|
+
let prev;
|
|
56
|
+
const totals = { missedTokens: 0, missedCost: 0, missCount: 0 };
|
|
57
|
+
const misses = new Map();
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (entry.type === "compaction" || entry.type === "branch_summary") {
|
|
60
|
+
// The context legitimately changed; the next turn's prompt is new content,
|
|
61
|
+
// not re-billed content. Model switches are NOT exempt: they re-bill the
|
|
62
|
+
// full prompt and should be counted.
|
|
63
|
+
prev = undefined;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
67
|
+
const miss = detectMiss(prev, entry.message, models);
|
|
68
|
+
if (miss) {
|
|
69
|
+
totals.missedTokens += miss.missedTokens;
|
|
70
|
+
totals.missedCost += miss.missedCost;
|
|
71
|
+
totals.missCount += 1;
|
|
72
|
+
misses.set(entry.message, miss);
|
|
73
|
+
}
|
|
74
|
+
prev = asPreviousRequest(entry.message, prev?.reportedCache ?? false) ?? prev;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { prev, totals, misses };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Cumulative cache waste across a session: prompt tokens that should have been
|
|
81
|
+
* cache reads (they were in the previous turn's prompt) but were re-billed.
|
|
82
|
+
*/
|
|
83
|
+
export function computeCacheWaste(entries, models) {
|
|
84
|
+
return scan(entries, models).totals;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* All counted cache misses across a session, keyed by the assistant message
|
|
88
|
+
* (by reference) that paid for them. Used to re-derive transcript notices when
|
|
89
|
+
* rebuilding the chat from entries (resume, post-compaction rebuild).
|
|
90
|
+
*/
|
|
91
|
+
export function collectCacheMisses(entries, models) {
|
|
92
|
+
return scan(entries, models).misses;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Detect a cache miss on a just-completed assistant message.
|
|
96
|
+
* `entries` must not yet contain `message` (message_end fires before persistence).
|
|
97
|
+
*/
|
|
98
|
+
export function detectCacheMiss(entries, message, models) {
|
|
99
|
+
return detectMiss(scan(entries, models).prev, message, models);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=cache-stats.js.map
|
|
@@ -0,0 +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 \"@handy_wote/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 === \"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"]}
|