@code-yeongyu/senpi 2026.7.23 → 2026.7.25-2
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 +162 -6
- package/README.md +14 -1
- package/dist/beta/omo-local-update.d.ts +207 -0
- package/dist/beta/omo-local-update.d.ts.map +1 -0
- package/dist/beta/omo-local-update.js +708 -0
- package/dist/beta/omo-local-update.js.map +1 -0
- 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 +115 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1317 -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 +69 -34
- package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/log.d.ts +4 -1
- package/dist/core/extensions/builtin/config-reload/log.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/log.js +3 -0
- package/dist/core/extensions/builtin/config-reload/log.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts +15 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts.map +1 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.js +102 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.js.map +1 -0
- 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/gpt-5.6.d.ts +48 -0
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +88 -64
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
- 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/builtin/websearch/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/index.js +2 -4
- package/dist/core/extensions/builtin/websearch/index.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.js +4 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/native.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/native.js +2 -4
- package/dist/core/extensions/builtin/websearch/websearch/native.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/renderers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/renderers.js +20 -8
- package/dist/core/extensions/builtin/websearch/websearch/renderers.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/search.d.ts +6 -1
- package/dist/core/extensions/builtin/websearch/websearch/search.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/search.js +15 -11
- package/dist/core/extensions/builtin/websearch/websearch/search.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/tool.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/tool.js +5 -10
- package/dist/core/extensions/builtin/websearch/websearch/tool.js.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/types.d.ts +1 -0
- package/dist/core/extensions/builtin/websearch/websearch/types.d.ts.map +1 -1
- package/dist/core/extensions/builtin/websearch/websearch/types.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 +32 -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 +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +22 -3
- 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 +39 -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/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts +7 -0
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +13 -0
- package/dist/core/retry-fallback/controller.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 +8 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +10 -0
- 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/thinking-levels.d.ts.map +1 -1
- package/dist/core/thinking-levels.js +9 -2
- package/dist/core/thinking-levels.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 +7 -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/custom-message.d.ts +3 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +9 -2
- package/dist/modes/interactive/components/custom-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 +105 -80
- 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/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +11 -0
- package/dist/package-manager-cli.js.map +1 -1
- 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 +27 -9
- 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 +13 -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/message-renderer.ts +2 -2
- 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 +36 -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 +13 -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 +63 -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 +130 -47
- 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 +36 -15
- 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 +55 -26
- 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 +16 -13
- 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 +19 -13
- 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 +92 -58
- 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 +263 -56
- 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 +48 -21
- 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/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.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/auth/oauth/radius.d.ts +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.js +37 -35
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +11 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js.map +1 -1
- 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 +6 -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 +6 -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 +7 -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 +5 -54
- 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 +52 -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/error-body.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +8 -3
- package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.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/server-fallback-receipt.d.ts +30 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js +70 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js.map +1 -0
- 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 -10
- 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, SERVER_FALLBACK_ABORTED_DIAGNOSTIC } 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,20 +240,52 @@ 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
|
+
if (event.type === "message_end" && event.message.role === "assistant") {
|
|
269
|
+
this._emitServerFallbackAborted(event.message);
|
|
270
|
+
}
|
|
271
|
+
const pendingMessage = event.type === "message_end" ? event.message : undefined;
|
|
272
|
+
if (pendingMessage !== undefined) {
|
|
273
|
+
this._messageEndsAwaitingPersistence.add(pendingMessage);
|
|
274
|
+
}
|
|
275
|
+
const processing = this._agentEventQueue.then(() => this._processAgentEvent(event, signal), () => this._processAgentEvent(event, signal));
|
|
276
|
+
this._agentEventQueue =
|
|
277
|
+
pendingMessage !== undefined
|
|
278
|
+
? processing.finally(() => {
|
|
279
|
+
this._messageEndsAwaitingPersistence.delete(pendingMessage);
|
|
280
|
+
})
|
|
281
|
+
: processing;
|
|
187
282
|
// Keep queue alive if an event handler fails
|
|
188
283
|
this._agentEventQueue.catch(() => { });
|
|
189
284
|
};
|
|
190
285
|
this.agent = config.agent;
|
|
191
286
|
this.sessionManager = config.sessionManager;
|
|
192
287
|
this.settingsManager = config.settingsManager;
|
|
288
|
+
this.agent.abortServerSideFallback = this.settingsManager.getAbortServerSideFallback();
|
|
193
289
|
const noModelFallback = config.resourceLoader.getExtensions().runtime.flagValues.get("no-model-fallback") === true ||
|
|
194
290
|
process.env.SENPI_NO_FALLBACK === "1";
|
|
195
291
|
if (noModelFallback) {
|
|
@@ -221,8 +317,9 @@ export class AgentSession {
|
|
|
221
317
|
persistDefault: false,
|
|
222
318
|
appendSessionEntry: true,
|
|
223
319
|
entryReason: reason,
|
|
224
|
-
emitModelSelect:
|
|
225
|
-
|
|
320
|
+
emitModelSelect: true,
|
|
321
|
+
modelSelectSource: reason,
|
|
322
|
+
invalidateCompaction: true,
|
|
226
323
|
ephemeralThinkingLevel: thinking,
|
|
227
324
|
});
|
|
228
325
|
},
|
|
@@ -268,7 +365,7 @@ export class AgentSession {
|
|
|
268
365
|
}
|
|
269
366
|
throw error;
|
|
270
367
|
}
|
|
271
|
-
if (result
|
|
368
|
+
if (result && (result.auth.apiKey || result.auth.headers)) {
|
|
272
369
|
return {
|
|
273
370
|
apiKey: result.auth.apiKey,
|
|
274
371
|
headers: withoutDeletedHeaders(result.auth.headers),
|
|
@@ -284,26 +381,31 @@ export class AgentSession {
|
|
|
284
381
|
}
|
|
285
382
|
throw new Error(formatNoApiKeyFoundMessage(model.provider));
|
|
286
383
|
}
|
|
287
|
-
|
|
288
|
-
|
|
384
|
+
/**
|
|
385
|
+
* Resolve optional auth for a summarization stream. Native/custom stream
|
|
386
|
+
* functions may provide ambient credentials, unlike streamSimple.
|
|
387
|
+
*/
|
|
388
|
+
async _getSummarizationRequestAuth(model) {
|
|
389
|
+
if (this.agent.streamFunction === streamSimple) {
|
|
289
390
|
return this._getRequiredRequestAuth(model);
|
|
290
391
|
}
|
|
291
|
-
const compatibility = this._modelRuntime.getCompatibilityRequestConfig(model);
|
|
292
392
|
try {
|
|
293
393
|
const result = await this._modelRuntime.getAuth(model);
|
|
294
394
|
return result
|
|
295
|
-
? {
|
|
296
|
-
|
|
297
|
-
headers: withoutDeletedHeaders(result.auth.headers),
|
|
298
|
-
extraBody: compatibility.extraBody,
|
|
299
|
-
env: result.env,
|
|
300
|
-
}
|
|
301
|
-
: { extraBody: compatibility.extraBody };
|
|
395
|
+
? { apiKey: result.auth.apiKey, headers: withoutDeletedHeaders(result.auth.headers), env: result.env }
|
|
396
|
+
: {};
|
|
302
397
|
}
|
|
303
398
|
catch {
|
|
304
|
-
return {
|
|
399
|
+
return {};
|
|
305
400
|
}
|
|
306
401
|
}
|
|
402
|
+
async _getCompactionRequestAuth(model) {
|
|
403
|
+
const auth = await this._getSummarizationRequestAuth(model);
|
|
404
|
+
return {
|
|
405
|
+
...auth,
|
|
406
|
+
extraBody: this._modelRuntime.getCompatibilityRequestConfig(model).extraBody,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
307
409
|
/**
|
|
308
410
|
* Install tool hooks once on the Agent instance.
|
|
309
411
|
*
|
|
@@ -312,6 +414,24 @@ export class AgentSession {
|
|
|
312
414
|
* registered tool execution to the extension context. Tool call and tool result interception now
|
|
313
415
|
* happens here instead of in wrappers.
|
|
314
416
|
*/
|
|
417
|
+
/**
|
|
418
|
+
* Surface a provider-level server-fallback abort before retry handling runs so
|
|
419
|
+
* the UI can explain the switch. Emitted synchronously here because retry work
|
|
420
|
+
* for the following agent_end starts before queued message_end processing
|
|
421
|
+
* drains. `chainConfigured` is required because the no-chain refusal path
|
|
422
|
+
* emits no retry_fallback_exhausted, leaving the UI no other signal.
|
|
423
|
+
*/
|
|
424
|
+
_emitServerFallbackAborted(message) {
|
|
425
|
+
const details = message.diagnostics?.find((entry) => entry.type === SERVER_FALLBACK_ABORTED_DIAGNOSTIC)?.details;
|
|
426
|
+
if (details === undefined)
|
|
427
|
+
return;
|
|
428
|
+
this._emit({
|
|
429
|
+
type: "server_fallback_aborted",
|
|
430
|
+
from: typeof details.from === "string" ? details.from : message.model,
|
|
431
|
+
to: typeof details.to === "string" ? details.to : message.model,
|
|
432
|
+
chainConfigured: this._retryFallback.hasConfiguredChain(),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
315
435
|
_installAgentToolHooks() {
|
|
316
436
|
this.agent.beforeToolCall = async ({ toolCall, args }) => {
|
|
317
437
|
return this._emitBeforeToolCallHooks(toolCall, args);
|
|
@@ -354,6 +474,7 @@ export class AgentSession {
|
|
|
354
474
|
content: result.content,
|
|
355
475
|
details: result.details,
|
|
356
476
|
isError,
|
|
477
|
+
usage: result.usage,
|
|
357
478
|
});
|
|
358
479
|
if (!hookResult) {
|
|
359
480
|
return undefined;
|
|
@@ -362,6 +483,7 @@ export class AgentSession {
|
|
|
362
483
|
content: hookResult.content,
|
|
363
484
|
details: hookResult.details,
|
|
364
485
|
isError: hookResult.isError ?? isError,
|
|
486
|
+
usage: hookResult.usage,
|
|
365
487
|
};
|
|
366
488
|
}
|
|
367
489
|
_installAgentNextTurnRefresh() {
|
|
@@ -370,31 +492,68 @@ export class AgentSession {
|
|
|
370
492
|
? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
|
|
371
493
|
: undefined);
|
|
372
494
|
this.agent.prepareNextTurnWithContext = async (turn, signal) => {
|
|
373
|
-
|
|
374
|
-
|
|
495
|
+
// Enforce compaction only when this prepare precedes an actual provider
|
|
496
|
+
// admission: a tool continuation or queued steer/follow-up messages. A
|
|
497
|
+
// completed turn with no continuation keeps pre-PR timing, while the
|
|
498
|
+
// prior prepare callback and context refresh below still run every turn.
|
|
499
|
+
const compactBeforeNextAdmission = async () => {
|
|
500
|
+
if (turn.toolResults.length === 0 && !this.agent.hasQueuedMessages()) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
375
503
|
await this._agentEventQueue;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
504
|
+
// A queue can be cleared while waiting for persistence. Re-sample it
|
|
505
|
+
// immediately before compaction so a completed turn never compacts
|
|
506
|
+
// merely because it once had a possible continuation.
|
|
507
|
+
if (turn.toolResults.length === 0 && !this.agent.hasQueuedMessages()) {
|
|
508
|
+
return false;
|
|
379
509
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
510
|
+
try {
|
|
511
|
+
return await this._enforceCompactionBeforeProvider(turn.message, true, "threshold");
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
if (error instanceof RequiredCompactionError && this.agent.hasQueuedMessages()) {
|
|
515
|
+
this._requiredCompactionAdmissionError = error;
|
|
385
516
|
}
|
|
517
|
+
throw error;
|
|
386
518
|
}
|
|
387
|
-
}
|
|
519
|
+
};
|
|
520
|
+
const compactedBeforeCallback = await compactBeforeNextAdmission();
|
|
521
|
+
const messages = compactedBeforeCallback ? this.agent.state.messages.slice() : turn.context.messages;
|
|
388
522
|
const postCompactionTurn = {
|
|
389
523
|
...turn,
|
|
390
524
|
context: { ...turn.context, messages },
|
|
391
525
|
};
|
|
392
|
-
|
|
393
|
-
|
|
526
|
+
let previousSnapshot = await previousPrepareNextTurnWithContext?.(postCompactionTurn, signal);
|
|
527
|
+
let previousContext = previousSnapshot?.context ?? postCompactionTurn.context;
|
|
528
|
+
// The previous callback may await while agent_end extensions enqueue
|
|
529
|
+
// continuation work. Re-sample after it returns so that work cannot
|
|
530
|
+
// slip through with the stale provider snapshot it observed on entry.
|
|
531
|
+
let compactedAfterCallback = false;
|
|
532
|
+
if (!compactedBeforeCallback) {
|
|
533
|
+
compactedAfterCallback = await compactBeforeNextAdmission();
|
|
534
|
+
}
|
|
535
|
+
if (compactedAfterCallback) {
|
|
536
|
+
// The callback's first result describes the stale pre-compaction
|
|
537
|
+
// context. Reapply it once to the compacted context so any host
|
|
538
|
+
// transformation reaches the provider request. Do not re-sample after
|
|
539
|
+
// this invocation: one replay is the bounded admission path.
|
|
540
|
+
const postLateCompactionTurn = {
|
|
541
|
+
...turn,
|
|
542
|
+
context: { ...turn.context, messages: this.agent.state.messages.slice() },
|
|
543
|
+
};
|
|
544
|
+
const postLateCompactionSnapshot = await previousPrepareNextTurnWithContext?.(postLateCompactionTurn, signal);
|
|
545
|
+
previousSnapshot = {
|
|
546
|
+
...previousSnapshot,
|
|
547
|
+
...postLateCompactionSnapshot,
|
|
548
|
+
context: postLateCompactionSnapshot?.context ?? postLateCompactionTurn.context,
|
|
549
|
+
};
|
|
550
|
+
previousContext = previousSnapshot.context ?? postLateCompactionTurn.context;
|
|
551
|
+
}
|
|
394
552
|
return {
|
|
395
553
|
...previousSnapshot,
|
|
396
554
|
context: {
|
|
397
555
|
...previousContext,
|
|
556
|
+
messages: previousContext.messages,
|
|
398
557
|
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
399
558
|
tools: this.agent.state.tools.slice(),
|
|
400
559
|
},
|
|
@@ -421,6 +580,7 @@ export class AgentSession {
|
|
|
421
580
|
}
|
|
422
581
|
_incrementMessageRevision() {
|
|
423
582
|
this._messageRevision++;
|
|
583
|
+
this._blockedPostCompactionAssistant = undefined;
|
|
424
584
|
}
|
|
425
585
|
getMessageRevision() {
|
|
426
586
|
return this._messageRevision;
|
|
@@ -481,14 +641,46 @@ export class AgentSession {
|
|
|
481
641
|
}
|
|
482
642
|
async _promptAgent(messages) {
|
|
483
643
|
this._isAgentRunActive = true;
|
|
644
|
+
this._requiredCompactionAdmissionError = undefined;
|
|
484
645
|
try {
|
|
485
646
|
await this.agent.prompt(messages);
|
|
647
|
+
// AgentSession's subscriber intentionally queues event work instead of
|
|
648
|
+
// blocking Agent core. Wait for this run's queued recovery decision
|
|
649
|
+
// before reporting prompt completion to the caller.
|
|
650
|
+
await this._agentEventQueue;
|
|
651
|
+
const requiredCompactionError = this._requiredCompactionAdmissionError;
|
|
652
|
+
this._requiredCompactionAdmissionError = undefined;
|
|
653
|
+
if (requiredCompactionError) {
|
|
654
|
+
throw requiredCompactionError;
|
|
655
|
+
}
|
|
486
656
|
}
|
|
487
657
|
catch (error) {
|
|
658
|
+
if (error instanceof Error &&
|
|
659
|
+
error.message ===
|
|
660
|
+
"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.") {
|
|
661
|
+
const queuedMessages = Array.isArray(messages) ? messages : [messages];
|
|
662
|
+
for (const message of queuedMessages)
|
|
663
|
+
this.agent.steer(message);
|
|
664
|
+
const userMessage = queuedMessages.find((message) => message.role === "user");
|
|
665
|
+
if (userMessage?.role === "user") {
|
|
666
|
+
this._steeringMessages.push(this._extractUserMessageText(userMessage.content));
|
|
667
|
+
this._emitQueueUpdate();
|
|
668
|
+
}
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
488
671
|
await this._emitAgentSettled();
|
|
489
672
|
throw error;
|
|
490
673
|
}
|
|
491
674
|
}
|
|
675
|
+
/** Extract text content used to track fork-owned queued user messages. */
|
|
676
|
+
_extractUserMessageText(content) {
|
|
677
|
+
if (typeof content === "string")
|
|
678
|
+
return content;
|
|
679
|
+
return content
|
|
680
|
+
.filter((part) => part.type === "text")
|
|
681
|
+
.map((part) => part.text)
|
|
682
|
+
.join("");
|
|
683
|
+
}
|
|
492
684
|
_createRetryPromiseForAgentEnd(event) {
|
|
493
685
|
if (event.type !== "agent_end" || this._retryPromise) {
|
|
494
686
|
return;
|
|
@@ -505,6 +697,10 @@ export class AgentSession {
|
|
|
505
697
|
this._retryPromise = new Promise((resolve) => {
|
|
506
698
|
this._retryResolve = resolve;
|
|
507
699
|
});
|
|
700
|
+
// Agent core normally drains queued input immediately after agent_end.
|
|
701
|
+
// Retry owns that continuation until its final provider-admission check has
|
|
702
|
+
// either started it or reported a terminal rejection.
|
|
703
|
+
this.agent.suppressQueuedMessageDrain();
|
|
508
704
|
}
|
|
509
705
|
_findLastAssistantInMessages(messages) {
|
|
510
706
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -515,6 +711,91 @@ export class AgentSession {
|
|
|
515
711
|
}
|
|
516
712
|
return undefined;
|
|
517
713
|
}
|
|
714
|
+
/**
|
|
715
|
+
* Synchronously mirror the auto-compaction decision that _checkCompaction()
|
|
716
|
+
* will make after agent_end. Agent core drains queues before that async work
|
|
717
|
+
* runs, so only this preflight can transfer required admissions safely.
|
|
718
|
+
*/
|
|
719
|
+
_getRequiredAutoCompactionReason(message) {
|
|
720
|
+
const reason = this._getAutoCompactionReason(message);
|
|
721
|
+
// Retry and post-compaction exemptions only cover stale usage estimates.
|
|
722
|
+
// A provider-confirmed overflow must always retain queue ownership and run
|
|
723
|
+
// fail-closed recovery.
|
|
724
|
+
if (reason === "overflow" &&
|
|
725
|
+
message.stopReason === "stop" &&
|
|
726
|
+
this._hasPendingPostCompactionUsageExemption(message)) {
|
|
727
|
+
// A successful post-compaction response can carry stale provider usage.
|
|
728
|
+
// Retain its queues while the asynchronous check consumes the exemption.
|
|
729
|
+
return "threshold";
|
|
730
|
+
}
|
|
731
|
+
if (reason === "overflow")
|
|
732
|
+
return reason;
|
|
733
|
+
// Keep queued continuations under AgentSession ownership while the
|
|
734
|
+
// asynchronous check consumes this post-compaction usage exemption.
|
|
735
|
+
if (this._hasPendingPostCompactionUsageExemption(message))
|
|
736
|
+
return "threshold";
|
|
737
|
+
if (reason === "threshold" &&
|
|
738
|
+
(this._skipNextPostRetryCompactionCheck || this._postCompactionUsageExemptAssistants.has(message))) {
|
|
739
|
+
return undefined;
|
|
740
|
+
}
|
|
741
|
+
return reason;
|
|
742
|
+
}
|
|
743
|
+
_getAutoCompactionReason(message) {
|
|
744
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
745
|
+
if (!settings.enabled || message.stopReason === "aborted") {
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
const model = this.model;
|
|
749
|
+
if (!model || this._isAssistantFromBeforeLatestCompaction(message)) {
|
|
750
|
+
return undefined;
|
|
751
|
+
}
|
|
752
|
+
const sameModel = isSameOverflowSource(message, model, this._modelRuntime.getCompatibilityRequestConfig(model).upstreamModelId);
|
|
753
|
+
const contextUsage = this.getContextUsage();
|
|
754
|
+
const currentContextNeedsCompaction = contextUsage !== undefined &&
|
|
755
|
+
contextUsage.tokens !== null &&
|
|
756
|
+
shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
|
|
757
|
+
if (isContextOverflow(message, model.contextWindow) && (sameModel || currentContextNeedsCompaction)) {
|
|
758
|
+
return "overflow";
|
|
759
|
+
}
|
|
760
|
+
let contextTokens;
|
|
761
|
+
const directContextTokens = message.usage ? calculateContextTokens(message.usage) : 0;
|
|
762
|
+
if (message.stopReason !== "error" && directContextTokens !== 0) {
|
|
763
|
+
contextTokens = directContextTokens;
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
const messages = filterContextExcludedMessages(this.agent.state.messages);
|
|
767
|
+
const estimate = estimateContextTokens(messages);
|
|
768
|
+
if (estimate.lastUsageIndex === null) {
|
|
769
|
+
return undefined;
|
|
770
|
+
}
|
|
771
|
+
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
772
|
+
const usageMessage = messages[estimate.lastUsageIndex];
|
|
773
|
+
if (compactionEntry &&
|
|
774
|
+
usageMessage?.role === "assistant" &&
|
|
775
|
+
this._isAssistantFromBeforeLatestCompaction(usageMessage)) {
|
|
776
|
+
return undefined;
|
|
777
|
+
}
|
|
778
|
+
contextTokens = estimate.tokens;
|
|
779
|
+
}
|
|
780
|
+
return shouldCompact(contextTokens, model.contextWindow, settings) ? "threshold" : undefined;
|
|
781
|
+
}
|
|
782
|
+
_hasPendingPostCompactionUsageExemption(message) {
|
|
783
|
+
return (this._skipNextPostCompactionAssistantCheck &&
|
|
784
|
+
!this._assistantsPendingAtCompaction.has(message) &&
|
|
785
|
+
!this._overflowRecoveryAttempted);
|
|
786
|
+
}
|
|
787
|
+
_isPostCompactionUsageExempt(message) {
|
|
788
|
+
return (this._postCompactionUsageExemptAssistants.has(message) || this._hasPendingPostCompactionUsageExemption(message));
|
|
789
|
+
}
|
|
790
|
+
_consumePostCompactionUsageExemption(message) {
|
|
791
|
+
if (this._postCompactionUsageExemptAssistants.has(message))
|
|
792
|
+
return true;
|
|
793
|
+
if (!this._isPostCompactionUsageExempt(message))
|
|
794
|
+
return false;
|
|
795
|
+
this._skipNextPostCompactionAssistantCheck = false;
|
|
796
|
+
this._postCompactionUsageExemptAssistants.add(message);
|
|
797
|
+
return true;
|
|
798
|
+
}
|
|
518
799
|
_agentEndAllowsQueuedContinuation(messages) {
|
|
519
800
|
let lastAssistantIndex = -1;
|
|
520
801
|
for (let index = messages.length - 1; index >= 0; index--) {
|
|
@@ -533,6 +814,9 @@ export class AgentSession {
|
|
|
533
814
|
if (lastAssistant.stopReason === "aborted" || lastAssistant.stopReason === "error") {
|
|
534
815
|
return false;
|
|
535
816
|
}
|
|
817
|
+
if (this._getRequiredAutoCompactionReason(lastAssistant)) {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
536
820
|
for (let index = lastAssistantIndex + 1; index < messages.length; index++) {
|
|
537
821
|
const message = messages[index];
|
|
538
822
|
if (message?.role === "toolResult" &&
|
|
@@ -572,13 +856,13 @@ export class AgentSession {
|
|
|
572
856
|
}
|
|
573
857
|
return providerDelayMs <= this.settingsManager.getProviderRetrySettings().maxRetryDelayMs;
|
|
574
858
|
}
|
|
575
|
-
async _processAgentEvent(event) {
|
|
859
|
+
async _processAgentEvent(event, signal) {
|
|
576
860
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
577
861
|
// This ensures the UI sees the updated queue state
|
|
578
862
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
579
863
|
this._overflowRecoveryAttempted = false;
|
|
580
864
|
this._retryFallback.resetTurn();
|
|
581
|
-
const messageText =
|
|
865
|
+
const messageText = contentText(event.message.content, "");
|
|
582
866
|
if (messageText) {
|
|
583
867
|
// Check steering queue first
|
|
584
868
|
const steeringIndex = this._steeringMessages.indexOf(messageText);
|
|
@@ -596,8 +880,15 @@ export class AgentSession {
|
|
|
596
880
|
}
|
|
597
881
|
}
|
|
598
882
|
}
|
|
599
|
-
// Emit to extensions first
|
|
600
|
-
|
|
883
|
+
// Emit to extensions first. Agent event persistence is intentionally
|
|
884
|
+
// asynchronous, so retain the source run signal while dispatching.
|
|
885
|
+
this._extensionEventSignal = signal;
|
|
886
|
+
try {
|
|
887
|
+
await this._emitExtensionEvent(event);
|
|
888
|
+
}
|
|
889
|
+
finally {
|
|
890
|
+
this._extensionEventSignal = undefined;
|
|
891
|
+
}
|
|
601
892
|
// Notify all listeners
|
|
602
893
|
this._emit(event.type === "agent_end" ? { ...event, willRetry: this._willRetryAfterAgentEnd(event.messages) } : event);
|
|
603
894
|
// Handle session persistence
|
|
@@ -643,8 +934,10 @@ export class AgentSession {
|
|
|
643
934
|
}
|
|
644
935
|
}
|
|
645
936
|
}
|
|
646
|
-
// Check auto-retry and auto-compaction after agent completes
|
|
937
|
+
// Check auto-retry and auto-compaction after agent completes.
|
|
647
938
|
let launchedContinuation = false;
|
|
939
|
+
let retryContinuationBlocked = false;
|
|
940
|
+
let allowsPostCompactionUsageExemptContinuation = false;
|
|
648
941
|
const userAbortSuppressedQueuedContinuation = event.type === "agent_end" && this._suppressQueuedContinuationAfterUserAbort;
|
|
649
942
|
if (userAbortSuppressedQueuedContinuation) {
|
|
650
943
|
this._suppressQueuedContinuationAfterUserAbort = false;
|
|
@@ -655,23 +948,62 @@ export class AgentSession {
|
|
|
655
948
|
if (event.type === "agent_end" && this._lastAssistantMessage) {
|
|
656
949
|
const msg = this._lastAssistantMessage;
|
|
657
950
|
this._lastAssistantMessage = undefined;
|
|
951
|
+
this._skipNextPostRetryCompactionCheck = false;
|
|
952
|
+
const requiredAutoCompaction = this._getRequiredAutoCompactionReason(msg);
|
|
658
953
|
// Retry transient failures normally and eligible hard errors only through a fallback.
|
|
659
954
|
const retryableError = this._isRetryableError(msg);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
955
|
+
const hardErrorFallbackEligible = this._isHardErrorFallbackEligible(msg);
|
|
956
|
+
const retryCanAdmitProvider = this.settingsManager.getRetrySettings().enabled && (retryableError || hardErrorFallbackEligible);
|
|
957
|
+
let compactedBeforeRetry = false;
|
|
958
|
+
if (retryCanAdmitProvider &&
|
|
959
|
+
requiredAutoCompaction &&
|
|
960
|
+
!(requiredAutoCompaction === "threshold" && this._hasPendingPostCompactionUsageExemption(msg))) {
|
|
961
|
+
this._retireFailedRetryAssistant(msg);
|
|
962
|
+
compactedBeforeRetry = await this._runPrePromptCompaction(msg, true, "threshold", true);
|
|
963
|
+
retryContinuationBlocked = !compactedBeforeRetry;
|
|
663
964
|
}
|
|
664
|
-
|
|
665
|
-
|
|
965
|
+
let retryOutcome = "not-handled";
|
|
966
|
+
if (!retryContinuationBlocked) {
|
|
967
|
+
if (retryableError) {
|
|
968
|
+
retryOutcome = await this._handleRetryableError(msg);
|
|
969
|
+
}
|
|
970
|
+
else if (hardErrorFallbackEligible) {
|
|
971
|
+
retryOutcome = await this._handleRetryableError(msg, { hardErrorFallback: true });
|
|
972
|
+
}
|
|
666
973
|
}
|
|
667
|
-
if (
|
|
668
|
-
return;
|
|
974
|
+
if (retryOutcome === "continued")
|
|
975
|
+
return;
|
|
669
976
|
this._resolveRetry();
|
|
670
|
-
|
|
977
|
+
retryContinuationBlocked ||= retryOutcome === "blocked";
|
|
978
|
+
if (!retryContinuationBlocked) {
|
|
979
|
+
if (compactedBeforeRetry && this.agent.hasQueuedMessages()) {
|
|
980
|
+
this._scheduleContinuationAfterCurrentEvent();
|
|
981
|
+
launchedContinuation = true;
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
launchedContinuation = await this._checkCompaction(msg);
|
|
985
|
+
allowsPostCompactionUsageExemptContinuation = this._postCompactionUsageExemptAssistants.has(msg);
|
|
986
|
+
if (allowsPostCompactionUsageExemptContinuation) {
|
|
987
|
+
this._flushPostCompactionDeferredMessages();
|
|
988
|
+
}
|
|
989
|
+
// _runAutoCompaction() returns false both when recovery was rejected and
|
|
990
|
+
// when an accepted compaction had no queue to continue. Re-sample after
|
|
991
|
+
// it settles so only the still-required (rejected) case fails admission.
|
|
992
|
+
if (requiredAutoCompaction &&
|
|
993
|
+
!launchedContinuation &&
|
|
994
|
+
this.agent.hasQueuedMessages() &&
|
|
995
|
+
this._getRequiredAutoCompactionReason(msg) !== undefined) {
|
|
996
|
+
this._requiredCompactionAdmissionError = new RequiredCompactionError();
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
671
1000
|
}
|
|
672
1001
|
if (event.type === "agent_end") {
|
|
673
1002
|
this._flushPendingBashMessages();
|
|
674
|
-
if (!launchedContinuation &&
|
|
1003
|
+
if (!launchedContinuation &&
|
|
1004
|
+
!retryContinuationBlocked &&
|
|
1005
|
+
(allowsQueuedContinuation || allowsPostCompactionUsageExemptContinuation) &&
|
|
1006
|
+
this.agent.hasQueuedMessages()) {
|
|
675
1007
|
this._scheduleContinuationAfterCurrentEvent();
|
|
676
1008
|
launchedContinuation = true;
|
|
677
1009
|
}
|
|
@@ -688,16 +1020,6 @@ export class AgentSession {
|
|
|
688
1020
|
this._retryPromise = undefined;
|
|
689
1021
|
}
|
|
690
1022
|
}
|
|
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
1023
|
/** Find the last assistant message in agent state (including aborted ones) */
|
|
702
1024
|
_findLastAssistantMessage() {
|
|
703
1025
|
const messages = this.agent.state.messages;
|
|
@@ -709,9 +1031,47 @@ export class AgentSession {
|
|
|
709
1031
|
}
|
|
710
1032
|
return undefined;
|
|
711
1033
|
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Retry failures stay in append-only history but must not be retained in the
|
|
1036
|
+
* active context branch. Otherwise split-turn compaction keeps the failed
|
|
1037
|
+
* assistant response verbatim and cannot make progress before a retry.
|
|
1038
|
+
*/
|
|
1039
|
+
_retireFailedRetryAssistant(message) {
|
|
1040
|
+
const position = this.sessionManager.getMessageEntryPosition(message);
|
|
1041
|
+
if (!position || this.sessionManager.getLeafId() !== position.entryId)
|
|
1042
|
+
return;
|
|
1043
|
+
const entry = this.sessionManager.getEntry(position.entryId);
|
|
1044
|
+
if (entry?.type !== "message")
|
|
1045
|
+
return;
|
|
1046
|
+
if (entry.parentId === null) {
|
|
1047
|
+
this.sessionManager.resetLeaf();
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
this.sessionManager.branch(entry.parentId);
|
|
1051
|
+
}
|
|
1052
|
+
const messageIndex = this.agent.state.messages.lastIndexOf(message);
|
|
1053
|
+
if (messageIndex !== -1) {
|
|
1054
|
+
this.agent.state.messages = this.agent.state.messages.slice(0, messageIndex);
|
|
1055
|
+
}
|
|
1056
|
+
this._incrementMessageRevision();
|
|
1057
|
+
}
|
|
712
1058
|
_isAssistantFromBeforeLatestCompaction(assistantMessage) {
|
|
713
1059
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
714
|
-
|
|
1060
|
+
if (compactionEntry === null)
|
|
1061
|
+
return false;
|
|
1062
|
+
// An agent message_end can still be awaiting persistence while compaction
|
|
1063
|
+
// commits. It is necessarily a post-boundary message, even when a provider
|
|
1064
|
+
// supplied an older payload timestamp.
|
|
1065
|
+
if (this._messageEndsAwaitingPersistence.has(assistantMessage))
|
|
1066
|
+
return false;
|
|
1067
|
+
const messagePosition = this.sessionManager.getMessageEntryPosition(assistantMessage);
|
|
1068
|
+
const compactionOrder = this.sessionManager.getEntryOrder(compactionEntry.id);
|
|
1069
|
+
if (messagePosition !== undefined && compactionOrder !== undefined) {
|
|
1070
|
+
return messagePosition.order <= compactionOrder;
|
|
1071
|
+
}
|
|
1072
|
+
// Reloaded/reconstructed messages have no runtime identity. Retain the
|
|
1073
|
+
// historical timestamp heuristic only for that compatibility path.
|
|
1074
|
+
return assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime();
|
|
715
1075
|
}
|
|
716
1076
|
_replaceMessageInPlace(target, replacement) {
|
|
717
1077
|
// Agent-core stores the finalized message object in its state before emitting message_end.
|
|
@@ -1006,17 +1366,28 @@ export class AgentSession {
|
|
|
1006
1366
|
validToolNames.push(name);
|
|
1007
1367
|
}
|
|
1008
1368
|
}
|
|
1369
|
+
const activeToolNamesChanged = validToolNames.length !== this.agent.state.tools.length ||
|
|
1370
|
+
validToolNames.some((name, index) => name !== this.agent.state.tools[index]?.name);
|
|
1009
1371
|
this.agent.state.tools = tools;
|
|
1010
1372
|
// Rebuild base system prompt with new tool set
|
|
1011
1373
|
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
1012
1374
|
this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
|
|
1375
|
+
if (activeToolNamesChanged) {
|
|
1376
|
+
this.abortCompaction();
|
|
1377
|
+
this._incrementMessageRevision();
|
|
1378
|
+
}
|
|
1013
1379
|
}
|
|
1014
1380
|
/** Whether compaction or branch summarization is currently running */
|
|
1015
1381
|
get isCompacting() {
|
|
1016
|
-
return (this.
|
|
1382
|
+
return (this._pendingCompactionAdmission !== undefined ||
|
|
1383
|
+
this._compactionLifecycle.state.status === "running" ||
|
|
1384
|
+
this._autoCompactionAbortController !== undefined ||
|
|
1017
1385
|
this._compactionAbortController !== undefined ||
|
|
1018
1386
|
this._branchSummaryAbortController !== undefined);
|
|
1019
1387
|
}
|
|
1388
|
+
get compactionState() {
|
|
1389
|
+
return this._compactionLifecycle.state;
|
|
1390
|
+
}
|
|
1020
1391
|
/** All messages including custom types like BashExecutionMessage */
|
|
1021
1392
|
get messages() {
|
|
1022
1393
|
return this.agent.state.messages;
|
|
@@ -1162,28 +1533,52 @@ export class AgentSession {
|
|
|
1162
1533
|
await userAbortPromise;
|
|
1163
1534
|
throwIfCancelled();
|
|
1164
1535
|
}
|
|
1165
|
-
const
|
|
1536
|
+
const ownsPromptStart = !this.isStreaming && !this._promptStartPending && options?.streamingBehavior === undefined;
|
|
1537
|
+
if (ownsPromptStart)
|
|
1538
|
+
this._promptStartPending = true;
|
|
1539
|
+
// Extension bindings deliberately fire-and-forget sendUserMessage(), so an
|
|
1540
|
+
// extension callback cannot deadlock on this wait. The resulting provider
|
|
1541
|
+
// turn must still serialize behind compaction and other session work just
|
|
1542
|
+
// like an interactive prompt does.
|
|
1543
|
+
const shouldWaitForSessionWork = true;
|
|
1544
|
+
const pendingCompactionAdmissionAtExtensionAdmission = options?.source === "extension" ? this._pendingCompactionAdmission : undefined;
|
|
1545
|
+
const compactionGenerationAtExtensionAdmission = options?.source === "extension" &&
|
|
1546
|
+
this._sessionWorkBarrier.hasActiveWork &&
|
|
1547
|
+
this._compactionLifecycle.state.status !== "idle"
|
|
1548
|
+
? this._compactionLifecycle.state.generation
|
|
1549
|
+
: undefined;
|
|
1166
1550
|
// A steer/followUp submission during an active run can be queued immediately.
|
|
1167
1551
|
// Waiting on the session-work barrier here would trap the message inside this
|
|
1168
1552
|
// call for the rest of the run whenever a queued continuation (e.g. an active
|
|
1169
1553
|
// goal chain) holds the barrier, making typed input invisible until the run
|
|
1170
1554
|
// ends or the user aborts.
|
|
1171
|
-
const canQueueWhileStreaming = this.isStreaming
|
|
1555
|
+
const canQueueWhileStreaming = (this.isStreaming || this._promptStartPending) &&
|
|
1556
|
+
!this.isCompacting &&
|
|
1557
|
+
options?.streamingBehavior !== undefined;
|
|
1172
1558
|
if (shouldWaitForSessionWork &&
|
|
1173
1559
|
!canQueueWhileStreaming &&
|
|
1174
1560
|
(!this.isStreaming || this.isCompacting || this._sessionWorkBarrier.hasActiveWork)) {
|
|
1175
1561
|
await this._waitForSettledSessionWork();
|
|
1176
1562
|
throwIfCancelled();
|
|
1563
|
+
options?.onSessionWorkReady?.();
|
|
1177
1564
|
}
|
|
1178
1565
|
// Turn boundary: restore the primary model if the fallback cooldown expired.
|
|
1179
1566
|
if (!this.isStreaming) {
|
|
1180
|
-
|
|
1567
|
+
try {
|
|
1568
|
+
await this._maybeRestoreFallbackPrimary();
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
if (ownsPromptStart)
|
|
1572
|
+
this._promptStartPending = false;
|
|
1573
|
+
throw error;
|
|
1574
|
+
}
|
|
1181
1575
|
}
|
|
1182
1576
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
1183
1577
|
const preflightResult = options?.preflightResult;
|
|
1184
1578
|
const promptDisposition = options?.promptDisposition;
|
|
1185
1579
|
let messages;
|
|
1186
1580
|
let titlePrompt;
|
|
1581
|
+
let consumedNextTurnMessages;
|
|
1187
1582
|
try {
|
|
1188
1583
|
// Handle extension commands first (execute immediately, even during streaming)
|
|
1189
1584
|
// Extension commands manage their own LLM interaction via pi.sendMessage()
|
|
@@ -1238,6 +1633,23 @@ export class AgentSession {
|
|
|
1238
1633
|
preflightResult?.(true);
|
|
1239
1634
|
return;
|
|
1240
1635
|
}
|
|
1636
|
+
// Input accepted while a run was active remains queued even if that run
|
|
1637
|
+
// ends while extension input handling or template expansion is pending.
|
|
1638
|
+
// Starting a fresh prompt here would let it overtake the held continuation.
|
|
1639
|
+
if (canQueueWhileStreaming && !this.isStreaming) {
|
|
1640
|
+
if (options?.thinkingLevel !== undefined) {
|
|
1641
|
+
throw new Error("Cannot set thinkingLevel on a queued prompt; set it after the current turn completes.");
|
|
1642
|
+
}
|
|
1643
|
+
if (options?.streamingBehavior === "followUp") {
|
|
1644
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
1645
|
+
}
|
|
1646
|
+
else {
|
|
1647
|
+
await this._queueSteer(expandedText, currentImages);
|
|
1648
|
+
}
|
|
1649
|
+
promptDisposition?.("queued");
|
|
1650
|
+
preflightResult?.(true);
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1241
1653
|
// The queue-while-streaming bypass above skipped the settled-work wait. If
|
|
1242
1654
|
// the run ended while input was being expanded, serialize with remaining
|
|
1243
1655
|
// session work before sending a fresh prompt, and re-queue if a scheduled
|
|
@@ -1262,6 +1674,25 @@ export class AgentSession {
|
|
|
1262
1674
|
return;
|
|
1263
1675
|
}
|
|
1264
1676
|
}
|
|
1677
|
+
// A background extension prompt that arrived during a manual compaction
|
|
1678
|
+
// must never overtake a rejected or aborted compaction. Keep its normal
|
|
1679
|
+
// queue ownership instead of attempting another provider admission.
|
|
1680
|
+
if ((pendingCompactionAdmissionAtExtensionAdmission !== undefined &&
|
|
1681
|
+
pendingCompactionAdmissionAtExtensionAdmission.outcome !== "completed") ||
|
|
1682
|
+
(compactionGenerationAtExtensionAdmission !== undefined &&
|
|
1683
|
+
this._compactionLifecycle.state.generation === compactionGenerationAtExtensionAdmission &&
|
|
1684
|
+
(this._compactionLifecycle.state.status === "failed" ||
|
|
1685
|
+
this._compactionLifecycle.state.status === "aborted"))) {
|
|
1686
|
+
if (options?.streamingBehavior === "followUp") {
|
|
1687
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
1688
|
+
}
|
|
1689
|
+
else {
|
|
1690
|
+
await this._queueSteer(expandedText, currentImages);
|
|
1691
|
+
}
|
|
1692
|
+
promptDisposition?.("queued");
|
|
1693
|
+
preflightResult?.(true);
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1265
1696
|
// Flush any pending bash messages before the new prompt
|
|
1266
1697
|
this._flushPendingBashMessages();
|
|
1267
1698
|
// Validate model
|
|
@@ -1279,19 +1710,8 @@ export class AgentSession {
|
|
|
1279
1710
|
}
|
|
1280
1711
|
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
1281
1712
|
}
|
|
1282
|
-
// Check if we need to compact before sending (catches aborted responses).
|
|
1283
1713
|
// 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
|
-
}
|
|
1714
|
+
await this._enforceCompactionBeforeProvider(this._findLastAssistantMessage(), false, "pre_prompt");
|
|
1295
1715
|
// Build messages array (custom message if any, then user message)
|
|
1296
1716
|
messages = [];
|
|
1297
1717
|
// Add user message
|
|
@@ -1304,11 +1724,14 @@ export class AgentSession {
|
|
|
1304
1724
|
content: userContent,
|
|
1305
1725
|
timestamp: Date.now(),
|
|
1306
1726
|
});
|
|
1307
|
-
//
|
|
1308
|
-
|
|
1727
|
+
// Consume next-turn messages transactionally: a final admission rejection
|
|
1728
|
+
// restores them in their original order rather than dropping or duplicating
|
|
1729
|
+
// one-shot extension state.
|
|
1730
|
+
consumedNextTurnMessages = this._pendingNextTurnMessages;
|
|
1731
|
+
this._pendingNextTurnMessages = [];
|
|
1732
|
+
for (const msg of consumedNextTurnMessages) {
|
|
1309
1733
|
messages.push(msg);
|
|
1310
1734
|
}
|
|
1311
|
-
this._pendingNextTurnMessages = [];
|
|
1312
1735
|
// Emit before_agent_start extension event
|
|
1313
1736
|
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPrompt, this._baseSystemPromptOptions);
|
|
1314
1737
|
// Add all custom messages from extensions
|
|
@@ -1335,11 +1758,22 @@ export class AgentSession {
|
|
|
1335
1758
|
this._systemPromptOverride = undefined;
|
|
1336
1759
|
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
1337
1760
|
}
|
|
1761
|
+
// The preflight above only sees persisted session context. These prompt,
|
|
1762
|
+
// next-turn, and before_agent_start additions are also provider-visible,
|
|
1763
|
+
// so make one final admission decision against the complete request.
|
|
1764
|
+
await this._enforceFinalProviderAdmission(messages);
|
|
1338
1765
|
}
|
|
1339
1766
|
catch (error) {
|
|
1767
|
+
if (consumedNextTurnMessages && consumedNextTurnMessages.length > 0) {
|
|
1768
|
+
this._pendingNextTurnMessages = [...consumedNextTurnMessages, ...this._pendingNextTurnMessages];
|
|
1769
|
+
}
|
|
1340
1770
|
preflightResult?.(false);
|
|
1341
1771
|
throw error;
|
|
1342
1772
|
}
|
|
1773
|
+
finally {
|
|
1774
|
+
if (ownsPromptStart)
|
|
1775
|
+
this._promptStartPending = false;
|
|
1776
|
+
}
|
|
1343
1777
|
if (!messages) {
|
|
1344
1778
|
return;
|
|
1345
1779
|
}
|
|
@@ -1352,7 +1786,14 @@ export class AgentSession {
|
|
|
1352
1786
|
await this._promptAgent(messages);
|
|
1353
1787
|
await this.waitForRetry();
|
|
1354
1788
|
await this.waitForIdle();
|
|
1355
|
-
if (
|
|
1789
|
+
if (options?.onSessionWorkReady) {
|
|
1790
|
+
// This prompt owns a session-work token acquired after waiting for a
|
|
1791
|
+
// prior operation, so waiting for the global barrier here would await
|
|
1792
|
+
// itself. _promptAgent() already drained its event queue; any newly
|
|
1793
|
+
// scheduled continuation retains its own barrier ownership.
|
|
1794
|
+
await this.agent.waitForIdle();
|
|
1795
|
+
}
|
|
1796
|
+
else if (shouldWaitForSessionWork) {
|
|
1356
1797
|
await this._waitForSettledSessionWork();
|
|
1357
1798
|
}
|
|
1358
1799
|
else {
|
|
@@ -1479,7 +1920,7 @@ export class AgentSession {
|
|
|
1479
1920
|
sessionId: this.sessionId,
|
|
1480
1921
|
baseOptions: this._buildSessionTitleBaseOptions(),
|
|
1481
1922
|
signal: abortController.signal,
|
|
1482
|
-
streamFn: this.agent.
|
|
1923
|
+
streamFn: this.agent.streamFunction,
|
|
1483
1924
|
});
|
|
1484
1925
|
if (abortController.signal.aborted) {
|
|
1485
1926
|
return;
|
|
@@ -1532,11 +1973,12 @@ export class AgentSession {
|
|
|
1532
1973
|
if (images) {
|
|
1533
1974
|
content.push(...images);
|
|
1534
1975
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
}
|
|
1976
|
+
const message = { role: "user", content, timestamp: Date.now() };
|
|
1977
|
+
if (this._promptStartPending && this._skipNextPostCompactionAssistantCheck) {
|
|
1978
|
+
this._postCompactionDeferredSteeringMessages.push(message);
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
this.agent.steer(message);
|
|
1540
1982
|
}
|
|
1541
1983
|
/**
|
|
1542
1984
|
* Internal: Queue a follow-up message (already expanded, no extension command check).
|
|
@@ -1548,11 +1990,22 @@ export class AgentSession {
|
|
|
1548
1990
|
if (images) {
|
|
1549
1991
|
content.push(...images);
|
|
1550
1992
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
}
|
|
1993
|
+
const message = { role: "user", content, timestamp: Date.now() };
|
|
1994
|
+
if (this._promptStartPending && this._skipNextPostCompactionAssistantCheck) {
|
|
1995
|
+
this._postCompactionDeferredFollowUpMessages.push(message);
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
this.agent.followUp(message);
|
|
1999
|
+
}
|
|
2000
|
+
_flushPostCompactionDeferredMessages() {
|
|
2001
|
+
for (const message of this._postCompactionDeferredSteeringMessages) {
|
|
2002
|
+
this.agent.steer(message);
|
|
2003
|
+
}
|
|
2004
|
+
this._postCompactionDeferredSteeringMessages = [];
|
|
2005
|
+
for (const message of this._postCompactionDeferredFollowUpMessages) {
|
|
2006
|
+
this.agent.followUp(message);
|
|
2007
|
+
}
|
|
2008
|
+
this._postCompactionDeferredFollowUpMessages = [];
|
|
1556
2009
|
}
|
|
1557
2010
|
/**
|
|
1558
2011
|
* Throw an error if the text is an extension command.
|
|
@@ -1587,26 +2040,56 @@ export class AgentSession {
|
|
|
1587
2040
|
details: message.details,
|
|
1588
2041
|
timestamp: Date.now(),
|
|
1589
2042
|
};
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
2043
|
+
const waitForExistingSessionWork = options?.triggerTurn === true &&
|
|
2044
|
+
options.deliverAs !== "nextTurn" &&
|
|
2045
|
+
!this.isStreaming &&
|
|
2046
|
+
this._sessionWorkBarrier.hasActiveWork;
|
|
2047
|
+
const pendingCompactionAdmission = this._pendingCompactionAdmission;
|
|
2048
|
+
const activeCompactionGeneration = this._compactionLifecycle.state.status === "running" ? this._compactionLifecycle.state.generation : undefined;
|
|
2049
|
+
let finishSessionWork;
|
|
2050
|
+
try {
|
|
2051
|
+
if (waitForExistingSessionWork) {
|
|
2052
|
+
await this._waitForSettledSessionWork();
|
|
2053
|
+
finishSessionWork = this._sessionWorkBarrier.begin();
|
|
2054
|
+
}
|
|
2055
|
+
if (options?.deliverAs === "nextTurn") {
|
|
2056
|
+
this._pendingNextTurnMessages.push(appMessage);
|
|
2057
|
+
}
|
|
2058
|
+
else if (this.isStreaming) {
|
|
2059
|
+
if (options?.deliverAs === "followUp") {
|
|
2060
|
+
this.agent.followUp(appMessage);
|
|
2061
|
+
}
|
|
2062
|
+
else {
|
|
2063
|
+
this.agent.steer(appMessage);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
else if (options?.triggerTurn === true &&
|
|
2067
|
+
((pendingCompactionAdmission !== undefined && pendingCompactionAdmission.outcome !== "completed") ||
|
|
2068
|
+
(activeCompactionGeneration !== undefined &&
|
|
2069
|
+
this._compactionLifecycle.state.generation === activeCompactionGeneration &&
|
|
2070
|
+
this._compactionLifecycle.state.status !== "completed"))) {
|
|
2071
|
+
if (options?.deliverAs === "followUp") {
|
|
2072
|
+
this.agent.followUp(appMessage);
|
|
2073
|
+
}
|
|
2074
|
+
else {
|
|
2075
|
+
this.agent.steer(appMessage);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
else if (options?.triggerTurn) {
|
|
2079
|
+
await this._enforceCompactionBeforeProvider(this._findLastAssistantMessage(), false, "pre_prompt");
|
|
2080
|
+
await this._enforceFinalProviderAdmission([appMessage]);
|
|
2081
|
+
await this._promptAgent(appMessage);
|
|
1596
2082
|
}
|
|
1597
2083
|
else {
|
|
1598
|
-
this.agent.
|
|
2084
|
+
this.agent.state.messages.push(appMessage);
|
|
2085
|
+
this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
|
|
2086
|
+
this._incrementMessageRevision();
|
|
2087
|
+
this._emit({ type: "message_start", message: appMessage });
|
|
2088
|
+
this._emit({ type: "message_end", message: appMessage });
|
|
1599
2089
|
}
|
|
1600
2090
|
}
|
|
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 });
|
|
2091
|
+
finally {
|
|
2092
|
+
finishSessionWork?.();
|
|
1610
2093
|
}
|
|
1611
2094
|
}
|
|
1612
2095
|
/**
|
|
@@ -1617,6 +2100,14 @@ export class AgentSession {
|
|
|
1617
2100
|
* @param options.deliverAs Delivery mode when streaming: "steer" or "followUp"
|
|
1618
2101
|
*/
|
|
1619
2102
|
async sendUserMessage(content, options) {
|
|
2103
|
+
const bindingPromptReadiness = this._extensionBindingPromptReadiness;
|
|
2104
|
+
let resolveBindingPromptReadiness;
|
|
2105
|
+
if (bindingPromptReadiness) {
|
|
2106
|
+
const readiness = new Promise((resolve) => {
|
|
2107
|
+
resolveBindingPromptReadiness = resolve;
|
|
2108
|
+
});
|
|
2109
|
+
bindingPromptReadiness.add(readiness);
|
|
2110
|
+
}
|
|
1620
2111
|
// Normalize content to text string + optional images
|
|
1621
2112
|
let text;
|
|
1622
2113
|
let images;
|
|
@@ -1638,14 +2129,31 @@ export class AgentSession {
|
|
|
1638
2129
|
if (images.length === 0)
|
|
1639
2130
|
images = undefined;
|
|
1640
2131
|
}
|
|
1641
|
-
//
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
2132
|
+
// An extension binding invokes this method fire-and-forget. When it
|
|
2133
|
+
// arrives during session work, retain a barrier token after the prior work
|
|
2134
|
+
// settles so callers observing session idle cannot race its provider turn.
|
|
2135
|
+
const waitForExistingSessionWork = this._sessionWorkBarrier.hasActiveWork;
|
|
2136
|
+
let finishSessionWork;
|
|
2137
|
+
try {
|
|
2138
|
+
// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion
|
|
2139
|
+
await this.prompt(text, {
|
|
2140
|
+
expandPromptTemplates: false,
|
|
2141
|
+
streamingBehavior: options?.deliverAs,
|
|
2142
|
+
images,
|
|
2143
|
+
source: "extension",
|
|
2144
|
+
promptDisposition: () => resolveBindingPromptReadiness?.(),
|
|
2145
|
+
onSessionWorkReady: waitForExistingSessionWork
|
|
2146
|
+
? () => {
|
|
2147
|
+
finishSessionWork ??= this._sessionWorkBarrier.begin();
|
|
2148
|
+
}
|
|
2149
|
+
: undefined,
|
|
2150
|
+
sessionTitlePrompt: false,
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
finally {
|
|
2154
|
+
resolveBindingPromptReadiness?.();
|
|
2155
|
+
finishSessionWork?.();
|
|
2156
|
+
}
|
|
1649
2157
|
}
|
|
1650
2158
|
/**
|
|
1651
2159
|
* Clear all queued messages and return them.
|
|
@@ -1655,8 +2163,13 @@ export class AgentSession {
|
|
|
1655
2163
|
clearQueue() {
|
|
1656
2164
|
const steering = [...this._steeringMessages];
|
|
1657
2165
|
const followUp = [...this._followUpMessages];
|
|
2166
|
+
// Clear every queue synchronously. Deferred post-compaction messages are
|
|
2167
|
+
// already represented in visible bookkeeping, so they must not be returned
|
|
2168
|
+
// a second time or later resurrected into Agent's native queues.
|
|
1658
2169
|
this._steeringMessages = [];
|
|
1659
2170
|
this._followUpMessages = [];
|
|
2171
|
+
this._postCompactionDeferredSteeringMessages = [];
|
|
2172
|
+
this._postCompactionDeferredFollowUpMessages = [];
|
|
1660
2173
|
this.agent.clearAllQueues();
|
|
1661
2174
|
this._emitQueueUpdate();
|
|
1662
2175
|
return { steering, followUp };
|
|
@@ -1680,30 +2193,8 @@ export class AgentSession {
|
|
|
1680
2193
|
* Abort current operation and wait for agent to become idle.
|
|
1681
2194
|
*/
|
|
1682
2195
|
async abort() {
|
|
1683
|
-
this.abortRetry();
|
|
1684
2196
|
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
|
-
}
|
|
2197
|
+
await this._abortActiveAgentAndRetry();
|
|
1707
2198
|
}
|
|
1708
2199
|
async waitForIdle() {
|
|
1709
2200
|
if (this.isIdle) {
|
|
@@ -1779,6 +2270,7 @@ export class AgentSession {
|
|
|
1779
2270
|
persistDefault: updateGlobalDefaults,
|
|
1780
2271
|
appendSessionEntry: true,
|
|
1781
2272
|
emitModelSelect: true,
|
|
2273
|
+
modelSelectSource: "set",
|
|
1782
2274
|
invalidateCompaction: true,
|
|
1783
2275
|
});
|
|
1784
2276
|
}
|
|
@@ -1817,7 +2309,7 @@ export class AgentSession {
|
|
|
1817
2309
|
}
|
|
1818
2310
|
if (!opts.emitModelSelect)
|
|
1819
2311
|
return undefined;
|
|
1820
|
-
return await this._emitModelSelect(model, previousModel,
|
|
2312
|
+
return await this._emitModelSelect(model, previousModel, opts.modelSelectSource);
|
|
1821
2313
|
}
|
|
1822
2314
|
_applyEphemeralThinkingLevel(level) {
|
|
1823
2315
|
const previousLevel = this.agent.state.thinkingLevel;
|
|
@@ -2009,29 +2501,133 @@ export class AgentSession {
|
|
|
2009
2501
|
// =========================================================================
|
|
2010
2502
|
// Compaction
|
|
2011
2503
|
// =========================================================================
|
|
2504
|
+
_claimCompactionController(controller, owner) {
|
|
2505
|
+
const supersedesCurrentOperation = (this._compactionAbortController !== undefined && this._compactionAbortController !== controller) ||
|
|
2506
|
+
(this._autoCompactionAbortController !== undefined && this._autoCompactionAbortController !== controller);
|
|
2507
|
+
if (supersedesCurrentOperation) {
|
|
2508
|
+
// Supersession has no public terminal event: the stale operation no
|
|
2509
|
+
// longer owns the route. Finishing its lifecycle state here prevents a
|
|
2510
|
+
// missing late feedback end from keeping isCompacting true.
|
|
2511
|
+
this._compactionLifecycle.abort(this._messageRevision);
|
|
2512
|
+
}
|
|
2513
|
+
if (this._compactionAbortController && this._compactionAbortController !== controller) {
|
|
2514
|
+
this._compactionAbortController.abort();
|
|
2515
|
+
this._compactionAbortController = undefined;
|
|
2516
|
+
}
|
|
2517
|
+
if (this._autoCompactionAbortController && this._autoCompactionAbortController !== controller) {
|
|
2518
|
+
this._autoCompactionAbortController.abort();
|
|
2519
|
+
this._autoCompactionAbortController = undefined;
|
|
2520
|
+
}
|
|
2521
|
+
if (owner === "auto") {
|
|
2522
|
+
this._autoCompactionAbortController = controller;
|
|
2523
|
+
}
|
|
2524
|
+
else {
|
|
2525
|
+
this._compactionAbortController = controller;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
_ownsCompactionController(controller, owner) {
|
|
2529
|
+
return (!controller.signal.aborted &&
|
|
2530
|
+
(owner === "auto"
|
|
2531
|
+
? this._autoCompactionAbortController === controller
|
|
2532
|
+
: this._compactionAbortController === controller));
|
|
2533
|
+
}
|
|
2534
|
+
_releaseCompactionController(signal) {
|
|
2535
|
+
if (this._compactionAbortController?.signal === signal) {
|
|
2536
|
+
this._compactionAbortController = undefined;
|
|
2537
|
+
}
|
|
2538
|
+
if (this._autoCompactionAbortController?.signal === signal) {
|
|
2539
|
+
this._autoCompactionAbortController = undefined;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
_claimPendingCompactionAdmission() {
|
|
2543
|
+
const priorAdmission = this._pendingCompactionAdmission;
|
|
2544
|
+
if (priorAdmission) {
|
|
2545
|
+
priorAdmission.controller.abort();
|
|
2546
|
+
priorAdmission.outcome = "aborted";
|
|
2547
|
+
priorAdmission.finishSessionWork();
|
|
2548
|
+
}
|
|
2549
|
+
const controller = new AbortController();
|
|
2550
|
+
const admission = {
|
|
2551
|
+
controller,
|
|
2552
|
+
finishSessionWork: this._sessionWorkBarrier.begin(),
|
|
2553
|
+
};
|
|
2554
|
+
this._pendingCompactionAdmission = admission;
|
|
2555
|
+
this._claimCompactionController(controller, "compaction");
|
|
2556
|
+
return admission;
|
|
2557
|
+
}
|
|
2558
|
+
_releasePendingCompactionAdmission(admission, outcome) {
|
|
2559
|
+
admission.outcome = outcome;
|
|
2560
|
+
if (this._pendingCompactionAdmission !== admission)
|
|
2561
|
+
return;
|
|
2562
|
+
this._pendingCompactionAdmission = undefined;
|
|
2563
|
+
admission.finishSessionWork();
|
|
2564
|
+
}
|
|
2565
|
+
async _abortActiveAgentAndRetry() {
|
|
2566
|
+
this.abortRetry();
|
|
2567
|
+
this.abortBranchSummary();
|
|
2568
|
+
if (this._userAbortPromise) {
|
|
2569
|
+
this.agent.abort();
|
|
2570
|
+
await this._userAbortPromise;
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (this.isStreaming) {
|
|
2574
|
+
this._suppressQueuedContinuationAfterUserAbort = true;
|
|
2575
|
+
}
|
|
2576
|
+
const abortPromise = (async () => {
|
|
2577
|
+
this.agent.abort();
|
|
2578
|
+
await this.waitForIdle();
|
|
2579
|
+
})();
|
|
2580
|
+
this._userAbortPromise = abortPromise;
|
|
2581
|
+
try {
|
|
2582
|
+
await abortPromise;
|
|
2583
|
+
}
|
|
2584
|
+
finally {
|
|
2585
|
+
if (this._userAbortPromise === abortPromise) {
|
|
2586
|
+
this._userAbortPromise = undefined;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2012
2590
|
/**
|
|
2013
2591
|
* Manually compact the session context.
|
|
2014
2592
|
* Aborts current agent operation first.
|
|
2015
2593
|
* @param customInstructions Optional instructions for the compaction summary
|
|
2016
2594
|
*/
|
|
2017
2595
|
async compact(customInstructions) {
|
|
2018
|
-
this.
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2596
|
+
const admission = this._claimPendingCompactionAdmission();
|
|
2597
|
+
const controller = admission.controller;
|
|
2598
|
+
let outcome = "failed";
|
|
2599
|
+
let disconnected = false;
|
|
2022
2600
|
try {
|
|
2023
|
-
|
|
2601
|
+
// Keep the session subscriber attached until the aborted run emits
|
|
2602
|
+
// agent_end. That event clears the active-run and retry state that
|
|
2603
|
+
// waitForIdle() depends on.
|
|
2604
|
+
await this._abortActiveAgentAndRetry();
|
|
2605
|
+
this._disconnectFromAgent();
|
|
2606
|
+
disconnected = true;
|
|
2607
|
+
this._emit({ type: "compaction_start", reason: "manual" });
|
|
2608
|
+
const execution = await this._executeCompaction({
|
|
2609
|
+
controller,
|
|
2610
|
+
owner: "compaction",
|
|
2611
|
+
reason: "manual",
|
|
2612
|
+
customInstructions,
|
|
2613
|
+
willRetry: false,
|
|
2614
|
+
});
|
|
2024
2615
|
if (!execution.accepted) {
|
|
2025
2616
|
throw new CompactionRejectedError(execution.rejectionCause);
|
|
2026
2617
|
}
|
|
2618
|
+
outcome = "completed";
|
|
2027
2619
|
return execution.result;
|
|
2028
2620
|
}
|
|
2029
2621
|
catch (error) {
|
|
2622
|
+
outcome = isCompactionExecutionAborted(error) ? "aborted" : "failed";
|
|
2030
2623
|
if (error instanceof CompactionRejectedError) {
|
|
2031
2624
|
throw new Error(error.message);
|
|
2032
2625
|
}
|
|
2626
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
2627
|
+
throw error;
|
|
2628
|
+
}
|
|
2033
2629
|
const message = error instanceof Error ? error.message : String(error);
|
|
2034
|
-
const aborted =
|
|
2630
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2035
2631
|
this._emit({
|
|
2036
2632
|
type: "compaction_end",
|
|
2037
2633
|
reason: "manual",
|
|
@@ -2043,21 +2639,34 @@ export class AgentSession {
|
|
|
2043
2639
|
throw error;
|
|
2044
2640
|
}
|
|
2045
2641
|
finally {
|
|
2046
|
-
this._compactionAbortController
|
|
2047
|
-
|
|
2642
|
+
if (this._compactionAbortController === controller && this._compactionLifecycle.state.status !== "running") {
|
|
2643
|
+
this._compactionAbortController = undefined;
|
|
2644
|
+
}
|
|
2645
|
+
this._releasePendingCompactionAdmission(admission, outcome);
|
|
2646
|
+
if (disconnected && !this.isCompacting)
|
|
2647
|
+
this._reconnectToAgent();
|
|
2048
2648
|
}
|
|
2049
2649
|
}
|
|
2050
2650
|
async applyCompaction(precomputed, options) {
|
|
2651
|
+
if (options.signal !== undefined && options.signal !== this._compactionAbortController?.signal) {
|
|
2652
|
+
return { applied: false, reason: "stale" };
|
|
2653
|
+
}
|
|
2051
2654
|
if (options.expectedRevision !== undefined && options.expectedRevision !== this._messageRevision) {
|
|
2052
2655
|
return { applied: false, reason: "stale" };
|
|
2053
2656
|
}
|
|
2054
2657
|
const ownsController = this._compactionAbortController === undefined;
|
|
2055
2658
|
if (ownsController) {
|
|
2056
|
-
this.
|
|
2659
|
+
this._claimCompactionController(new AbortController(), "compaction");
|
|
2057
2660
|
this._emit({ type: "compaction_start", reason: options.reason });
|
|
2058
2661
|
}
|
|
2662
|
+
const controller = this._compactionAbortController;
|
|
2663
|
+
if (!controller)
|
|
2664
|
+
return { applied: false, reason: "rejected" };
|
|
2665
|
+
this._claimCompactionController(controller, "compaction");
|
|
2059
2666
|
try {
|
|
2060
2667
|
const execution = await this._executeCompaction({
|
|
2668
|
+
controller,
|
|
2669
|
+
owner: "compaction",
|
|
2061
2670
|
reason: options.reason,
|
|
2062
2671
|
willRetry: false,
|
|
2063
2672
|
precomputed,
|
|
@@ -2068,8 +2677,11 @@ export class AgentSession {
|
|
|
2068
2677
|
return { applied: true, reason: "ok" };
|
|
2069
2678
|
}
|
|
2070
2679
|
catch (error) {
|
|
2680
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
2681
|
+
return { applied: false, reason: "rejected" };
|
|
2682
|
+
}
|
|
2071
2683
|
const message = error instanceof Error ? error.message : String(error);
|
|
2072
|
-
const aborted =
|
|
2684
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2073
2685
|
this._emit({
|
|
2074
2686
|
type: "compaction_end",
|
|
2075
2687
|
reason: options.reason,
|
|
@@ -2081,18 +2693,27 @@ export class AgentSession {
|
|
|
2081
2693
|
return { applied: false, reason: "rejected" };
|
|
2082
2694
|
}
|
|
2083
2695
|
finally {
|
|
2084
|
-
this._compactionAbortController
|
|
2696
|
+
if (this._compactionAbortController === controller && this._compactionLifecycle.state.status !== "running") {
|
|
2697
|
+
this._compactionAbortController = undefined;
|
|
2698
|
+
}
|
|
2085
2699
|
}
|
|
2086
2700
|
}
|
|
2087
2701
|
_beginExtensionCompactionFeedback(reason) {
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2702
|
+
const controller = new AbortController();
|
|
2703
|
+
this._claimCompactionController(controller, "compaction");
|
|
2704
|
+
const model = this.model;
|
|
2705
|
+
this._compactionLifecycle.begin({
|
|
2706
|
+
operationId: randomUUID(),
|
|
2707
|
+
stage: "feedback",
|
|
2708
|
+
reason,
|
|
2709
|
+
model: model ? { provider: model.provider, id: model.id } : undefined,
|
|
2710
|
+
startedRevision: this._messageRevision,
|
|
2711
|
+
}, controller);
|
|
2712
|
+
this._emit({ type: "compaction_start", reason });
|
|
2713
|
+
return controller.signal;
|
|
2093
2714
|
}
|
|
2094
2715
|
_updateExtensionCompactionFeedback(options) {
|
|
2095
|
-
if (!
|
|
2716
|
+
if (!options.signal || !this._compactionLifecycle.hasCurrentSignal(options.signal))
|
|
2096
2717
|
return;
|
|
2097
2718
|
this._emit({
|
|
2098
2719
|
type: "compaction_progress",
|
|
@@ -2102,10 +2723,24 @@ export class AgentSession {
|
|
|
2102
2723
|
});
|
|
2103
2724
|
}
|
|
2104
2725
|
_endExtensionCompactionFeedback(options) {
|
|
2105
|
-
|
|
2106
|
-
|
|
2726
|
+
if (!options.signal)
|
|
2727
|
+
return;
|
|
2728
|
+
if (!this._compactionLifecycle.hasCurrentSignal(options.signal)) {
|
|
2729
|
+
this._releaseCompactionController(options.signal);
|
|
2107
2730
|
return;
|
|
2108
|
-
|
|
2731
|
+
}
|
|
2732
|
+
const operation = this._compactionLifecycle.state;
|
|
2733
|
+
if (operation.status !== "running" || operation.stage !== "feedback")
|
|
2734
|
+
return;
|
|
2735
|
+
const aborted = options.aborted ?? options.signal.aborted;
|
|
2736
|
+
this._compactionLifecycle.finish({
|
|
2737
|
+
operationId: operation.operationId,
|
|
2738
|
+
status: aborted ? "aborted" : "failed",
|
|
2739
|
+
endedRevision: this._messageRevision,
|
|
2740
|
+
...(aborted
|
|
2741
|
+
? { errorMessage: "Compaction cancelled" }
|
|
2742
|
+
: { errorMessage: options.errorMessage ?? "Compaction did not apply" }),
|
|
2743
|
+
});
|
|
2109
2744
|
this._emit({
|
|
2110
2745
|
type: "compaction_end",
|
|
2111
2746
|
reason: options.reason,
|
|
@@ -2114,26 +2749,41 @@ export class AgentSession {
|
|
|
2114
2749
|
willRetry: false,
|
|
2115
2750
|
errorMessage: aborted ? undefined : options.errorMessage,
|
|
2116
2751
|
});
|
|
2117
|
-
this.
|
|
2752
|
+
this._releaseCompactionController(options.signal);
|
|
2118
2753
|
}
|
|
2119
2754
|
async _executeCompaction(request) {
|
|
2755
|
+
const model = this.model;
|
|
2756
|
+
if (!model)
|
|
2757
|
+
throw new Error(formatNoModelSelectedMessage());
|
|
2758
|
+
const thinkingLevel = this.thinkingLevel;
|
|
2759
|
+
const controller = request.controller;
|
|
2760
|
+
// Async auth and provider preparation may yield to a newer route. A stale
|
|
2761
|
+
// controller must never promote itself into a lifecycle generation.
|
|
2762
|
+
if (!this._ownsCompactionController(controller, request.owner)) {
|
|
2763
|
+
throw new CompactionExecutionError(new CompactionCancelledError(), false, true);
|
|
2764
|
+
}
|
|
2765
|
+
const requestId = randomUUID();
|
|
2766
|
+
const operationId = this._compactionLifecycle.begin({
|
|
2767
|
+
operationId: requestId,
|
|
2768
|
+
stage: "execution",
|
|
2769
|
+
reason: request.reason,
|
|
2770
|
+
model: { provider: model.provider, id: model.id },
|
|
2771
|
+
startedRevision: this._messageRevision,
|
|
2772
|
+
}, controller);
|
|
2120
2773
|
const finishCompactionWork = this._sessionWorkBarrier.begin();
|
|
2121
2774
|
const agentMessagesAtStart = request.agentMessagesAtStart ?? this.agent.state.messages.slice();
|
|
2775
|
+
const compactionOwnedDiagnosticMessages = new Set();
|
|
2776
|
+
const signal = controller.signal;
|
|
2122
2777
|
try {
|
|
2123
|
-
if (
|
|
2124
|
-
throw new
|
|
2778
|
+
if (signal.aborted) {
|
|
2779
|
+
throw new CompactionCancelledError();
|
|
2125
2780
|
}
|
|
2126
|
-
const requestId = randomUUID();
|
|
2127
2781
|
const pathEntries = this.sessionManager.getBranch();
|
|
2128
2782
|
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
2783
|
let compactionResult = request.precomputed;
|
|
2134
2784
|
let fromExtension = request.precomputed !== undefined;
|
|
2135
2785
|
if (!compactionResult) {
|
|
2136
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
2786
|
+
const preparation = prepareCompaction(pathEntries, settings, request.reason === "overflow", request.allowSummaryOnly);
|
|
2137
2787
|
if (!preparation) {
|
|
2138
2788
|
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
2139
2789
|
if (lastEntry?.type === "compaction") {
|
|
@@ -2142,6 +2792,7 @@ export class AgentSession {
|
|
|
2142
2792
|
throw new Error("Nothing to compact (session too small)");
|
|
2143
2793
|
}
|
|
2144
2794
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2795
|
+
const messagesBeforeExtension = new Set(this.agent.state.messages);
|
|
2145
2796
|
const extensionResult = (await this._extensionRunner.emit({
|
|
2146
2797
|
type: "session_before_compact",
|
|
2147
2798
|
reason: request.reason,
|
|
@@ -2152,8 +2803,17 @@ export class AgentSession {
|
|
|
2152
2803
|
customInstructions: request.customInstructions,
|
|
2153
2804
|
signal,
|
|
2154
2805
|
}));
|
|
2806
|
+
for (const message of this.agent.state.messages) {
|
|
2807
|
+
if (!messagesBeforeExtension.has(message) &&
|
|
2808
|
+
isCompactionOwnedPreCompactDiagnostic(message, requestId)) {
|
|
2809
|
+
compactionOwnedDiagnosticMessages.add(message);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (!this._compactionLifecycle.isCurrent(operationId, controller)) {
|
|
2813
|
+
throw new CompactionCancelledError();
|
|
2814
|
+
}
|
|
2155
2815
|
if (extensionResult?.cancel) {
|
|
2156
|
-
return await this._rejectCompaction(request, requestId, extensionResult.rejectionCause ?? "cancelled-by-extension", true, extensionResult.reason);
|
|
2816
|
+
return await this._rejectCompaction(request, requestId, operationId, extensionResult.rejectionCause ?? "cancelled-by-extension", true, extensionResult.reason);
|
|
2157
2817
|
}
|
|
2158
2818
|
if (extensionResult?.compaction) {
|
|
2159
2819
|
compactionResult = extensionResult.compaction;
|
|
@@ -2161,17 +2821,38 @@ export class AgentSession {
|
|
|
2161
2821
|
}
|
|
2162
2822
|
}
|
|
2163
2823
|
if (!compactionResult) {
|
|
2164
|
-
const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(
|
|
2165
|
-
compactionResult = await compact(preparation,
|
|
2824
|
+
const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(model);
|
|
2825
|
+
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
2826
|
}
|
|
2167
2827
|
}
|
|
2168
2828
|
if (signal.aborted) {
|
|
2169
|
-
throw new
|
|
2829
|
+
throw new CompactionCancelledError();
|
|
2830
|
+
}
|
|
2831
|
+
if (!this._compactionLifecycle.isCurrent(operationId, controller)) {
|
|
2832
|
+
throw new CompactionCancelledError();
|
|
2170
2833
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2834
|
+
const lifecycleState = this._compactionLifecycle.state;
|
|
2835
|
+
const currentMessagesAtCheck = this.agent.state.messages;
|
|
2836
|
+
const startPrefixIntact = agentMessagesAtStart.every((message, index) => currentMessagesAtCheck[index] === message);
|
|
2837
|
+
// Appends after the start snapshot are fresh only while they are exact
|
|
2838
|
+
// message_end identities still awaiting persistence. Any revision change,
|
|
2839
|
+
// other append, replacement, or reorder still makes this compaction stale.
|
|
2840
|
+
const onlyPendingPersistenceAppends = startPrefixIntact &&
|
|
2841
|
+
currentMessagesAtCheck
|
|
2842
|
+
.slice(agentMessagesAtStart.length)
|
|
2843
|
+
.every((message) => this._messageEndsAwaitingPersistence.has(message) ||
|
|
2844
|
+
compactionOwnedDiagnosticMessages.has(message));
|
|
2845
|
+
const sourceChanged = lifecycleState.status !== "running" ||
|
|
2846
|
+
lifecycleState.operationId !== operationId ||
|
|
2847
|
+
lifecycleState.startedRevision + compactionOwnedDiagnosticMessages.size !== this._messageRevision ||
|
|
2848
|
+
!onlyPendingPersistenceAppends;
|
|
2849
|
+
if (sourceChanged) {
|
|
2850
|
+
return await this._rejectCompaction(request, requestId, operationId, "stale-revision", false);
|
|
2173
2851
|
}
|
|
2174
|
-
|
|
2852
|
+
if (this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
|
|
2853
|
+
return await this._rejectCompaction(request, requestId, operationId, "would-overflow", false);
|
|
2854
|
+
}
|
|
2855
|
+
const compactionEntryId = this.sessionManager.appendCompaction(compactionResult.summary, compactionResult.firstKeptEntryId, compactionResult.tokensBefore, compactionResult.details, fromExtension, compactionResult.usage);
|
|
2175
2856
|
const savedEntry = this.sessionManager.getEntry(compactionEntryId);
|
|
2176
2857
|
if (savedEntry?.type !== "compaction") {
|
|
2177
2858
|
throw new Error("Compaction entry was not saved");
|
|
@@ -2182,18 +2863,33 @@ export class AgentSession {
|
|
|
2182
2863
|
const messagesAppendedDuringCompaction = hasUnchangedPrefix
|
|
2183
2864
|
? currentAgentMessages.slice(agentMessagesAtStart.length)
|
|
2184
2865
|
: [];
|
|
2185
|
-
|
|
2866
|
+
// Preserve an identity-deduped, agent-ordered union of the append-during-
|
|
2867
|
+
// compaction suffix and exact messages still awaiting persistence. Their
|
|
2868
|
+
// queued message_end owns exactly-once persistence, so they are kept in
|
|
2869
|
+
// agent state only and never persisted here.
|
|
2870
|
+
const preservedIdentities = new Set(messagesAppendedDuringCompaction);
|
|
2871
|
+
for (const message of currentAgentMessages) {
|
|
2872
|
+
if (this._messageEndsAwaitingPersistence.has(message)) {
|
|
2873
|
+
preservedIdentities.add(message);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
const preservedPendingMessages = currentAgentMessages.filter((message) => preservedIdentities.delete(message));
|
|
2877
|
+
this._skipNextPostCompactionAssistantCheck = true;
|
|
2878
|
+
for (const message of preservedPendingMessages) {
|
|
2879
|
+
if (message.role === "assistant") {
|
|
2880
|
+
this._assistantsPendingAtCompaction.add(message);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
this.agent.state.messages = [...sessionContext.messages, ...preservedPendingMessages];
|
|
2186
2884
|
compactionResult.estimatedTokensAfter = estimateMessagesTokens(sessionContext.messages);
|
|
2187
2885
|
this._incrementMessageRevision();
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
willRetry: request.willRetry,
|
|
2196
|
-
});
|
|
2886
|
+
if (!this._compactionLifecycle.finish({
|
|
2887
|
+
operationId,
|
|
2888
|
+
status: "completed",
|
|
2889
|
+
endedRevision: this._messageRevision,
|
|
2890
|
+
})) {
|
|
2891
|
+
throw new CompactionCancelledError();
|
|
2892
|
+
}
|
|
2197
2893
|
this._emit({
|
|
2198
2894
|
type: "compaction_end",
|
|
2199
2895
|
reason: request.reason,
|
|
@@ -2203,15 +2899,41 @@ export class AgentSession {
|
|
|
2203
2899
|
requestId,
|
|
2204
2900
|
accepted: true,
|
|
2205
2901
|
});
|
|
2902
|
+
await this._extensionRunner.emit({
|
|
2903
|
+
type: "session_compact",
|
|
2904
|
+
reason: request.reason,
|
|
2905
|
+
requestId,
|
|
2906
|
+
accepted: true,
|
|
2907
|
+
compactionEntry: savedEntry,
|
|
2908
|
+
fromExtension,
|
|
2909
|
+
willRetry: request.willRetry,
|
|
2910
|
+
});
|
|
2206
2911
|
return { accepted: true, requestId, result: compactionResult, compactionEntry: savedEntry, fromExtension };
|
|
2207
2912
|
}
|
|
2913
|
+
catch (error) {
|
|
2914
|
+
if (error instanceof CompactionExecutionError) {
|
|
2915
|
+
throw error;
|
|
2916
|
+
}
|
|
2917
|
+
const lifecycleState = this._compactionLifecycle.state;
|
|
2918
|
+
const ownsTerminalTransition = lifecycleState.status !== "idle" && lifecycleState.operationId === operationId;
|
|
2919
|
+
const aborted = signal.aborted || isCompactionExecutionAborted(error);
|
|
2920
|
+
if (ownsTerminalTransition && lifecycleState.status === "running") {
|
|
2921
|
+
this._compactionLifecycle.finish({
|
|
2922
|
+
operationId,
|
|
2923
|
+
status: aborted ? "aborted" : "failed",
|
|
2924
|
+
endedRevision: this._messageRevision,
|
|
2925
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
throw new CompactionExecutionError(error, ownsTerminalTransition, aborted);
|
|
2929
|
+
}
|
|
2208
2930
|
finally {
|
|
2209
2931
|
finishCompactionWork();
|
|
2210
2932
|
}
|
|
2211
2933
|
}
|
|
2212
|
-
_wouldCompactionOverflow(pathEntries, compactionResult, fromExtension) {
|
|
2934
|
+
_wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model) {
|
|
2213
2935
|
const currentLeaf = pathEntries[pathEntries.length - 1];
|
|
2214
|
-
if (!currentLeaf
|
|
2936
|
+
if (!currentLeaf)
|
|
2215
2937
|
return false;
|
|
2216
2938
|
const simulatedCompactionEntry = {
|
|
2217
2939
|
type: "compaction",
|
|
@@ -2227,9 +2949,28 @@ export class AgentSession {
|
|
|
2227
2949
|
const simulatedMessages = buildSessionContext([...pathEntries, simulatedCompactionEntry], simulatedCompactionEntry.id).messages;
|
|
2228
2950
|
const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(simulatedMessages));
|
|
2229
2951
|
const settings = this.settingsManager.getCompactionSettings();
|
|
2230
|
-
return contextTokens >
|
|
2952
|
+
return contextTokens > model.contextWindow - settings.reserveTokens;
|
|
2953
|
+
}
|
|
2954
|
+
/**
|
|
2955
|
+
* Replaces agent state with the canonical session context while keeping exact
|
|
2956
|
+
* message objects whose message_end persistence is still queued. Identities
|
|
2957
|
+
* already present in the session context are kept from the context only, so
|
|
2958
|
+
* nothing is duplicated; the queued message_end still owns exactly-once
|
|
2959
|
+
* persistence for the rest.
|
|
2960
|
+
*/
|
|
2961
|
+
_restoreAgentMessagesFromSession() {
|
|
2962
|
+
const sessionMessages = this.sessionManager.buildSessionContext().messages;
|
|
2963
|
+
const seen = new Set(sessionMessages);
|
|
2964
|
+
const pendingMessages = [];
|
|
2965
|
+
for (const message of this.agent.state.messages) {
|
|
2966
|
+
if (seen.has(message) || !this._messageEndsAwaitingPersistence.has(message))
|
|
2967
|
+
continue;
|
|
2968
|
+
seen.add(message);
|
|
2969
|
+
pendingMessages.push(message);
|
|
2970
|
+
}
|
|
2971
|
+
this.agent.state.messages = [...sessionMessages, ...pendingMessages];
|
|
2231
2972
|
}
|
|
2232
|
-
async _rejectCompaction(request, requestId, rejectionCause, aborted, extensionReason) {
|
|
2973
|
+
async _rejectCompaction(request, requestId, operationId, rejectionCause, aborted, extensionReason) {
|
|
2233
2974
|
// Per plan Section 1: rejection must never be silent. The compaction_end event
|
|
2234
2975
|
// carries a non-empty human-readable errorMessage (unless the user aborted, where
|
|
2235
2976
|
// the aborted branch already renders "Compaction cancelled"). session_compact is
|
|
@@ -2241,6 +2982,15 @@ export class AgentSession {
|
|
|
2241
2982
|
? `Compaction rejected: ${trimmedExtensionReason}`
|
|
2242
2983
|
: describeCompactionRejection(rejectionCause);
|
|
2243
2984
|
const errorMessage = aborted && !trimmedExtensionReason ? undefined : detailedMessage;
|
|
2985
|
+
if (!this._compactionLifecycle.finish({
|
|
2986
|
+
operationId,
|
|
2987
|
+
status: "failed",
|
|
2988
|
+
endedRevision: this._messageRevision,
|
|
2989
|
+
rejectionCause,
|
|
2990
|
+
...(trimmedExtensionReason !== undefined ? { errorMessage: trimmedExtensionReason } : {}),
|
|
2991
|
+
})) {
|
|
2992
|
+
throw new CompactionCancelledError();
|
|
2993
|
+
}
|
|
2244
2994
|
this._emit({
|
|
2245
2995
|
type: "compaction_end",
|
|
2246
2996
|
reason: request.reason,
|
|
@@ -2267,8 +3017,19 @@ export class AgentSession {
|
|
|
2267
3017
|
* Cancel in-progress compaction (manual or auto).
|
|
2268
3018
|
*/
|
|
2269
3019
|
abortCompaction() {
|
|
3020
|
+
const feedbackOperation = this._compactionLifecycle.abort(this._messageRevision);
|
|
2270
3021
|
this._compactionAbortController?.abort();
|
|
2271
3022
|
this._autoCompactionAbortController?.abort();
|
|
3023
|
+
if (feedbackOperation?.stage === "feedback") {
|
|
3024
|
+
this._compactionAbortController = undefined;
|
|
3025
|
+
this._emit({
|
|
3026
|
+
type: "compaction_end",
|
|
3027
|
+
reason: feedbackOperation.reason,
|
|
3028
|
+
result: undefined,
|
|
3029
|
+
aborted: true,
|
|
3030
|
+
willRetry: false,
|
|
3031
|
+
});
|
|
3032
|
+
}
|
|
2272
3033
|
}
|
|
2273
3034
|
/**
|
|
2274
3035
|
* Cancel in-progress branch summarization.
|
|
@@ -2287,7 +3048,80 @@ export class AgentSession {
|
|
|
2287
3048
|
* @param assistantMessage The assistant message to check
|
|
2288
3049
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
2289
3050
|
*/
|
|
2290
|
-
async
|
|
3051
|
+
async _enforceCompactionBeforeProvider(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction = false) {
|
|
3052
|
+
const blockedAdmission = this._blockedPostCompactionAssistant;
|
|
3053
|
+
if (blockedAdmission !== undefined &&
|
|
3054
|
+
blockedAdmission.assistant === assistantMessage &&
|
|
3055
|
+
blockedAdmission.revision === this._messageRevision) {
|
|
3056
|
+
throw new RequiredCompactionError();
|
|
3057
|
+
}
|
|
3058
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
3059
|
+
const model = this.model;
|
|
3060
|
+
const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
|
|
3061
|
+
const compacted = assistantMessage
|
|
3062
|
+
? await this._checkCompaction(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction)
|
|
3063
|
+
: false;
|
|
3064
|
+
if (compacted || (assistantMessage && this._postCompactionUsageExemptAssistants.has(assistantMessage))) {
|
|
3065
|
+
return compacted;
|
|
3066
|
+
}
|
|
3067
|
+
if (!settings.enabled || !model || !shouldCompact(contextTokens, model.contextWindow, settings)) {
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
const latestCompaction = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
3071
|
+
const assistantBeforeLatestCompaction = assistantMessage !== undefined && this._isAssistantFromBeforeLatestCompaction(assistantMessage);
|
|
3072
|
+
const hasPostCompactionCustomState = latestCompaction !== null &&
|
|
3073
|
+
this.agent.state.messages.some((message) => message.role === "custom" && message.timestamp >= new Date(latestCompaction.timestamp).getTime());
|
|
3074
|
+
if (assistantBeforeLatestCompaction && !hasPostCompactionCustomState) {
|
|
3075
|
+
return false;
|
|
3076
|
+
}
|
|
3077
|
+
if (assistantBeforeLatestCompaction && assistantMessage) {
|
|
3078
|
+
const compacted = await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, inlineReason);
|
|
3079
|
+
if (compacted)
|
|
3080
|
+
return true;
|
|
3081
|
+
}
|
|
3082
|
+
throw new RequiredCompactionError();
|
|
3083
|
+
}
|
|
3084
|
+
/**
|
|
3085
|
+
* The normal pre-prompt check only estimates persisted session context. This
|
|
3086
|
+
* final gate also includes turn-local messages which the provider will see:
|
|
3087
|
+
* the current prompt, next-turn custom messages, and before_agent_start
|
|
3088
|
+
* additions. Compaction rewrites only session context, so callers retain and
|
|
3089
|
+
* reapply their already-assembled one-shot additions after it succeeds.
|
|
3090
|
+
*/
|
|
3091
|
+
async _enforceFinalProviderAdmission(messages) {
|
|
3092
|
+
// Normal user-only prompts keep the established provider-overflow recovery
|
|
3093
|
+
// path. This final gate closes the separate gap introduced by turn-local
|
|
3094
|
+
// custom additions, which the normal pre-prompt check cannot observe.
|
|
3095
|
+
if (!messages.some((message) => message.role === "custom"))
|
|
3096
|
+
return;
|
|
3097
|
+
const model = this.model;
|
|
3098
|
+
if (!model)
|
|
3099
|
+
return;
|
|
3100
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
3101
|
+
const isOversized = () => {
|
|
3102
|
+
const providerMessages = filterContextExcludedMessages([...this.agent.state.messages, ...messages]);
|
|
3103
|
+
const estimate = estimateContextTokens(providerMessages);
|
|
3104
|
+
const usageMessage = estimate.lastUsageIndex === null ? undefined : providerMessages[estimate.lastUsageIndex];
|
|
3105
|
+
// Kept assistant usage can describe the pre-compaction request. Once a
|
|
3106
|
+
// compaction boundary exists, fall back to byte-derived message estimates
|
|
3107
|
+
// until a provider response refreshes that usage.
|
|
3108
|
+
const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
|
|
3109
|
+
? estimateMessagesTokens(providerMessages)
|
|
3110
|
+
: estimate.tokens;
|
|
3111
|
+
return contextTokens > model.contextWindow - settings.reserveTokens;
|
|
3112
|
+
};
|
|
3113
|
+
if (!settings.enabled || !isOversized())
|
|
3114
|
+
return;
|
|
3115
|
+
const lastAssistantMessage = this._findLastAssistantMessage();
|
|
3116
|
+
if (!lastAssistantMessage) {
|
|
3117
|
+
throw new RequiredCompactionError();
|
|
3118
|
+
}
|
|
3119
|
+
const compacted = await this._runPrePromptCompaction(lastAssistantMessage, false, "pre_prompt");
|
|
3120
|
+
if (!compacted || isOversized()) {
|
|
3121
|
+
throw new RequiredCompactionError();
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
async _checkCompaction(assistantMessage, skipAbortedCheck = true, inlineReason, retryAfterCompaction = false) {
|
|
2291
3125
|
const settings = this.settingsManager.getCompactionSettings();
|
|
2292
3126
|
if (!settings.enabled)
|
|
2293
3127
|
return false;
|
|
@@ -2315,10 +3149,26 @@ export class AgentSession {
|
|
|
2315
3149
|
const currentContextNeedsCompaction = contextUsage !== undefined &&
|
|
2316
3150
|
contextUsage.tokens !== null &&
|
|
2317
3151
|
shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
|
|
2318
|
-
|
|
2319
|
-
|
|
3152
|
+
const isOverflow = isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction);
|
|
3153
|
+
if (isOverflow &&
|
|
3154
|
+
assistantMessage.stopReason === "stop" &&
|
|
3155
|
+
this._consumePostCompactionUsageExemption(assistantMessage)) {
|
|
3156
|
+
return false;
|
|
3157
|
+
}
|
|
3158
|
+
if (isOverflow) {
|
|
3159
|
+
this._flushPostCompactionDeferredMessages();
|
|
3160
|
+
const willRetry = retryAfterCompaction || assistantMessage.stopReason !== "stop";
|
|
2320
3161
|
if (!willRetry) {
|
|
2321
|
-
|
|
3162
|
+
const compacted = await this._runAutoCompaction("overflow", false);
|
|
3163
|
+
if (!compacted &&
|
|
3164
|
+
this._compactionLifecycle.state.status === "failed" &&
|
|
3165
|
+
getLatestCompactionEntry(this.sessionManager.getBranch()) !== null) {
|
|
3166
|
+
this._blockedPostCompactionAssistant = {
|
|
3167
|
+
assistant: assistantMessage,
|
|
3168
|
+
revision: this._messageRevision,
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
3171
|
+
return compacted;
|
|
2322
3172
|
}
|
|
2323
3173
|
if (this._overflowRecoveryAttempted) {
|
|
2324
3174
|
const errorMessage = "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.";
|
|
@@ -2339,17 +3189,29 @@ export class AgentSession {
|
|
|
2339
3189
|
// Remove the error message from agent state (it IS saved to session for history,
|
|
2340
3190
|
// but we don't want it in context for the retry)
|
|
2341
3191
|
const messages = this.agent.state.messages;
|
|
3192
|
+
let removedOverflowAssistant = false;
|
|
2342
3193
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
2343
3194
|
this.agent.state.messages = messages.slice(0, -1);
|
|
3195
|
+
removedOverflowAssistant = true;
|
|
2344
3196
|
this._incrementMessageRevision();
|
|
2345
3197
|
}
|
|
2346
|
-
|
|
2347
|
-
|
|
3198
|
+
const compacted = inlineReason
|
|
3199
|
+
? await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, "overflow", willRetry)
|
|
3200
|
+
: await this._runAutoCompaction("overflow", willRetry);
|
|
3201
|
+
if (!compacted && removedOverflowAssistant) {
|
|
3202
|
+
this._restoreAgentMessagesFromSession();
|
|
3203
|
+
this._incrementMessageRevision();
|
|
2348
3204
|
}
|
|
2349
|
-
|
|
2350
|
-
|
|
3205
|
+
if (!compacted && inlineReason) {
|
|
3206
|
+
throw new RequiredCompactionError();
|
|
2351
3207
|
}
|
|
3208
|
+
return compacted;
|
|
2352
3209
|
}
|
|
3210
|
+
// The first ordinary response can carry provider usage calculated before
|
|
3211
|
+
// compaction. Consume that exemption only after proving this is not an
|
|
3212
|
+
// overflow, then retain the decision for later admission re-sampling.
|
|
3213
|
+
if (this._consumePostCompactionUsageExemption(assistantMessage))
|
|
3214
|
+
return false;
|
|
2353
3215
|
// Case 2: Threshold - context is getting large
|
|
2354
3216
|
// For error messages or all-zero usage messages, estimate from the last valid response.
|
|
2355
3217
|
// This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
|
|
@@ -2375,7 +3237,7 @@ export class AgentSession {
|
|
|
2375
3237
|
const usageMsg = messages[estimate.lastUsageIndex];
|
|
2376
3238
|
if (compactionEntry &&
|
|
2377
3239
|
usageMsg.role === "assistant" &&
|
|
2378
|
-
|
|
3240
|
+
this._isAssistantFromBeforeLatestCompaction(usageMsg)) {
|
|
2379
3241
|
return false;
|
|
2380
3242
|
}
|
|
2381
3243
|
contextTokens = estimate.tokens;
|
|
@@ -2383,35 +3245,53 @@ export class AgentSession {
|
|
|
2383
3245
|
}
|
|
2384
3246
|
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
2385
3247
|
if (inlineReason) {
|
|
2386
|
-
return await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, inlineReason);
|
|
3248
|
+
return await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction);
|
|
2387
3249
|
}
|
|
2388
3250
|
else {
|
|
2389
|
-
|
|
3251
|
+
const compacted = await this._runAutoCompaction("threshold", false);
|
|
3252
|
+
if (!compacted &&
|
|
3253
|
+
this._compactionLifecycle.state.status === "failed" &&
|
|
3254
|
+
getLatestCompactionEntry(this.sessionManager.getBranch()) !== null) {
|
|
3255
|
+
this._blockedPostCompactionAssistant = {
|
|
3256
|
+
assistant: assistantMessage,
|
|
3257
|
+
revision: this._messageRevision,
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
return compacted;
|
|
2390
3261
|
}
|
|
2391
3262
|
}
|
|
2392
3263
|
return false;
|
|
2393
3264
|
}
|
|
2394
|
-
async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false) {
|
|
3265
|
+
async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false, allowSummaryOnly = false) {
|
|
3266
|
+
const controller = new AbortController();
|
|
3267
|
+
this._claimCompactionController(controller, "compaction");
|
|
2395
3268
|
this._emit({ type: "compaction_start", reason });
|
|
2396
|
-
this._compactionAbortController = new AbortController();
|
|
2397
3269
|
try {
|
|
2398
3270
|
const execution = await this._executeCompaction({
|
|
3271
|
+
controller,
|
|
3272
|
+
owner: "compaction",
|
|
2399
3273
|
reason,
|
|
2400
3274
|
willRetry,
|
|
2401
3275
|
lastAssistantMessage,
|
|
2402
3276
|
skipAbortedCheck,
|
|
3277
|
+
allowSummaryOnly,
|
|
2403
3278
|
});
|
|
2404
|
-
if (!execution.accepted &&
|
|
3279
|
+
if (!execution.accepted &&
|
|
3280
|
+
lastAssistantMessage &&
|
|
3281
|
+
isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
|
|
2405
3282
|
this._overflowRecoveryAttempted = false;
|
|
2406
3283
|
}
|
|
2407
3284
|
return execution.accepted;
|
|
2408
3285
|
}
|
|
2409
3286
|
catch (error) {
|
|
2410
|
-
if (
|
|
3287
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
3288
|
+
return false;
|
|
3289
|
+
}
|
|
3290
|
+
if (lastAssistantMessage && isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
|
|
2411
3291
|
this._overflowRecoveryAttempted = false;
|
|
2412
3292
|
}
|
|
2413
3293
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
2414
|
-
const aborted =
|
|
3294
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2415
3295
|
this._emit({
|
|
2416
3296
|
type: "compaction_end",
|
|
2417
3297
|
reason,
|
|
@@ -2423,12 +3303,44 @@ export class AgentSession {
|
|
|
2423
3303
|
return false;
|
|
2424
3304
|
}
|
|
2425
3305
|
finally {
|
|
2426
|
-
this._compactionAbortController
|
|
3306
|
+
if (this._compactionAbortController === controller && this._compactionLifecycle.state.status !== "running") {
|
|
3307
|
+
this._compactionAbortController = undefined;
|
|
3308
|
+
}
|
|
2427
3309
|
}
|
|
2428
3310
|
}
|
|
3311
|
+
async _revalidateScheduledContinuationAdmission() {
|
|
3312
|
+
const model = this.model;
|
|
3313
|
+
const settings = this.settingsManager.getCompactionSettings();
|
|
3314
|
+
if (!model || !settings.enabled)
|
|
3315
|
+
return;
|
|
3316
|
+
const canonicalMessages = filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages);
|
|
3317
|
+
const estimate = estimateContextTokens(canonicalMessages);
|
|
3318
|
+
const usageMessage = estimate.lastUsageIndex === null ? undefined : canonicalMessages[estimate.lastUsageIndex];
|
|
3319
|
+
const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
|
|
3320
|
+
? estimateMessagesTokens(canonicalMessages)
|
|
3321
|
+
: estimate.tokens;
|
|
3322
|
+
if (!shouldCompact(contextTokens, model.contextWindow, settings))
|
|
3323
|
+
return;
|
|
3324
|
+
const compacted = await this._runPrePromptCompaction(this._findLastAssistantMessage(), true, "pre_prompt");
|
|
3325
|
+
if (!compacted)
|
|
3326
|
+
throw new RequiredCompactionError();
|
|
3327
|
+
this._scheduledContinuationRecompacted = true;
|
|
3328
|
+
}
|
|
2429
3329
|
async _continueAgentAfterCurrentRun() {
|
|
2430
3330
|
await this.agent.waitForIdle();
|
|
2431
|
-
await this.
|
|
3331
|
+
await this._revalidateScheduledContinuationAdmission();
|
|
3332
|
+
const useQueuedContinuation = this._scheduledContinuationRecompacted;
|
|
3333
|
+
try {
|
|
3334
|
+
if (useQueuedContinuation) {
|
|
3335
|
+
await this.agent.continueWithQueuedMessages();
|
|
3336
|
+
}
|
|
3337
|
+
else {
|
|
3338
|
+
await this.agent.continue();
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
finally {
|
|
3342
|
+
this._scheduledContinuationRecompacted = false;
|
|
3343
|
+
}
|
|
2432
3344
|
}
|
|
2433
3345
|
_scheduleContinuationAfterCurrentEvent() {
|
|
2434
3346
|
// Tool hooks wait for queued message persistence, so continue() cannot run inside this event promise.
|
|
@@ -2458,49 +3370,53 @@ export class AgentSession {
|
|
|
2458
3370
|
async _runAutoCompaction(reason, willRetry) {
|
|
2459
3371
|
const finishCompactionWork = this._sessionWorkBarrier.begin();
|
|
2460
3372
|
const agentMessagesAtStart = this.agent.state.messages.slice();
|
|
2461
|
-
this._emit({ type: "compaction_start", reason });
|
|
2462
3373
|
const autoCompactionController = new AbortController();
|
|
2463
|
-
this.
|
|
3374
|
+
this._claimCompactionController(autoCompactionController, "auto");
|
|
3375
|
+
const endBeforeExecution = () => {
|
|
3376
|
+
this._emit({ type: "compaction_start", reason });
|
|
3377
|
+
if (reason === "overflow")
|
|
3378
|
+
this._overflowRecoveryAttempted = false;
|
|
3379
|
+
this._emit({
|
|
3380
|
+
type: "compaction_end",
|
|
3381
|
+
reason,
|
|
3382
|
+
result: undefined,
|
|
3383
|
+
aborted: false,
|
|
3384
|
+
willRetry: false,
|
|
3385
|
+
});
|
|
3386
|
+
return false;
|
|
3387
|
+
};
|
|
2464
3388
|
try {
|
|
2465
3389
|
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;
|
|
3390
|
+
return endBeforeExecution();
|
|
2476
3391
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
reason,
|
|
2484
|
-
result: undefined,
|
|
2485
|
-
aborted: false,
|
|
2486
|
-
willRetry: false,
|
|
2487
|
-
});
|
|
2488
|
-
return false;
|
|
3392
|
+
try {
|
|
3393
|
+
// Resolve once before admission so a pending auth refresh remains a
|
|
3394
|
+
// cancellable boundary. _executeCompaction resolves the policy-specific
|
|
3395
|
+
// auth after extension compaction hooks have had a chance to provide a
|
|
3396
|
+
// summary without credentials.
|
|
3397
|
+
await this._modelRuntime.getAuth(this.model);
|
|
2489
3398
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
reason,
|
|
2497
|
-
result: undefined,
|
|
2498
|
-
aborted: false,
|
|
2499
|
-
willRetry: false,
|
|
2500
|
-
});
|
|
3399
|
+
catch {
|
|
3400
|
+
if (!this._ownsCompactionController(autoCompactionController, "auto"))
|
|
3401
|
+
return false;
|
|
3402
|
+
return endBeforeExecution();
|
|
3403
|
+
}
|
|
3404
|
+
if (!this._ownsCompactionController(autoCompactionController, "auto"))
|
|
2501
3405
|
return false;
|
|
3406
|
+
const preparation = prepareCompaction(this.sessionManager.getBranch(), this.settingsManager.getCompactionSettings(), reason === "overflow");
|
|
3407
|
+
if (!preparation) {
|
|
3408
|
+
return endBeforeExecution();
|
|
2502
3409
|
}
|
|
2503
|
-
|
|
3410
|
+
if (!this._ownsCompactionController(autoCompactionController, "auto"))
|
|
3411
|
+
return false;
|
|
3412
|
+
this._emit({ type: "compaction_start", reason });
|
|
3413
|
+
const execution = await this._executeCompaction({
|
|
3414
|
+
controller: autoCompactionController,
|
|
3415
|
+
owner: "auto",
|
|
3416
|
+
reason,
|
|
3417
|
+
willRetry,
|
|
3418
|
+
agentMessagesAtStart,
|
|
3419
|
+
});
|
|
2504
3420
|
if (!execution.accepted) {
|
|
2505
3421
|
if (reason === "overflow")
|
|
2506
3422
|
this._overflowRecoveryAttempted = false;
|
|
@@ -2530,10 +3446,13 @@ export class AgentSession {
|
|
|
2530
3446
|
return false;
|
|
2531
3447
|
}
|
|
2532
3448
|
catch (error) {
|
|
3449
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
3450
|
+
return false;
|
|
3451
|
+
}
|
|
2533
3452
|
if (reason === "overflow")
|
|
2534
3453
|
this._overflowRecoveryAttempted = false;
|
|
2535
3454
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
2536
|
-
const aborted =
|
|
3455
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2537
3456
|
this._emit({
|
|
2538
3457
|
type: "compaction_end",
|
|
2539
3458
|
reason,
|
|
@@ -2566,27 +3485,39 @@ export class AgentSession {
|
|
|
2566
3485
|
return this.settingsManager.getCompactionEnabled();
|
|
2567
3486
|
}
|
|
2568
3487
|
async bindExtensions(bindings) {
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
3488
|
+
const finishBindingWork = this._sessionWorkBarrier.begin();
|
|
3489
|
+
const bindingPromptReadiness = new Set();
|
|
3490
|
+
this._extensionBindingPromptReadiness = bindingPromptReadiness;
|
|
3491
|
+
try {
|
|
3492
|
+
if (bindings.uiContext !== undefined) {
|
|
3493
|
+
this._extensionUIContext = bindings.uiContext;
|
|
3494
|
+
}
|
|
3495
|
+
if (bindings.mode !== undefined) {
|
|
3496
|
+
this._extensionMode = bindings.mode;
|
|
3497
|
+
}
|
|
3498
|
+
if (bindings.commandContextActions !== undefined) {
|
|
3499
|
+
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
3500
|
+
}
|
|
3501
|
+
if (bindings.abortHandler !== undefined) {
|
|
3502
|
+
this._extensionAbortHandler = bindings.abortHandler;
|
|
3503
|
+
}
|
|
3504
|
+
if (bindings.shutdownHandler !== undefined) {
|
|
3505
|
+
this._extensionShutdownHandler = bindings.shutdownHandler;
|
|
3506
|
+
}
|
|
3507
|
+
if (bindings.onError !== undefined) {
|
|
3508
|
+
this._extensionErrorListener = bindings.onError;
|
|
3509
|
+
}
|
|
3510
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
3511
|
+
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
3512
|
+
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
2583
3513
|
}
|
|
2584
|
-
|
|
2585
|
-
this.
|
|
3514
|
+
finally {
|
|
3515
|
+
if (this._extensionBindingPromptReadiness === bindingPromptReadiness) {
|
|
3516
|
+
this._extensionBindingPromptReadiness = undefined;
|
|
3517
|
+
}
|
|
3518
|
+
finishBindingWork();
|
|
2586
3519
|
}
|
|
2587
|
-
|
|
2588
|
-
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
2589
|
-
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
3520
|
+
await Promise.all(bindingPromptReadiness);
|
|
2590
3521
|
}
|
|
2591
3522
|
async extendResourcesFromExtensions(reason) {
|
|
2592
3523
|
if (!this._extensionRunner.hasHandlers("resources_discover")) {
|
|
@@ -2729,7 +3660,7 @@ export class AgentSession {
|
|
|
2729
3660
|
getServiceTier: () => this.serviceTier,
|
|
2730
3661
|
isIdle: () => this.isIdle,
|
|
2731
3662
|
isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
|
|
2732
|
-
getSignal: () => this.agent.signal,
|
|
3663
|
+
getSignal: () => this._extensionEventSignal ?? this.agent.signal,
|
|
2733
3664
|
abort: () => {
|
|
2734
3665
|
if (this._extensionAbortHandler) {
|
|
2735
3666
|
this._extensionAbortHandler();
|
|
@@ -2788,27 +3719,39 @@ export class AgentSession {
|
|
|
2788
3719
|
},
|
|
2789
3720
|
},
|
|
2790
3721
|
compact: (options) => {
|
|
3722
|
+
const admission = this._claimPendingCompactionAdmission();
|
|
3723
|
+
const controller = admission.controller;
|
|
2791
3724
|
void (async () => {
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
this._compactionAbortController = new AbortController();
|
|
2795
|
-
this._emit({ type: "compaction_start", reason: "extension" });
|
|
3725
|
+
let outcome = "failed";
|
|
3726
|
+
let disconnected = false;
|
|
2796
3727
|
try {
|
|
3728
|
+
await this._abortActiveAgentAndRetry();
|
|
3729
|
+
this._disconnectFromAgent();
|
|
3730
|
+
disconnected = true;
|
|
3731
|
+
this._emit({ type: "compaction_start", reason: "extension" });
|
|
2797
3732
|
const execution = await this._executeCompaction({
|
|
3733
|
+
controller,
|
|
3734
|
+
owner: "compaction",
|
|
2798
3735
|
reason: "extension",
|
|
2799
3736
|
customInstructions: options?.customInstructions,
|
|
2800
3737
|
willRetry: false,
|
|
2801
3738
|
});
|
|
2802
3739
|
if (execution.accepted) {
|
|
3740
|
+
outcome = "completed";
|
|
2803
3741
|
options?.onComplete?.(execution.result);
|
|
2804
3742
|
}
|
|
2805
3743
|
else {
|
|
3744
|
+
outcome = "failed";
|
|
2806
3745
|
options?.onError?.(new CompactionRejectedError(execution.rejectionCause));
|
|
2807
3746
|
}
|
|
2808
3747
|
}
|
|
2809
3748
|
catch (error) {
|
|
3749
|
+
outcome = isCompactionExecutionAborted(error) ? "aborted" : "failed";
|
|
3750
|
+
if (!compactionExecutionOwnsTerminalTransition(error)) {
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
2810
3753
|
const message = error instanceof Error ? error.message : String(error);
|
|
2811
|
-
const aborted =
|
|
3754
|
+
const aborted = isCompactionExecutionAborted(error);
|
|
2812
3755
|
this._emit({
|
|
2813
3756
|
type: "compaction_end",
|
|
2814
3757
|
reason: "extension",
|
|
@@ -2821,8 +3764,13 @@ export class AgentSession {
|
|
|
2821
3764
|
options?.onError?.(err);
|
|
2822
3765
|
}
|
|
2823
3766
|
finally {
|
|
2824
|
-
this._compactionAbortController
|
|
2825
|
-
|
|
3767
|
+
if (this._compactionAbortController === controller &&
|
|
3768
|
+
this._compactionLifecycle.state.status !== "running") {
|
|
3769
|
+
this._compactionAbortController = undefined;
|
|
3770
|
+
}
|
|
3771
|
+
this._releasePendingCompactionAdmission(admission, outcome);
|
|
3772
|
+
if (disconnected && !this.isCompacting)
|
|
3773
|
+
this._reconnectToAgent();
|
|
2826
3774
|
}
|
|
2827
3775
|
})();
|
|
2828
3776
|
},
|
|
@@ -3056,15 +4004,43 @@ export class AgentSession {
|
|
|
3056
4004
|
}
|
|
3057
4005
|
return Math.ceil(value);
|
|
3058
4006
|
}
|
|
4007
|
+
/**
|
|
4008
|
+
* Retry policy + callbacks shared by compaction and branch-summary summarization calls.
|
|
4009
|
+
* Uses the same `settings.retry` budget/backoff as agent-turn retries so a single transient
|
|
4010
|
+
* stream drop no longer fails the whole operation. `source` carries the context
|
|
4011
|
+
* the TUI needs to render the retry and recreate the underlying indicator.
|
|
4012
|
+
*/
|
|
4013
|
+
_summarizationRetryCallbacks(source) {
|
|
4014
|
+
return {
|
|
4015
|
+
onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => {
|
|
4016
|
+
this._emit({
|
|
4017
|
+
type: "summarization_retry_scheduled",
|
|
4018
|
+
attempt,
|
|
4019
|
+
maxAttempts,
|
|
4020
|
+
delayMs,
|
|
4021
|
+
errorMessage,
|
|
4022
|
+
});
|
|
4023
|
+
},
|
|
4024
|
+
onRetryAttemptStart: () => {
|
|
4025
|
+
this._emit({
|
|
4026
|
+
type: "summarization_retry_attempt_start",
|
|
4027
|
+
...source,
|
|
4028
|
+
});
|
|
4029
|
+
},
|
|
4030
|
+
onRetryFinished: () => {
|
|
4031
|
+
this._emit({ type: "summarization_retry_finished" });
|
|
4032
|
+
},
|
|
4033
|
+
};
|
|
4034
|
+
}
|
|
3059
4035
|
/**
|
|
3060
4036
|
* Handle retryable errors with exponential backoff.
|
|
3061
|
-
* @returns
|
|
4037
|
+
* @returns whether retry continuation started, was blocked by compaction, or was not handled
|
|
3062
4038
|
*/
|
|
3063
4039
|
async _handleRetryableError(message, options = {}) {
|
|
3064
4040
|
const settings = this.settingsManager.getRetrySettings();
|
|
3065
4041
|
if (!settings.enabled) {
|
|
3066
4042
|
this._resolveRetry();
|
|
3067
|
-
return
|
|
4043
|
+
return "not-handled";
|
|
3068
4044
|
}
|
|
3069
4045
|
// Retry promise is created synchronously in _handleAgentEvent for agent_end.
|
|
3070
4046
|
// Keep a defensive fallback here in case a future refactor bypasses that path.
|
|
@@ -3082,7 +4058,7 @@ export class AgentSession {
|
|
|
3082
4058
|
switchedFallback = await this._retryFallback.tryFallback("hard-error", { errorMessage });
|
|
3083
4059
|
if (!switchedFallback) {
|
|
3084
4060
|
this._resolveRetry();
|
|
3085
|
-
return
|
|
4061
|
+
return "not-handled";
|
|
3086
4062
|
}
|
|
3087
4063
|
// The fallback starts fresh; the failed model's transient attempts do not carry over.
|
|
3088
4064
|
this._retryAttempt = 1;
|
|
@@ -3092,7 +4068,7 @@ export class AgentSession {
|
|
|
3092
4068
|
// same-model retries or the transient over-budget fallback escape hatch.
|
|
3093
4069
|
if (this._retryAttempt + 1 > settings.maxRetries) {
|
|
3094
4070
|
this._resolveRetry();
|
|
3095
|
-
return
|
|
4071
|
+
return "not-handled";
|
|
3096
4072
|
}
|
|
3097
4073
|
switchedFallback = await this._retryFallback.tryFallback("refusal", {});
|
|
3098
4074
|
if (!switchedFallback) {
|
|
@@ -3101,7 +4077,7 @@ export class AgentSession {
|
|
|
3101
4077
|
this._emit({ type: "retry_fallback_exhausted", chainKey: exhaustedChainKey, lastError: errorMessage });
|
|
3102
4078
|
}
|
|
3103
4079
|
this._resolveRetry();
|
|
3104
|
-
return
|
|
4080
|
+
return "not-handled";
|
|
3105
4081
|
}
|
|
3106
4082
|
this._retryAttempt++;
|
|
3107
4083
|
}
|
|
@@ -3133,7 +4109,7 @@ export class AgentSession {
|
|
|
3133
4109
|
});
|
|
3134
4110
|
this._retryAttempt = 0;
|
|
3135
4111
|
this._resolveRetry();
|
|
3136
|
-
return
|
|
4112
|
+
return "not-handled";
|
|
3137
4113
|
}
|
|
3138
4114
|
}
|
|
3139
4115
|
}
|
|
@@ -3148,7 +4124,7 @@ export class AgentSession {
|
|
|
3148
4124
|
});
|
|
3149
4125
|
this._retryAttempt = 0;
|
|
3150
4126
|
this._resolveRetry();
|
|
3151
|
-
return
|
|
4127
|
+
return "not-handled";
|
|
3152
4128
|
}
|
|
3153
4129
|
if (!switchedFallback) {
|
|
3154
4130
|
switchedFallback = await this._retryFallback.tryFallback("transient", {
|
|
@@ -3189,19 +4165,52 @@ export class AgentSession {
|
|
|
3189
4165
|
finalError: "Retry cancelled",
|
|
3190
4166
|
});
|
|
3191
4167
|
this._resolveRetry();
|
|
3192
|
-
return
|
|
4168
|
+
return "not-handled";
|
|
3193
4169
|
}
|
|
3194
4170
|
this._retryAbortController = undefined;
|
|
3195
4171
|
// Turn boundary: a suppression that expired during the backoff sleep lets
|
|
3196
4172
|
// the retry continue on the restored primary instead of the fallback model.
|
|
3197
4173
|
await this._maybeRestoreFallbackPrimary();
|
|
3198
|
-
//
|
|
4174
|
+
// Model fallback (or reversion) can select a smaller context window after
|
|
4175
|
+
// the prior retry checks. Revalidate canonical session context immediately
|
|
4176
|
+
// before the continuation so a rejected compaction never admits that model.
|
|
4177
|
+
const model = this.model;
|
|
4178
|
+
const compactionSettings = this.settingsManager.getCompactionSettings();
|
|
4179
|
+
const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
|
|
4180
|
+
if (compactionSettings.enabled &&
|
|
4181
|
+
model &&
|
|
4182
|
+
shouldCompact(contextTokens, model.contextWindow, compactionSettings)) {
|
|
4183
|
+
if (!(await this._runPrePromptCompaction(message, true, "threshold", true, true))) {
|
|
4184
|
+
const attempt = this._retryAttempt;
|
|
4185
|
+
this._retryAttempt = 0;
|
|
4186
|
+
this._emit({
|
|
4187
|
+
type: "auto_retry_end",
|
|
4188
|
+
success: false,
|
|
4189
|
+
attempt,
|
|
4190
|
+
finalError: new RequiredCompactionError().message,
|
|
4191
|
+
});
|
|
4192
|
+
this._resolveRetry();
|
|
4193
|
+
return "blocked";
|
|
4194
|
+
}
|
|
4195
|
+
this._skipNextPostRetryCompactionCheck = true;
|
|
4196
|
+
}
|
|
4197
|
+
// Retry via the shared continuation path after the event handler chain settles.
|
|
4198
|
+
// Agent core would otherwise drain these queues before the continuation
|
|
4199
|
+
// revalidates its provider admission. The scheduled continuation owns them
|
|
4200
|
+
// until it either starts or reports a terminal admission failure.
|
|
4201
|
+
this.agent.suppressQueuedMessageDrain();
|
|
3199
4202
|
setTimeout(() => {
|
|
3200
|
-
this.
|
|
3201
|
-
|
|
4203
|
+
void this._continueAgentAfterCurrentRun().catch(async (error) => {
|
|
4204
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4205
|
+
this._emit({
|
|
4206
|
+
type: "continuation_error",
|
|
4207
|
+
errorMessage: `Failed to continue queued messages: ${message}`,
|
|
4208
|
+
});
|
|
4209
|
+
await this._emitAgentSettled();
|
|
4210
|
+
this._resolveRetry();
|
|
3202
4211
|
});
|
|
3203
4212
|
}, 0);
|
|
3204
|
-
return
|
|
4213
|
+
return "continued";
|
|
3205
4214
|
}
|
|
3206
4215
|
/**
|
|
3207
4216
|
* Cancel in-progress retry.
|
|
@@ -3245,6 +4254,7 @@ export class AgentSession {
|
|
|
3245
4254
|
* @param command The bash command to execute
|
|
3246
4255
|
* @param onChunk Optional streaming callback for output
|
|
3247
4256
|
* @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)
|
|
4257
|
+
* @param options.id Optional identifier included in bash execution update events
|
|
3248
4258
|
* @param options.operations Custom BashOperations for remote execution
|
|
3249
4259
|
*/
|
|
3250
4260
|
async executeBash(command, onChunk, options) {
|
|
@@ -3255,7 +4265,10 @@ export class AgentSession {
|
|
|
3255
4265
|
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
3256
4266
|
try {
|
|
3257
4267
|
const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
|
|
3258
|
-
onChunk
|
|
4268
|
+
onChunk: (delta) => {
|
|
4269
|
+
onChunk?.(delta);
|
|
4270
|
+
this._emit({ type: "bash_execution_update", id: options?.id, delta });
|
|
4271
|
+
},
|
|
3259
4272
|
signal: this._bashAbortController.signal,
|
|
3260
4273
|
});
|
|
3261
4274
|
this.recordBashResult(command, result, options);
|
|
@@ -3413,6 +4426,7 @@ export class AgentSession {
|
|
|
3413
4426
|
// Run default summarizer if needed
|
|
3414
4427
|
let summaryText;
|
|
3415
4428
|
let summaryDetails;
|
|
4429
|
+
let summaryUsage;
|
|
3416
4430
|
if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
|
|
3417
4431
|
const model = this.model;
|
|
3418
4432
|
const { apiKey, headers, extraBody, env } = await this._getCompactionRequestAuth(model);
|
|
@@ -3427,7 +4441,9 @@ export class AgentSession {
|
|
|
3427
4441
|
customInstructions,
|
|
3428
4442
|
replaceInstructions,
|
|
3429
4443
|
reserveTokens: branchSummarySettings.reserveTokens,
|
|
3430
|
-
streamFn: this.agent.
|
|
4444
|
+
streamFn: this.agent.streamFunction,
|
|
4445
|
+
retry: this.settingsManager.getRetrySettings(),
|
|
4446
|
+
callbacks: this._summarizationRetryCallbacks({ source: "branchSummary" }),
|
|
3431
4447
|
});
|
|
3432
4448
|
if (result.aborted) {
|
|
3433
4449
|
return { cancelled: true, aborted: true };
|
|
@@ -3436,6 +4452,7 @@ export class AgentSession {
|
|
|
3436
4452
|
throw new Error(result.error);
|
|
3437
4453
|
}
|
|
3438
4454
|
summaryText = result.summary;
|
|
4455
|
+
summaryUsage = result.usage;
|
|
3439
4456
|
summaryDetails = {
|
|
3440
4457
|
readFiles: result.readFiles || [],
|
|
3441
4458
|
modifiedFiles: result.modifiedFiles || [],
|
|
@@ -3444,6 +4461,7 @@ export class AgentSession {
|
|
|
3444
4461
|
else if (extensionSummary) {
|
|
3445
4462
|
summaryText = extensionSummary.summary;
|
|
3446
4463
|
summaryDetails = extensionSummary.details;
|
|
4464
|
+
summaryUsage = extensionSummary.usage;
|
|
3447
4465
|
}
|
|
3448
4466
|
// Determine the new leaf position based on target type
|
|
3449
4467
|
let newLeafId;
|
|
@@ -3451,18 +4469,12 @@ export class AgentSession {
|
|
|
3451
4469
|
if (targetEntry.type === "message" && targetEntry.message.role === "user") {
|
|
3452
4470
|
// User message: leaf = parent (null if root), text goes to editor
|
|
3453
4471
|
newLeafId = targetEntry.parentId;
|
|
3454
|
-
editorText =
|
|
4472
|
+
editorText = contentText(targetEntry.message.content, "");
|
|
3455
4473
|
}
|
|
3456
4474
|
else if (targetEntry.type === "custom_message") {
|
|
3457
4475
|
// Custom message: leaf = parent (null if root), text goes to editor
|
|
3458
4476
|
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("");
|
|
4477
|
+
editorText = contentText(targetEntry.content, "");
|
|
3466
4478
|
}
|
|
3467
4479
|
else {
|
|
3468
4480
|
// Non-user message: leaf = selected node
|
|
@@ -3473,7 +4485,7 @@ export class AgentSession {
|
|
|
3473
4485
|
let summaryEntry;
|
|
3474
4486
|
if (summaryText) {
|
|
3475
4487
|
// Create summary at target position (can be null for root)
|
|
3476
|
-
const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
|
|
4488
|
+
const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension, summaryUsage);
|
|
3477
4489
|
summaryEntry = this.sessionManager.getEntry(summaryId);
|
|
3478
4490
|
// Attach label to the summary entry
|
|
3479
4491
|
if (label) {
|
|
@@ -3492,9 +4504,8 @@ export class AgentSession {
|
|
|
3492
4504
|
if (label && !summaryText) {
|
|
3493
4505
|
this.sessionManager.appendLabelChange(targetId, label);
|
|
3494
4506
|
}
|
|
3495
|
-
// Update agent state
|
|
3496
|
-
|
|
3497
|
-
this.agent.state.messages = sessionContext.messages;
|
|
4507
|
+
// Update agent state (preserving exact messages still awaiting persistence)
|
|
4508
|
+
this._restoreAgentMessagesFromSession();
|
|
3498
4509
|
this._incrementMessageRevision();
|
|
3499
4510
|
// Emit session_tree event
|
|
3500
4511
|
await this._extensionRunner.emit({
|
|
@@ -3522,24 +4533,13 @@ export class AgentSession {
|
|
|
3522
4533
|
continue;
|
|
3523
4534
|
if (entry.message.role !== "user")
|
|
3524
4535
|
continue;
|
|
3525
|
-
const text =
|
|
4536
|
+
const text = contentText(entry.message.content, "");
|
|
3526
4537
|
if (text) {
|
|
3527
4538
|
result.push({ entryId: entry.id, text });
|
|
3528
4539
|
}
|
|
3529
4540
|
}
|
|
3530
4541
|
return result;
|
|
3531
4542
|
}
|
|
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
4543
|
/**
|
|
3544
4544
|
* Get session statistics. Aggregates over ALL session entries (including
|
|
3545
4545
|
* history that was compacted away), so token/cost totals reflect what was
|
|
@@ -3551,12 +4551,11 @@ export class AgentSession {
|
|
|
3551
4551
|
let toolResults = 0;
|
|
3552
4552
|
let totalMessages = 0;
|
|
3553
4553
|
let toolCalls = 0;
|
|
3554
|
-
|
|
3555
|
-
let totalOutput = 0;
|
|
3556
|
-
let totalCacheRead = 0;
|
|
3557
|
-
let totalCacheWrite = 0;
|
|
3558
|
-
let totalCost = 0;
|
|
4554
|
+
const usageTotals = createUsageTotals();
|
|
3559
4555
|
for (const entry of this.sessionManager.getEntries()) {
|
|
4556
|
+
if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
|
|
4557
|
+
addUsageToTotals(usageTotals, entry.usage);
|
|
4558
|
+
}
|
|
3560
4559
|
if (entry.type !== "message")
|
|
3561
4560
|
continue;
|
|
3562
4561
|
totalMessages++;
|
|
@@ -3566,6 +4565,9 @@ export class AgentSession {
|
|
|
3566
4565
|
}
|
|
3567
4566
|
else if (message.role === "toolResult") {
|
|
3568
4567
|
toolResults++;
|
|
4568
|
+
if (message.usage) {
|
|
4569
|
+
addUsageToTotals(usageTotals, message.usage);
|
|
4570
|
+
}
|
|
3569
4571
|
}
|
|
3570
4572
|
else if (message.role === "assistant") {
|
|
3571
4573
|
assistantMessages++;
|
|
@@ -3573,12 +4575,7 @@ export class AgentSession {
|
|
|
3573
4575
|
if (Array.isArray(assistantMsg.content)) {
|
|
3574
4576
|
toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
3575
4577
|
}
|
|
3576
|
-
|
|
3577
|
-
totalInput += usage.input;
|
|
3578
|
-
totalOutput += usage.output;
|
|
3579
|
-
totalCacheRead += usage.cacheRead;
|
|
3580
|
-
totalCacheWrite += usage.cacheWrite;
|
|
3581
|
-
totalCost += usage.cost.total;
|
|
4578
|
+
addUsageToTotals(usageTotals, assistantMsg.usage);
|
|
3582
4579
|
}
|
|
3583
4580
|
}
|
|
3584
4581
|
return {
|
|
@@ -3590,13 +4587,13 @@ export class AgentSession {
|
|
|
3590
4587
|
toolResults,
|
|
3591
4588
|
totalMessages,
|
|
3592
4589
|
tokens: {
|
|
3593
|
-
input:
|
|
3594
|
-
output:
|
|
3595
|
-
cacheRead:
|
|
3596
|
-
cacheWrite:
|
|
3597
|
-
total:
|
|
4590
|
+
input: usageTotals.input,
|
|
4591
|
+
output: usageTotals.output,
|
|
4592
|
+
cacheRead: usageTotals.cacheRead,
|
|
4593
|
+
cacheWrite: usageTotals.cacheWrite,
|
|
4594
|
+
total: usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite,
|
|
3598
4595
|
},
|
|
3599
|
-
cost:
|
|
4596
|
+
cost: usageTotals.cost,
|
|
3600
4597
|
contextUsage: this.getContextUsage(),
|
|
3601
4598
|
};
|
|
3602
4599
|
}
|