gsd-pi 2.34.0-dev.ed0bfbf → 2.35.0-dev.30eec3f
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/dist/cli.js +7 -2
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +13 -1
- package/dist/resources/extensions/async-jobs/await-tool.js +0 -2
- package/dist/resources/extensions/async-jobs/job-manager.js +0 -6
- package/dist/resources/extensions/bg-shell/output-formatter.js +1 -19
- package/dist/resources/extensions/bg-shell/process-manager.js +0 -4
- package/dist/resources/extensions/bg-shell/types.js +0 -2
- package/dist/resources/extensions/context7/index.js +5 -0
- package/dist/resources/extensions/get-secrets-from-user.js +2 -30
- package/dist/resources/extensions/google-search/index.js +5 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +43 -1
- package/dist/resources/extensions/gsd/auto-loop.js +10 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +35 -0
- package/dist/resources/extensions/gsd/auto-start.js +35 -2
- package/dist/resources/extensions/gsd/auto.js +59 -4
- package/dist/resources/extensions/gsd/changelog.js +162 -0
- package/dist/resources/extensions/gsd/commands-bootstrap.js +1 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +2 -2
- package/dist/resources/extensions/gsd/commands-inspect.js +10 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +5 -1
- package/dist/resources/extensions/gsd/commands.js +8 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +113 -5
- package/dist/resources/extensions/gsd/doctor-environment.js +26 -17
- package/dist/resources/extensions/gsd/doctor-proactive.js +22 -0
- package/dist/resources/extensions/gsd/doctor.js +36 -0
- package/dist/resources/extensions/gsd/files.js +11 -2
- package/dist/resources/extensions/gsd/gitignore.js +54 -7
- package/dist/resources/extensions/gsd/guided-flow.js +5 -3
- package/dist/resources/extensions/gsd/health-widget-core.js +96 -0
- package/dist/resources/extensions/gsd/health-widget.js +97 -46
- package/dist/resources/extensions/gsd/index.js +10 -1
- package/dist/resources/extensions/gsd/migrate-external.js +55 -2
- package/dist/resources/extensions/gsd/paths.js +74 -7
- package/dist/resources/extensions/gsd/post-unit-hooks.js +4 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +54 -1
- package/dist/resources/extensions/gsd/preferences.js +2 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +55 -0
- package/dist/resources/extensions/gsd/session-lock.js +26 -2
- package/dist/resources/extensions/gsd/templates/plan.md +8 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +12 -0
- package/dist/resources/extensions/remote-questions/remote-command.js +2 -22
- package/dist/resources/extensions/shared/mod.js +1 -1
- package/dist/resources/extensions/shared/sanitize.js +30 -0
- package/dist/resources/extensions/subagent/index.js +6 -14
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
- package/package.json +2 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts +14 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +24 -27
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +1 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +11 -22
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/proxy.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/proxy.js +2 -8
- package/packages/pi-agent-core/dist/proxy.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +30 -27
- package/packages/pi-agent-core/src/agent.ts +12 -23
- package/packages/pi-agent-core/src/proxy.ts +2 -8
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +5 -41
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +10 -73
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +9 -80
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-shared.d.ts +65 -0
- package/packages/pi-ai/dist/providers/openai-shared.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-shared.js +146 -0
- package/packages/pi-ai/dist/providers/openai-shared.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +7 -135
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +7 -135
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts +46 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js +160 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js.map +1 -0
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +11 -45
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -86
- package/packages/pi-ai/src/providers/openai-responses.ts +16 -96
- package/packages/pi-ai/src/providers/openai-shared.ts +193 -0
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +14 -162
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +13 -161
- package/packages/pi-ai/src/utils/oauth/google-oauth-utils.ts +201 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +16 -63
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +104 -641
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +4 -35
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js +5 -43
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +11 -69
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +40 -0
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +78 -0
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +77 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +331 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +129 -243
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +49 -42
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js +2 -21
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lock-utils.d.ts +39 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.js +89 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +4 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +52 -107
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +2 -21
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +0 -28
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +2 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +2 -4
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +46 -60
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +87 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.js +295 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +3 -28
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +76 -166
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +1 -3
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +9 -26
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +1 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts +44 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js +61 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js +6 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +65 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +6 -16
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +175 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.js +2 -30
- package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -28
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts +19 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js +45 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/error.d.ts +5 -0
- package/packages/pi-coding-agent/dist/utils/error.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/error.js +7 -0
- package/packages/pi-coding-agent/dist/utils/error.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +117 -745
- package/packages/pi-coding-agent/src/core/auth-storage.ts +4 -38
- package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +7 -53
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +14 -74
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +100 -0
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +424 -0
- package/packages/pi-coding-agent/src/core/extensions/index.ts +1 -21
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +119 -243
- package/packages/pi-coding-agent/src/core/extensions/types.ts +50 -69
- package/packages/pi-coding-agent/src/core/lock-utils.ts +113 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +4 -1
- package/packages/pi-coding-agent/src/core/lsp/index.ts +83 -152
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +2 -22
- package/packages/pi-coding-agent/src/core/lsp/types.ts +0 -29
- package/packages/pi-coding-agent/src/core/package-manager.ts +1 -4
- package/packages/pi-coding-agent/src/core/resource-loader.ts +56 -69
- package/packages/pi-coding-agent/src/core/retry-handler.ts +359 -0
- package/packages/pi-coding-agent/src/core/session-manager.ts +3 -30
- package/packages/pi-coding-agent/src/core/settings-manager.ts +85 -164
- package/packages/pi-coding-agent/src/core/skills.ts +1 -4
- package/packages/pi-coding-agent/src/index.ts +1 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +17 -29
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -13
- package/packages/pi-coding-agent/src/modes/interactive/components/tree-render-utils.ts +81 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tree-selector.ts +14 -19
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +7 -18
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +196 -0
- package/packages/pi-coding-agent/src/modes/interactive/utils/shorten-path.ts +14 -0
- package/packages/pi-coding-agent/src/modes/print-mode.ts +2 -30
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -28
- package/packages/pi-coding-agent/src/modes/shared/command-context-actions.ts +53 -0
- package/packages/pi-coding-agent/src/utils/error.ts +6 -0
- package/packages/pi-tui/dist/components/markdown.d.ts +5 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +25 -31
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/dist/keys.d.ts +0 -4
- package/packages/pi-tui/dist/keys.d.ts.map +1 -1
- package/packages/pi-tui/dist/keys.js +94 -162
- package/packages/pi-tui/dist/keys.js.map +1 -1
- package/packages/pi-tui/src/components/markdown.ts +25 -29
- package/packages/pi-tui/src/keys.ts +94 -173
- package/pkg/dist/modes/interactive/theme/theme.d.ts +65 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +6 -16
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts +12 -0
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/themes.js +175 -0
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/await-tool.ts +0 -2
- package/src/resources/extensions/async-jobs/job-manager.ts +0 -7
- package/src/resources/extensions/bg-shell/output-formatter.ts +0 -17
- package/src/resources/extensions/bg-shell/process-manager.ts +0 -4
- package/src/resources/extensions/bg-shell/types.ts +0 -12
- package/src/resources/extensions/context7/index.ts +7 -0
- package/src/resources/extensions/get-secrets-from-user.ts +2 -35
- package/src/resources/extensions/google-search/index.ts +7 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +49 -1
- package/src/resources/extensions/gsd/auto-loop.ts +11 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +39 -0
- package/src/resources/extensions/gsd/auto-start.ts +42 -2
- package/src/resources/extensions/gsd/auto.ts +61 -3
- package/src/resources/extensions/gsd/changelog.ts +213 -0
- package/src/resources/extensions/gsd/commands-bootstrap.ts +1 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -2
- package/src/resources/extensions/gsd/commands-inspect.ts +10 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +5 -1
- package/src/resources/extensions/gsd/commands.ts +9 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +107 -5
- package/src/resources/extensions/gsd/doctor-environment.ts +26 -16
- package/src/resources/extensions/gsd/doctor-proactive.ts +24 -0
- package/src/resources/extensions/gsd/doctor-types.ts +9 -1
- package/src/resources/extensions/gsd/doctor.ts +35 -0
- package/src/resources/extensions/gsd/files.ts +12 -2
- package/src/resources/extensions/gsd/gitignore.ts +54 -7
- package/src/resources/extensions/gsd/guided-flow.ts +5 -3
- package/src/resources/extensions/gsd/health-widget-core.ts +129 -0
- package/src/resources/extensions/gsd/health-widget.ts +103 -59
- package/src/resources/extensions/gsd/index.ts +10 -1
- package/src/resources/extensions/gsd/migrate-external.ts +47 -2
- package/src/resources/extensions/gsd/paths.ts +73 -7
- package/src/resources/extensions/gsd/post-unit-hooks.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +54 -1
- package/src/resources/extensions/gsd/preferences.ts +2 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +66 -0
- package/src/resources/extensions/gsd/session-lock.ts +29 -2
- package/src/resources/extensions/gsd/templates/plan.md +8 -0
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +98 -2
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +59 -3
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +214 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/paths.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +40 -2
- package/src/resources/extensions/gsd/tests/test-utils.ts +165 -0
- package/src/resources/extensions/gsd/tests/validate-directory.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +32 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +11 -0
- package/src/resources/extensions/remote-questions/remote-command.ts +2 -23
- package/src/resources/extensions/shared/mod.ts +1 -1
- package/src/resources/extensions/shared/sanitize.ts +36 -0
- package/src/resources/extensions/subagent/index.ts +6 -12
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
- package/dist/resources/extensions/shared/wizard-ui.js +0 -478
- package/packages/pi-coding-agent/dist/modes/interactive/theme/dark.json +0 -85
- package/packages/pi-coding-agent/dist/modes/interactive/theme/light.json +0 -84
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.json +0 -335
- package/packages/pi-coding-agent/src/modes/interactive/theme/dark.json +0 -85
- package/packages/pi-coding-agent/src/modes/interactive/theme/light.json +0 -84
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json +0 -335
- package/pkg/dist/modes/interactive/theme/dark.json +0 -85
- package/pkg/dist/modes/interactive/theme/light.json +0 -84
- package/pkg/dist/modes/interactive/theme/theme-schema.json +0 -335
- package/src/resources/extensions/shared/wizard-ui.ts +0 -551
|
@@ -14,19 +14,21 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { readFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname, join } from "node:path";
|
|
17
|
-
import {
|
|
17
|
+
import { modelsAreEqual, resetApiProviders, supportsXhigh } from "@gsd/pi-ai";
|
|
18
18
|
import { getDocsPath } from "../config.js";
|
|
19
|
+
import { getErrorMessage } from "../utils/error.js";
|
|
19
20
|
import { theme } from "../modes/interactive/theme/theme.js";
|
|
20
21
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
21
|
-
import { sleep } from "../utils/sleep.js";
|
|
22
22
|
import { executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
|
|
23
|
-
import { calculateContextTokens, collectEntriesForBranchSummary,
|
|
23
|
+
import { calculateContextTokens, collectEntriesForBranchSummary, estimateContextTokens, generateBranchSummary, } from "./compaction/index.js";
|
|
24
|
+
import { CompactionOrchestrator } from "./compaction-orchestrator.js";
|
|
24
25
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
25
26
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
26
27
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
27
28
|
import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
28
29
|
import { FallbackResolver } from "./fallback-resolver.js";
|
|
29
30
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
31
|
+
import { RetryHandler } from "./retry-handler.js";
|
|
30
32
|
import { getLatestCompactionEntry } from "./session-manager.js";
|
|
31
33
|
import { BUILTIN_SLASH_COMMANDS } from "./slash-commands.js";
|
|
32
34
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
@@ -66,17 +68,11 @@ export class AgentSession {
|
|
|
66
68
|
this._followUpMessages = [];
|
|
67
69
|
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
68
70
|
this._pendingNextTurnMessages = [];
|
|
69
|
-
//
|
|
70
|
-
this.
|
|
71
|
-
this.
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
this._branchSummaryAbortController = undefined;
|
|
75
|
-
// Retry state
|
|
76
|
-
this._retryAbortController = undefined;
|
|
77
|
-
this._retryAttempt = 0;
|
|
78
|
-
this._retryPromise = undefined;
|
|
79
|
-
this._retryResolve = undefined;
|
|
71
|
+
// Cumulative session stats — survives compaction (#1423)
|
|
72
|
+
this._cumulativeCost = 0;
|
|
73
|
+
this._cumulativeInputTokens = 0;
|
|
74
|
+
this._cumulativeOutputTokens = 0;
|
|
75
|
+
this._cumulativeToolCalls = 0;
|
|
80
76
|
// Bash execution state
|
|
81
77
|
this._bashAbortController = undefined;
|
|
82
78
|
this._pendingBashMessages = [];
|
|
@@ -96,7 +92,7 @@ export class AgentSession {
|
|
|
96
92
|
this._handleAgentEvent = (event) => {
|
|
97
93
|
// Create retry promise synchronously before queueing async processing.
|
|
98
94
|
// Agent.emit() calls this handler synchronously, and prompt() calls waitForRetry()
|
|
99
|
-
// as soon as agent.prompt() resolves. If
|
|
95
|
+
// as soon as agent.prompt() resolves. If the retry promise is created only inside
|
|
100
96
|
// _processAgentEvent, slow earlier queued events can delay agent_end processing
|
|
101
97
|
// and waitForRetry() can miss the in-flight retry.
|
|
102
98
|
this._createRetryPromiseForAgentEnd(event);
|
|
@@ -116,6 +112,30 @@ export class AgentSession {
|
|
|
116
112
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
117
113
|
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
118
114
|
this._baseToolsOverride = config.baseToolsOverride;
|
|
115
|
+
// Initialize delegated subsystems
|
|
116
|
+
this._retryHandler = new RetryHandler({
|
|
117
|
+
agent: this.agent,
|
|
118
|
+
settingsManager: this.settingsManager,
|
|
119
|
+
modelRegistry: this._modelRegistry,
|
|
120
|
+
fallbackResolver: this._fallbackResolver,
|
|
121
|
+
getModel: () => this.model,
|
|
122
|
+
getSessionId: () => this.sessionId,
|
|
123
|
+
emit: (event) => this._emit(event),
|
|
124
|
+
onModelChange: (model) => this.sessionManager.appendModelChange(model.provider, model.id),
|
|
125
|
+
});
|
|
126
|
+
this._compactionOrchestrator = new CompactionOrchestrator({
|
|
127
|
+
agent: this.agent,
|
|
128
|
+
sessionManager: this.sessionManager,
|
|
129
|
+
settingsManager: this.settingsManager,
|
|
130
|
+
modelRegistry: this._modelRegistry,
|
|
131
|
+
getModel: () => this.model,
|
|
132
|
+
getSessionId: () => this.sessionId,
|
|
133
|
+
getExtensionRunner: () => this._extensionRunner,
|
|
134
|
+
emit: (event) => this._emit(event),
|
|
135
|
+
disconnectFromAgent: () => this._disconnectFromAgent(),
|
|
136
|
+
reconnectToAgent: () => this._reconnectToAgent(),
|
|
137
|
+
abort: () => this.abort(),
|
|
138
|
+
});
|
|
119
139
|
// Always subscribe to agent events for internal handling
|
|
120
140
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
121
141
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
@@ -146,35 +166,15 @@ export class AgentSession {
|
|
|
146
166
|
}
|
|
147
167
|
}
|
|
148
168
|
_createRetryPromiseForAgentEnd(event) {
|
|
149
|
-
if (event.type !== "agent_end"
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const settings = this.settingsManager.getRetrySettings();
|
|
153
|
-
if (!settings.enabled) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
const lastAssistant = this._findLastAssistantInMessages(event.messages);
|
|
157
|
-
if (!lastAssistant || !this._isRetryableError(lastAssistant)) {
|
|
169
|
+
if (event.type !== "agent_end")
|
|
158
170
|
return;
|
|
159
|
-
|
|
160
|
-
this._retryPromise = new Promise((resolve) => {
|
|
161
|
-
this._retryResolve = resolve;
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
_findLastAssistantInMessages(messages) {
|
|
165
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
166
|
-
const message = messages[i];
|
|
167
|
-
if (message.role === "assistant") {
|
|
168
|
-
return message;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return undefined;
|
|
171
|
+
this._retryHandler.createRetryPromiseForAgentEnd(event.messages);
|
|
172
172
|
}
|
|
173
173
|
async _processAgentEvent(event) {
|
|
174
174
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
175
175
|
// This ensures the UI sees the updated queue state
|
|
176
176
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
177
|
-
this.
|
|
177
|
+
this._compactionOrchestrator.resetOverflowRecovery();
|
|
178
178
|
const messageText = this._getUserMessageText(event.message);
|
|
179
179
|
if (messageText) {
|
|
180
180
|
// Check steering queue first
|
|
@@ -212,20 +212,19 @@ export class AgentSession {
|
|
|
212
212
|
// Track assistant message for auto-compaction (checked on agent_end)
|
|
213
213
|
if (event.message.role === "assistant") {
|
|
214
214
|
this._lastAssistantMessage = event.message;
|
|
215
|
+
// Accumulate session stats that survive compaction (#1423)
|
|
215
216
|
const assistantMsg = event.message;
|
|
217
|
+
this._cumulativeCost += assistantMsg.usage?.cost?.total ?? 0;
|
|
218
|
+
this._cumulativeInputTokens += assistantMsg.usage?.input ?? 0;
|
|
219
|
+
this._cumulativeOutputTokens += assistantMsg.usage?.output ?? 0;
|
|
220
|
+
this._cumulativeToolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
216
221
|
if (assistantMsg.stopReason !== "error") {
|
|
217
|
-
this.
|
|
222
|
+
this._compactionOrchestrator.clearOverflowRecovery();
|
|
218
223
|
}
|
|
219
224
|
// Reset retry counter immediately on successful assistant response
|
|
220
225
|
// This prevents accumulation across multiple LLM calls within a turn
|
|
221
|
-
if (assistantMsg.stopReason !== "error"
|
|
222
|
-
this.
|
|
223
|
-
type: "auto_retry_end",
|
|
224
|
-
success: true,
|
|
225
|
-
attempt: this._retryAttempt,
|
|
226
|
-
});
|
|
227
|
-
this._retryAttempt = 0;
|
|
228
|
-
this._resolveRetry();
|
|
226
|
+
if (assistantMsg.stopReason !== "error") {
|
|
227
|
+
this._retryHandler.handleSuccessfulResponse();
|
|
229
228
|
}
|
|
230
229
|
}
|
|
231
230
|
}
|
|
@@ -234,20 +233,12 @@ export class AgentSession {
|
|
|
234
233
|
const msg = this._lastAssistantMessage;
|
|
235
234
|
this._lastAssistantMessage = undefined;
|
|
236
235
|
// Check for retryable errors first (overloaded, rate limit, server errors)
|
|
237
|
-
if (this.
|
|
238
|
-
const didRetry = await this.
|
|
236
|
+
if (this._retryHandler.isRetryableError(msg)) {
|
|
237
|
+
const didRetry = await this._retryHandler.handleRetryableError(msg);
|
|
239
238
|
if (didRetry)
|
|
240
239
|
return; // Retry was initiated, don't proceed to compaction
|
|
241
240
|
}
|
|
242
|
-
await this.
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
/** Resolve the pending retry promise */
|
|
246
|
-
_resolveRetry() {
|
|
247
|
-
if (this._retryResolve) {
|
|
248
|
-
this._retryResolve();
|
|
249
|
-
this._retryResolve = undefined;
|
|
250
|
-
this._retryPromise = undefined;
|
|
241
|
+
await this._compactionOrchestrator.checkCompaction(msg);
|
|
251
242
|
}
|
|
252
243
|
}
|
|
253
244
|
/**
|
|
@@ -280,10 +271,7 @@ export class AgentSession {
|
|
|
280
271
|
}
|
|
281
272
|
}
|
|
282
273
|
catch (err) {
|
|
283
|
-
|
|
284
|
-
return { block: true, reason: err.message };
|
|
285
|
-
}
|
|
286
|
-
return { block: true, reason: `Extension failed, blocking execution: ${String(err)}` };
|
|
274
|
+
return { block: true, reason: err instanceof Error ? err.message : `Extension failed, blocking execution: ${String(err)}` };
|
|
287
275
|
}
|
|
288
276
|
return undefined;
|
|
289
277
|
});
|
|
@@ -480,7 +468,7 @@ export class AgentSession {
|
|
|
480
468
|
}
|
|
481
469
|
/** Current retry attempt (0 if not retrying) */
|
|
482
470
|
get retryAttempt() {
|
|
483
|
-
return this.
|
|
471
|
+
return this._retryHandler.retryAttempt;
|
|
484
472
|
}
|
|
485
473
|
/**
|
|
486
474
|
* Get the names of currently active tools.
|
|
@@ -522,9 +510,7 @@ export class AgentSession {
|
|
|
522
510
|
}
|
|
523
511
|
/** Whether compaction or branch summarization is currently running */
|
|
524
512
|
get isCompacting() {
|
|
525
|
-
return
|
|
526
|
-
this._compactionAbortController !== undefined ||
|
|
527
|
-
this._branchSummaryAbortController !== undefined);
|
|
513
|
+
return this._compactionOrchestrator.isCompacting;
|
|
528
514
|
}
|
|
529
515
|
/**
|
|
530
516
|
* Switch edit mode between standard (text-match) and hashline (LINE#ID anchors).
|
|
@@ -736,7 +722,7 @@ export class AgentSession {
|
|
|
736
722
|
// Check if we need to compact before sending (catches aborted responses)
|
|
737
723
|
const lastAssistant = this._findLastAssistantMessage();
|
|
738
724
|
if (lastAssistant) {
|
|
739
|
-
await this.
|
|
725
|
+
await this._compactionOrchestrator.checkCompaction(lastAssistant, false);
|
|
740
726
|
}
|
|
741
727
|
// Build messages array (custom message if any, then user message)
|
|
742
728
|
const messages = [];
|
|
@@ -781,7 +767,7 @@ export class AgentSession {
|
|
|
781
767
|
}
|
|
782
768
|
}
|
|
783
769
|
await this.agent.prompt(messages);
|
|
784
|
-
await this.waitForRetry();
|
|
770
|
+
await this._retryHandler.waitForRetry();
|
|
785
771
|
}
|
|
786
772
|
/**
|
|
787
773
|
* Try to execute an extension command. Returns true if command was found and executed.
|
|
@@ -807,7 +793,7 @@ export class AgentSession {
|
|
|
807
793
|
this._extensionRunner.emitError({
|
|
808
794
|
extensionPath: `command:${commandName}`,
|
|
809
795
|
event: "command",
|
|
810
|
-
error:
|
|
796
|
+
error: getErrorMessage(err),
|
|
811
797
|
});
|
|
812
798
|
return true;
|
|
813
799
|
}
|
|
@@ -837,7 +823,7 @@ export class AgentSession {
|
|
|
837
823
|
this._extensionRunner?.emitError({
|
|
838
824
|
extensionPath: skill.filePath,
|
|
839
825
|
event: "skill_expansion",
|
|
840
|
-
error:
|
|
826
|
+
error: getErrorMessage(err),
|
|
841
827
|
});
|
|
842
828
|
return text; // Return original on error
|
|
843
829
|
}
|
|
@@ -1030,7 +1016,7 @@ export class AgentSession {
|
|
|
1030
1016
|
* Abort current operation and wait for agent to become idle.
|
|
1031
1017
|
*/
|
|
1032
1018
|
async abort() {
|
|
1033
|
-
this.abortRetry();
|
|
1019
|
+
this._retryHandler.abortRetry();
|
|
1034
1020
|
this.agent.abort();
|
|
1035
1021
|
await this.agent.waitForIdle();
|
|
1036
1022
|
// Ensure agent_end is emitted even when abort interrupts a tool call (#1414).
|
|
@@ -1120,6 +1106,20 @@ export class AgentSession {
|
|
|
1120
1106
|
source,
|
|
1121
1107
|
});
|
|
1122
1108
|
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Apply a model change: set the model on the agent, persist to session/settings,
|
|
1111
|
+
* re-clamp thinking level, and emit the model_select event.
|
|
1112
|
+
*/
|
|
1113
|
+
async _applyModelChange(model, thinkingLevel, source, options) {
|
|
1114
|
+
const previousModel = this.model;
|
|
1115
|
+
this.agent.setModel(model);
|
|
1116
|
+
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1117
|
+
if (options?.persist !== false) {
|
|
1118
|
+
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1119
|
+
}
|
|
1120
|
+
this.setThinkingLevel(thinkingLevel);
|
|
1121
|
+
await this._emitModelSelect(model, previousModel, source);
|
|
1122
|
+
}
|
|
1123
1123
|
/**
|
|
1124
1124
|
* Set model directly.
|
|
1125
1125
|
* Validates API key, saves to session and settings.
|
|
@@ -1130,16 +1130,8 @@ export class AgentSession {
|
|
|
1130
1130
|
if (!apiKey) {
|
|
1131
1131
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
1132
1132
|
}
|
|
1133
|
-
const previousModel = this.model;
|
|
1134
1133
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1135
|
-
this.
|
|
1136
|
-
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1137
|
-
if (options?.persist !== false) {
|
|
1138
|
-
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1139
|
-
}
|
|
1140
|
-
// Re-clamp thinking level for new model's capabilities
|
|
1141
|
-
this.setThinkingLevel(thinkingLevel);
|
|
1142
|
-
await this._emitModelSelect(model, previousModel, "set");
|
|
1134
|
+
await this._applyModelChange(model, thinkingLevel, "set", options);
|
|
1143
1135
|
}
|
|
1144
1136
|
/**
|
|
1145
1137
|
* Cycle to next/previous model.
|
|
@@ -1183,19 +1175,10 @@ export class AgentSession {
|
|
|
1183
1175
|
const len = scopedModels.length;
|
|
1184
1176
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1185
1177
|
const next = scopedModels[nextIndex];
|
|
1178
|
+
// Explicit scoped model thinking level overrides current session level;
|
|
1179
|
+
// undefined scoped model thinking level inherits the current session preference.
|
|
1186
1180
|
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
|
|
1187
|
-
|
|
1188
|
-
this.agent.setModel(next.model);
|
|
1189
|
-
this.sessionManager.appendModelChange(next.model.provider, next.model.id);
|
|
1190
|
-
if (options?.persist !== false) {
|
|
1191
|
-
this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
|
|
1192
|
-
}
|
|
1193
|
-
// Apply thinking level.
|
|
1194
|
-
// - Explicit scoped model thinking level overrides current session level
|
|
1195
|
-
// - Undefined scoped model thinking level inherits the current session preference
|
|
1196
|
-
// setThinkingLevel clamps to model capabilities.
|
|
1197
|
-
this.setThinkingLevel(thinkingLevel);
|
|
1198
|
-
await this._emitModelSelect(next.model, currentModel, "cycle");
|
|
1181
|
+
await this._applyModelChange(next.model, thinkingLevel, "cycle", options);
|
|
1199
1182
|
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
1200
1183
|
}
|
|
1201
1184
|
async _cycleAvailableModel(direction, options) {
|
|
@@ -1214,14 +1197,7 @@ export class AgentSession {
|
|
|
1214
1197
|
throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
|
|
1215
1198
|
}
|
|
1216
1199
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1217
|
-
this.
|
|
1218
|
-
this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
|
|
1219
|
-
if (options?.persist !== false) {
|
|
1220
|
-
this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
|
|
1221
|
-
}
|
|
1222
|
-
// Re-clamp thinking level for new model's capabilities
|
|
1223
|
-
this.setThinkingLevel(thinkingLevel);
|
|
1224
|
-
await this._emitModelSelect(nextModel, currentModel, "cycle");
|
|
1200
|
+
await this._applyModelChange(nextModel, thinkingLevel, "cycle", options);
|
|
1225
1201
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
1226
1202
|
}
|
|
1227
1203
|
// =========================================================================
|
|
@@ -1336,315 +1312,23 @@ export class AgentSession {
|
|
|
1336
1312
|
* @param customInstructions Optional instructions for the compaction summary
|
|
1337
1313
|
*/
|
|
1338
1314
|
async compact(customInstructions) {
|
|
1339
|
-
this.
|
|
1340
|
-
await this.abort();
|
|
1341
|
-
this._compactionAbortController = new AbortController();
|
|
1342
|
-
try {
|
|
1343
|
-
if (!this.model) {
|
|
1344
|
-
throw new Error("No model selected");
|
|
1345
|
-
}
|
|
1346
|
-
const apiKey = await this._modelRegistry.getApiKey(this.model, this.sessionId);
|
|
1347
|
-
if (!apiKey) {
|
|
1348
|
-
throw new Error(`No API key for ${this.model.provider}`);
|
|
1349
|
-
}
|
|
1350
|
-
const pathEntries = this.sessionManager.getBranch();
|
|
1351
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1352
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
1353
|
-
if (!preparation) {
|
|
1354
|
-
// Check why we can't compact
|
|
1355
|
-
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
1356
|
-
if (lastEntry?.type === "compaction") {
|
|
1357
|
-
throw new Error("Already compacted");
|
|
1358
|
-
}
|
|
1359
|
-
throw new Error("Nothing to compact (session too small)");
|
|
1360
|
-
}
|
|
1361
|
-
let extensionCompaction;
|
|
1362
|
-
let fromExtension = false;
|
|
1363
|
-
if (this._extensionRunner?.hasHandlers("session_before_compact")) {
|
|
1364
|
-
const result = (await this._extensionRunner.emit({
|
|
1365
|
-
type: "session_before_compact",
|
|
1366
|
-
preparation,
|
|
1367
|
-
branchEntries: pathEntries,
|
|
1368
|
-
customInstructions,
|
|
1369
|
-
signal: this._compactionAbortController.signal,
|
|
1370
|
-
}));
|
|
1371
|
-
if (result?.cancel) {
|
|
1372
|
-
throw new Error("Compaction cancelled");
|
|
1373
|
-
}
|
|
1374
|
-
if (result?.compaction) {
|
|
1375
|
-
extensionCompaction = result.compaction;
|
|
1376
|
-
fromExtension = true;
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
let summary;
|
|
1380
|
-
let firstKeptEntryId;
|
|
1381
|
-
let tokensBefore;
|
|
1382
|
-
let details;
|
|
1383
|
-
if (extensionCompaction) {
|
|
1384
|
-
// Extension provided compaction content
|
|
1385
|
-
summary = extensionCompaction.summary;
|
|
1386
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
1387
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
1388
|
-
details = extensionCompaction.details;
|
|
1389
|
-
}
|
|
1390
|
-
else {
|
|
1391
|
-
// Generate compaction result
|
|
1392
|
-
const result = await compact(preparation, this.model, apiKey, customInstructions, this._compactionAbortController.signal);
|
|
1393
|
-
summary = result.summary;
|
|
1394
|
-
firstKeptEntryId = result.firstKeptEntryId;
|
|
1395
|
-
tokensBefore = result.tokensBefore;
|
|
1396
|
-
details = result.details;
|
|
1397
|
-
}
|
|
1398
|
-
if (this._compactionAbortController.signal.aborted) {
|
|
1399
|
-
throw new Error("Compaction cancelled");
|
|
1400
|
-
}
|
|
1401
|
-
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
|
|
1402
|
-
const newEntries = this.sessionManager.getEntries();
|
|
1403
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1404
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
1405
|
-
// Get the saved compaction entry for the extension event
|
|
1406
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
1407
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
1408
|
-
await this._extensionRunner.emit({
|
|
1409
|
-
type: "session_compact",
|
|
1410
|
-
compactionEntry: savedCompactionEntry,
|
|
1411
|
-
fromExtension,
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
|
-
return {
|
|
1415
|
-
summary,
|
|
1416
|
-
firstKeptEntryId,
|
|
1417
|
-
tokensBefore,
|
|
1418
|
-
details,
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
finally {
|
|
1422
|
-
this._compactionAbortController = undefined;
|
|
1423
|
-
this._reconnectToAgent();
|
|
1424
|
-
}
|
|
1315
|
+
return this._compactionOrchestrator.compact(customInstructions);
|
|
1425
1316
|
}
|
|
1426
|
-
/**
|
|
1427
|
-
* Cancel in-progress compaction (manual or auto).
|
|
1428
|
-
*/
|
|
1317
|
+
/** Cancel in-progress compaction (manual or auto) */
|
|
1429
1318
|
abortCompaction() {
|
|
1430
|
-
this.
|
|
1431
|
-
this._autoCompactionAbortController?.abort();
|
|
1319
|
+
this._compactionOrchestrator.abortCompaction();
|
|
1432
1320
|
}
|
|
1433
|
-
/**
|
|
1434
|
-
* Cancel in-progress branch summarization.
|
|
1435
|
-
*/
|
|
1321
|
+
/** Cancel in-progress branch summarization */
|
|
1436
1322
|
abortBranchSummary() {
|
|
1437
|
-
this.
|
|
1438
|
-
}
|
|
1439
|
-
/**
|
|
1440
|
-
* Check if compaction is needed and run it.
|
|
1441
|
-
* Called after agent_end and before prompt submission.
|
|
1442
|
-
*
|
|
1443
|
-
* Two cases:
|
|
1444
|
-
* 1. Overflow: LLM returned context overflow error, remove error message from agent state, compact, auto-retry
|
|
1445
|
-
* 2. Threshold: Context over threshold, compact, NO auto-retry (user continues manually)
|
|
1446
|
-
*
|
|
1447
|
-
* @param assistantMessage The assistant message to check
|
|
1448
|
-
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
1449
|
-
*/
|
|
1450
|
-
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
1451
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1452
|
-
if (!settings.enabled)
|
|
1453
|
-
return;
|
|
1454
|
-
// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
|
|
1455
|
-
if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
|
|
1456
|
-
return;
|
|
1457
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
1458
|
-
// Skip overflow check if the message came from a different model.
|
|
1459
|
-
// This handles the case where user switched from a smaller-context model (e.g. opus)
|
|
1460
|
-
// to a larger-context model (e.g. codex) - the overflow error from the old model
|
|
1461
|
-
// shouldn't trigger compaction for the new model.
|
|
1462
|
-
const sameModel = this.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;
|
|
1463
|
-
// Skip compaction checks if this assistant message is older than the latest
|
|
1464
|
-
// compaction boundary. This prevents a stale pre-compaction usage/error
|
|
1465
|
-
// from retriggering compaction on the first prompt after compaction.
|
|
1466
|
-
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
1467
|
-
const assistantIsFromBeforeCompaction = compactionEntry !== null && assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime();
|
|
1468
|
-
if (assistantIsFromBeforeCompaction) {
|
|
1469
|
-
return;
|
|
1470
|
-
}
|
|
1471
|
-
// Case 1: Overflow - LLM returned context overflow error
|
|
1472
|
-
if (sameModel && isContextOverflow(assistantMessage, contextWindow)) {
|
|
1473
|
-
if (this._overflowRecoveryAttempted) {
|
|
1474
|
-
this._emit({
|
|
1475
|
-
type: "auto_compaction_end",
|
|
1476
|
-
result: undefined,
|
|
1477
|
-
aborted: false,
|
|
1478
|
-
willRetry: false,
|
|
1479
|
-
errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
|
|
1480
|
-
});
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
this._overflowRecoveryAttempted = true;
|
|
1484
|
-
// Remove the error message from agent state (it IS saved to session for history,
|
|
1485
|
-
// but we don't want it in context for the retry)
|
|
1486
|
-
const messages = this.agent.state.messages;
|
|
1487
|
-
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
1488
|
-
this.agent.replaceMessages(messages.slice(0, -1));
|
|
1489
|
-
}
|
|
1490
|
-
await this._runAutoCompaction("overflow", true);
|
|
1491
|
-
return;
|
|
1492
|
-
}
|
|
1493
|
-
// Case 2: Threshold - context is getting large
|
|
1494
|
-
// For error messages (no usage data), estimate from last successful response.
|
|
1495
|
-
// This ensures sessions that hit persistent API errors (e.g. 529) can still compact.
|
|
1496
|
-
let contextTokens;
|
|
1497
|
-
if (assistantMessage.stopReason === "error") {
|
|
1498
|
-
const messages = this.agent.state.messages;
|
|
1499
|
-
const estimate = estimateContextTokens(messages);
|
|
1500
|
-
if (estimate.lastUsageIndex === null)
|
|
1501
|
-
return; // No usage data at all
|
|
1502
|
-
// Verify the usage source is post-compaction. Kept pre-compaction messages
|
|
1503
|
-
// have stale usage reflecting the old (larger) context and would falsely
|
|
1504
|
-
// trigger compaction right after one just finished.
|
|
1505
|
-
const usageMsg = messages[estimate.lastUsageIndex];
|
|
1506
|
-
if (compactionEntry &&
|
|
1507
|
-
usageMsg.role === "assistant" &&
|
|
1508
|
-
usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
|
|
1509
|
-
return;
|
|
1510
|
-
}
|
|
1511
|
-
contextTokens = estimate.tokens;
|
|
1512
|
-
}
|
|
1513
|
-
else {
|
|
1514
|
-
contextTokens = calculateContextTokens(assistantMessage.usage);
|
|
1515
|
-
}
|
|
1516
|
-
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
1517
|
-
await this._runAutoCompaction("threshold", false);
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
/**
|
|
1521
|
-
* Internal: Run auto-compaction with events.
|
|
1522
|
-
*/
|
|
1523
|
-
async _runAutoCompaction(reason, willRetry) {
|
|
1524
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1525
|
-
this._emit({ type: "auto_compaction_start", reason });
|
|
1526
|
-
this._autoCompactionAbortController = new AbortController();
|
|
1527
|
-
try {
|
|
1528
|
-
if (!this.model) {
|
|
1529
|
-
this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
const apiKey = await this._modelRegistry.getApiKey(this.model, this.sessionId);
|
|
1533
|
-
if (!apiKey) {
|
|
1534
|
-
this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
|
|
1535
|
-
return;
|
|
1536
|
-
}
|
|
1537
|
-
const pathEntries = this.sessionManager.getBranch();
|
|
1538
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
1539
|
-
if (!preparation) {
|
|
1540
|
-
this._emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
let extensionCompaction;
|
|
1544
|
-
let fromExtension = false;
|
|
1545
|
-
if (this._extensionRunner?.hasHandlers("session_before_compact")) {
|
|
1546
|
-
const extensionResult = (await this._extensionRunner.emit({
|
|
1547
|
-
type: "session_before_compact",
|
|
1548
|
-
preparation,
|
|
1549
|
-
branchEntries: pathEntries,
|
|
1550
|
-
customInstructions: undefined,
|
|
1551
|
-
signal: this._autoCompactionAbortController.signal,
|
|
1552
|
-
}));
|
|
1553
|
-
if (extensionResult?.cancel) {
|
|
1554
|
-
this._emit({ type: "auto_compaction_end", result: undefined, aborted: true, willRetry: false });
|
|
1555
|
-
return;
|
|
1556
|
-
}
|
|
1557
|
-
if (extensionResult?.compaction) {
|
|
1558
|
-
extensionCompaction = extensionResult.compaction;
|
|
1559
|
-
fromExtension = true;
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
let summary;
|
|
1563
|
-
let firstKeptEntryId;
|
|
1564
|
-
let tokensBefore;
|
|
1565
|
-
let details;
|
|
1566
|
-
if (extensionCompaction) {
|
|
1567
|
-
// Extension provided compaction content
|
|
1568
|
-
summary = extensionCompaction.summary;
|
|
1569
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
1570
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
1571
|
-
details = extensionCompaction.details;
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
// Generate compaction result
|
|
1575
|
-
const compactResult = await compact(preparation, this.model, apiKey, undefined, this._autoCompactionAbortController.signal);
|
|
1576
|
-
summary = compactResult.summary;
|
|
1577
|
-
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
1578
|
-
tokensBefore = compactResult.tokensBefore;
|
|
1579
|
-
details = compactResult.details;
|
|
1580
|
-
}
|
|
1581
|
-
if (this._autoCompactionAbortController.signal.aborted) {
|
|
1582
|
-
this._emit({ type: "auto_compaction_end", result: undefined, aborted: true, willRetry: false });
|
|
1583
|
-
return;
|
|
1584
|
-
}
|
|
1585
|
-
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
|
|
1586
|
-
const newEntries = this.sessionManager.getEntries();
|
|
1587
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1588
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
1589
|
-
// Get the saved compaction entry for the extension event
|
|
1590
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
1591
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
1592
|
-
await this._extensionRunner.emit({
|
|
1593
|
-
type: "session_compact",
|
|
1594
|
-
compactionEntry: savedCompactionEntry,
|
|
1595
|
-
fromExtension,
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
const result = {
|
|
1599
|
-
summary,
|
|
1600
|
-
firstKeptEntryId,
|
|
1601
|
-
tokensBefore,
|
|
1602
|
-
details,
|
|
1603
|
-
};
|
|
1604
|
-
this._emit({ type: "auto_compaction_end", result, aborted: false, willRetry });
|
|
1605
|
-
if (willRetry) {
|
|
1606
|
-
const messages = this.agent.state.messages;
|
|
1607
|
-
const lastMsg = messages[messages.length - 1];
|
|
1608
|
-
if (lastMsg?.role === "assistant" && lastMsg.stopReason === "error") {
|
|
1609
|
-
this.agent.replaceMessages(messages.slice(0, -1));
|
|
1610
|
-
}
|
|
1611
|
-
setTimeout(() => {
|
|
1612
|
-
this.agent.continue().catch(() => { });
|
|
1613
|
-
}, 100);
|
|
1614
|
-
}
|
|
1615
|
-
else if (this.agent.hasQueuedMessages()) {
|
|
1616
|
-
// Auto-compaction can complete while follow-up/steering/custom messages are waiting.
|
|
1617
|
-
// Kick the loop so queued messages are actually delivered.
|
|
1618
|
-
setTimeout(() => {
|
|
1619
|
-
this.agent.continue().catch(() => { });
|
|
1620
|
-
}, 100);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
catch (error) {
|
|
1624
|
-
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
1625
|
-
this._emit({
|
|
1626
|
-
type: "auto_compaction_end",
|
|
1627
|
-
result: undefined,
|
|
1628
|
-
aborted: false,
|
|
1629
|
-
willRetry: false,
|
|
1630
|
-
errorMessage: reason === "overflow"
|
|
1631
|
-
? `Context overflow recovery failed: ${errorMessage}`
|
|
1632
|
-
: `Auto-compaction failed: ${errorMessage}`,
|
|
1633
|
-
});
|
|
1634
|
-
}
|
|
1635
|
-
finally {
|
|
1636
|
-
this._autoCompactionAbortController = undefined;
|
|
1637
|
-
}
|
|
1323
|
+
this._compactionOrchestrator.abortBranchSummary();
|
|
1638
1324
|
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Toggle auto-compaction setting.
|
|
1641
|
-
*/
|
|
1325
|
+
/** Toggle auto-compaction setting */
|
|
1642
1326
|
setAutoCompactionEnabled(enabled) {
|
|
1643
|
-
this.
|
|
1327
|
+
this._compactionOrchestrator.setAutoCompactionEnabled(enabled);
|
|
1644
1328
|
}
|
|
1645
1329
|
/** Whether auto-compaction is enabled */
|
|
1646
1330
|
get autoCompactionEnabled() {
|
|
1647
|
-
return this.
|
|
1331
|
+
return this._compactionOrchestrator.autoCompactionEnabled;
|
|
1648
1332
|
}
|
|
1649
1333
|
async bindExtensions(bindings) {
|
|
1650
1334
|
if (bindings.uiContext !== undefined) {
|
|
@@ -1753,7 +1437,7 @@ export class AgentSession {
|
|
|
1753
1437
|
runner.emitError({
|
|
1754
1438
|
extensionPath: "<runtime>",
|
|
1755
1439
|
event: "send_message",
|
|
1756
|
-
error:
|
|
1440
|
+
error: getErrorMessage(err),
|
|
1757
1441
|
});
|
|
1758
1442
|
});
|
|
1759
1443
|
},
|
|
@@ -1762,7 +1446,7 @@ export class AgentSession {
|
|
|
1762
1446
|
runner.emitError({
|
|
1763
1447
|
extensionPath: "<runtime>",
|
|
1764
1448
|
event: "send_user_message",
|
|
1765
|
-
error:
|
|
1449
|
+
error: getErrorMessage(err),
|
|
1766
1450
|
});
|
|
1767
1451
|
});
|
|
1768
1452
|
},
|
|
@@ -1775,7 +1459,7 @@ export class AgentSession {
|
|
|
1775
1459
|
runner.emitError({
|
|
1776
1460
|
extensionPath: "<runtime>",
|
|
1777
1461
|
event: "retry_last_turn",
|
|
1778
|
-
error:
|
|
1462
|
+
error: getErrorMessage(err),
|
|
1779
1463
|
});
|
|
1780
1464
|
});
|
|
1781
1465
|
}
|
|
@@ -1944,244 +1628,23 @@ export class AgentSession {
|
|
|
1944
1628
|
}
|
|
1945
1629
|
}
|
|
1946
1630
|
// =========================================================================
|
|
1947
|
-
// Auto-Retry
|
|
1631
|
+
// Auto-Retry (delegated to RetryHandler)
|
|
1948
1632
|
// =========================================================================
|
|
1949
|
-
/**
|
|
1950
|
-
* Check if an error is retryable (overloaded, rate limit, server errors).
|
|
1951
|
-
* Context overflow errors are NOT retryable (handled by compaction instead).
|
|
1952
|
-
*/
|
|
1953
|
-
_isRetryableError(message) {
|
|
1954
|
-
if (message.stopReason !== "error" || !message.errorMessage)
|
|
1955
|
-
return false;
|
|
1956
|
-
// Context overflow is handled by compaction, not retry
|
|
1957
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
1958
|
-
if (isContextOverflow(message, contextWindow))
|
|
1959
|
-
return false;
|
|
1960
|
-
const err = message.errorMessage;
|
|
1961
|
-
// Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection errors, fetch failed, terminated, retry delay exceeded, network unavailable / auth expired (transient network failures)
|
|
1962
|
-
return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay|network.?(?:is\s+)?unavailable|credentials.*expired|temporarily backed off/i.test(err);
|
|
1963
|
-
}
|
|
1964
|
-
/**
|
|
1965
|
-
* Classify an error message into a usage-limit error type for credential backoff.
|
|
1966
|
-
*/
|
|
1967
|
-
_classifyErrorType(errorMessage) {
|
|
1968
|
-
const err = errorMessage.toLowerCase();
|
|
1969
|
-
if (/quota|billing|exceeded.*limit|usage.*limit/i.test(err))
|
|
1970
|
-
return "quota_exhausted";
|
|
1971
|
-
if (/rate.?limit|too many requests|429/i.test(err))
|
|
1972
|
-
return "rate_limit";
|
|
1973
|
-
if (/500|502|503|504|server.?error|internal.?error|service.?unavailable/i.test(err))
|
|
1974
|
-
return "server_error";
|
|
1975
|
-
return "unknown";
|
|
1976
|
-
}
|
|
1977
|
-
/**
|
|
1978
|
-
* Handle retryable errors with exponential backoff.
|
|
1979
|
-
* When multiple credentials are available, marks the failing credential
|
|
1980
|
-
* as backed off and retries immediately with the next one.
|
|
1981
|
-
* @returns true if retry was initiated, false if max retries exceeded or disabled
|
|
1982
|
-
*/
|
|
1983
|
-
async _handleRetryableError(message) {
|
|
1984
|
-
const settings = this.settingsManager.getRetrySettings();
|
|
1985
|
-
if (!settings.enabled) {
|
|
1986
|
-
this._resolveRetry();
|
|
1987
|
-
return false;
|
|
1988
|
-
}
|
|
1989
|
-
// Retry promise is created synchronously in _handleAgentEvent for agent_end.
|
|
1990
|
-
// Keep a defensive fallback here in case a future refactor bypasses that path.
|
|
1991
|
-
if (!this._retryPromise) {
|
|
1992
|
-
this._retryPromise = new Promise((resolve) => {
|
|
1993
|
-
this._retryResolve = resolve;
|
|
1994
|
-
});
|
|
1995
|
-
}
|
|
1996
|
-
// Try credential fallback before counting against retry budget.
|
|
1997
|
-
// If another credential is available, switch to it and retry immediately.
|
|
1998
|
-
// Only attempt credential rotation for errors that indicate a credential-level
|
|
1999
|
-
// problem (rate limit, quota exhaustion, server error). Transport failures
|
|
2000
|
-
// ("unknown") like connection resets are not credential-specific — rotating
|
|
2001
|
-
// won't help and backing off the only credential causes "Authentication failed".
|
|
2002
|
-
if (this.model && message.errorMessage) {
|
|
2003
|
-
const errorType = this._classifyErrorType(message.errorMessage);
|
|
2004
|
-
const isCredentialError = errorType !== "unknown";
|
|
2005
|
-
const hasAlternate = isCredentialError && this._modelRegistry.authStorage.markUsageLimitReached(this.model.provider, this.sessionId, { errorType });
|
|
2006
|
-
if (hasAlternate) {
|
|
2007
|
-
// Remove error message from agent state
|
|
2008
|
-
const messages = this.agent.state.messages;
|
|
2009
|
-
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
2010
|
-
this.agent.replaceMessages(messages.slice(0, -1));
|
|
2011
|
-
}
|
|
2012
|
-
this._emit({
|
|
2013
|
-
type: "auto_retry_start",
|
|
2014
|
-
attempt: this._retryAttempt + 1,
|
|
2015
|
-
maxAttempts: settings.maxRetries,
|
|
2016
|
-
delayMs: 0,
|
|
2017
|
-
errorMessage: `${message.errorMessage} (switching credential)`,
|
|
2018
|
-
});
|
|
2019
|
-
// Retry immediately with the next credential - don't increment _retryAttempt
|
|
2020
|
-
setTimeout(() => {
|
|
2021
|
-
this.agent.continue().catch(() => {
|
|
2022
|
-
// Retry failed - will be caught by next agent_end
|
|
2023
|
-
});
|
|
2024
|
-
}, 0);
|
|
2025
|
-
return true;
|
|
2026
|
-
}
|
|
2027
|
-
// All credentials are backed off. Try cross-provider fallback before giving up.
|
|
2028
|
-
if (isCredentialError) {
|
|
2029
|
-
const fallbackResult = await this._fallbackResolver.findFallback(this.model, errorType);
|
|
2030
|
-
if (fallbackResult) {
|
|
2031
|
-
// Swap to fallback model — don't persist to settings
|
|
2032
|
-
const previousProvider = this.model.provider;
|
|
2033
|
-
this.agent.setModel(fallbackResult.model);
|
|
2034
|
-
this.sessionManager.appendModelChange(fallbackResult.model.provider, fallbackResult.model.id);
|
|
2035
|
-
// Remove error message from agent state
|
|
2036
|
-
const msgs = this.agent.state.messages;
|
|
2037
|
-
if (msgs.length > 0 && msgs[msgs.length - 1].role === "assistant") {
|
|
2038
|
-
this.agent.replaceMessages(msgs.slice(0, -1));
|
|
2039
|
-
}
|
|
2040
|
-
this._emit({
|
|
2041
|
-
type: "fallback_provider_switch",
|
|
2042
|
-
from: `${previousProvider}/${this.model?.id}`,
|
|
2043
|
-
to: `${fallbackResult.model.provider}/${fallbackResult.model.id}`,
|
|
2044
|
-
reason: fallbackResult.reason,
|
|
2045
|
-
});
|
|
2046
|
-
this._emit({
|
|
2047
|
-
type: "auto_retry_start",
|
|
2048
|
-
attempt: this._retryAttempt + 1,
|
|
2049
|
-
maxAttempts: settings.maxRetries,
|
|
2050
|
-
delayMs: 0,
|
|
2051
|
-
errorMessage: `${message.errorMessage} (${fallbackResult.reason})`,
|
|
2052
|
-
});
|
|
2053
|
-
// Retry immediately with fallback provider - don't increment _retryAttempt
|
|
2054
|
-
setTimeout(() => {
|
|
2055
|
-
this.agent.continue().catch(() => {
|
|
2056
|
-
// Retry failed - will be caught by next agent_end
|
|
2057
|
-
});
|
|
2058
|
-
}, 0);
|
|
2059
|
-
return true;
|
|
2060
|
-
}
|
|
2061
|
-
// No fallback available either
|
|
2062
|
-
if (errorType === "quota_exhausted") {
|
|
2063
|
-
this._emit({
|
|
2064
|
-
type: "fallback_chain_exhausted",
|
|
2065
|
-
reason: `All providers exhausted for ${this.model.provider}/${this.model.id}`,
|
|
2066
|
-
});
|
|
2067
|
-
this._emit({
|
|
2068
|
-
type: "auto_retry_end",
|
|
2069
|
-
success: false,
|
|
2070
|
-
attempt: this._retryAttempt,
|
|
2071
|
-
finalError: message.errorMessage,
|
|
2072
|
-
});
|
|
2073
|
-
this._retryAttempt = 0;
|
|
2074
|
-
this._resolveRetry();
|
|
2075
|
-
return false;
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
this._retryAttempt++;
|
|
2080
|
-
if (this._retryAttempt > settings.maxRetries) {
|
|
2081
|
-
// Max retries exceeded, emit final failure and reset
|
|
2082
|
-
this._emit({
|
|
2083
|
-
type: "auto_retry_end",
|
|
2084
|
-
success: false,
|
|
2085
|
-
attempt: this._retryAttempt - 1,
|
|
2086
|
-
finalError: message.errorMessage,
|
|
2087
|
-
});
|
|
2088
|
-
this._retryAttempt = 0;
|
|
2089
|
-
this._resolveRetry(); // Resolve so waitForRetry() completes
|
|
2090
|
-
return false;
|
|
2091
|
-
}
|
|
2092
|
-
// Use server-requested delay when available (rate limit headers), capped by maxDelayMs.
|
|
2093
|
-
// Fall back to exponential backoff when no server hint is present.
|
|
2094
|
-
const exponentialDelayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
|
|
2095
|
-
let delayMs;
|
|
2096
|
-
if (message.retryAfterMs !== undefined) {
|
|
2097
|
-
const cap = settings.maxDelayMs > 0 ? settings.maxDelayMs : Infinity;
|
|
2098
|
-
if (message.retryAfterMs > cap) {
|
|
2099
|
-
// Server wants us to wait longer than maxDelayMs — give up to let auto-mode handle recovery
|
|
2100
|
-
this._emit({
|
|
2101
|
-
type: "auto_retry_end",
|
|
2102
|
-
success: false,
|
|
2103
|
-
attempt: this._retryAttempt - 1,
|
|
2104
|
-
finalError: `Rate limit reset in ${Math.ceil(message.retryAfterMs / 1000)}s (max: ${Math.ceil(cap / 1000)}s). ${message.errorMessage || ""}`.trim(),
|
|
2105
|
-
});
|
|
2106
|
-
this._retryAttempt = 0;
|
|
2107
|
-
this._resolveRetry();
|
|
2108
|
-
return false;
|
|
2109
|
-
}
|
|
2110
|
-
delayMs = message.retryAfterMs;
|
|
2111
|
-
}
|
|
2112
|
-
else {
|
|
2113
|
-
delayMs = exponentialDelayMs;
|
|
2114
|
-
}
|
|
2115
|
-
this._emit({
|
|
2116
|
-
type: "auto_retry_start",
|
|
2117
|
-
attempt: this._retryAttempt,
|
|
2118
|
-
maxAttempts: settings.maxRetries,
|
|
2119
|
-
delayMs,
|
|
2120
|
-
errorMessage: message.errorMessage || "Unknown error",
|
|
2121
|
-
});
|
|
2122
|
-
// Remove error message from agent state (keep in session for history)
|
|
2123
|
-
const messages = this.agent.state.messages;
|
|
2124
|
-
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
2125
|
-
this.agent.replaceMessages(messages.slice(0, -1));
|
|
2126
|
-
}
|
|
2127
|
-
// Wait with exponential backoff (abortable)
|
|
2128
|
-
this._retryAbortController = new AbortController();
|
|
2129
|
-
try {
|
|
2130
|
-
await sleep(delayMs, this._retryAbortController.signal);
|
|
2131
|
-
}
|
|
2132
|
-
catch {
|
|
2133
|
-
// Aborted during sleep - emit end event so UI can clean up
|
|
2134
|
-
const attempt = this._retryAttempt;
|
|
2135
|
-
this._retryAttempt = 0;
|
|
2136
|
-
this._retryAbortController = undefined;
|
|
2137
|
-
this._emit({
|
|
2138
|
-
type: "auto_retry_end",
|
|
2139
|
-
success: false,
|
|
2140
|
-
attempt,
|
|
2141
|
-
finalError: "Retry cancelled",
|
|
2142
|
-
});
|
|
2143
|
-
this._resolveRetry();
|
|
2144
|
-
return false;
|
|
2145
|
-
}
|
|
2146
|
-
this._retryAbortController = undefined;
|
|
2147
|
-
// Retry via continue() - use setTimeout to break out of event handler chain
|
|
2148
|
-
setTimeout(() => {
|
|
2149
|
-
this.agent.continue().catch(() => {
|
|
2150
|
-
// Retry failed - will be caught by next agent_end
|
|
2151
|
-
});
|
|
2152
|
-
}, 0);
|
|
2153
|
-
return true;
|
|
2154
|
-
}
|
|
2155
|
-
/**
|
|
2156
|
-
* Cancel in-progress retry.
|
|
2157
|
-
*/
|
|
1633
|
+
/** Cancel in-progress retry */
|
|
2158
1634
|
abortRetry() {
|
|
2159
|
-
this.
|
|
2160
|
-
// Note: _retryAttempt is reset in the catch block of _autoRetry
|
|
2161
|
-
this._resolveRetry();
|
|
2162
|
-
}
|
|
2163
|
-
/**
|
|
2164
|
-
* Wait for any in-progress retry to complete.
|
|
2165
|
-
* Returns immediately if no retry is in progress.
|
|
2166
|
-
*/
|
|
2167
|
-
async waitForRetry() {
|
|
2168
|
-
if (this._retryPromise) {
|
|
2169
|
-
await this._retryPromise;
|
|
2170
|
-
}
|
|
1635
|
+
this._retryHandler.abortRetry();
|
|
2171
1636
|
}
|
|
2172
1637
|
/** Whether auto-retry is currently in progress */
|
|
2173
1638
|
get isRetrying() {
|
|
2174
|
-
return this.
|
|
1639
|
+
return this._retryHandler.isRetrying;
|
|
2175
1640
|
}
|
|
2176
1641
|
/** Whether auto-retry is enabled */
|
|
2177
1642
|
get autoRetryEnabled() {
|
|
2178
|
-
return this.
|
|
1643
|
+
return this._retryHandler.autoRetryEnabled;
|
|
2179
1644
|
}
|
|
2180
|
-
/**
|
|
2181
|
-
* Toggle auto-retry setting.
|
|
2182
|
-
*/
|
|
1645
|
+
/** Toggle auto-retry setting */
|
|
2183
1646
|
setAutoRetryEnabled(enabled) {
|
|
2184
|
-
this.
|
|
1647
|
+
this._retryHandler.setAutoRetryEnabled(enabled);
|
|
2185
1648
|
}
|
|
2186
1649
|
// =========================================================================
|
|
2187
1650
|
// Bash Execution
|
|
@@ -2445,7 +1908,7 @@ export class AgentSession {
|
|
|
2445
1908
|
label,
|
|
2446
1909
|
};
|
|
2447
1910
|
// Set up abort controller for summarization
|
|
2448
|
-
this.
|
|
1911
|
+
this._compactionOrchestrator.branchSummaryAbortController = new AbortController();
|
|
2449
1912
|
let extensionSummary;
|
|
2450
1913
|
let fromExtension = false;
|
|
2451
1914
|
// Emit session_before_tree event
|
|
@@ -2453,7 +1916,7 @@ export class AgentSession {
|
|
|
2453
1916
|
const result = (await this._extensionRunner.emit({
|
|
2454
1917
|
type: "session_before_tree",
|
|
2455
1918
|
preparation,
|
|
2456
|
-
signal: this.
|
|
1919
|
+
signal: this._compactionOrchestrator.branchSummaryAbortController.signal,
|
|
2457
1920
|
}));
|
|
2458
1921
|
if (result?.cancel) {
|
|
2459
1922
|
return { cancelled: true };
|
|
@@ -2486,12 +1949,12 @@ export class AgentSession {
|
|
|
2486
1949
|
const result = await generateBranchSummary(entriesToSummarize, {
|
|
2487
1950
|
model,
|
|
2488
1951
|
apiKey,
|
|
2489
|
-
signal: this.
|
|
1952
|
+
signal: this._compactionOrchestrator.branchSummaryAbortController.signal,
|
|
2490
1953
|
customInstructions,
|
|
2491
1954
|
replaceInstructions,
|
|
2492
1955
|
reserveTokens: branchSummarySettings.reserveTokens,
|
|
2493
1956
|
});
|
|
2494
|
-
this.
|
|
1957
|
+
this._compactionOrchestrator.branchSummaryAbortController = undefined;
|
|
2495
1958
|
if (result.aborted) {
|
|
2496
1959
|
return { cancelled: true, aborted: true };
|
|
2497
1960
|
}
|
|
@@ -2569,7 +2032,7 @@ export class AgentSession {
|
|
|
2569
2032
|
});
|
|
2570
2033
|
}
|
|
2571
2034
|
// Emit to custom tools
|
|
2572
|
-
this.
|
|
2035
|
+
this._compactionOrchestrator.branchSummaryAbortController = undefined;
|
|
2573
2036
|
return { editorText, cancelled: false, summaryEntry };
|
|
2574
2037
|
}
|
|
2575
2038
|
/**
|
|
@@ -2631,17 +2094,17 @@ export class AgentSession {
|
|
|
2631
2094
|
sessionId: this.sessionId,
|
|
2632
2095
|
userMessages,
|
|
2633
2096
|
assistantMessages,
|
|
2634
|
-
toolCalls,
|
|
2097
|
+
toolCalls: Math.max(toolCalls, this._cumulativeToolCalls),
|
|
2635
2098
|
toolResults,
|
|
2636
2099
|
totalMessages: state.messages.length,
|
|
2637
2100
|
tokens: {
|
|
2638
|
-
input: totalInput,
|
|
2639
|
-
output: totalOutput,
|
|
2101
|
+
input: Math.max(totalInput, this._cumulativeInputTokens),
|
|
2102
|
+
output: Math.max(totalOutput, this._cumulativeOutputTokens),
|
|
2640
2103
|
cacheRead: totalCacheRead,
|
|
2641
2104
|
cacheWrite: totalCacheWrite,
|
|
2642
|
-
total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
|
|
2105
|
+
total: Math.max(totalInput + totalOutput, this._cumulativeInputTokens + this._cumulativeOutputTokens) + totalCacheRead + totalCacheWrite,
|
|
2643
2106
|
},
|
|
2644
|
-
cost: totalCost,
|
|
2107
|
+
cost: Math.max(totalCost, this._cumulativeCost),
|
|
2645
2108
|
};
|
|
2646
2109
|
}
|
|
2647
2110
|
getContextUsage() {
|