@code-yeongyu/senpi 2026.7.23 → 2026.7.24
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 +131 -6
- package/README.md +14 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +6 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +15 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +12 -2
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +102 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1295 -320
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +7 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +8 -12
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +22 -4
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +114 -51
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/lifecycle.d.ts +69 -0
- package/dist/core/compaction/lifecycle.d.ts.map +1 -0
- package/dist/core/compaction/lifecycle.js +104 -0
- package/dist/core/compaction/lifecycle.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +12 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +22 -17
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +11 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts +2 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +42 -30
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-model.d.ts +9 -2
- package/dist/core/extensions/builtin/compaction/openai-remote-model.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-model.js +78 -28
- package/dist/core/extensions/builtin/compaction/openai-remote-model.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-schema.d.ts +3 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-schema.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-schema.js +19 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-schema.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +20 -6
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js +207 -22
- package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +4 -2
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +25 -13
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/index.d.ts +7 -0
- package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/index.js +49 -17
- package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/index.js +1 -1
- package/dist/core/extensions/builtin/hooks/index.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.d.ts +3 -1
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.d.ts.map +1 -1
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.js +2 -1
- package/dist/core/extensions/builtin/hooks/lifecycle-adapter.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.d.ts +2 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.js +5 -3
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.d.ts +5 -0
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +8 -3
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -1
- package/dist/core/extensions/builtin/mcp/catalog.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/catalog.js +2 -0
- package/dist/core/extensions/builtin/mcp/catalog.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/commands.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/commands.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/commands.js +34 -39
- package/dist/core/extensions/builtin/mcp/commands.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/connection-types.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/connection-types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/connection-types.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/connection.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/connection.js +3 -0
- package/dist/core/extensions/builtin/mcp/connection.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/elicitation.d.ts +7 -3
- package/dist/core/extensions/builtin/mcp/elicitation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/elicitation.js +5 -5
- package/dist/core/extensions/builtin/mcp/elicitation.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/register.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/register.js +1 -0
- package/dist/core/extensions/builtin/mcp/expose/register.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/session.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/expose/session.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/session.js +3 -0
- package/dist/core/extensions/builtin/mcp/expose/session.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/guard/output-guard.d.ts +9 -1
- package/dist/core/extensions/builtin/mcp/guard/output-guard.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/guard/output-guard.js +18 -6
- package/dist/core/extensions/builtin/mcp/guard/output-guard.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/index.d.ts +4 -2
- package/dist/core/extensions/builtin/mcp/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/index.js +150 -127
- package/dist/core/extensions/builtin/mcp/index.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/instructions.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/instructions.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/instructions.js +6 -6
- package/dist/core/extensions/builtin/mcp/instructions.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/prompts.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/prompts.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/prompts.js +9 -3
- package/dist/core/extensions/builtin/mcp/prompts.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/resources.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/resources.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/resources.js +1 -0
- package/dist/core/extensions/builtin/mcp/resources.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/service-register.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/service-register.js +1 -0
- package/dist/core/extensions/builtin/mcp/service-register.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/service-types.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/service-types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/service-types.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/service.d.ts +12 -1
- package/dist/core/extensions/builtin/mcp/service.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/service.js +44 -2
- package/dist/core/extensions/builtin/mcp/service.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/transport.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/transport.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/transport.js +7 -7
- package/dist/core/extensions/builtin/mcp/transport.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +86 -7
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +102 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +96 -6
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.js +9 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +1 -0
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash.d.ts +15 -3
- package/dist/core/extensions/builtin/terminal/tools/bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash.js +30 -6
- package/dist/core/extensions/builtin/terminal/tools/bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +14 -7
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +64 -37
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +30 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +16 -0
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +10 -8
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +3 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +9 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +4 -2
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +34 -18
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +21 -7
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +12 -8
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +8 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +22 -3
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +155 -58
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +34 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +3 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/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/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +3 -1
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/index.js +3 -3
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +1 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +37 -5
- package/dist/main.js.map +1 -1
- package/dist/modes/app-server/daemon/occupancy.d.ts +22 -0
- package/dist/modes/app-server/daemon/occupancy.d.ts.map +1 -0
- package/dist/modes/app-server/daemon/occupancy.js +67 -0
- package/dist/modes/app-server/daemon/occupancy.js.map +1 -0
- package/dist/modes/app-server/daemon/probe.d.ts +3 -3
- package/dist/modes/app-server/daemon/probe.d.ts.map +1 -1
- package/dist/modes/app-server/daemon/probe.js +33 -10
- package/dist/modes/app-server/daemon/probe.js.map +1 -1
- package/dist/modes/app-server/daemon.d.ts.map +1 -1
- package/dist/modes/app-server/daemon.js +58 -18
- package/dist/modes/app-server/daemon.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +8 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -46
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +4 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +13 -0
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +37 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +3 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +96 -78
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +6 -0
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +34 -9
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts +15 -0
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -0
- package/dist/modes/rpc/multi-session-host.js +60 -0
- package/dist/modes/rpc/multi-session-host.js.map +1 -0
- package/dist/modes/rpc/neo-runtime-options.d.ts.map +1 -1
- package/dist/modes/rpc/neo-runtime-options.js +1 -0
- package/dist/modes/rpc/neo-runtime-options.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +4 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +49 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +49 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +85 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +8 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-binding.d.ts +13 -0
- package/dist/modes/rpc/session-binding.d.ts.map +1 -0
- package/dist/modes/rpc/session-binding.js +33 -0
- package/dist/modes/rpc/session-binding.js.map +1 -0
- package/dist/modes/rpc/session-command-router.d.ts +19 -0
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -0
- package/dist/modes/rpc/session-command-router.js +159 -0
- package/dist/modes/rpc/session-command-router.js.map +1 -0
- package/dist/modes/rpc/session-event-writer.d.ts +34 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -0
- package/dist/modes/rpc/session-event-writer.js +92 -0
- package/dist/modes/rpc/session-event-writer.js.map +1 -0
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +15 -0
- package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -0
- package/dist/modes/rpc/session-extension-ui-requests.js +27 -0
- package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -0
- package/dist/modes/rpc/session-registry.d.ts +58 -0
- package/dist/modes/rpc/session-registry.d.ts.map +1 -0
- package/dist/modes/rpc/session-registry.js +162 -0
- package/dist/modes/rpc/session-registry.js.map +1 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +19 -8
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +3 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/app-server-daemon.md +3 -2
- package/docs/compaction.md +9 -4
- package/docs/custom-provider.md +5 -2
- package/docs/environment-variables.md +88 -0
- package/docs/extensions.md +25 -7
- package/docs/index.md +1 -0
- package/docs/json.md +5 -1
- package/docs/models.md +6 -2
- package/docs/providers.md +12 -1
- package/docs/rpc.md +154 -8
- package/docs/sdk.md +3 -0
- package/docs/session-format.md +17 -3
- package/docs/settings.md +2 -2
- package/docs/usage.md +1 -14
- package/examples/extensions/custom-compaction.ts +4 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +9 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +3 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
- package/node_modules/@earendil-works/pi-agent-core/README.md +31 -12
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +8 -7
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +10 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +61 -27
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts +11 -12
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +47 -22
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts +5 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +6 -8
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts +17 -5
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +82 -27
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/utils.d.ts +12 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/utils.js +22 -17
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts +3 -9
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +118 -11
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.d.ts +11 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.js +56 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.d.ts +11 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.js +56 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory-storage.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo-utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo-utils.js +2 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo-utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts +6 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +30 -14
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/bash.d.ts +27 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/bash.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/bash.js +132 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/bash.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit-diff.d.ts +89 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit-diff.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit-diff.js +386 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit-diff.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.d.ts +19 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.js +95 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/file-mutation-queue.d.ts +4 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/file-mutation-queue.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/file-mutation-queue.js +46 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/file-mutation-queue.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/image.d.ts +3 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/image.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/image.js +106 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/image.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/index.d.ts +6 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/index.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/index.js +5 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/index.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/path-utils.d.ts +4 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/path-utils.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/path-utils.js +26 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/path-utils.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts +34 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js +110 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/tool-context.d.ts +6 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/tool-context.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/tool-context.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/tool-context.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/write.d.ts +11 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/write.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/write.js +31 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/write.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +80 -18
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +12 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +90 -35
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/truncate.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/truncate.js +10 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/truncate.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +4 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js +5 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-fn.d.ts +10 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-fn.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-fn.js +17 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-fn.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +8 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +4 -3
- package/node_modules/@earendil-works/pi-ai/README.md +44 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +45 -27
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +21 -8
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +17 -10
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js +104 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +7 -11
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts +4 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js +17 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +7 -11
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +13 -9
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +73 -47
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts +7 -9
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +142 -33
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts +7 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +180 -80
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +37 -15
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +8 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js +2 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.d.ts +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.js +52 -6
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js +249 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.js +2 -18
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openrouter.d.ts +13 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openrouter.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openrouter.js +222 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openrouter.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/context-provenance.d.ts +16 -0
- package/node_modules/@earendil-works/pi-ai/dist/context-provenance.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/context-provenance.js +32 -0
- package/node_modules/@earendil-works/pi-ai/dist/context-provenance.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +11 -3
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/images-api-registry.d.ts +19 -2
- package/node_modules/@earendil-works/pi-ai/dist/images-api-registry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/images-api-registry.js +68 -3
- package/node_modules/@earendil-works/pi-ai/dist/images-api-registry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/model-catalog.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/model-catalog.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/model-catalog.js +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/model-catalog.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/model.d.ts +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/model.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models-store.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/models-store.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models-store.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.d.ts +0 -6
- package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +76 -4448
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.js +0 -53
- package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/node/provider-scope.d.ts +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/node/provider-scope.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/node/provider-scope.js +34 -0
- package/node_modules/@earendil-works/pi-ai/dist/node/provider-scope.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/alibaba-token-plan.models.d.ts +3 -63
- package/node_modules/@earendil-works/pi-ai/dist/providers/alibaba-token-plan.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/alibaba-token-plan.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/alibaba-token-plan.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts +3 -439
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +30 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts +3 -59
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts +3 -187
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts +3 -171
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.d.ts +3 -55
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/alibaba-token-plan.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/ant-ling.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/anthropic.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/deepseek.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/google-vertex.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/groq.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/kimi-coding.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/minimax-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/minimax.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/mistral.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan-cn.json +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.d.ts +3 -11
- package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts +3 -67
- package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts +3 -115
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.d.ts +3 -51
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.models.d.ts +3 -75
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.d.ts +3 -31
- package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.d.ts +3 -203
- package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/register-builtins.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/register-builtins.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.js +10 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts +3 -19
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.d.ts +3 -123
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai-cn.models.d.ts +3 -43
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai-cn.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai-cn.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai-cn.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai.models.d.ts +3 -43
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.d.ts +3 -79
- package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts +3 -31
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts +3 -187
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts +3 -67
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts +3 -227
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter-images.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter-images.js +10 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter-images.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.js +10 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +3 -1095
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.js +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.models.d.ts +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.models.js +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan-cn.models.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.js +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.models.d.ts +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.models.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.models.js +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/qwen-token-plan.models.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/radius-config.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/radius-config.js +18 -14
- package/node_modules/@earendil-works/pi-ai/dist/providers/radius-config.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.d.ts +3 -67
- package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +3 -775
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.d.ts +3 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.d.ts +3 -27
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.d.ts +3 -27
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.d.ts +3 -27
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +43 -3
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/openai-codex-auth.d.ts +11 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/openai-codex-auth.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/openai-codex-auth.js +30 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/openai-codex-auth.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.d.ts +16 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.js +94 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +41 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +82 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/text.d.ts +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/text.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/text.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/text.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/uuid.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/uuid.d.ts.map +1 -0
- package/node_modules/@earendil-works/{pi-agent-core/dist/harness/session → pi-ai/dist/utils}/uuid.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/utils/uuid.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/package.json +11 -4
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +31 -22
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +2 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -6
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/uuid.d.ts +0 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/uuid.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/uuid.js.map +0 -1
|
@@ -16,6 +16,7 @@ import { randomUUID } from "node:crypto";
|
|
|
16
16
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { basename, dirname } from "node:path";
|
|
18
18
|
import { prepareAgentToolCall } from "@earendil-works/pi-agent-core";
|
|
19
|
+
import { contentText } from "@earendil-works/pi-ai";
|
|
19
20
|
import { cleanupSessionResources, isClassifierRefusal, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
20
21
|
import { getAgentDir } from "../config.js";
|
|
21
22
|
import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
|
|
@@ -25,6 +26,7 @@ import { sleep } from "../utils/sleep.js";
|
|
|
25
26
|
import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
|
|
26
27
|
import { executeBashWithOperations } from "./bash-executor.js";
|
|
27
28
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
29
|
+
import { CompactionLifecycleCoordinator } from "./compaction/lifecycle.js";
|
|
28
30
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
29
31
|
import { buildDynamicSystemPrompt } from "./dynamic-prompt/index.js";
|
|
30
32
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
@@ -47,6 +49,7 @@ import { getSupportedThinkingLevels, supportsMax, supportsXhigh } from "./thinki
|
|
|
47
49
|
import { createLocalBashOperations } from "./tools/bash.js";
|
|
48
50
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
49
51
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
52
|
+
import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
|
|
50
53
|
/**
|
|
51
54
|
* Parse a skill block from message text.
|
|
52
55
|
* Returns null if the text doesn't contain a skill block.
|
|
@@ -70,6 +73,15 @@ function withoutDeletedHeaders(headers) {
|
|
|
70
73
|
? Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] !== null))
|
|
71
74
|
: undefined;
|
|
72
75
|
}
|
|
76
|
+
function isCompactionOwnedPreCompactDiagnostic(message, requestId) {
|
|
77
|
+
if (message.role !== "custom" || message.customType !== "senpi.hook")
|
|
78
|
+
return false;
|
|
79
|
+
const details = message.details;
|
|
80
|
+
if (!details || typeof details !== "object")
|
|
81
|
+
return false;
|
|
82
|
+
const diagnostic = details;
|
|
83
|
+
return diagnostic.event === "PreCompact" && diagnostic.compactionRequestId === requestId;
|
|
84
|
+
}
|
|
73
85
|
/**
|
|
74
86
|
* Human-readable rejection message paired with a `CompactionRejectionCause`.
|
|
75
87
|
*
|
|
@@ -86,6 +98,8 @@ function describeCompactionRejection(cause) {
|
|
|
86
98
|
return "Compaction rejected: the compaction circuit breaker is open after repeated failures. Wait for the cooldown and retry.";
|
|
87
99
|
case "per-turn-cap":
|
|
88
100
|
return "Compaction rejected: per-turn compaction cap reached for this turn.";
|
|
101
|
+
case "stale-revision":
|
|
102
|
+
return "Compaction rejected: the session changed while the summary was being prepared. Retry compaction against the latest context.";
|
|
89
103
|
}
|
|
90
104
|
}
|
|
91
105
|
class CompactionRejectedError extends Error {
|
|
@@ -97,6 +111,33 @@ class CompactionRejectedError extends Error {
|
|
|
97
111
|
this.rejectionCause = rejectionCause;
|
|
98
112
|
}
|
|
99
113
|
}
|
|
114
|
+
class CompactionCancelledError extends Error {
|
|
115
|
+
constructor() {
|
|
116
|
+
super("Compaction cancelled");
|
|
117
|
+
this.name = "CompactionCancelledError";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* An execution failure annotated with whether this operation still owns its
|
|
122
|
+
* terminal transition. Callers must not publish a terminal event for an
|
|
123
|
+
* operation that a newer compaction generation has superseded.
|
|
124
|
+
*/
|
|
125
|
+
class CompactionExecutionError extends Error {
|
|
126
|
+
constructor(error, ownsTerminalTransition, aborted) {
|
|
127
|
+
super(error instanceof Error ? error.message : String(error));
|
|
128
|
+
this.name = "CompactionExecutionError";
|
|
129
|
+
this.ownsTerminalTransition = ownsTerminalTransition;
|
|
130
|
+
this.aborted = aborted;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function compactionExecutionOwnsTerminalTransition(error) {
|
|
134
|
+
return !(error instanceof CompactionExecutionError) || error.ownsTerminalTransition;
|
|
135
|
+
}
|
|
136
|
+
function isCompactionExecutionAborted(error) {
|
|
137
|
+
return ((error instanceof CompactionExecutionError && error.aborted) ||
|
|
138
|
+
error instanceof CompactionCancelledError ||
|
|
139
|
+
(error instanceof Error && error.name === "AbortError"));
|
|
140
|
+
}
|
|
100
141
|
class RequiredCompactionError extends Error {
|
|
101
142
|
constructor() {
|
|
102
143
|
super("Context remains above the compaction threshold because compaction did not complete");
|
|
@@ -135,18 +176,40 @@ export class AgentSession {
|
|
|
135
176
|
constructor(config) {
|
|
136
177
|
this._eventListeners = [];
|
|
137
178
|
this._agentEventQueue = Promise.resolve();
|
|
179
|
+
/**
|
|
180
|
+
* Exact message objects whose message_end persistence is still queued.
|
|
181
|
+
* Agent core appends messages to agent.state.messages before emitting
|
|
182
|
+
* message_end; until that event settles on _agentEventQueue, compaction must
|
|
183
|
+
* treat these identities as pending persistence, never as stale or droppable.
|
|
184
|
+
*/
|
|
185
|
+
this._messageEndsAwaitingPersistence = new Set();
|
|
138
186
|
this._isAgentRunActive = false;
|
|
187
|
+
this._promptStartPending = false;
|
|
139
188
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
140
189
|
this._steeringMessages = [];
|
|
141
190
|
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
142
191
|
this._followUpMessages = [];
|
|
143
192
|
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
144
193
|
this._pendingNextTurnMessages = [];
|
|
194
|
+
// Queues held while the first post-compaction response is classified. Agent
|
|
195
|
+
// core otherwise drains steering immediately before AgentSession can consume
|
|
196
|
+
// the stale-usage exemption and schedule the continuation itself.
|
|
197
|
+
this._postCompactionDeferredSteeringMessages = [];
|
|
198
|
+
this._postCompactionDeferredFollowUpMessages = [];
|
|
145
199
|
// Compaction state
|
|
146
200
|
this._compactionAbortController = undefined;
|
|
147
201
|
this._autoCompactionAbortController = undefined;
|
|
202
|
+
this._pendingCompactionAdmission = undefined;
|
|
203
|
+
this._compactionLifecycle = new CompactionLifecycleCoordinator();
|
|
148
204
|
this._sessionWorkBarrier = new SessionWorkBarrier();
|
|
149
205
|
this._overflowRecoveryAttempted = false;
|
|
206
|
+
// A retry continuation immediately follows an accepted compaction. Its first
|
|
207
|
+
// response must not retrigger threshold compaction from stale provider usage.
|
|
208
|
+
this._skipNextPostRetryCompactionCheck = false;
|
|
209
|
+
this._skipNextPostCompactionAssistantCheck = false;
|
|
210
|
+
this._scheduledContinuationRecompacted = false;
|
|
211
|
+
this._assistantsPendingAtCompaction = new WeakSet();
|
|
212
|
+
this._postCompactionUsageExemptAssistants = new WeakSet();
|
|
150
213
|
this._messageRevision = 0;
|
|
151
214
|
// Branch summarization state
|
|
152
215
|
this._branchSummaryAbortController = undefined;
|
|
@@ -159,6 +222,7 @@ export class AgentSession {
|
|
|
159
222
|
this._retryResolve = undefined;
|
|
160
223
|
this._userAbortPromise = undefined;
|
|
161
224
|
this._suppressQueuedContinuationAfterUserAbort = false;
|
|
225
|
+
this._extensionEventSignal = undefined;
|
|
162
226
|
// Bash execution state
|
|
163
227
|
this._bashAbortController = undefined;
|
|
164
228
|
this._pendingBashMessages = [];
|
|
@@ -176,14 +240,42 @@ export class AgentSession {
|
|
|
176
240
|
// Track last assistant message for auto-compaction check
|
|
177
241
|
this._lastAssistantMessage = undefined;
|
|
178
242
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
179
|
-
this._handleAgentEvent = (event) => {
|
|
243
|
+
this._handleAgentEvent = (event, signal) => {
|
|
244
|
+
// Agent core drains native steer/follow-up queues immediately after its
|
|
245
|
+
// final agent_end. This subscriber intentionally processes its own event
|
|
246
|
+
// queue asynchronously, so a later recovery rejection cannot abort that
|
|
247
|
+
// drain in time. agent_end itself is still an awaited synchronous boundary
|
|
248
|
+
// before that drain: transfer every required overflow or threshold
|
|
249
|
+
// compaction to AgentSession, retaining queues until recovery is accepted.
|
|
250
|
+
if (event.type === "agent_end") {
|
|
251
|
+
const lastAssistant = this._findLastAssistantInMessages(event.messages);
|
|
252
|
+
const requiredAutoCompaction = lastAssistant
|
|
253
|
+
? this._getRequiredAutoCompactionReason(lastAssistant)
|
|
254
|
+
: undefined;
|
|
255
|
+
if (requiredAutoCompaction) {
|
|
256
|
+
this.agent.suppressQueuedMessageDrain();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
180
259
|
// Create retry promise synchronously before queueing async processing.
|
|
181
260
|
// Agent.emit() calls this handler synchronously, and prompt() calls waitForRetry()
|
|
182
261
|
// as soon as agent.prompt() resolves. If _retryPromise is created only inside
|
|
183
262
|
// _processAgentEvent, slow earlier queued events can delay agent_end processing
|
|
184
263
|
// and waitForRetry() can miss the in-flight retry.
|
|
185
264
|
this._createRetryPromiseForAgentEnd(event);
|
|
186
|
-
|
|
265
|
+
// The message object is already in agent.state.messages when message_end
|
|
266
|
+
// fires; track its exact identity until this event's queued processing
|
|
267
|
+
// settles so compaction can distinguish pending persistence from stale state.
|
|
268
|
+
const pendingMessage = event.type === "message_end" ? event.message : undefined;
|
|
269
|
+
if (pendingMessage !== undefined) {
|
|
270
|
+
this._messageEndsAwaitingPersistence.add(pendingMessage);
|
|
271
|
+
}
|
|
272
|
+
const processing = this._agentEventQueue.then(() => this._processAgentEvent(event, signal), () => this._processAgentEvent(event, signal));
|
|
273
|
+
this._agentEventQueue =
|
|
274
|
+
pendingMessage !== undefined
|
|
275
|
+
? processing.finally(() => {
|
|
276
|
+
this._messageEndsAwaitingPersistence.delete(pendingMessage);
|
|
277
|
+
})
|
|
278
|
+
: processing;
|
|
187
279
|
// Keep queue alive if an event handler fails
|
|
188
280
|
this._agentEventQueue.catch(() => { });
|
|
189
281
|
};
|
|
@@ -221,8 +313,9 @@ export class AgentSession {
|
|
|
221
313
|
persistDefault: false,
|
|
222
314
|
appendSessionEntry: true,
|
|
223
315
|
entryReason: reason,
|
|
224
|
-
emitModelSelect:
|
|
225
|
-
|
|
316
|
+
emitModelSelect: true,
|
|
317
|
+
modelSelectSource: reason,
|
|
318
|
+
invalidateCompaction: true,
|
|
226
319
|
ephemeralThinkingLevel: thinking,
|
|
227
320
|
});
|
|
228
321
|
},
|
|
@@ -268,7 +361,7 @@ export class AgentSession {
|
|
|
268
361
|
}
|
|
269
362
|
throw error;
|
|
270
363
|
}
|
|
271
|
-
if (result
|
|
364
|
+
if (result && (result.auth.apiKey || result.auth.headers)) {
|
|
272
365
|
return {
|
|
273
366
|
apiKey: result.auth.apiKey,
|
|
274
367
|
headers: withoutDeletedHeaders(result.auth.headers),
|
|
@@ -284,26 +377,31 @@ export class AgentSession {
|
|
|
284
377
|
}
|
|
285
378
|
throw new Error(formatNoApiKeyFoundMessage(model.provider));
|
|
286
379
|
}
|
|
287
|
-
|
|
288
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Resolve optional auth for a summarization stream. Native/custom stream
|
|
382
|
+
* functions may provide ambient credentials, unlike streamSimple.
|
|
383
|
+
*/
|
|
384
|
+
async _getSummarizationRequestAuth(model) {
|
|
385
|
+
if (this.agent.streamFunction === streamSimple) {
|
|
289
386
|
return this._getRequiredRequestAuth(model);
|
|
290
387
|
}
|
|
291
|
-
const compatibility = this._modelRuntime.getCompatibilityRequestConfig(model);
|
|
292
388
|
try {
|
|
293
389
|
const result = await this._modelRuntime.getAuth(model);
|
|
294
390
|
return result
|
|
295
|
-
? {
|
|
296
|
-
|
|
297
|
-
headers: withoutDeletedHeaders(result.auth.headers),
|
|
298
|
-
extraBody: compatibility.extraBody,
|
|
299
|
-
env: result.env,
|
|
300
|
-
}
|
|
301
|
-
: { extraBody: compatibility.extraBody };
|
|
391
|
+
? { apiKey: result.auth.apiKey, headers: withoutDeletedHeaders(result.auth.headers), env: result.env }
|
|
392
|
+
: {};
|
|
302
393
|
}
|
|
303
394
|
catch {
|
|
304
|
-
return {
|
|
395
|
+
return {};
|
|
305
396
|
}
|
|
306
397
|
}
|
|
398
|
+
async _getCompactionRequestAuth(model) {
|
|
399
|
+
const auth = await this._getSummarizationRequestAuth(model);
|
|
400
|
+
return {
|
|
401
|
+
...auth,
|
|
402
|
+
extraBody: this._modelRuntime.getCompatibilityRequestConfig(model).extraBody,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
307
405
|
/**
|
|
308
406
|
* Install tool hooks once on the Agent instance.
|
|
309
407
|
*
|
|
@@ -354,6 +452,7 @@ export class AgentSession {
|
|
|
354
452
|
content: result.content,
|
|
355
453
|
details: result.details,
|
|
356
454
|
isError,
|
|
455
|
+
usage: result.usage,
|
|
357
456
|
});
|
|
358
457
|
if (!hookResult) {
|
|
359
458
|
return undefined;
|
|
@@ -362,6 +461,7 @@ export class AgentSession {
|
|
|
362
461
|
content: hookResult.content,
|
|
363
462
|
details: hookResult.details,
|
|
364
463
|
isError: hookResult.isError ?? isError,
|
|
464
|
+
usage: hookResult.usage,
|
|
365
465
|
};
|
|
366
466
|
}
|
|
367
467
|
_installAgentNextTurnRefresh() {
|
|
@@ -370,31 +470,68 @@ export class AgentSession {
|
|
|
370
470
|
? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
|
|
371
471
|
: undefined);
|
|
372
472
|
this.agent.prepareNextTurnWithContext = async (turn, signal) => {
|
|
373
|
-
|
|
374
|
-
|
|
473
|
+
// Enforce compaction only when this prepare precedes an actual provider
|
|
474
|
+
// admission: a tool continuation or queued steer/follow-up messages. A
|
|
475
|
+
// completed turn with no continuation keeps pre-PR timing, while the
|
|
476
|
+
// prior prepare callback and context refresh below still run every turn.
|
|
477
|
+
const compactBeforeNextAdmission = async () => {
|
|
478
|
+
if (turn.toolResults.length === 0 && !this.agent.hasQueuedMessages()) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
375
481
|
await this._agentEventQueue;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
482
|
+
// A queue can be cleared while waiting for persistence. Re-sample it
|
|
483
|
+
// immediately before compaction so a completed turn never compacts
|
|
484
|
+
// merely because it once had a possible continuation.
|
|
485
|
+
if (turn.toolResults.length === 0 && !this.agent.hasQueuedMessages()) {
|
|
486
|
+
return false;
|
|
379
487
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
488
|
+
try {
|
|
489
|
+
return await this._enforceCompactionBeforeProvider(turn.message, true, "threshold");
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
if (error instanceof RequiredCompactionError && this.agent.hasQueuedMessages()) {
|
|
493
|
+
this._requiredCompactionAdmissionError = error;
|
|
385
494
|
}
|
|
495
|
+
throw error;
|
|
386
496
|
}
|
|
387
|
-
}
|
|
497
|
+
};
|
|
498
|
+
const compactedBeforeCallback = await compactBeforeNextAdmission();
|
|
499
|
+
const messages = compactedBeforeCallback ? this.agent.state.messages.slice() : turn.context.messages;
|
|
388
500
|
const postCompactionTurn = {
|
|
389
501
|
...turn,
|
|
390
502
|
context: { ...turn.context, messages },
|
|
391
503
|
};
|
|
392
|
-
|
|
393
|
-
|
|
504
|
+
let previousSnapshot = await previousPrepareNextTurnWithContext?.(postCompactionTurn, signal);
|
|
505
|
+
let previousContext = previousSnapshot?.context ?? postCompactionTurn.context;
|
|
506
|
+
// The previous callback may await while agent_end extensions enqueue
|
|
507
|
+
// continuation work. Re-sample after it returns so that work cannot
|
|
508
|
+
// slip through with the stale provider snapshot it observed on entry.
|
|
509
|
+
let compactedAfterCallback = false;
|
|
510
|
+
if (!compactedBeforeCallback) {
|
|
511
|
+
compactedAfterCallback = await compactBeforeNextAdmission();
|
|
512
|
+
}
|
|
513
|
+
if (compactedAfterCallback) {
|
|
514
|
+
// The callback's first result describes the stale pre-compaction
|
|
515
|
+
// context. Reapply it once to the compacted context so any host
|
|
516
|
+
// transformation reaches the provider request. Do not re-sample after
|
|
517
|
+
// this invocation: one replay is the bounded admission path.
|
|
518
|
+
const postLateCompactionTurn = {
|
|
519
|
+
...turn,
|
|
520
|
+
context: { ...turn.context, messages: this.agent.state.messages.slice() },
|
|
521
|
+
};
|
|
522
|
+
const postLateCompactionSnapshot = await previousPrepareNextTurnWithContext?.(postLateCompactionTurn, signal);
|
|
523
|
+
previousSnapshot = {
|
|
524
|
+
...previousSnapshot,
|
|
525
|
+
...postLateCompactionSnapshot,
|
|
526
|
+
context: postLateCompactionSnapshot?.context ?? postLateCompactionTurn.context,
|
|
527
|
+
};
|
|
528
|
+
previousContext = previousSnapshot.context ?? postLateCompactionTurn.context;
|
|
529
|
+
}
|
|
394
530
|
return {
|
|
395
531
|
...previousSnapshot,
|
|
396
532
|
context: {
|
|
397
533
|
...previousContext,
|
|
534
|
+
messages: previousContext.messages,
|
|
398
535
|
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
399
536
|
tools: this.agent.state.tools.slice(),
|
|
400
537
|
},
|
|
@@ -421,6 +558,7 @@ export class AgentSession {
|
|
|
421
558
|
}
|
|
422
559
|
_incrementMessageRevision() {
|
|
423
560
|
this._messageRevision++;
|
|
561
|
+
this._blockedPostCompactionAssistant = undefined;
|
|
424
562
|
}
|
|
425
563
|
getMessageRevision() {
|
|
426
564
|
return this._messageRevision;
|
|
@@ -481,14 +619,46 @@ export class AgentSession {
|
|
|
481
619
|
}
|
|
482
620
|
async _promptAgent(messages) {
|
|
483
621
|
this._isAgentRunActive = true;
|
|
622
|
+
this._requiredCompactionAdmissionError = undefined;
|
|
484
623
|
try {
|
|
485
624
|
await this.agent.prompt(messages);
|
|
625
|
+
// AgentSession's subscriber intentionally queues event work instead of
|
|
626
|
+
// blocking Agent core. Wait for this run's queued recovery decision
|
|
627
|
+
// before reporting prompt completion to the caller.
|
|
628
|
+
await this._agentEventQueue;
|
|
629
|
+
const requiredCompactionError = this._requiredCompactionAdmissionError;
|
|
630
|
+
this._requiredCompactionAdmissionError = undefined;
|
|
631
|
+
if (requiredCompactionError) {
|
|
632
|
+
throw requiredCompactionError;
|
|
633
|
+
}
|
|
486
634
|
}
|
|
487
635
|
catch (error) {
|
|
636
|
+
if (error instanceof Error &&
|
|
637
|
+
error.message ===
|
|
638
|
+
"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.") {
|
|
639
|
+
const queuedMessages = Array.isArray(messages) ? messages : [messages];
|
|
640
|
+
for (const message of queuedMessages)
|
|
641
|
+
this.agent.steer(message);
|
|
642
|
+
const userMessage = queuedMessages.find((message) => message.role === "user");
|
|
643
|
+
if (userMessage?.role === "user") {
|
|
644
|
+
this._steeringMessages.push(this._extractUserMessageText(userMessage.content));
|
|
645
|
+
this._emitQueueUpdate();
|
|
646
|
+
}
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
488
649
|
await this._emitAgentSettled();
|
|
489
650
|
throw error;
|
|
490
651
|
}
|
|
491
652
|
}
|
|
653
|
+
/** Extract text content used to track fork-owned queued user messages. */
|
|
654
|
+
_extractUserMessageText(content) {
|
|
655
|
+
if (typeof content === "string")
|
|
656
|
+
return content;
|
|
657
|
+
return content
|
|
658
|
+
.filter((part) => part.type === "text")
|
|
659
|
+
.map((part) => part.text)
|
|
660
|
+
.join("");
|
|
661
|
+
}
|
|
492
662
|
_createRetryPromiseForAgentEnd(event) {
|
|
493
663
|
if (event.type !== "agent_end" || this._retryPromise) {
|
|
494
664
|
return;
|
|
@@ -505,6 +675,10 @@ export class AgentSession {
|
|
|
505
675
|
this._retryPromise = new Promise((resolve) => {
|
|
506
676
|
this._retryResolve = resolve;
|
|
507
677
|
});
|
|
678
|
+
// Agent core normally drains queued input immediately after agent_end.
|
|
679
|
+
// Retry owns that continuation until its final provider-admission check has
|
|
680
|
+
// either started it or reported a terminal rejection.
|
|
681
|
+
this.agent.suppressQueuedMessageDrain();
|
|
508
682
|
}
|
|
509
683
|
_findLastAssistantInMessages(messages) {
|
|
510
684
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -515,6 +689,91 @@ export class AgentSession {
|
|
|
515
689
|
}
|
|
516
690
|
return undefined;
|
|
517
691
|
}
|
|
692
|
+
/**
|
|
693
|
+
* Synchronously mirror the auto-compaction decision that _checkCompaction()
|
|
694
|
+
* will make after agent_end. Agent core drains queues before that async work
|
|
695
|
+
* runs, so only this preflight can transfer required admissions safely.
|
|
696
|
+
*/
|
|
697
|
+
_getRequiredAutoCompactionReason(message) {
|
|
698
|
+
const reason = this._getAutoCompactionReason(message);
|
|
699
|
+
// Retry and post-compaction exemptions only cover stale usage estimates.
|
|
700
|
+
// A provider-confirmed overflow must always retain queue ownership and run
|
|
701
|
+
// fail-closed recovery.
|
|
702
|
+
if (reason === "overflow" &&
|
|
703
|
+
message.stopReason === "stop" &&
|
|
704
|
+
this._hasPendingPostCompactionUsageExemption(message)) {
|
|
705
|
+
// A successful post-compaction response can carry stale provider usage.
|
|
706
|
+
// Retain its queues while the asynchronous check consumes the exemption.
|
|
707
|
+
return "threshold";
|
|
708
|
+
}
|
|
709
|
+
if (reason === "overflow")
|
|
710
|
+
return reason;
|
|
711
|
+
// Keep queued continuations under AgentSession ownership while the
|
|
712
|
+
// asynchronous check consumes this post-compaction usage exemption.
|
|
713
|
+
if (this._hasPendingPostCompactionUsageExemption(message))
|
|
714
|
+
return "threshold";
|
|
715
|
+
if (reason === "threshold" &&
|
|
716
|
+
(this._skipNextPostRetryCompactionCheck || this._postCompactionUsageExemptAssistants.has(message))) {
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
719
|
+
return reason;
|
|
720
|
+
}
|
|
721
|
+
_getAutoCompactionReason(message) {
|
|
722
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
723
|
+
if (!settings.enabled || message.stopReason === "aborted") {
|
|
724
|
+
return undefined;
|
|
725
|
+
}
|
|
726
|
+
const model = this.model;
|
|
727
|
+
if (!model || this._isAssistantFromBeforeLatestCompaction(message)) {
|
|
728
|
+
return undefined;
|
|
729
|
+
}
|
|
730
|
+
const sameModel = isSameOverflowSource(message, model, this._modelRuntime.getCompatibilityRequestConfig(model).upstreamModelId);
|
|
731
|
+
const contextUsage = this.getContextUsage();
|
|
732
|
+
const currentContextNeedsCompaction = contextUsage !== undefined &&
|
|
733
|
+
contextUsage.tokens !== null &&
|
|
734
|
+
shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
|
|
735
|
+
if (isContextOverflow(message, model.contextWindow) && (sameModel || currentContextNeedsCompaction)) {
|
|
736
|
+
return "overflow";
|
|
737
|
+
}
|
|
738
|
+
let contextTokens;
|
|
739
|
+
const directContextTokens = message.usage ? calculateContextTokens(message.usage) : 0;
|
|
740
|
+
if (message.stopReason !== "error" && directContextTokens !== 0) {
|
|
741
|
+
contextTokens = directContextTokens;
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
const messages = filterContextExcludedMessages(this.agent.state.messages);
|
|
745
|
+
const estimate = estimateContextTokens(messages);
|
|
746
|
+
if (estimate.lastUsageIndex === null) {
|
|
747
|
+
return undefined;
|
|
748
|
+
}
|
|
749
|
+
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
750
|
+
const usageMessage = messages[estimate.lastUsageIndex];
|
|
751
|
+
if (compactionEntry &&
|
|
752
|
+
usageMessage?.role === "assistant" &&
|
|
753
|
+
this._isAssistantFromBeforeLatestCompaction(usageMessage)) {
|
|
754
|
+
return undefined;
|
|
755
|
+
}
|
|
756
|
+
contextTokens = estimate.tokens;
|
|
757
|
+
}
|
|
758
|
+
return shouldCompact(contextTokens, model.contextWindow, settings) ? "threshold" : undefined;
|
|
759
|
+
}
|
|
760
|
+
_hasPendingPostCompactionUsageExemption(message) {
|
|
761
|
+
return (this._skipNextPostCompactionAssistantCheck &&
|
|
762
|
+
!this._assistantsPendingAtCompaction.has(message) &&
|
|
763
|
+
!this._overflowRecoveryAttempted);
|
|
764
|
+
}
|
|
765
|
+
_isPostCompactionUsageExempt(message) {
|
|
766
|
+
return (this._postCompactionUsageExemptAssistants.has(message) || this._hasPendingPostCompactionUsageExemption(message));
|
|
767
|
+
}
|
|
768
|
+
_consumePostCompactionUsageExemption(message) {
|
|
769
|
+
if (this._postCompactionUsageExemptAssistants.has(message))
|
|
770
|
+
return true;
|
|
771
|
+
if (!this._isPostCompactionUsageExempt(message))
|
|
772
|
+
return false;
|
|
773
|
+
this._skipNextPostCompactionAssistantCheck = false;
|
|
774
|
+
this._postCompactionUsageExemptAssistants.add(message);
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
518
777
|
_agentEndAllowsQueuedContinuation(messages) {
|
|
519
778
|
let lastAssistantIndex = -1;
|
|
520
779
|
for (let index = messages.length - 1; index >= 0; index--) {
|
|
@@ -533,6 +792,9 @@ export class AgentSession {
|
|
|
533
792
|
if (lastAssistant.stopReason === "aborted" || lastAssistant.stopReason === "error") {
|
|
534
793
|
return false;
|
|
535
794
|
}
|
|
795
|
+
if (this._getRequiredAutoCompactionReason(lastAssistant)) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
536
798
|
for (let index = lastAssistantIndex + 1; index < messages.length; index++) {
|
|
537
799
|
const message = messages[index];
|
|
538
800
|
if (message?.role === "toolResult" &&
|
|
@@ -572,13 +834,13 @@ export class AgentSession {
|
|
|
572
834
|
}
|
|
573
835
|
return providerDelayMs <= this.settingsManager.getProviderRetrySettings().maxRetryDelayMs;
|
|
574
836
|
}
|
|
575
|
-
async _processAgentEvent(event) {
|
|
837
|
+
async _processAgentEvent(event, signal) {
|
|
576
838
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
577
839
|
// This ensures the UI sees the updated queue state
|
|
578
840
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
579
841
|
this._overflowRecoveryAttempted = false;
|
|
580
842
|
this._retryFallback.resetTurn();
|
|
581
|
-
const messageText =
|
|
843
|
+
const messageText = contentText(event.message.content, "");
|
|
582
844
|
if (messageText) {
|
|
583
845
|
// Check steering queue first
|
|
584
846
|
const steeringIndex = this._steeringMessages.indexOf(messageText);
|
|
@@ -596,8 +858,15 @@ export class AgentSession {
|
|
|
596
858
|
}
|
|
597
859
|
}
|
|
598
860
|
}
|
|
599
|
-
// Emit to extensions first
|
|
600
|
-
|
|
861
|
+
// Emit to extensions first. Agent event persistence is intentionally
|
|
862
|
+
// asynchronous, so retain the source run signal while dispatching.
|
|
863
|
+
this._extensionEventSignal = signal;
|
|
864
|
+
try {
|
|
865
|
+
await this._emitExtensionEvent(event);
|
|
866
|
+
}
|
|
867
|
+
finally {
|
|
868
|
+
this._extensionEventSignal = undefined;
|
|
869
|
+
}
|
|
601
870
|
// Notify all listeners
|
|
602
871
|
this._emit(event.type === "agent_end" ? { ...event, willRetry: this._willRetryAfterAgentEnd(event.messages) } : event);
|
|
603
872
|
// Handle session persistence
|
|
@@ -643,8 +912,10 @@ export class AgentSession {
|
|
|
643
912
|
}
|
|
644
913
|
}
|
|
645
914
|
}
|
|
646
|
-
// Check auto-retry and auto-compaction after agent completes
|
|
915
|
+
// Check auto-retry and auto-compaction after agent completes.
|
|
647
916
|
let launchedContinuation = false;
|
|
917
|
+
let retryContinuationBlocked = false;
|
|
918
|
+
let allowsPostCompactionUsageExemptContinuation = false;
|
|
648
919
|
const userAbortSuppressedQueuedContinuation = event.type === "agent_end" && this._suppressQueuedContinuationAfterUserAbort;
|
|
649
920
|
if (userAbortSuppressedQueuedContinuation) {
|
|
650
921
|
this._suppressQueuedContinuationAfterUserAbort = false;
|
|
@@ -655,23 +926,62 @@ export class AgentSession {
|
|
|
655
926
|
if (event.type === "agent_end" && this._lastAssistantMessage) {
|
|
656
927
|
const msg = this._lastAssistantMessage;
|
|
657
928
|
this._lastAssistantMessage = undefined;
|
|
929
|
+
this._skipNextPostRetryCompactionCheck = false;
|
|
930
|
+
const requiredAutoCompaction = this._getRequiredAutoCompactionReason(msg);
|
|
658
931
|
// Retry transient failures normally and eligible hard errors only through a fallback.
|
|
659
932
|
const retryableError = this._isRetryableError(msg);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
933
|
+
const hardErrorFallbackEligible = this._isHardErrorFallbackEligible(msg);
|
|
934
|
+
const retryCanAdmitProvider = this.settingsManager.getRetrySettings().enabled && (retryableError || hardErrorFallbackEligible);
|
|
935
|
+
let compactedBeforeRetry = false;
|
|
936
|
+
if (retryCanAdmitProvider &&
|
|
937
|
+
requiredAutoCompaction &&
|
|
938
|
+
!(requiredAutoCompaction === "threshold" && this._hasPendingPostCompactionUsageExemption(msg))) {
|
|
939
|
+
this._retireFailedRetryAssistant(msg);
|
|
940
|
+
compactedBeforeRetry = await this._runPrePromptCompaction(msg, true, "threshold", true);
|
|
941
|
+
retryContinuationBlocked = !compactedBeforeRetry;
|
|
663
942
|
}
|
|
664
|
-
|
|
665
|
-
|
|
943
|
+
let retryOutcome = "not-handled";
|
|
944
|
+
if (!retryContinuationBlocked) {
|
|
945
|
+
if (retryableError) {
|
|
946
|
+
retryOutcome = await this._handleRetryableError(msg);
|
|
947
|
+
}
|
|
948
|
+
else if (hardErrorFallbackEligible) {
|
|
949
|
+
retryOutcome = await this._handleRetryableError(msg, { hardErrorFallback: true });
|
|
950
|
+
}
|
|
666
951
|
}
|
|
667
|
-
if (
|
|
668
|
-
return;
|
|
952
|
+
if (retryOutcome === "continued")
|
|
953
|
+
return;
|
|
669
954
|
this._resolveRetry();
|
|
670
|
-
|
|
955
|
+
retryContinuationBlocked ||= retryOutcome === "blocked";
|
|
956
|
+
if (!retryContinuationBlocked) {
|
|
957
|
+
if (compactedBeforeRetry && this.agent.hasQueuedMessages()) {
|
|
958
|
+
this._scheduleContinuationAfterCurrentEvent();
|
|
959
|
+
launchedContinuation = true;
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
launchedContinuation = await this._checkCompaction(msg);
|
|
963
|
+
allowsPostCompactionUsageExemptContinuation = this._postCompactionUsageExemptAssistants.has(msg);
|
|
964
|
+
if (allowsPostCompactionUsageExemptContinuation) {
|
|
965
|
+
this._flushPostCompactionDeferredMessages();
|
|
966
|
+
}
|
|
967
|
+
// _runAutoCompaction() returns false both when recovery was rejected and
|
|
968
|
+
// when an accepted compaction had no queue to continue. Re-sample after
|
|
969
|
+
// it settles so only the still-required (rejected) case fails admission.
|
|
970
|
+
if (requiredAutoCompaction &&
|
|
971
|
+
!launchedContinuation &&
|
|
972
|
+
this.agent.hasQueuedMessages() &&
|
|
973
|
+
this._getRequiredAutoCompactionReason(msg) !== undefined) {
|
|
974
|
+
this._requiredCompactionAdmissionError = new RequiredCompactionError();
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
671
978
|
}
|
|
672
979
|
if (event.type === "agent_end") {
|
|
673
980
|
this._flushPendingBashMessages();
|
|
674
|
-
if (!launchedContinuation &&
|
|
981
|
+
if (!launchedContinuation &&
|
|
982
|
+
!retryContinuationBlocked &&
|
|
983
|
+
(allowsQueuedContinuation || allowsPostCompactionUsageExemptContinuation) &&
|
|
984
|
+
this.agent.hasQueuedMessages()) {
|
|
675
985
|
this._scheduleContinuationAfterCurrentEvent();
|
|
676
986
|
launchedContinuation = true;
|
|
677
987
|
}
|
|
@@ -688,16 +998,6 @@ export class AgentSession {
|
|
|
688
998
|
this._retryPromise = undefined;
|
|
689
999
|
}
|
|
690
1000
|
}
|
|
691
|
-
/** Extract text content from a message */
|
|
692
|
-
_getUserMessageText(message) {
|
|
693
|
-
if (message.role !== "user")
|
|
694
|
-
return "";
|
|
695
|
-
const content = message.content;
|
|
696
|
-
if (typeof content === "string")
|
|
697
|
-
return content;
|
|
698
|
-
const textBlocks = content.filter((c) => c.type === "text");
|
|
699
|
-
return textBlocks.map((c) => c.text).join("");
|
|
700
|
-
}
|
|
701
1001
|
/** Find the last assistant message in agent state (including aborted ones) */
|
|
702
1002
|
_findLastAssistantMessage() {
|
|
703
1003
|
const messages = this.agent.state.messages;
|
|
@@ -709,9 +1009,47 @@ export class AgentSession {
|
|
|
709
1009
|
}
|
|
710
1010
|
return undefined;
|
|
711
1011
|
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Retry failures stay in append-only history but must not be retained in the
|
|
1014
|
+
* active context branch. Otherwise split-turn compaction keeps the failed
|
|
1015
|
+
* assistant response verbatim and cannot make progress before a retry.
|
|
1016
|
+
*/
|
|
1017
|
+
_retireFailedRetryAssistant(message) {
|
|
1018
|
+
const position = this.sessionManager.getMessageEntryPosition(message);
|
|
1019
|
+
if (!position || this.sessionManager.getLeafId() !== position.entryId)
|
|
1020
|
+
return;
|
|
1021
|
+
const entry = this.sessionManager.getEntry(position.entryId);
|
|
1022
|
+
if (entry?.type !== "message")
|
|
1023
|
+
return;
|
|
1024
|
+
if (entry.parentId === null) {
|
|
1025
|
+
this.sessionManager.resetLeaf();
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
this.sessionManager.branch(entry.parentId);
|
|
1029
|
+
}
|
|
1030
|
+
const messageIndex = this.agent.state.messages.lastIndexOf(message);
|
|
1031
|
+
if (messageIndex !== -1) {
|
|
1032
|
+
this.agent.state.messages = this.agent.state.messages.slice(0, messageIndex);
|
|
1033
|
+
}
|
|
1034
|
+
this._incrementMessageRevision();
|
|
1035
|
+
}
|
|
712
1036
|
_isAssistantFromBeforeLatestCompaction(assistantMessage) {
|
|
713
1037
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
714
|
-
|
|
1038
|
+
if (compactionEntry === null)
|
|
1039
|
+
return false;
|
|
1040
|
+
// An agent message_end can still be awaiting persistence while compaction
|
|
1041
|
+
// commits. It is necessarily a post-boundary message, even when a provider
|
|
1042
|
+
// supplied an older payload timestamp.
|
|
1043
|
+
if (this._messageEndsAwaitingPersistence.has(assistantMessage))
|
|
1044
|
+
return false;
|
|
1045
|
+
const messagePosition = this.sessionManager.getMessageEntryPosition(assistantMessage);
|
|
1046
|
+
const compactionOrder = this.sessionManager.getEntryOrder(compactionEntry.id);
|
|
1047
|
+
if (messagePosition !== undefined && compactionOrder !== undefined) {
|
|
1048
|
+
return messagePosition.order <= compactionOrder;
|
|
1049
|
+
}
|
|
1050
|
+
// Reloaded/reconstructed messages have no runtime identity. Retain the
|
|
1051
|
+
// historical timestamp heuristic only for that compatibility path.
|
|
1052
|
+
return assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime();
|
|
715
1053
|
}
|
|
716
1054
|
_replaceMessageInPlace(target, replacement) {
|
|
717
1055
|
// Agent-core stores the finalized message object in its state before emitting message_end.
|
|
@@ -1006,17 +1344,28 @@ export class AgentSession {
|
|
|
1006
1344
|
validToolNames.push(name);
|
|
1007
1345
|
}
|
|
1008
1346
|
}
|
|
1347
|
+
const activeToolNamesChanged = validToolNames.length !== this.agent.state.tools.length ||
|
|
1348
|
+
validToolNames.some((name, index) => name !== this.agent.state.tools[index]?.name);
|
|
1009
1349
|
this.agent.state.tools = tools;
|
|
1010
1350
|
// Rebuild base system prompt with new tool set
|
|
1011
1351
|
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
1012
1352
|
this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
|
|
1353
|
+
if (activeToolNamesChanged) {
|
|
1354
|
+
this.abortCompaction();
|
|
1355
|
+
this._incrementMessageRevision();
|
|
1356
|
+
}
|
|
1013
1357
|
}
|
|
1014
1358
|
/** Whether compaction or branch summarization is currently running */
|
|
1015
1359
|
get isCompacting() {
|
|
1016
|
-
return (this.
|
|
1360
|
+
return (this._pendingCompactionAdmission !== undefined ||
|
|
1361
|
+
this._compactionLifecycle.state.status === "running" ||
|
|
1362
|
+
this._autoCompactionAbortController !== undefined ||
|
|
1017
1363
|
this._compactionAbortController !== undefined ||
|
|
1018
1364
|
this._branchSummaryAbortController !== undefined);
|
|
1019
1365
|
}
|
|
1366
|
+
get compactionState() {
|
|
1367
|
+
return this._compactionLifecycle.state;
|
|
1368
|
+
}
|
|
1020
1369
|
/** All messages including custom types like BashExecutionMessage */
|
|
1021
1370
|
get messages() {
|
|
1022
1371
|
return this.agent.state.messages;
|
|
@@ -1162,28 +1511,52 @@ export class AgentSession {
|
|
|
1162
1511
|
await userAbortPromise;
|
|
1163
1512
|
throwIfCancelled();
|
|
1164
1513
|
}
|
|
1165
|
-
const
|
|
1514
|
+
const ownsPromptStart = !this.isStreaming && !this._promptStartPending && options?.streamingBehavior === undefined;
|
|
1515
|
+
if (ownsPromptStart)
|
|
1516
|
+
this._promptStartPending = true;
|
|
1517
|
+
// Extension bindings deliberately fire-and-forget sendUserMessage(), so an
|
|
1518
|
+
// extension callback cannot deadlock on this wait. The resulting provider
|
|
1519
|
+
// turn must still serialize behind compaction and other session work just
|
|
1520
|
+
// like an interactive prompt does.
|
|
1521
|
+
const shouldWaitForSessionWork = true;
|
|
1522
|
+
const pendingCompactionAdmissionAtExtensionAdmission = options?.source === "extension" ? this._pendingCompactionAdmission : undefined;
|
|
1523
|
+
const compactionGenerationAtExtensionAdmission = options?.source === "extension" &&
|
|
1524
|
+
this._sessionWorkBarrier.hasActiveWork &&
|
|
1525
|
+
this._compactionLifecycle.state.status !== "idle"
|
|
1526
|
+
? this._compactionLifecycle.state.generation
|
|
1527
|
+
: undefined;
|
|
1166
1528
|
// A steer/followUp submission during an active run can be queued immediately.
|
|
1167
1529
|
// Waiting on the session-work barrier here would trap the message inside this
|
|
1168
1530
|
// call for the rest of the run whenever a queued continuation (e.g. an active
|
|
1169
1531
|
// goal chain) holds the barrier, making typed input invisible until the run
|
|
1170
1532
|
// ends or the user aborts.
|
|
1171
|
-
const canQueueWhileStreaming = this.isStreaming
|
|
1533
|
+
const canQueueWhileStreaming = (this.isStreaming || this._promptStartPending) &&
|
|
1534
|
+
!this.isCompacting &&
|
|
1535
|
+
options?.streamingBehavior !== undefined;
|
|
1172
1536
|
if (shouldWaitForSessionWork &&
|
|
1173
1537
|
!canQueueWhileStreaming &&
|
|
1174
1538
|
(!this.isStreaming || this.isCompacting || this._sessionWorkBarrier.hasActiveWork)) {
|
|
1175
1539
|
await this._waitForSettledSessionWork();
|
|
1176
1540
|
throwIfCancelled();
|
|
1541
|
+
options?.onSessionWorkReady?.();
|
|
1177
1542
|
}
|
|
1178
1543
|
// Turn boundary: restore the primary model if the fallback cooldown expired.
|
|
1179
1544
|
if (!this.isStreaming) {
|
|
1180
|
-
|
|
1545
|
+
try {
|
|
1546
|
+
await this._maybeRestoreFallbackPrimary();
|
|
1547
|
+
}
|
|
1548
|
+
catch (error) {
|
|
1549
|
+
if (ownsPromptStart)
|
|
1550
|
+
this._promptStartPending = false;
|
|
1551
|
+
throw error;
|
|
1552
|
+
}
|
|
1181
1553
|
}
|
|
1182
1554
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
1183
1555
|
const preflightResult = options?.preflightResult;
|
|
1184
1556
|
const promptDisposition = options?.promptDisposition;
|
|
1185
1557
|
let messages;
|
|
1186
1558
|
let titlePrompt;
|
|
1559
|
+
let consumedNextTurnMessages;
|
|
1187
1560
|
try {
|
|
1188
1561
|
// Handle extension commands first (execute immediately, even during streaming)
|
|
1189
1562
|
// Extension commands manage their own LLM interaction via pi.sendMessage()
|
|
@@ -1238,6 +1611,23 @@ export class AgentSession {
|
|
|
1238
1611
|
preflightResult?.(true);
|
|
1239
1612
|
return;
|
|
1240
1613
|
}
|
|
1614
|
+
// Input accepted while a run was active remains queued even if that run
|
|
1615
|
+
// ends while extension input handling or template expansion is pending.
|
|
1616
|
+
// Starting a fresh prompt here would let it overtake the held continuation.
|
|
1617
|
+
if (canQueueWhileStreaming && !this.isStreaming) {
|
|
1618
|
+
if (options?.thinkingLevel !== undefined) {
|
|
1619
|
+
throw new Error("Cannot set thinkingLevel on a queued prompt; set it after the current turn completes.");
|
|
1620
|
+
}
|
|
1621
|
+
if (options?.streamingBehavior === "followUp") {
|
|
1622
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
1623
|
+
}
|
|
1624
|
+
else {
|
|
1625
|
+
await this._queueSteer(expandedText, currentImages);
|
|
1626
|
+
}
|
|
1627
|
+
promptDisposition?.("queued");
|
|
1628
|
+
preflightResult?.(true);
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1241
1631
|
// The queue-while-streaming bypass above skipped the settled-work wait. If
|
|
1242
1632
|
// the run ended while input was being expanded, serialize with remaining
|
|
1243
1633
|
// session work before sending a fresh prompt, and re-queue if a scheduled
|
|
@@ -1262,6 +1652,25 @@ export class AgentSession {
|
|
|
1262
1652
|
return;
|
|
1263
1653
|
}
|
|
1264
1654
|
}
|
|
1655
|
+
// A background extension prompt that arrived during a manual compaction
|
|
1656
|
+
// must never overtake a rejected or aborted compaction. Keep its normal
|
|
1657
|
+
// queue ownership instead of attempting another provider admission.
|
|
1658
|
+
if ((pendingCompactionAdmissionAtExtensionAdmission !== undefined &&
|
|
1659
|
+
pendingCompactionAdmissionAtExtensionAdmission.outcome !== "completed") ||
|
|
1660
|
+
(compactionGenerationAtExtensionAdmission !== undefined &&
|
|
1661
|
+
this._compactionLifecycle.state.generation === compactionGenerationAtExtensionAdmission &&
|
|
1662
|
+
(this._compactionLifecycle.state.status === "failed" ||
|
|
1663
|
+
this._compactionLifecycle.state.status === "aborted"))) {
|
|
1664
|
+
if (options?.streamingBehavior === "followUp") {
|
|
1665
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
await this._queueSteer(expandedText, currentImages);
|
|
1669
|
+
}
|
|
1670
|
+
promptDisposition?.("queued");
|
|
1671
|
+
preflightResult?.(true);
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1265
1674
|
// Flush any pending bash messages before the new prompt
|
|
1266
1675
|
this._flushPendingBashMessages();
|
|
1267
1676
|
// Validate model
|
|
@@ -1279,19 +1688,8 @@ export class AgentSession {
|
|
|
1279
1688
|
}
|
|
1280
1689
|
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
1281
1690
|
}
|
|
1282
|
-
// Check if we need to compact before sending (catches aborted responses).
|
|
1283
1691
|
// The user's new prompt is sent below, so do not call agent.continue() here.
|
|
1284
|
-
|
|
1285
|
-
if (lastAssistant) {
|
|
1286
|
-
const compacted = await this._checkCompaction(lastAssistant, false, "pre_prompt");
|
|
1287
|
-
if (!compacted && !this._isAssistantFromBeforeLatestCompaction(lastAssistant)) {
|
|
1288
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1289
|
-
const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
|
|
1290
|
-
if (settings.enabled && this.model && shouldCompact(contextTokens, this.model.contextWindow, settings)) {
|
|
1291
|
-
throw new RequiredCompactionError();
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1692
|
+
await this._enforceCompactionBeforeProvider(this._findLastAssistantMessage(), false, "pre_prompt");
|
|
1295
1693
|
// Build messages array (custom message if any, then user message)
|
|
1296
1694
|
messages = [];
|
|
1297
1695
|
// Add user message
|
|
@@ -1304,11 +1702,14 @@ export class AgentSession {
|
|
|
1304
1702
|
content: userContent,
|
|
1305
1703
|
timestamp: Date.now(),
|
|
1306
1704
|
});
|
|
1307
|
-
//
|
|
1308
|
-
|
|
1705
|
+
// Consume next-turn messages transactionally: a final admission rejection
|
|
1706
|
+
// restores them in their original order rather than dropping or duplicating
|
|
1707
|
+
// one-shot extension state.
|
|
1708
|
+
consumedNextTurnMessages = this._pendingNextTurnMessages;
|
|
1709
|
+
this._pendingNextTurnMessages = [];
|
|
1710
|
+
for (const msg of consumedNextTurnMessages) {
|
|
1309
1711
|
messages.push(msg);
|
|
1310
1712
|
}
|
|
1311
|
-
this._pendingNextTurnMessages = [];
|
|
1312
1713
|
// Emit before_agent_start extension event
|
|
1313
1714
|
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPrompt, this._baseSystemPromptOptions);
|
|
1314
1715
|
// Add all custom messages from extensions
|
|
@@ -1335,11 +1736,22 @@ export class AgentSession {
|
|
|
1335
1736
|
this._systemPromptOverride = undefined;
|
|
1336
1737
|
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
1337
1738
|
}
|
|
1739
|
+
// The preflight above only sees persisted session context. These prompt,
|
|
1740
|
+
// next-turn, and before_agent_start additions are also provider-visible,
|
|
1741
|
+
// so make one final admission decision against the complete request.
|
|
1742
|
+
await this._enforceFinalProviderAdmission(messages);
|
|
1338
1743
|
}
|
|
1339
1744
|
catch (error) {
|
|
1745
|
+
if (consumedNextTurnMessages && consumedNextTurnMessages.length > 0) {
|
|
1746
|
+
this._pendingNextTurnMessages = [...consumedNextTurnMessages, ...this._pendingNextTurnMessages];
|
|
1747
|
+
}
|
|
1340
1748
|
preflightResult?.(false);
|
|
1341
1749
|
throw error;
|
|
1342
1750
|
}
|
|
1751
|
+
finally {
|
|
1752
|
+
if (ownsPromptStart)
|
|
1753
|
+
this._promptStartPending = false;
|
|
1754
|
+
}
|
|
1343
1755
|
if (!messages) {
|
|
1344
1756
|
return;
|
|
1345
1757
|
}
|
|
@@ -1352,7 +1764,14 @@ export class AgentSession {
|
|
|
1352
1764
|
await this._promptAgent(messages);
|
|
1353
1765
|
await this.waitForRetry();
|
|
1354
1766
|
await this.waitForIdle();
|
|
1355
|
-
if (
|
|
1767
|
+
if (options?.onSessionWorkReady) {
|
|
1768
|
+
// This prompt owns a session-work token acquired after waiting for a
|
|
1769
|
+
// prior operation, so waiting for the global barrier here would await
|
|
1770
|
+
// itself. _promptAgent() already drained its event queue; any newly
|
|
1771
|
+
// scheduled continuation retains its own barrier ownership.
|
|
1772
|
+
await this.agent.waitForIdle();
|
|
1773
|
+
}
|
|
1774
|
+
else if (shouldWaitForSessionWork) {
|
|
1356
1775
|
await this._waitForSettledSessionWork();
|
|
1357
1776
|
}
|
|
1358
1777
|
else {
|
|
@@ -1479,7 +1898,7 @@ export class AgentSession {
|
|
|
1479
1898
|
sessionId: this.sessionId,
|
|
1480
1899
|
baseOptions: this._buildSessionTitleBaseOptions(),
|
|
1481
1900
|
signal: abortController.signal,
|
|
1482
|
-
streamFn: this.agent.
|
|
1901
|
+
streamFn: this.agent.streamFunction,
|
|
1483
1902
|
});
|
|
1484
1903
|
if (abortController.signal.aborted) {
|
|
1485
1904
|
return;
|
|
@@ -1532,11 +1951,12 @@ export class AgentSession {
|
|
|
1532
1951
|
if (images) {
|
|
1533
1952
|
content.push(...images);
|
|
1534
1953
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
}
|
|
1954
|
+
const message = { role: "user", content, timestamp: Date.now() };
|
|
1955
|
+
if (this._promptStartPending && this._skipNextPostCompactionAssistantCheck) {
|
|
1956
|
+
this._postCompactionDeferredSteeringMessages.push(message);
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
this.agent.steer(message);
|
|
1540
1960
|
}
|
|
1541
1961
|
/**
|
|
1542
1962
|
* Internal: Queue a follow-up message (already expanded, no extension command check).
|
|
@@ -1548,11 +1968,22 @@ export class AgentSession {
|
|
|
1548
1968
|
if (images) {
|
|
1549
1969
|
content.push(...images);
|
|
1550
1970
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
}
|
|
1971
|
+
const message = { role: "user", content, timestamp: Date.now() };
|
|
1972
|
+
if (this._promptStartPending && this._skipNextPostCompactionAssistantCheck) {
|
|
1973
|
+
this._postCompactionDeferredFollowUpMessages.push(message);
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
this.agent.followUp(message);
|
|
1977
|
+
}
|
|
1978
|
+
_flushPostCompactionDeferredMessages() {
|
|
1979
|
+
for (const message of this._postCompactionDeferredSteeringMessages) {
|
|
1980
|
+
this.agent.steer(message);
|
|
1981
|
+
}
|
|
1982
|
+
this._postCompactionDeferredSteeringMessages = [];
|
|
1983
|
+
for (const message of this._postCompactionDeferredFollowUpMessages) {
|
|
1984
|
+
this.agent.followUp(message);
|
|
1985
|
+
}
|
|
1986
|
+
this._postCompactionDeferredFollowUpMessages = [];
|
|
1556
1987
|
}
|
|
1557
1988
|
/**
|
|
1558
1989
|
* Throw an error if the text is an extension command.
|
|
@@ -1587,26 +2018,56 @@ export class AgentSession {
|
|
|
1587
2018
|
details: message.details,
|
|
1588
2019
|
timestamp: Date.now(),
|
|
1589
2020
|
};
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
2021
|
+
const waitForExistingSessionWork = options?.triggerTurn === true &&
|
|
2022
|
+
options.deliverAs !== "nextTurn" &&
|
|
2023
|
+
!this.isStreaming &&
|
|
2024
|
+
this._sessionWorkBarrier.hasActiveWork;
|
|
2025
|
+
const pendingCompactionAdmission = this._pendingCompactionAdmission;
|
|
2026
|
+
const activeCompactionGeneration = this._compactionLifecycle.state.status === "running" ? this._compactionLifecycle.state.generation : undefined;
|
|
2027
|
+
let finishSessionWork;
|
|
2028
|
+
try {
|
|
2029
|
+
if (waitForExistingSessionWork) {
|
|
2030
|
+
await this._waitForSettledSessionWork();
|
|
2031
|
+
finishSessionWork = this._sessionWorkBarrier.begin();
|
|
2032
|
+
}
|
|
2033
|
+
if (options?.deliverAs === "nextTurn") {
|
|
2034
|
+
this._pendingNextTurnMessages.push(appMessage);
|
|
2035
|
+
}
|
|
2036
|
+
else if (this.isStreaming) {
|
|
2037
|
+
if (options?.deliverAs === "followUp") {
|
|
2038
|
+
this.agent.followUp(appMessage);
|
|
2039
|
+
}
|
|
2040
|
+
else {
|
|
2041
|
+
this.agent.steer(appMessage);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
else if (options?.triggerTurn === true &&
|
|
2045
|
+
((pendingCompactionAdmission !== undefined && pendingCompactionAdmission.outcome !== "completed") ||
|
|
2046
|
+
(activeCompactionGeneration !== undefined &&
|
|
2047
|
+
this._compactionLifecycle.state.generation === activeCompactionGeneration &&
|
|
2048
|
+
this._compactionLifecycle.state.status !== "completed"))) {
|
|
2049
|
+
if (options?.deliverAs === "followUp") {
|
|
2050
|
+
this.agent.followUp(appMessage);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
this.agent.steer(appMessage);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
else if (options?.triggerTurn) {
|
|
2057
|
+
await this._enforceCompactionBeforeProvider(this._findLastAssistantMessage(), false, "pre_prompt");
|
|
2058
|
+
await this._enforceFinalProviderAdmission([appMessage]);
|
|
2059
|
+
await this._promptAgent(appMessage);
|
|
1596
2060
|
}
|
|
1597
2061
|
else {
|
|
1598
|
-
this.agent.
|
|
2062
|
+
this.agent.state.messages.push(appMessage);
|
|
2063
|
+
this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
|
|
2064
|
+
this._incrementMessageRevision();
|
|
2065
|
+
this._emit({ type: "message_start", message: appMessage });
|
|
2066
|
+
this._emit({ type: "message_end", message: appMessage });
|
|
1599
2067
|
}
|
|
1600
2068
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
}
|
|
1604
|
-
else {
|
|
1605
|
-
this.agent.state.messages.push(appMessage);
|
|
1606
|
-
this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
|
|
1607
|
-
this._incrementMessageRevision();
|
|
1608
|
-
this._emit({ type: "message_start", message: appMessage });
|
|
1609
|
-
this._emit({ type: "message_end", message: appMessage });
|
|
2069
|
+
finally {
|
|
2070
|
+
finishSessionWork?.();
|
|
1610
2071
|
}
|
|
1611
2072
|
}
|
|
1612
2073
|
/**
|
|
@@ -1617,6 +2078,14 @@ export class AgentSession {
|
|
|
1617
2078
|
* @param options.deliverAs Delivery mode when streaming: "steer" or "followUp"
|
|
1618
2079
|
*/
|
|
1619
2080
|
async sendUserMessage(content, options) {
|
|
2081
|
+
const bindingPromptReadiness = this._extensionBindingPromptReadiness;
|
|
2082
|
+
let resolveBindingPromptReadiness;
|
|
2083
|
+
if (bindingPromptReadiness) {
|
|
2084
|
+
const readiness = new Promise((resolve) => {
|
|
2085
|
+
resolveBindingPromptReadiness = resolve;
|
|
2086
|
+
});
|
|
2087
|
+
bindingPromptReadiness.add(readiness);
|
|
2088
|
+
}
|
|
1620
2089
|
// Normalize content to text string + optional images
|
|
1621
2090
|
let text;
|
|
1622
2091
|
let images;
|
|
@@ -1638,14 +2107,31 @@ export class AgentSession {
|
|
|
1638
2107
|
if (images.length === 0)
|
|
1639
2108
|
images = undefined;
|
|
1640
2109
|
}
|
|
1641
|
-
//
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
2110
|
+
// An extension binding invokes this method fire-and-forget. When it
|
|
2111
|
+
// arrives during session work, retain a barrier token after the prior work
|
|
2112
|
+
// settles so callers observing session idle cannot race its provider turn.
|
|
2113
|
+
const waitForExistingSessionWork = this._sessionWorkBarrier.hasActiveWork;
|
|
2114
|
+
let finishSessionWork;
|
|
2115
|
+
try {
|
|
2116
|
+
// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion
|
|
2117
|
+
await this.prompt(text, {
|
|
2118
|
+
expandPromptTemplates: false,
|
|
2119
|
+
streamingBehavior: options?.deliverAs,
|
|
2120
|
+
images,
|
|
2121
|
+
source: "extension",
|
|
2122
|
+
promptDisposition: () => resolveBindingPromptReadiness?.(),
|
|
2123
|
+
onSessionWorkReady: waitForExistingSessionWork
|
|
2124
|
+
? () => {
|
|
2125
|
+
finishSessionWork ??= this._sessionWorkBarrier.begin();
|
|
2126
|
+
}
|
|
2127
|
+
: undefined,
|
|
2128
|
+
sessionTitlePrompt: false,
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
finally {
|
|
2132
|
+
resolveBindingPromptReadiness?.();
|
|
2133
|
+
finishSessionWork?.();
|
|
2134
|
+
}
|
|
1649
2135
|
}
|
|
1650
2136
|
/**
|
|
1651
2137
|
* Clear all queued messages and return them.
|
|
@@ -1655,8 +2141,13 @@ export class AgentSession {
|
|
|
1655
2141
|
clearQueue() {
|
|
1656
2142
|
const steering = [...this._steeringMessages];
|
|
1657
2143
|
const followUp = [...this._followUpMessages];
|
|
2144
|
+
// Clear every queue synchronously. Deferred post-compaction messages are
|
|
2145
|
+
// already represented in visible bookkeeping, so they must not be returned
|
|
2146
|
+
// a second time or later resurrected into Agent's native queues.
|
|
1658
2147
|
this._steeringMessages = [];
|
|
1659
2148
|
this._followUpMessages = [];
|
|
2149
|
+
this._postCompactionDeferredSteeringMessages = [];
|
|
2150
|
+
this._postCompactionDeferredFollowUpMessages = [];
|
|
1660
2151
|
this.agent.clearAllQueues();
|
|
1661
2152
|
this._emitQueueUpdate();
|
|
1662
2153
|
return { steering, followUp };
|
|
@@ -1680,30 +2171,8 @@ export class AgentSession {
|
|
|
1680
2171
|
* Abort current operation and wait for agent to become idle.
|
|
1681
2172
|
*/
|
|
1682
2173
|
async abort() {
|
|
1683
|
-
this.abortRetry();
|
|
1684
2174
|
this.abortCompaction();
|
|
1685
|
-
this.
|
|
1686
|
-
if (this._userAbortPromise) {
|
|
1687
|
-
this.agent.abort();
|
|
1688
|
-
await this._userAbortPromise;
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
if (this.isStreaming) {
|
|
1692
|
-
this._suppressQueuedContinuationAfterUserAbort = true;
|
|
1693
|
-
}
|
|
1694
|
-
const abortPromise = (async () => {
|
|
1695
|
-
this.agent.abort();
|
|
1696
|
-
await this.waitForIdle();
|
|
1697
|
-
})();
|
|
1698
|
-
this._userAbortPromise = abortPromise;
|
|
1699
|
-
try {
|
|
1700
|
-
await abortPromise;
|
|
1701
|
-
}
|
|
1702
|
-
finally {
|
|
1703
|
-
if (this._userAbortPromise === abortPromise) {
|
|
1704
|
-
this._userAbortPromise = undefined;
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
2175
|
+
await this._abortActiveAgentAndRetry();
|
|
1707
2176
|
}
|
|
1708
2177
|
async waitForIdle() {
|
|
1709
2178
|
if (this.isIdle) {
|
|
@@ -1779,6 +2248,7 @@ export class AgentSession {
|
|
|
1779
2248
|
persistDefault: updateGlobalDefaults,
|
|
1780
2249
|
appendSessionEntry: true,
|
|
1781
2250
|
emitModelSelect: true,
|
|
2251
|
+
modelSelectSource: "set",
|
|
1782
2252
|
invalidateCompaction: true,
|
|
1783
2253
|
});
|
|
1784
2254
|
}
|
|
@@ -1817,7 +2287,7 @@ export class AgentSession {
|
|
|
1817
2287
|
}
|
|
1818
2288
|
if (!opts.emitModelSelect)
|
|
1819
2289
|
return undefined;
|
|
1820
|
-
return await this._emitModelSelect(model, previousModel,
|
|
2290
|
+
return await this._emitModelSelect(model, previousModel, opts.modelSelectSource);
|
|
1821
2291
|
}
|
|
1822
2292
|
_applyEphemeralThinkingLevel(level) {
|
|
1823
2293
|
const previousLevel = this.agent.state.thinkingLevel;
|
|
@@ -2009,29 +2479,133 @@ export class AgentSession {
|
|
|
2009
2479
|
// =========================================================================
|
|
2010
2480
|
// Compaction
|
|
2011
2481
|
// =========================================================================
|
|
2482
|
+
_claimCompactionController(controller, owner) {
|
|
2483
|
+
const supersedesCurrentOperation = (this._compactionAbortController !== undefined && this._compactionAbortController !== controller) ||
|
|
2484
|
+
(this._autoCompactionAbortController !== undefined && this._autoCompactionAbortController !== controller);
|
|
2485
|
+
if (supersedesCurrentOperation) {
|
|
2486
|
+
// Supersession has no public terminal event: the stale operation no
|
|
2487
|
+
// longer owns the route. Finishing its lifecycle state here prevents a
|
|
2488
|
+
// missing late feedback end from keeping isCompacting true.
|
|
2489
|
+
this._compactionLifecycle.abort(this._messageRevision);
|
|
2490
|
+
}
|
|
2491
|
+
if (this._compactionAbortController && this._compactionAbortController !== controller) {
|
|
2492
|
+
this._compactionAbortController.abort();
|
|
2493
|
+
this._compactionAbortController = undefined;
|
|
2494
|
+
}
|
|
2495
|
+
if (this._autoCompactionAbortController && this._autoCompactionAbortController !== controller) {
|
|
2496
|
+
this._autoCompactionAbortController.abort();
|
|
2497
|
+
this._autoCompactionAbortController = undefined;
|
|
2498
|
+
}
|
|
2499
|
+
if (owner === "auto") {
|
|
2500
|
+
this._autoCompactionAbortController = controller;
|
|
2501
|
+
}
|
|
2502
|
+
else {
|
|
2503
|
+
this._compactionAbortController = controller;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
_ownsCompactionController(controller, owner) {
|
|
2507
|
+
return (!controller.signal.aborted &&
|
|
2508
|
+
(owner === "auto"
|
|
2509
|
+
? this._autoCompactionAbortController === controller
|
|
2510
|
+
: this._compactionAbortController === controller));
|
|
2511
|
+
}
|
|
2512
|
+
_releaseCompactionController(signal) {
|
|
2513
|
+
if (this._compactionAbortController?.signal === signal) {
|
|
2514
|
+
this._compactionAbortController = undefined;
|
|
2515
|
+
}
|
|
2516
|
+
if (this._autoCompactionAbortController?.signal === signal) {
|
|
2517
|
+
this._autoCompactionAbortController = undefined;
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
_claimPendingCompactionAdmission() {
|
|
2521
|
+
const priorAdmission = this._pendingCompactionAdmission;
|
|
2522
|
+
if (priorAdmission) {
|
|
2523
|
+
priorAdmission.controller.abort();
|
|
2524
|
+
priorAdmission.outcome = "aborted";
|
|
2525
|
+
priorAdmission.finishSessionWork();
|
|
2526
|
+
}
|
|
2527
|
+
const controller = new AbortController();
|
|
2528
|
+
const admission = {
|
|
2529
|
+
controller,
|
|
2530
|
+
finishSessionWork: this._sessionWorkBarrier.begin(),
|
|
2531
|
+
};
|
|
2532
|
+
this._pendingCompactionAdmission = admission;
|
|
2533
|
+
this._claimCompactionController(controller, "compaction");
|
|
2534
|
+
return admission;
|
|
2535
|
+
}
|
|
2536
|
+
_releasePendingCompactionAdmission(admission, outcome) {
|
|
2537
|
+
admission.outcome = outcome;
|
|
2538
|
+
if (this._pendingCompactionAdmission !== admission)
|
|
2539
|
+
return;
|
|
2540
|
+
this._pendingCompactionAdmission = undefined;
|
|
2541
|
+
admission.finishSessionWork();
|
|
2542
|
+
}
|
|
2543
|
+
async _abortActiveAgentAndRetry() {
|
|
2544
|
+
this.abortRetry();
|
|
2545
|
+
this.abortBranchSummary();
|
|
2546
|
+
if (this._userAbortPromise) {
|
|
2547
|
+
this.agent.abort();
|
|
2548
|
+
await this._userAbortPromise;
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
if (this.isStreaming) {
|
|
2552
|
+
this._suppressQueuedContinuationAfterUserAbort = true;
|
|
2553
|
+
}
|
|
2554
|
+
const abortPromise = (async () => {
|
|
2555
|
+
this.agent.abort();
|
|
2556
|
+
await this.waitForIdle();
|
|
2557
|
+
})();
|
|
2558
|
+
this._userAbortPromise = abortPromise;
|
|
2559
|
+
try {
|
|
2560
|
+
await abortPromise;
|
|
2561
|
+
}
|
|
2562
|
+
finally {
|
|
2563
|
+
if (this._userAbortPromise === abortPromise) {
|
|
2564
|
+
this._userAbortPromise = undefined;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2012
2568
|
/**
|
|
2013
2569
|
* Manually compact the session context.
|
|
2014
2570
|
* Aborts current agent operation first.
|
|
2015
2571
|
* @param customInstructions Optional instructions for the compaction summary
|
|
2016
2572
|
*/
|
|
2017
2573
|
async compact(customInstructions) {
|
|
2018
|
-
this.
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2574
|
+
const admission = this._claimPendingCompactionAdmission();
|
|
2575
|
+
const controller = admission.controller;
|
|
2576
|
+
let outcome = "failed";
|
|
2577
|
+
let disconnected = false;
|
|
2022
2578
|
try {
|
|
2023
|
-
|
|
2579
|
+
// Keep the session subscriber attached until the aborted run emits
|
|
2580
|
+
// agent_end. That event clears the active-run and retry state that
|
|
2581
|
+
// waitForIdle() depends on.
|
|
2582
|
+
await this._abortActiveAgentAndRetry();
|
|
2583
|
+
this._disconnectFromAgent();
|
|
2584
|
+
disconnected = true;
|
|
2585
|
+
this._emit({ type: "compaction_start", reason: "manual" });
|
|
2586
|
+
const execution = await this._executeCompaction({
|
|
2587
|
+
controller,
|
|
2588
|
+
owner: "compaction",
|
|
2589
|
+
reason: "manual",
|
|
2590
|
+
customInstructions,
|
|
2591
|
+
willRetry: false,
|
|
2592
|
+
});
|
|
2024
2593
|
if (!execution.accepted) {
|
|
2025
2594
|
throw new CompactionRejectedError(execution.rejectionCause);
|
|
2026
2595
|
}
|
|
2596
|
+
outcome = "completed";
|
|
2027
2597
|
return execution.result;
|
|
2028
2598
|
}
|
|
2029
2599
|
catch (error) {
|
|
2600
|
+
outcome = isCompactionExecutionAborted(error) ? "aborted" : "failed";
|
|
2030
2601
|
if (error instanceof CompactionRejectedError) {
|
|
2031
2602
|
throw new Error(error.message);
|
|
2032
2603
|
}
|
|
2604
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
2605
|
+
throw error;
|
|
2606
|
+
}
|
|
2033
2607
|
const message = error instanceof Error ? error.message : String(error);
|
|
2034
|
-
const aborted =
|
|
2608
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2035
2609
|
this._emit({
|
|
2036
2610
|
type: "compaction_end",
|
|
2037
2611
|
reason: "manual",
|
|
@@ -2043,21 +2617,34 @@ export class AgentSession {
|
|
|
2043
2617
|
throw error;
|
|
2044
2618
|
}
|
|
2045
2619
|
finally {
|
|
2046
|
-
this._compactionAbortController
|
|
2047
|
-
|
|
2620
|
+
if (this._compactionAbortController === controller && this._compactionLifecycle.state.status !== "running") {
|
|
2621
|
+
this._compactionAbortController = undefined;
|
|
2622
|
+
}
|
|
2623
|
+
this._releasePendingCompactionAdmission(admission, outcome);
|
|
2624
|
+
if (disconnected && !this.isCompacting)
|
|
2625
|
+
this._reconnectToAgent();
|
|
2048
2626
|
}
|
|
2049
2627
|
}
|
|
2050
2628
|
async applyCompaction(precomputed, options) {
|
|
2629
|
+
if (options.signal !== undefined && options.signal !== this._compactionAbortController?.signal) {
|
|
2630
|
+
return { applied: false, reason: "stale" };
|
|
2631
|
+
}
|
|
2051
2632
|
if (options.expectedRevision !== undefined && options.expectedRevision !== this._messageRevision) {
|
|
2052
2633
|
return { applied: false, reason: "stale" };
|
|
2053
2634
|
}
|
|
2054
2635
|
const ownsController = this._compactionAbortController === undefined;
|
|
2055
2636
|
if (ownsController) {
|
|
2056
|
-
this.
|
|
2637
|
+
this._claimCompactionController(new AbortController(), "compaction");
|
|
2057
2638
|
this._emit({ type: "compaction_start", reason: options.reason });
|
|
2058
2639
|
}
|
|
2640
|
+
const controller = this._compactionAbortController;
|
|
2641
|
+
if (!controller)
|
|
2642
|
+
return { applied: false, reason: "rejected" };
|
|
2643
|
+
this._claimCompactionController(controller, "compaction");
|
|
2059
2644
|
try {
|
|
2060
2645
|
const execution = await this._executeCompaction({
|
|
2646
|
+
controller,
|
|
2647
|
+
owner: "compaction",
|
|
2061
2648
|
reason: options.reason,
|
|
2062
2649
|
willRetry: false,
|
|
2063
2650
|
precomputed,
|
|
@@ -2068,8 +2655,11 @@ export class AgentSession {
|
|
|
2068
2655
|
return { applied: true, reason: "ok" };
|
|
2069
2656
|
}
|
|
2070
2657
|
catch (error) {
|
|
2658
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
2659
|
+
return { applied: false, reason: "rejected" };
|
|
2660
|
+
}
|
|
2071
2661
|
const message = error instanceof Error ? error.message : String(error);
|
|
2072
|
-
const aborted =
|
|
2662
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2073
2663
|
this._emit({
|
|
2074
2664
|
type: "compaction_end",
|
|
2075
2665
|
reason: options.reason,
|
|
@@ -2081,18 +2671,27 @@ export class AgentSession {
|
|
|
2081
2671
|
return { applied: false, reason: "rejected" };
|
|
2082
2672
|
}
|
|
2083
2673
|
finally {
|
|
2084
|
-
this._compactionAbortController
|
|
2674
|
+
if (this._compactionAbortController === controller && this._compactionLifecycle.state.status !== "running") {
|
|
2675
|
+
this._compactionAbortController = undefined;
|
|
2676
|
+
}
|
|
2085
2677
|
}
|
|
2086
2678
|
}
|
|
2087
2679
|
_beginExtensionCompactionFeedback(reason) {
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2680
|
+
const controller = new AbortController();
|
|
2681
|
+
this._claimCompactionController(controller, "compaction");
|
|
2682
|
+
const model = this.model;
|
|
2683
|
+
this._compactionLifecycle.begin({
|
|
2684
|
+
operationId: randomUUID(),
|
|
2685
|
+
stage: "feedback",
|
|
2686
|
+
reason,
|
|
2687
|
+
model: model ? { provider: model.provider, id: model.id } : undefined,
|
|
2688
|
+
startedRevision: this._messageRevision,
|
|
2689
|
+
}, controller);
|
|
2690
|
+
this._emit({ type: "compaction_start", reason });
|
|
2691
|
+
return controller.signal;
|
|
2093
2692
|
}
|
|
2094
2693
|
_updateExtensionCompactionFeedback(options) {
|
|
2095
|
-
if (!
|
|
2694
|
+
if (!options.signal || !this._compactionLifecycle.hasCurrentSignal(options.signal))
|
|
2096
2695
|
return;
|
|
2097
2696
|
this._emit({
|
|
2098
2697
|
type: "compaction_progress",
|
|
@@ -2102,10 +2701,24 @@ export class AgentSession {
|
|
|
2102
2701
|
});
|
|
2103
2702
|
}
|
|
2104
2703
|
_endExtensionCompactionFeedback(options) {
|
|
2105
|
-
|
|
2106
|
-
if (!controller)
|
|
2704
|
+
if (!options.signal)
|
|
2107
2705
|
return;
|
|
2108
|
-
|
|
2706
|
+
if (!this._compactionLifecycle.hasCurrentSignal(options.signal)) {
|
|
2707
|
+
this._releaseCompactionController(options.signal);
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
const operation = this._compactionLifecycle.state;
|
|
2711
|
+
if (operation.status !== "running" || operation.stage !== "feedback")
|
|
2712
|
+
return;
|
|
2713
|
+
const aborted = options.aborted ?? options.signal.aborted;
|
|
2714
|
+
this._compactionLifecycle.finish({
|
|
2715
|
+
operationId: operation.operationId,
|
|
2716
|
+
status: aborted ? "aborted" : "failed",
|
|
2717
|
+
endedRevision: this._messageRevision,
|
|
2718
|
+
...(aborted
|
|
2719
|
+
? { errorMessage: "Compaction cancelled" }
|
|
2720
|
+
: { errorMessage: options.errorMessage ?? "Compaction did not apply" }),
|
|
2721
|
+
});
|
|
2109
2722
|
this._emit({
|
|
2110
2723
|
type: "compaction_end",
|
|
2111
2724
|
reason: options.reason,
|
|
@@ -2114,26 +2727,41 @@ export class AgentSession {
|
|
|
2114
2727
|
willRetry: false,
|
|
2115
2728
|
errorMessage: aborted ? undefined : options.errorMessage,
|
|
2116
2729
|
});
|
|
2117
|
-
this.
|
|
2730
|
+
this._releaseCompactionController(options.signal);
|
|
2118
2731
|
}
|
|
2119
2732
|
async _executeCompaction(request) {
|
|
2733
|
+
const model = this.model;
|
|
2734
|
+
if (!model)
|
|
2735
|
+
throw new Error(formatNoModelSelectedMessage());
|
|
2736
|
+
const thinkingLevel = this.thinkingLevel;
|
|
2737
|
+
const controller = request.controller;
|
|
2738
|
+
// Async auth and provider preparation may yield to a newer route. A stale
|
|
2739
|
+
// controller must never promote itself into a lifecycle generation.
|
|
2740
|
+
if (!this._ownsCompactionController(controller, request.owner)) {
|
|
2741
|
+
throw new CompactionExecutionError(new CompactionCancelledError(), false, true);
|
|
2742
|
+
}
|
|
2743
|
+
const requestId = randomUUID();
|
|
2744
|
+
const operationId = this._compactionLifecycle.begin({
|
|
2745
|
+
operationId: requestId,
|
|
2746
|
+
stage: "execution",
|
|
2747
|
+
reason: request.reason,
|
|
2748
|
+
model: { provider: model.provider, id: model.id },
|
|
2749
|
+
startedRevision: this._messageRevision,
|
|
2750
|
+
}, controller);
|
|
2120
2751
|
const finishCompactionWork = this._sessionWorkBarrier.begin();
|
|
2121
2752
|
const agentMessagesAtStart = request.agentMessagesAtStart ?? this.agent.state.messages.slice();
|
|
2753
|
+
const compactionOwnedDiagnosticMessages = new Set();
|
|
2754
|
+
const signal = controller.signal;
|
|
2122
2755
|
try {
|
|
2123
|
-
if (
|
|
2124
|
-
throw new
|
|
2756
|
+
if (signal.aborted) {
|
|
2757
|
+
throw new CompactionCancelledError();
|
|
2125
2758
|
}
|
|
2126
|
-
const requestId = randomUUID();
|
|
2127
2759
|
const pathEntries = this.sessionManager.getBranch();
|
|
2128
2760
|
const settings = this.settingsManager.getCompactionSettings();
|
|
2129
|
-
const signal = this._compactionAbortController?.signal ?? this._autoCompactionAbortController?.signal;
|
|
2130
|
-
if (!signal) {
|
|
2131
|
-
throw new Error("Compaction abort controller unavailable");
|
|
2132
|
-
}
|
|
2133
2761
|
let compactionResult = request.precomputed;
|
|
2134
2762
|
let fromExtension = request.precomputed !== undefined;
|
|
2135
2763
|
if (!compactionResult) {
|
|
2136
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
2764
|
+
const preparation = prepareCompaction(pathEntries, settings, request.reason === "overflow", request.allowSummaryOnly);
|
|
2137
2765
|
if (!preparation) {
|
|
2138
2766
|
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
2139
2767
|
if (lastEntry?.type === "compaction") {
|
|
@@ -2142,6 +2770,7 @@ export class AgentSession {
|
|
|
2142
2770
|
throw new Error("Nothing to compact (session too small)");
|
|
2143
2771
|
}
|
|
2144
2772
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2773
|
+
const messagesBeforeExtension = new Set(this.agent.state.messages);
|
|
2145
2774
|
const extensionResult = (await this._extensionRunner.emit({
|
|
2146
2775
|
type: "session_before_compact",
|
|
2147
2776
|
reason: request.reason,
|
|
@@ -2152,8 +2781,17 @@ export class AgentSession {
|
|
|
2152
2781
|
customInstructions: request.customInstructions,
|
|
2153
2782
|
signal,
|
|
2154
2783
|
}));
|
|
2784
|
+
for (const message of this.agent.state.messages) {
|
|
2785
|
+
if (!messagesBeforeExtension.has(message) &&
|
|
2786
|
+
isCompactionOwnedPreCompactDiagnostic(message, requestId)) {
|
|
2787
|
+
compactionOwnedDiagnosticMessages.add(message);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
if (!this._compactionLifecycle.isCurrent(operationId, controller)) {
|
|
2791
|
+
throw new CompactionCancelledError();
|
|
2792
|
+
}
|
|
2155
2793
|
if (extensionResult?.cancel) {
|
|
2156
|
-
return await this._rejectCompaction(request, requestId, extensionResult.rejectionCause ?? "cancelled-by-extension", true, extensionResult.reason);
|
|
2794
|
+
return await this._rejectCompaction(request, requestId, operationId, extensionResult.rejectionCause ?? "cancelled-by-extension", true, extensionResult.reason);
|
|
2157
2795
|
}
|
|
2158
2796
|
if (extensionResult?.compaction) {
|
|
2159
2797
|
compactionResult = extensionResult.compaction;
|
|
@@ -2161,17 +2799,38 @@ export class AgentSession {
|
|
|
2161
2799
|
}
|
|
2162
2800
|
}
|
|
2163
2801
|
if (!compactionResult) {
|
|
2164
|
-
const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(
|
|
2165
|
-
compactionResult = await compact(preparation,
|
|
2802
|
+
const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(model);
|
|
2803
|
+
compactionResult = await compact(preparation, model, apiKey, headers, request.customInstructions, signal, extraBody, thinkingLevel, this.agent.streamFunction, env, this.agent.transformContext, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: request.reason }));
|
|
2166
2804
|
}
|
|
2167
2805
|
}
|
|
2168
2806
|
if (signal.aborted) {
|
|
2169
|
-
throw new
|
|
2807
|
+
throw new CompactionCancelledError();
|
|
2170
2808
|
}
|
|
2171
|
-
if (this.
|
|
2172
|
-
|
|
2809
|
+
if (!this._compactionLifecycle.isCurrent(operationId, controller)) {
|
|
2810
|
+
throw new CompactionCancelledError();
|
|
2173
2811
|
}
|
|
2174
|
-
const
|
|
2812
|
+
const lifecycleState = this._compactionLifecycle.state;
|
|
2813
|
+
const currentMessagesAtCheck = this.agent.state.messages;
|
|
2814
|
+
const startPrefixIntact = agentMessagesAtStart.every((message, index) => currentMessagesAtCheck[index] === message);
|
|
2815
|
+
// Appends after the start snapshot are fresh only while they are exact
|
|
2816
|
+
// message_end identities still awaiting persistence. Any revision change,
|
|
2817
|
+
// other append, replacement, or reorder still makes this compaction stale.
|
|
2818
|
+
const onlyPendingPersistenceAppends = startPrefixIntact &&
|
|
2819
|
+
currentMessagesAtCheck
|
|
2820
|
+
.slice(agentMessagesAtStart.length)
|
|
2821
|
+
.every((message) => this._messageEndsAwaitingPersistence.has(message) ||
|
|
2822
|
+
compactionOwnedDiagnosticMessages.has(message));
|
|
2823
|
+
const sourceChanged = lifecycleState.status !== "running" ||
|
|
2824
|
+
lifecycleState.operationId !== operationId ||
|
|
2825
|
+
lifecycleState.startedRevision + compactionOwnedDiagnosticMessages.size !== this._messageRevision ||
|
|
2826
|
+
!onlyPendingPersistenceAppends;
|
|
2827
|
+
if (sourceChanged) {
|
|
2828
|
+
return await this._rejectCompaction(request, requestId, operationId, "stale-revision", false);
|
|
2829
|
+
}
|
|
2830
|
+
if (this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
|
|
2831
|
+
return await this._rejectCompaction(request, requestId, operationId, "would-overflow", false);
|
|
2832
|
+
}
|
|
2833
|
+
const compactionEntryId = this.sessionManager.appendCompaction(compactionResult.summary, compactionResult.firstKeptEntryId, compactionResult.tokensBefore, compactionResult.details, fromExtension, compactionResult.usage);
|
|
2175
2834
|
const savedEntry = this.sessionManager.getEntry(compactionEntryId);
|
|
2176
2835
|
if (savedEntry?.type !== "compaction") {
|
|
2177
2836
|
throw new Error("Compaction entry was not saved");
|
|
@@ -2182,18 +2841,33 @@ export class AgentSession {
|
|
|
2182
2841
|
const messagesAppendedDuringCompaction = hasUnchangedPrefix
|
|
2183
2842
|
? currentAgentMessages.slice(agentMessagesAtStart.length)
|
|
2184
2843
|
: [];
|
|
2185
|
-
|
|
2844
|
+
// Preserve an identity-deduped, agent-ordered union of the append-during-
|
|
2845
|
+
// compaction suffix and exact messages still awaiting persistence. Their
|
|
2846
|
+
// queued message_end owns exactly-once persistence, so they are kept in
|
|
2847
|
+
// agent state only and never persisted here.
|
|
2848
|
+
const preservedIdentities = new Set(messagesAppendedDuringCompaction);
|
|
2849
|
+
for (const message of currentAgentMessages) {
|
|
2850
|
+
if (this._messageEndsAwaitingPersistence.has(message)) {
|
|
2851
|
+
preservedIdentities.add(message);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
const preservedPendingMessages = currentAgentMessages.filter((message) => preservedIdentities.delete(message));
|
|
2855
|
+
this._skipNextPostCompactionAssistantCheck = true;
|
|
2856
|
+
for (const message of preservedPendingMessages) {
|
|
2857
|
+
if (message.role === "assistant") {
|
|
2858
|
+
this._assistantsPendingAtCompaction.add(message);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
this.agent.state.messages = [...sessionContext.messages, ...preservedPendingMessages];
|
|
2186
2862
|
compactionResult.estimatedTokensAfter = estimateMessagesTokens(sessionContext.messages);
|
|
2187
2863
|
this._incrementMessageRevision();
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
willRetry: request.willRetry,
|
|
2196
|
-
});
|
|
2864
|
+
if (!this._compactionLifecycle.finish({
|
|
2865
|
+
operationId,
|
|
2866
|
+
status: "completed",
|
|
2867
|
+
endedRevision: this._messageRevision,
|
|
2868
|
+
})) {
|
|
2869
|
+
throw new CompactionCancelledError();
|
|
2870
|
+
}
|
|
2197
2871
|
this._emit({
|
|
2198
2872
|
type: "compaction_end",
|
|
2199
2873
|
reason: request.reason,
|
|
@@ -2203,15 +2877,41 @@ export class AgentSession {
|
|
|
2203
2877
|
requestId,
|
|
2204
2878
|
accepted: true,
|
|
2205
2879
|
});
|
|
2880
|
+
await this._extensionRunner.emit({
|
|
2881
|
+
type: "session_compact",
|
|
2882
|
+
reason: request.reason,
|
|
2883
|
+
requestId,
|
|
2884
|
+
accepted: true,
|
|
2885
|
+
compactionEntry: savedEntry,
|
|
2886
|
+
fromExtension,
|
|
2887
|
+
willRetry: request.willRetry,
|
|
2888
|
+
});
|
|
2206
2889
|
return { accepted: true, requestId, result: compactionResult, compactionEntry: savedEntry, fromExtension };
|
|
2207
2890
|
}
|
|
2891
|
+
catch (error) {
|
|
2892
|
+
if (error instanceof CompactionExecutionError) {
|
|
2893
|
+
throw error;
|
|
2894
|
+
}
|
|
2895
|
+
const lifecycleState = this._compactionLifecycle.state;
|
|
2896
|
+
const ownsTerminalTransition = lifecycleState.status !== "idle" && lifecycleState.operationId === operationId;
|
|
2897
|
+
const aborted = signal.aborted || isCompactionExecutionAborted(error);
|
|
2898
|
+
if (ownsTerminalTransition && lifecycleState.status === "running") {
|
|
2899
|
+
this._compactionLifecycle.finish({
|
|
2900
|
+
operationId,
|
|
2901
|
+
status: aborted ? "aborted" : "failed",
|
|
2902
|
+
endedRevision: this._messageRevision,
|
|
2903
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2906
|
+
throw new CompactionExecutionError(error, ownsTerminalTransition, aborted);
|
|
2907
|
+
}
|
|
2208
2908
|
finally {
|
|
2209
2909
|
finishCompactionWork();
|
|
2210
2910
|
}
|
|
2211
2911
|
}
|
|
2212
|
-
_wouldCompactionOverflow(pathEntries, compactionResult, fromExtension) {
|
|
2912
|
+
_wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model) {
|
|
2213
2913
|
const currentLeaf = pathEntries[pathEntries.length - 1];
|
|
2214
|
-
if (!currentLeaf
|
|
2914
|
+
if (!currentLeaf)
|
|
2215
2915
|
return false;
|
|
2216
2916
|
const simulatedCompactionEntry = {
|
|
2217
2917
|
type: "compaction",
|
|
@@ -2227,9 +2927,28 @@ export class AgentSession {
|
|
|
2227
2927
|
const simulatedMessages = buildSessionContext([...pathEntries, simulatedCompactionEntry], simulatedCompactionEntry.id).messages;
|
|
2228
2928
|
const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(simulatedMessages));
|
|
2229
2929
|
const settings = this.settingsManager.getCompactionSettings();
|
|
2230
|
-
return contextTokens >
|
|
2930
|
+
return contextTokens > model.contextWindow - settings.reserveTokens;
|
|
2231
2931
|
}
|
|
2232
|
-
|
|
2932
|
+
/**
|
|
2933
|
+
* Replaces agent state with the canonical session context while keeping exact
|
|
2934
|
+
* message objects whose message_end persistence is still queued. Identities
|
|
2935
|
+
* already present in the session context are kept from the context only, so
|
|
2936
|
+
* nothing is duplicated; the queued message_end still owns exactly-once
|
|
2937
|
+
* persistence for the rest.
|
|
2938
|
+
*/
|
|
2939
|
+
_restoreAgentMessagesFromSession() {
|
|
2940
|
+
const sessionMessages = this.sessionManager.buildSessionContext().messages;
|
|
2941
|
+
const seen = new Set(sessionMessages);
|
|
2942
|
+
const pendingMessages = [];
|
|
2943
|
+
for (const message of this.agent.state.messages) {
|
|
2944
|
+
if (seen.has(message) || !this._messageEndsAwaitingPersistence.has(message))
|
|
2945
|
+
continue;
|
|
2946
|
+
seen.add(message);
|
|
2947
|
+
pendingMessages.push(message);
|
|
2948
|
+
}
|
|
2949
|
+
this.agent.state.messages = [...sessionMessages, ...pendingMessages];
|
|
2950
|
+
}
|
|
2951
|
+
async _rejectCompaction(request, requestId, operationId, rejectionCause, aborted, extensionReason) {
|
|
2233
2952
|
// Per plan Section 1: rejection must never be silent. The compaction_end event
|
|
2234
2953
|
// carries a non-empty human-readable errorMessage (unless the user aborted, where
|
|
2235
2954
|
// the aborted branch already renders "Compaction cancelled"). session_compact is
|
|
@@ -2241,6 +2960,15 @@ export class AgentSession {
|
|
|
2241
2960
|
? `Compaction rejected: ${trimmedExtensionReason}`
|
|
2242
2961
|
: describeCompactionRejection(rejectionCause);
|
|
2243
2962
|
const errorMessage = aborted && !trimmedExtensionReason ? undefined : detailedMessage;
|
|
2963
|
+
if (!this._compactionLifecycle.finish({
|
|
2964
|
+
operationId,
|
|
2965
|
+
status: "failed",
|
|
2966
|
+
endedRevision: this._messageRevision,
|
|
2967
|
+
rejectionCause,
|
|
2968
|
+
...(trimmedExtensionReason !== undefined ? { errorMessage: trimmedExtensionReason } : {}),
|
|
2969
|
+
})) {
|
|
2970
|
+
throw new CompactionCancelledError();
|
|
2971
|
+
}
|
|
2244
2972
|
this._emit({
|
|
2245
2973
|
type: "compaction_end",
|
|
2246
2974
|
reason: request.reason,
|
|
@@ -2267,8 +2995,19 @@ export class AgentSession {
|
|
|
2267
2995
|
* Cancel in-progress compaction (manual or auto).
|
|
2268
2996
|
*/
|
|
2269
2997
|
abortCompaction() {
|
|
2998
|
+
const feedbackOperation = this._compactionLifecycle.abort(this._messageRevision);
|
|
2270
2999
|
this._compactionAbortController?.abort();
|
|
2271
3000
|
this._autoCompactionAbortController?.abort();
|
|
3001
|
+
if (feedbackOperation?.stage === "feedback") {
|
|
3002
|
+
this._compactionAbortController = undefined;
|
|
3003
|
+
this._emit({
|
|
3004
|
+
type: "compaction_end",
|
|
3005
|
+
reason: feedbackOperation.reason,
|
|
3006
|
+
result: undefined,
|
|
3007
|
+
aborted: true,
|
|
3008
|
+
willRetry: false,
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
2272
3011
|
}
|
|
2273
3012
|
/**
|
|
2274
3013
|
* Cancel in-progress branch summarization.
|
|
@@ -2287,7 +3026,80 @@ export class AgentSession {
|
|
|
2287
3026
|
* @param assistantMessage The assistant message to check
|
|
2288
3027
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
2289
3028
|
*/
|
|
2290
|
-
async
|
|
3029
|
+
async _enforceCompactionBeforeProvider(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction = false) {
|
|
3030
|
+
const blockedAdmission = this._blockedPostCompactionAssistant;
|
|
3031
|
+
if (blockedAdmission !== undefined &&
|
|
3032
|
+
blockedAdmission.assistant === assistantMessage &&
|
|
3033
|
+
blockedAdmission.revision === this._messageRevision) {
|
|
3034
|
+
throw new RequiredCompactionError();
|
|
3035
|
+
}
|
|
3036
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
3037
|
+
const model = this.model;
|
|
3038
|
+
const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
|
|
3039
|
+
const compacted = assistantMessage
|
|
3040
|
+
? await this._checkCompaction(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction)
|
|
3041
|
+
: false;
|
|
3042
|
+
if (compacted || (assistantMessage && this._postCompactionUsageExemptAssistants.has(assistantMessage))) {
|
|
3043
|
+
return compacted;
|
|
3044
|
+
}
|
|
3045
|
+
if (!settings.enabled || !model || !shouldCompact(contextTokens, model.contextWindow, settings)) {
|
|
3046
|
+
return false;
|
|
3047
|
+
}
|
|
3048
|
+
const latestCompaction = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
3049
|
+
const assistantBeforeLatestCompaction = assistantMessage !== undefined && this._isAssistantFromBeforeLatestCompaction(assistantMessage);
|
|
3050
|
+
const hasPostCompactionCustomState = latestCompaction !== null &&
|
|
3051
|
+
this.agent.state.messages.some((message) => message.role === "custom" && message.timestamp >= new Date(latestCompaction.timestamp).getTime());
|
|
3052
|
+
if (assistantBeforeLatestCompaction && !hasPostCompactionCustomState) {
|
|
3053
|
+
return false;
|
|
3054
|
+
}
|
|
3055
|
+
if (assistantBeforeLatestCompaction && assistantMessage) {
|
|
3056
|
+
const compacted = await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, inlineReason);
|
|
3057
|
+
if (compacted)
|
|
3058
|
+
return true;
|
|
3059
|
+
}
|
|
3060
|
+
throw new RequiredCompactionError();
|
|
3061
|
+
}
|
|
3062
|
+
/**
|
|
3063
|
+
* The normal pre-prompt check only estimates persisted session context. This
|
|
3064
|
+
* final gate also includes turn-local messages which the provider will see:
|
|
3065
|
+
* the current prompt, next-turn custom messages, and before_agent_start
|
|
3066
|
+
* additions. Compaction rewrites only session context, so callers retain and
|
|
3067
|
+
* reapply their already-assembled one-shot additions after it succeeds.
|
|
3068
|
+
*/
|
|
3069
|
+
async _enforceFinalProviderAdmission(messages) {
|
|
3070
|
+
// Normal user-only prompts keep the established provider-overflow recovery
|
|
3071
|
+
// path. This final gate closes the separate gap introduced by turn-local
|
|
3072
|
+
// custom additions, which the normal pre-prompt check cannot observe.
|
|
3073
|
+
if (!messages.some((message) => message.role === "custom"))
|
|
3074
|
+
return;
|
|
3075
|
+
const model = this.model;
|
|
3076
|
+
if (!model)
|
|
3077
|
+
return;
|
|
3078
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
3079
|
+
const isOversized = () => {
|
|
3080
|
+
const providerMessages = filterContextExcludedMessages([...this.agent.state.messages, ...messages]);
|
|
3081
|
+
const estimate = estimateContextTokens(providerMessages);
|
|
3082
|
+
const usageMessage = estimate.lastUsageIndex === null ? undefined : providerMessages[estimate.lastUsageIndex];
|
|
3083
|
+
// Kept assistant usage can describe the pre-compaction request. Once a
|
|
3084
|
+
// compaction boundary exists, fall back to byte-derived message estimates
|
|
3085
|
+
// until a provider response refreshes that usage.
|
|
3086
|
+
const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
|
|
3087
|
+
? estimateMessagesTokens(providerMessages)
|
|
3088
|
+
: estimate.tokens;
|
|
3089
|
+
return contextTokens > model.contextWindow - settings.reserveTokens;
|
|
3090
|
+
};
|
|
3091
|
+
if (!settings.enabled || !isOversized())
|
|
3092
|
+
return;
|
|
3093
|
+
const lastAssistantMessage = this._findLastAssistantMessage();
|
|
3094
|
+
if (!lastAssistantMessage) {
|
|
3095
|
+
throw new RequiredCompactionError();
|
|
3096
|
+
}
|
|
3097
|
+
const compacted = await this._runPrePromptCompaction(lastAssistantMessage, false, "pre_prompt");
|
|
3098
|
+
if (!compacted || isOversized()) {
|
|
3099
|
+
throw new RequiredCompactionError();
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
async _checkCompaction(assistantMessage, skipAbortedCheck = true, inlineReason, retryAfterCompaction = false) {
|
|
2291
3103
|
const settings = this.settingsManager.getCompactionSettings();
|
|
2292
3104
|
if (!settings.enabled)
|
|
2293
3105
|
return false;
|
|
@@ -2315,10 +3127,26 @@ export class AgentSession {
|
|
|
2315
3127
|
const currentContextNeedsCompaction = contextUsage !== undefined &&
|
|
2316
3128
|
contextUsage.tokens !== null &&
|
|
2317
3129
|
shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
|
|
2318
|
-
|
|
2319
|
-
|
|
3130
|
+
const isOverflow = isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction);
|
|
3131
|
+
if (isOverflow &&
|
|
3132
|
+
assistantMessage.stopReason === "stop" &&
|
|
3133
|
+
this._consumePostCompactionUsageExemption(assistantMessage)) {
|
|
3134
|
+
return false;
|
|
3135
|
+
}
|
|
3136
|
+
if (isOverflow) {
|
|
3137
|
+
this._flushPostCompactionDeferredMessages();
|
|
3138
|
+
const willRetry = retryAfterCompaction || assistantMessage.stopReason !== "stop";
|
|
2320
3139
|
if (!willRetry) {
|
|
2321
|
-
|
|
3140
|
+
const compacted = await this._runAutoCompaction("overflow", false);
|
|
3141
|
+
if (!compacted &&
|
|
3142
|
+
this._compactionLifecycle.state.status === "failed" &&
|
|
3143
|
+
getLatestCompactionEntry(this.sessionManager.getBranch()) !== null) {
|
|
3144
|
+
this._blockedPostCompactionAssistant = {
|
|
3145
|
+
assistant: assistantMessage,
|
|
3146
|
+
revision: this._messageRevision,
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
return compacted;
|
|
2322
3150
|
}
|
|
2323
3151
|
if (this._overflowRecoveryAttempted) {
|
|
2324
3152
|
const errorMessage = "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.";
|
|
@@ -2339,17 +3167,29 @@ export class AgentSession {
|
|
|
2339
3167
|
// Remove the error message from agent state (it IS saved to session for history,
|
|
2340
3168
|
// but we don't want it in context for the retry)
|
|
2341
3169
|
const messages = this.agent.state.messages;
|
|
3170
|
+
let removedOverflowAssistant = false;
|
|
2342
3171
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
2343
3172
|
this.agent.state.messages = messages.slice(0, -1);
|
|
3173
|
+
removedOverflowAssistant = true;
|
|
2344
3174
|
this._incrementMessageRevision();
|
|
2345
3175
|
}
|
|
2346
|
-
|
|
2347
|
-
|
|
3176
|
+
const compacted = inlineReason
|
|
3177
|
+
? await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, "overflow", willRetry)
|
|
3178
|
+
: await this._runAutoCompaction("overflow", willRetry);
|
|
3179
|
+
if (!compacted && removedOverflowAssistant) {
|
|
3180
|
+
this._restoreAgentMessagesFromSession();
|
|
3181
|
+
this._incrementMessageRevision();
|
|
2348
3182
|
}
|
|
2349
|
-
|
|
2350
|
-
|
|
3183
|
+
if (!compacted && inlineReason) {
|
|
3184
|
+
throw new RequiredCompactionError();
|
|
2351
3185
|
}
|
|
3186
|
+
return compacted;
|
|
2352
3187
|
}
|
|
3188
|
+
// The first ordinary response can carry provider usage calculated before
|
|
3189
|
+
// compaction. Consume that exemption only after proving this is not an
|
|
3190
|
+
// overflow, then retain the decision for later admission re-sampling.
|
|
3191
|
+
if (this._consumePostCompactionUsageExemption(assistantMessage))
|
|
3192
|
+
return false;
|
|
2353
3193
|
// Case 2: Threshold - context is getting large
|
|
2354
3194
|
// For error messages or all-zero usage messages, estimate from the last valid response.
|
|
2355
3195
|
// This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
|
|
@@ -2375,7 +3215,7 @@ export class AgentSession {
|
|
|
2375
3215
|
const usageMsg = messages[estimate.lastUsageIndex];
|
|
2376
3216
|
if (compactionEntry &&
|
|
2377
3217
|
usageMsg.role === "assistant" &&
|
|
2378
|
-
|
|
3218
|
+
this._isAssistantFromBeforeLatestCompaction(usageMsg)) {
|
|
2379
3219
|
return false;
|
|
2380
3220
|
}
|
|
2381
3221
|
contextTokens = estimate.tokens;
|
|
@@ -2383,35 +3223,53 @@ export class AgentSession {
|
|
|
2383
3223
|
}
|
|
2384
3224
|
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
2385
3225
|
if (inlineReason) {
|
|
2386
|
-
return await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, inlineReason);
|
|
3226
|
+
return await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction);
|
|
2387
3227
|
}
|
|
2388
3228
|
else {
|
|
2389
|
-
|
|
3229
|
+
const compacted = await this._runAutoCompaction("threshold", false);
|
|
3230
|
+
if (!compacted &&
|
|
3231
|
+
this._compactionLifecycle.state.status === "failed" &&
|
|
3232
|
+
getLatestCompactionEntry(this.sessionManager.getBranch()) !== null) {
|
|
3233
|
+
this._blockedPostCompactionAssistant = {
|
|
3234
|
+
assistant: assistantMessage,
|
|
3235
|
+
revision: this._messageRevision,
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
return compacted;
|
|
2390
3239
|
}
|
|
2391
3240
|
}
|
|
2392
3241
|
return false;
|
|
2393
3242
|
}
|
|
2394
|
-
async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false) {
|
|
3243
|
+
async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false, allowSummaryOnly = false) {
|
|
3244
|
+
const controller = new AbortController();
|
|
3245
|
+
this._claimCompactionController(controller, "compaction");
|
|
2395
3246
|
this._emit({ type: "compaction_start", reason });
|
|
2396
|
-
this._compactionAbortController = new AbortController();
|
|
2397
3247
|
try {
|
|
2398
3248
|
const execution = await this._executeCompaction({
|
|
3249
|
+
controller,
|
|
3250
|
+
owner: "compaction",
|
|
2399
3251
|
reason,
|
|
2400
3252
|
willRetry,
|
|
2401
3253
|
lastAssistantMessage,
|
|
2402
3254
|
skipAbortedCheck,
|
|
3255
|
+
allowSummaryOnly,
|
|
2403
3256
|
});
|
|
2404
|
-
if (!execution.accepted &&
|
|
3257
|
+
if (!execution.accepted &&
|
|
3258
|
+
lastAssistantMessage &&
|
|
3259
|
+
isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
|
|
2405
3260
|
this._overflowRecoveryAttempted = false;
|
|
2406
3261
|
}
|
|
2407
3262
|
return execution.accepted;
|
|
2408
3263
|
}
|
|
2409
3264
|
catch (error) {
|
|
2410
|
-
if (
|
|
3265
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
3266
|
+
return false;
|
|
3267
|
+
}
|
|
3268
|
+
if (lastAssistantMessage && isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
|
|
2411
3269
|
this._overflowRecoveryAttempted = false;
|
|
2412
3270
|
}
|
|
2413
3271
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
2414
|
-
const aborted =
|
|
3272
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2415
3273
|
this._emit({
|
|
2416
3274
|
type: "compaction_end",
|
|
2417
3275
|
reason,
|
|
@@ -2423,12 +3281,44 @@ export class AgentSession {
|
|
|
2423
3281
|
return false;
|
|
2424
3282
|
}
|
|
2425
3283
|
finally {
|
|
2426
|
-
this._compactionAbortController
|
|
3284
|
+
if (this._compactionAbortController === controller && this._compactionLifecycle.state.status !== "running") {
|
|
3285
|
+
this._compactionAbortController = undefined;
|
|
3286
|
+
}
|
|
2427
3287
|
}
|
|
2428
3288
|
}
|
|
3289
|
+
async _revalidateScheduledContinuationAdmission() {
|
|
3290
|
+
const model = this.model;
|
|
3291
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
3292
|
+
if (!model || !settings.enabled)
|
|
3293
|
+
return;
|
|
3294
|
+
const canonicalMessages = filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages);
|
|
3295
|
+
const estimate = estimateContextTokens(canonicalMessages);
|
|
3296
|
+
const usageMessage = estimate.lastUsageIndex === null ? undefined : canonicalMessages[estimate.lastUsageIndex];
|
|
3297
|
+
const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
|
|
3298
|
+
? estimateMessagesTokens(canonicalMessages)
|
|
3299
|
+
: estimate.tokens;
|
|
3300
|
+
if (!shouldCompact(contextTokens, model.contextWindow, settings))
|
|
3301
|
+
return;
|
|
3302
|
+
const compacted = await this._runPrePromptCompaction(this._findLastAssistantMessage(), true, "pre_prompt");
|
|
3303
|
+
if (!compacted)
|
|
3304
|
+
throw new RequiredCompactionError();
|
|
3305
|
+
this._scheduledContinuationRecompacted = true;
|
|
3306
|
+
}
|
|
2429
3307
|
async _continueAgentAfterCurrentRun() {
|
|
2430
3308
|
await this.agent.waitForIdle();
|
|
2431
|
-
await this.
|
|
3309
|
+
await this._revalidateScheduledContinuationAdmission();
|
|
3310
|
+
const useQueuedContinuation = this._scheduledContinuationRecompacted;
|
|
3311
|
+
try {
|
|
3312
|
+
if (useQueuedContinuation) {
|
|
3313
|
+
await this.agent.continueWithQueuedMessages();
|
|
3314
|
+
}
|
|
3315
|
+
else {
|
|
3316
|
+
await this.agent.continue();
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
finally {
|
|
3320
|
+
this._scheduledContinuationRecompacted = false;
|
|
3321
|
+
}
|
|
2432
3322
|
}
|
|
2433
3323
|
_scheduleContinuationAfterCurrentEvent() {
|
|
2434
3324
|
// Tool hooks wait for queued message persistence, so continue() cannot run inside this event promise.
|
|
@@ -2458,49 +3348,53 @@ export class AgentSession {
|
|
|
2458
3348
|
async _runAutoCompaction(reason, willRetry) {
|
|
2459
3349
|
const finishCompactionWork = this._sessionWorkBarrier.begin();
|
|
2460
3350
|
const agentMessagesAtStart = this.agent.state.messages.slice();
|
|
2461
|
-
this._emit({ type: "compaction_start", reason });
|
|
2462
3351
|
const autoCompactionController = new AbortController();
|
|
2463
|
-
this.
|
|
3352
|
+
this._claimCompactionController(autoCompactionController, "auto");
|
|
3353
|
+
const endBeforeExecution = () => {
|
|
3354
|
+
this._emit({ type: "compaction_start", reason });
|
|
3355
|
+
if (reason === "overflow")
|
|
3356
|
+
this._overflowRecoveryAttempted = false;
|
|
3357
|
+
this._emit({
|
|
3358
|
+
type: "compaction_end",
|
|
3359
|
+
reason,
|
|
3360
|
+
result: undefined,
|
|
3361
|
+
aborted: false,
|
|
3362
|
+
willRetry: false,
|
|
3363
|
+
});
|
|
3364
|
+
return false;
|
|
3365
|
+
};
|
|
2464
3366
|
try {
|
|
2465
3367
|
if (!this.model) {
|
|
2466
|
-
|
|
2467
|
-
this._overflowRecoveryAttempted = false;
|
|
2468
|
-
this._emit({
|
|
2469
|
-
type: "compaction_end",
|
|
2470
|
-
reason,
|
|
2471
|
-
result: undefined,
|
|
2472
|
-
aborted: false,
|
|
2473
|
-
willRetry: false,
|
|
2474
|
-
});
|
|
2475
|
-
return false;
|
|
3368
|
+
return endBeforeExecution();
|
|
2476
3369
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
reason,
|
|
2484
|
-
result: undefined,
|
|
2485
|
-
aborted: false,
|
|
2486
|
-
willRetry: false,
|
|
2487
|
-
});
|
|
2488
|
-
return false;
|
|
3370
|
+
try {
|
|
3371
|
+
// Resolve once before admission so a pending auth refresh remains a
|
|
3372
|
+
// cancellable boundary. _executeCompaction resolves the policy-specific
|
|
3373
|
+
// auth after extension compaction hooks have had a chance to provide a
|
|
3374
|
+
// summary without credentials.
|
|
3375
|
+
await this._modelRuntime.getAuth(this.model);
|
|
2489
3376
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
reason,
|
|
2497
|
-
result: undefined,
|
|
2498
|
-
aborted: false,
|
|
2499
|
-
willRetry: false,
|
|
2500
|
-
});
|
|
3377
|
+
catch {
|
|
3378
|
+
if (!this._ownsCompactionController(autoCompactionController, "auto"))
|
|
3379
|
+
return false;
|
|
3380
|
+
return endBeforeExecution();
|
|
3381
|
+
}
|
|
3382
|
+
if (!this._ownsCompactionController(autoCompactionController, "auto"))
|
|
2501
3383
|
return false;
|
|
3384
|
+
const preparation = prepareCompaction(this.sessionManager.getBranch(), this.settingsManager.getCompactionSettings(), reason === "overflow");
|
|
3385
|
+
if (!preparation) {
|
|
3386
|
+
return endBeforeExecution();
|
|
2502
3387
|
}
|
|
2503
|
-
|
|
3388
|
+
if (!this._ownsCompactionController(autoCompactionController, "auto"))
|
|
3389
|
+
return false;
|
|
3390
|
+
this._emit({ type: "compaction_start", reason });
|
|
3391
|
+
const execution = await this._executeCompaction({
|
|
3392
|
+
controller: autoCompactionController,
|
|
3393
|
+
owner: "auto",
|
|
3394
|
+
reason,
|
|
3395
|
+
willRetry,
|
|
3396
|
+
agentMessagesAtStart,
|
|
3397
|
+
});
|
|
2504
3398
|
if (!execution.accepted) {
|
|
2505
3399
|
if (reason === "overflow")
|
|
2506
3400
|
this._overflowRecoveryAttempted = false;
|
|
@@ -2530,10 +3424,13 @@ export class AgentSession {
|
|
|
2530
3424
|
return false;
|
|
2531
3425
|
}
|
|
2532
3426
|
catch (error) {
|
|
3427
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
3428
|
+
return false;
|
|
3429
|
+
}
|
|
2533
3430
|
if (reason === "overflow")
|
|
2534
3431
|
this._overflowRecoveryAttempted = false;
|
|
2535
3432
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
2536
|
-
const aborted =
|
|
3433
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2537
3434
|
this._emit({
|
|
2538
3435
|
type: "compaction_end",
|
|
2539
3436
|
reason,
|
|
@@ -2566,27 +3463,39 @@ export class AgentSession {
|
|
|
2566
3463
|
return this.settingsManager.getCompactionEnabled();
|
|
2567
3464
|
}
|
|
2568
3465
|
async bindExtensions(bindings) {
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
3466
|
+
const finishBindingWork = this._sessionWorkBarrier.begin();
|
|
3467
|
+
const bindingPromptReadiness = new Set();
|
|
3468
|
+
this._extensionBindingPromptReadiness = bindingPromptReadiness;
|
|
3469
|
+
try {
|
|
3470
|
+
if (bindings.uiContext !== undefined) {
|
|
3471
|
+
this._extensionUIContext = bindings.uiContext;
|
|
3472
|
+
}
|
|
3473
|
+
if (bindings.mode !== undefined) {
|
|
3474
|
+
this._extensionMode = bindings.mode;
|
|
3475
|
+
}
|
|
3476
|
+
if (bindings.commandContextActions !== undefined) {
|
|
3477
|
+
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
3478
|
+
}
|
|
3479
|
+
if (bindings.abortHandler !== undefined) {
|
|
3480
|
+
this._extensionAbortHandler = bindings.abortHandler;
|
|
3481
|
+
}
|
|
3482
|
+
if (bindings.shutdownHandler !== undefined) {
|
|
3483
|
+
this._extensionShutdownHandler = bindings.shutdownHandler;
|
|
3484
|
+
}
|
|
3485
|
+
if (bindings.onError !== undefined) {
|
|
3486
|
+
this._extensionErrorListener = bindings.onError;
|
|
3487
|
+
}
|
|
3488
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
3489
|
+
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
3490
|
+
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
2583
3491
|
}
|
|
2584
|
-
|
|
2585
|
-
this.
|
|
3492
|
+
finally {
|
|
3493
|
+
if (this._extensionBindingPromptReadiness === bindingPromptReadiness) {
|
|
3494
|
+
this._extensionBindingPromptReadiness = undefined;
|
|
3495
|
+
}
|
|
3496
|
+
finishBindingWork();
|
|
2586
3497
|
}
|
|
2587
|
-
|
|
2588
|
-
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
2589
|
-
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
3498
|
+
await Promise.all(bindingPromptReadiness);
|
|
2590
3499
|
}
|
|
2591
3500
|
async extendResourcesFromExtensions(reason) {
|
|
2592
3501
|
if (!this._extensionRunner.hasHandlers("resources_discover")) {
|
|
@@ -2729,7 +3638,7 @@ export class AgentSession {
|
|
|
2729
3638
|
getServiceTier: () => this.serviceTier,
|
|
2730
3639
|
isIdle: () => this.isIdle,
|
|
2731
3640
|
isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
|
|
2732
|
-
getSignal: () => this.agent.signal,
|
|
3641
|
+
getSignal: () => this._extensionEventSignal ?? this.agent.signal,
|
|
2733
3642
|
abort: () => {
|
|
2734
3643
|
if (this._extensionAbortHandler) {
|
|
2735
3644
|
this._extensionAbortHandler();
|
|
@@ -2788,27 +3697,39 @@ export class AgentSession {
|
|
|
2788
3697
|
},
|
|
2789
3698
|
},
|
|
2790
3699
|
compact: (options) => {
|
|
3700
|
+
const admission = this._claimPendingCompactionAdmission();
|
|
3701
|
+
const controller = admission.controller;
|
|
2791
3702
|
void (async () => {
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
this._compactionAbortController = new AbortController();
|
|
2795
|
-
this._emit({ type: "compaction_start", reason: "extension" });
|
|
3703
|
+
let outcome = "failed";
|
|
3704
|
+
let disconnected = false;
|
|
2796
3705
|
try {
|
|
3706
|
+
await this._abortActiveAgentAndRetry();
|
|
3707
|
+
this._disconnectFromAgent();
|
|
3708
|
+
disconnected = true;
|
|
3709
|
+
this._emit({ type: "compaction_start", reason: "extension" });
|
|
2797
3710
|
const execution = await this._executeCompaction({
|
|
3711
|
+
controller,
|
|
3712
|
+
owner: "compaction",
|
|
2798
3713
|
reason: "extension",
|
|
2799
3714
|
customInstructions: options?.customInstructions,
|
|
2800
3715
|
willRetry: false,
|
|
2801
3716
|
});
|
|
2802
3717
|
if (execution.accepted) {
|
|
3718
|
+
outcome = "completed";
|
|
2803
3719
|
options?.onComplete?.(execution.result);
|
|
2804
3720
|
}
|
|
2805
3721
|
else {
|
|
3722
|
+
outcome = "failed";
|
|
2806
3723
|
options?.onError?.(new CompactionRejectedError(execution.rejectionCause));
|
|
2807
3724
|
}
|
|
2808
3725
|
}
|
|
2809
3726
|
catch (error) {
|
|
3727
|
+
outcome = isCompactionExecutionAborted(error) ? "aborted" : "failed";
|
|
3728
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
2810
3731
|
const message = error instanceof Error ? error.message : String(error);
|
|
2811
|
-
const aborted =
|
|
3732
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2812
3733
|
this._emit({
|
|
2813
3734
|
type: "compaction_end",
|
|
2814
3735
|
reason: "extension",
|
|
@@ -2821,8 +3742,13 @@ export class AgentSession {
|
|
|
2821
3742
|
options?.onError?.(err);
|
|
2822
3743
|
}
|
|
2823
3744
|
finally {
|
|
2824
|
-
this._compactionAbortController
|
|
2825
|
-
|
|
3745
|
+
if (this._compactionAbortController === controller &&
|
|
3746
|
+
this._compactionLifecycle.state.status !== "running") {
|
|
3747
|
+
this._compactionAbortController = undefined;
|
|
3748
|
+
}
|
|
3749
|
+
this._releasePendingCompactionAdmission(admission, outcome);
|
|
3750
|
+
if (disconnected && !this.isCompacting)
|
|
3751
|
+
this._reconnectToAgent();
|
|
2826
3752
|
}
|
|
2827
3753
|
})();
|
|
2828
3754
|
},
|
|
@@ -3056,15 +3982,43 @@ export class AgentSession {
|
|
|
3056
3982
|
}
|
|
3057
3983
|
return Math.ceil(value);
|
|
3058
3984
|
}
|
|
3985
|
+
/**
|
|
3986
|
+
* Retry policy + callbacks shared by compaction and branch-summary summarization calls.
|
|
3987
|
+
* Uses the same `settings.retry` budget/backoff as agent-turn retries so a single transient
|
|
3988
|
+
* stream drop no longer fails the whole operation. `source` carries the context
|
|
3989
|
+
* the TUI needs to render the retry and recreate the underlying indicator.
|
|
3990
|
+
*/
|
|
3991
|
+
_summarizationRetryCallbacks(source) {
|
|
3992
|
+
return {
|
|
3993
|
+
onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => {
|
|
3994
|
+
this._emit({
|
|
3995
|
+
type: "summarization_retry_scheduled",
|
|
3996
|
+
attempt,
|
|
3997
|
+
maxAttempts,
|
|
3998
|
+
delayMs,
|
|
3999
|
+
errorMessage,
|
|
4000
|
+
});
|
|
4001
|
+
},
|
|
4002
|
+
onRetryAttemptStart: () => {
|
|
4003
|
+
this._emit({
|
|
4004
|
+
type: "summarization_retry_attempt_start",
|
|
4005
|
+
...source,
|
|
4006
|
+
});
|
|
4007
|
+
},
|
|
4008
|
+
onRetryFinished: () => {
|
|
4009
|
+
this._emit({ type: "summarization_retry_finished" });
|
|
4010
|
+
},
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
3059
4013
|
/**
|
|
3060
4014
|
* Handle retryable errors with exponential backoff.
|
|
3061
|
-
* @returns
|
|
4015
|
+
* @returns whether retry continuation started, was blocked by compaction, or was not handled
|
|
3062
4016
|
*/
|
|
3063
4017
|
async _handleRetryableError(message, options = {}) {
|
|
3064
4018
|
const settings = this.settingsManager.getRetrySettings();
|
|
3065
4019
|
if (!settings.enabled) {
|
|
3066
4020
|
this._resolveRetry();
|
|
3067
|
-
return
|
|
4021
|
+
return "not-handled";
|
|
3068
4022
|
}
|
|
3069
4023
|
// Retry promise is created synchronously in _handleAgentEvent for agent_end.
|
|
3070
4024
|
// Keep a defensive fallback here in case a future refactor bypasses that path.
|
|
@@ -3082,7 +4036,7 @@ export class AgentSession {
|
|
|
3082
4036
|
switchedFallback = await this._retryFallback.tryFallback("hard-error", { errorMessage });
|
|
3083
4037
|
if (!switchedFallback) {
|
|
3084
4038
|
this._resolveRetry();
|
|
3085
|
-
return
|
|
4039
|
+
return "not-handled";
|
|
3086
4040
|
}
|
|
3087
4041
|
// The fallback starts fresh; the failed model's transient attempts do not carry over.
|
|
3088
4042
|
this._retryAttempt = 1;
|
|
@@ -3092,7 +4046,7 @@ export class AgentSession {
|
|
|
3092
4046
|
// same-model retries or the transient over-budget fallback escape hatch.
|
|
3093
4047
|
if (this._retryAttempt + 1 > settings.maxRetries) {
|
|
3094
4048
|
this._resolveRetry();
|
|
3095
|
-
return
|
|
4049
|
+
return "not-handled";
|
|
3096
4050
|
}
|
|
3097
4051
|
switchedFallback = await this._retryFallback.tryFallback("refusal", {});
|
|
3098
4052
|
if (!switchedFallback) {
|
|
@@ -3101,7 +4055,7 @@ export class AgentSession {
|
|
|
3101
4055
|
this._emit({ type: "retry_fallback_exhausted", chainKey: exhaustedChainKey, lastError: errorMessage });
|
|
3102
4056
|
}
|
|
3103
4057
|
this._resolveRetry();
|
|
3104
|
-
return
|
|
4058
|
+
return "not-handled";
|
|
3105
4059
|
}
|
|
3106
4060
|
this._retryAttempt++;
|
|
3107
4061
|
}
|
|
@@ -3133,7 +4087,7 @@ export class AgentSession {
|
|
|
3133
4087
|
});
|
|
3134
4088
|
this._retryAttempt = 0;
|
|
3135
4089
|
this._resolveRetry();
|
|
3136
|
-
return
|
|
4090
|
+
return "not-handled";
|
|
3137
4091
|
}
|
|
3138
4092
|
}
|
|
3139
4093
|
}
|
|
@@ -3148,7 +4102,7 @@ export class AgentSession {
|
|
|
3148
4102
|
});
|
|
3149
4103
|
this._retryAttempt = 0;
|
|
3150
4104
|
this._resolveRetry();
|
|
3151
|
-
return
|
|
4105
|
+
return "not-handled";
|
|
3152
4106
|
}
|
|
3153
4107
|
if (!switchedFallback) {
|
|
3154
4108
|
switchedFallback = await this._retryFallback.tryFallback("transient", {
|
|
@@ -3189,19 +4143,52 @@ export class AgentSession {
|
|
|
3189
4143
|
finalError: "Retry cancelled",
|
|
3190
4144
|
});
|
|
3191
4145
|
this._resolveRetry();
|
|
3192
|
-
return
|
|
4146
|
+
return "not-handled";
|
|
3193
4147
|
}
|
|
3194
4148
|
this._retryAbortController = undefined;
|
|
3195
4149
|
// Turn boundary: a suppression that expired during the backoff sleep lets
|
|
3196
4150
|
// the retry continue on the restored primary instead of the fallback model.
|
|
3197
4151
|
await this._maybeRestoreFallbackPrimary();
|
|
3198
|
-
//
|
|
4152
|
+
// Model fallback (or reversion) can select a smaller context window after
|
|
4153
|
+
// the prior retry checks. Revalidate canonical session context immediately
|
|
4154
|
+
// before the continuation so a rejected compaction never admits that model.
|
|
4155
|
+
const model = this.model;
|
|
4156
|
+
const compactionSettings = this.settingsManager.getCompactionSettings();
|
|
4157
|
+
const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
|
|
4158
|
+
if (compactionSettings.enabled &&
|
|
4159
|
+
model &&
|
|
4160
|
+
shouldCompact(contextTokens, model.contextWindow, compactionSettings)) {
|
|
4161
|
+
if (!(await this._runPrePromptCompaction(message, true, "threshold", true, true))) {
|
|
4162
|
+
const attempt = this._retryAttempt;
|
|
4163
|
+
this._retryAttempt = 0;
|
|
4164
|
+
this._emit({
|
|
4165
|
+
type: "auto_retry_end",
|
|
4166
|
+
success: false,
|
|
4167
|
+
attempt,
|
|
4168
|
+
finalError: new RequiredCompactionError().message,
|
|
4169
|
+
});
|
|
4170
|
+
this._resolveRetry();
|
|
4171
|
+
return "blocked";
|
|
4172
|
+
}
|
|
4173
|
+
this._skipNextPostRetryCompactionCheck = true;
|
|
4174
|
+
}
|
|
4175
|
+
// Retry via the shared continuation path after the event handler chain settles.
|
|
4176
|
+
// Agent core would otherwise drain these queues before the continuation
|
|
4177
|
+
// revalidates its provider admission. The scheduled continuation owns them
|
|
4178
|
+
// until it either starts or reports a terminal admission failure.
|
|
4179
|
+
this.agent.suppressQueuedMessageDrain();
|
|
3199
4180
|
setTimeout(() => {
|
|
3200
|
-
this.
|
|
3201
|
-
|
|
4181
|
+
void this._continueAgentAfterCurrentRun().catch(async (error) => {
|
|
4182
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4183
|
+
this._emit({
|
|
4184
|
+
type: "continuation_error",
|
|
4185
|
+
errorMessage: `Failed to continue queued messages: ${message}`,
|
|
4186
|
+
});
|
|
4187
|
+
await this._emitAgentSettled();
|
|
4188
|
+
this._resolveRetry();
|
|
3202
4189
|
});
|
|
3203
4190
|
}, 0);
|
|
3204
|
-
return
|
|
4191
|
+
return "continued";
|
|
3205
4192
|
}
|
|
3206
4193
|
/**
|
|
3207
4194
|
* Cancel in-progress retry.
|
|
@@ -3245,6 +4232,7 @@ export class AgentSession {
|
|
|
3245
4232
|
* @param command The bash command to execute
|
|
3246
4233
|
* @param onChunk Optional streaming callback for output
|
|
3247
4234
|
* @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)
|
|
4235
|
+
* @param options.id Optional identifier included in bash execution update events
|
|
3248
4236
|
* @param options.operations Custom BashOperations for remote execution
|
|
3249
4237
|
*/
|
|
3250
4238
|
async executeBash(command, onChunk, options) {
|
|
@@ -3255,7 +4243,10 @@ export class AgentSession {
|
|
|
3255
4243
|
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
3256
4244
|
try {
|
|
3257
4245
|
const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
|
|
3258
|
-
onChunk
|
|
4246
|
+
onChunk: (delta) => {
|
|
4247
|
+
onChunk?.(delta);
|
|
4248
|
+
this._emit({ type: "bash_execution_update", id: options?.id, delta });
|
|
4249
|
+
},
|
|
3259
4250
|
signal: this._bashAbortController.signal,
|
|
3260
4251
|
});
|
|
3261
4252
|
this.recordBashResult(command, result, options);
|
|
@@ -3413,6 +4404,7 @@ export class AgentSession {
|
|
|
3413
4404
|
// Run default summarizer if needed
|
|
3414
4405
|
let summaryText;
|
|
3415
4406
|
let summaryDetails;
|
|
4407
|
+
let summaryUsage;
|
|
3416
4408
|
if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
|
|
3417
4409
|
const model = this.model;
|
|
3418
4410
|
const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(model);
|
|
@@ -3427,7 +4419,9 @@ export class AgentSession {
|
|
|
3427
4419
|
customInstructions,
|
|
3428
4420
|
replaceInstructions,
|
|
3429
4421
|
reserveTokens: branchSummarySettings.reserveTokens,
|
|
3430
|
-
streamFn: this.agent.
|
|
4422
|
+
streamFn: this.agent.streamFunction,
|
|
4423
|
+
retry: this.settingsManager.getRetrySettings(),
|
|
4424
|
+
callbacks: this._summarizationRetryCallbacks({ source: "branchSummary" }),
|
|
3431
4425
|
});
|
|
3432
4426
|
if (result.aborted) {
|
|
3433
4427
|
return { cancelled: true, aborted: true };
|
|
@@ -3436,6 +4430,7 @@ export class AgentSession {
|
|
|
3436
4430
|
throw new Error(result.error);
|
|
3437
4431
|
}
|
|
3438
4432
|
summaryText = result.summary;
|
|
4433
|
+
summaryUsage = result.usage;
|
|
3439
4434
|
summaryDetails = {
|
|
3440
4435
|
readFiles: result.readFiles || [],
|
|
3441
4436
|
modifiedFiles: result.modifiedFiles || [],
|
|
@@ -3444,6 +4439,7 @@ export class AgentSession {
|
|
|
3444
4439
|
else if (extensionSummary) {
|
|
3445
4440
|
summaryText = extensionSummary.summary;
|
|
3446
4441
|
summaryDetails = extensionSummary.details;
|
|
4442
|
+
summaryUsage = extensionSummary.usage;
|
|
3447
4443
|
}
|
|
3448
4444
|
// Determine the new leaf position based on target type
|
|
3449
4445
|
let newLeafId;
|
|
@@ -3451,18 +4447,12 @@ export class AgentSession {
|
|
|
3451
4447
|
if (targetEntry.type === "message" && targetEntry.message.role === "user") {
|
|
3452
4448
|
// User message: leaf = parent (null if root), text goes to editor
|
|
3453
4449
|
newLeafId = targetEntry.parentId;
|
|
3454
|
-
editorText =
|
|
4450
|
+
editorText = contentText(targetEntry.message.content, "");
|
|
3455
4451
|
}
|
|
3456
4452
|
else if (targetEntry.type === "custom_message") {
|
|
3457
4453
|
// Custom message: leaf = parent (null if root), text goes to editor
|
|
3458
4454
|
newLeafId = targetEntry.parentId;
|
|
3459
|
-
editorText =
|
|
3460
|
-
typeof targetEntry.content === "string"
|
|
3461
|
-
? targetEntry.content
|
|
3462
|
-
: targetEntry.content
|
|
3463
|
-
.filter((c) => c.type === "text")
|
|
3464
|
-
.map((c) => c.text)
|
|
3465
|
-
.join("");
|
|
4455
|
+
editorText = contentText(targetEntry.content, "");
|
|
3466
4456
|
}
|
|
3467
4457
|
else {
|
|
3468
4458
|
// Non-user message: leaf = selected node
|
|
@@ -3473,7 +4463,7 @@ export class AgentSession {
|
|
|
3473
4463
|
let summaryEntry;
|
|
3474
4464
|
if (summaryText) {
|
|
3475
4465
|
// Create summary at target position (can be null for root)
|
|
3476
|
-
const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
|
|
4466
|
+
const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension, summaryUsage);
|
|
3477
4467
|
summaryEntry = this.sessionManager.getEntry(summaryId);
|
|
3478
4468
|
// Attach label to the summary entry
|
|
3479
4469
|
if (label) {
|
|
@@ -3492,9 +4482,8 @@ export class AgentSession {
|
|
|
3492
4482
|
if (label && !summaryText) {
|
|
3493
4483
|
this.sessionManager.appendLabelChange(targetId, label);
|
|
3494
4484
|
}
|
|
3495
|
-
// Update agent state
|
|
3496
|
-
|
|
3497
|
-
this.agent.state.messages = sessionContext.messages;
|
|
4485
|
+
// Update agent state (preserving exact messages still awaiting persistence)
|
|
4486
|
+
this._restoreAgentMessagesFromSession();
|
|
3498
4487
|
this._incrementMessageRevision();
|
|
3499
4488
|
// Emit session_tree event
|
|
3500
4489
|
await this._extensionRunner.emit({
|
|
@@ -3522,24 +4511,13 @@ export class AgentSession {
|
|
|
3522
4511
|
continue;
|
|
3523
4512
|
if (entry.message.role !== "user")
|
|
3524
4513
|
continue;
|
|
3525
|
-
const text =
|
|
4514
|
+
const text = contentText(entry.message.content, "");
|
|
3526
4515
|
if (text) {
|
|
3527
4516
|
result.push({ entryId: entry.id, text });
|
|
3528
4517
|
}
|
|
3529
4518
|
}
|
|
3530
4519
|
return result;
|
|
3531
4520
|
}
|
|
3532
|
-
_extractUserMessageText(content) {
|
|
3533
|
-
if (typeof content === "string")
|
|
3534
|
-
return content;
|
|
3535
|
-
if (Array.isArray(content)) {
|
|
3536
|
-
return content
|
|
3537
|
-
.filter((c) => c.type === "text")
|
|
3538
|
-
.map((c) => c.text)
|
|
3539
|
-
.join("");
|
|
3540
|
-
}
|
|
3541
|
-
return "";
|
|
3542
|
-
}
|
|
3543
4521
|
/**
|
|
3544
4522
|
* Get session statistics. Aggregates over ALL session entries (including
|
|
3545
4523
|
* history that was compacted away), so token/cost totals reflect what was
|
|
@@ -3551,12 +4529,11 @@ export class AgentSession {
|
|
|
3551
4529
|
let toolResults = 0;
|
|
3552
4530
|
let totalMessages = 0;
|
|
3553
4531
|
let toolCalls = 0;
|
|
3554
|
-
|
|
3555
|
-
let totalOutput = 0;
|
|
3556
|
-
let totalCacheRead = 0;
|
|
3557
|
-
let totalCacheWrite = 0;
|
|
3558
|
-
let totalCost = 0;
|
|
4532
|
+
const usageTotals = createUsageTotals();
|
|
3559
4533
|
for (const entry of this.sessionManager.getEntries()) {
|
|
4534
|
+
if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
|
|
4535
|
+
addUsageToTotals(usageTotals, entry.usage);
|
|
4536
|
+
}
|
|
3560
4537
|
if (entry.type !== "message")
|
|
3561
4538
|
continue;
|
|
3562
4539
|
totalMessages++;
|
|
@@ -3566,6 +4543,9 @@ export class AgentSession {
|
|
|
3566
4543
|
}
|
|
3567
4544
|
else if (message.role === "toolResult") {
|
|
3568
4545
|
toolResults++;
|
|
4546
|
+
if (message.usage) {
|
|
4547
|
+
addUsageToTotals(usageTotals, message.usage);
|
|
4548
|
+
}
|
|
3569
4549
|
}
|
|
3570
4550
|
else if (message.role === "assistant") {
|
|
3571
4551
|
assistantMessages++;
|
|
@@ -3573,12 +4553,7 @@ export class AgentSession {
|
|
|
3573
4553
|
if (Array.isArray(assistantMsg.content)) {
|
|
3574
4554
|
toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
3575
4555
|
}
|
|
3576
|
-
|
|
3577
|
-
totalInput += usage.input;
|
|
3578
|
-
totalOutput += usage.output;
|
|
3579
|
-
totalCacheRead += usage.cacheRead;
|
|
3580
|
-
totalCacheWrite += usage.cacheWrite;
|
|
3581
|
-
totalCost += usage.cost.total;
|
|
4556
|
+
addUsageToTotals(usageTotals, assistantMsg.usage);
|
|
3582
4557
|
}
|
|
3583
4558
|
}
|
|
3584
4559
|
return {
|
|
@@ -3590,13 +4565,13 @@ export class AgentSession {
|
|
|
3590
4565
|
toolResults,
|
|
3591
4566
|
totalMessages,
|
|
3592
4567
|
tokens: {
|
|
3593
|
-
input:
|
|
3594
|
-
output:
|
|
3595
|
-
cacheRead:
|
|
3596
|
-
cacheWrite:
|
|
3597
|
-
total:
|
|
4568
|
+
input: usageTotals.input,
|
|
4569
|
+
output: usageTotals.output,
|
|
4570
|
+
cacheRead: usageTotals.cacheRead,
|
|
4571
|
+
cacheWrite: usageTotals.cacheWrite,
|
|
4572
|
+
total: usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite,
|
|
3598
4573
|
},
|
|
3599
|
-
cost:
|
|
4574
|
+
cost: usageTotals.cost,
|
|
3600
4575
|
contextUsage: this.getContextUsage(),
|
|
3601
4576
|
};
|
|
3602
4577
|
}
|