@phi-code-admin/phi-code 0.74.2 → 0.75.0
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 +1186 -4
- package/README.md +478 -379
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +9 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/bun/restore-sandbox-env.d.ts +13 -0
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
- package/dist/bun/restore-sandbox-env.js +32 -0
- package/dist/bun/restore-sandbox-env.js.map +1 -0
- package/dist/cli/args.d.ts +12 -7
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +87 -45
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +4 -0
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +7 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +2 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +226 -30
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +117 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +300 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +86 -0
- package/dist/core/agent-session-services.d.ts.map +1 -0
- package/dist/core/agent-session-services.js +117 -0
- package/dist/core/agent-session-services.js.map +1 -0
- package/dist/core/agent-session.d.ts +63 -82
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +674 -628
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/api-key-store.d.ts +87 -0
- package/dist/core/api-key-store.d.ts.map +1 -0
- package/dist/core/api-key-store.js +168 -0
- package/dist/core/api-key-store.js.map +1 -0
- package/dist/core/auth-guidance.d.ts +5 -0
- package/dist/core/auth-guidance.d.ts.map +1 -0
- package/dist/core/auth-guidance.js +21 -0
- package/dist/core/auth-guidance.js.map +1 -0
- package/dist/core/auth-storage.d.ts +12 -5
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +34 -8
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts +0 -15
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +28 -129
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -4
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +32 -27
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/config-watcher.d.ts +47 -0
- package/dist/core/config-watcher.d.ts.map +1 -0
- package/dist/core/config-watcher.js +135 -0
- package/dist/core/config-watcher.js.map +1 -0
- package/dist/core/default-models.json +80 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +7 -3
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/dist/core/export-html/ansi-to-html.js +1 -1
- package/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/dist/core/export-html/index.d.ts +7 -4
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +15 -13
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.css +112 -17
- package/dist/core/export-html/template.html +1 -0
- package/dist/core/export-html/template.js +312 -64
- package/dist/core/export-html/tool-renderer.d.ts +9 -10
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +61 -16
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +5 -4
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +2 -2
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +0 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +98 -18
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +27 -14
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +299 -115
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +200 -44
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +10 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +4 -11
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +7 -87
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +22 -2
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +225 -49
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +348 -50
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +276 -132
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +41 -5
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +316 -136
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +6 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +70 -37
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/output-guard.d.ts +6 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +59 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-manager.d.ts +49 -7
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +655 -122
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +12 -10
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +37 -38
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-display-names.d.ts +2 -0
- package/dist/core/provider-display-names.d.ts.map +1 -0
- package/dist/core/provider-display-names.js +33 -0
- package/dist/core/provider-display-names.js.map +1 -0
- package/dist/core/resolve-config-value.d.ts +6 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +75 -8
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +18 -8
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +217 -123
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +25 -8
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +84 -37
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts +19 -0
- package/dist/core/session-cwd.d.ts.map +1 -0
- package/dist/core/session-cwd.js +38 -0
- package/dist/core/session-cwd.js.map +1 -0
- package/dist/core/session-manager.d.ts +11 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +42 -27
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +34 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +113 -13
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +13 -11
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +59 -19
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +2 -3
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +9 -6
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/source-info.d.ts +18 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +19 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts +3 -3
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +16 -55
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts +3 -0
- package/dist/core/telemetry.d.ts.map +1 -0
- package/dist/core/telemetry.js +9 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/timings.d.ts +1 -0
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +6 -0
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts +27 -14
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +301 -208
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +23 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +154 -59
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +22 -12
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +243 -65
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +37 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +10 -14
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +202 -110
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +14 -22
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +100 -35
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +27 -60
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +96 -45
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +8 -11
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +66 -15
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +50 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -0
- package/dist/core/tools/output-accumulator.js +178 -0
- package/dist/core/tools/output-accumulator.js.map +1 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +1 -1
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts +9 -13
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +175 -52
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +21 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +49 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +34 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts +8 -11
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +167 -32
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +12 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +5 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +326 -404
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +2 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +24 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +10 -6
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +5 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +32 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +31 -12
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +7 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +5 -3
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +5 -3
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +49 -16
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +5 -0
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +14 -7
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +6 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js +8 -6
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +1 -0
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
- package/dist/modes/interactive/components/earendil-announcement.js +40 -0
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -10
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +13 -7
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +18 -11
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +8 -36
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +23 -48
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +5 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +35 -14
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +41 -22
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +18 -6
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +104 -31
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +5 -12
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +61 -42
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +2 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +109 -73
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +9 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +84 -4
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +6 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +5 -3
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +7 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +6 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +20 -34
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +158 -636
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +21 -2
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +224 -52
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts +2 -2
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +20 -16
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +8 -6
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +67 -39
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1556 -680
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -1
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +3 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +101 -72
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +2 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +107 -77
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +8 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +22 -16
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +184 -94
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +14 -4
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +4 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +460 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/child-process.d.ts +12 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +86 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +94 -11
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +96 -46
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +5 -0
- package/dist/utils/fs-watch.d.ts.map +1 -0
- package/dist/utils/fs-watch.js +25 -0
- package/dist/utils/fs-watch.js.map +1 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +5 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts +5 -5
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +51 -95
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/paths.d.ts +16 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +50 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/utils/pi-user-agent.d.ts.map +1 -0
- package/dist/utils/pi-user-agent.js +5 -0
- package/dist/utils/pi-user-agent.js.map +1 -0
- package/dist/utils/shell.d.ts +10 -6
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +29 -25
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +11 -6
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +14 -0
- package/dist/utils/version-check.d.ts.map +1 -0
- package/dist/utils/version-check.js +77 -0
- package/dist/utils/version-check.js.map +1 -0
- package/docs/compaction.md +394 -0
- package/docs/custom-provider.md +646 -0
- package/docs/development.md +71 -0
- package/docs/docs.json +148 -0
- package/docs/extensions.md +2596 -0
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/exy.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/index.md +70 -0
- package/docs/json.md +82 -0
- package/docs/keybindings.md +197 -0
- package/docs/models.md +474 -0
- package/docs/packages.md +223 -0
- package/docs/prompt-templates.md +88 -0
- package/docs/providers.md +243 -0
- package/docs/quickstart.md +142 -0
- package/docs/rpc.md +1407 -0
- package/docs/sdk.md +1149 -0
- package/docs/session-format.md +412 -0
- package/docs/sessions.md +137 -0
- package/docs/settings.md +279 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +232 -0
- package/docs/terminal-setup.md +106 -0
- package/docs/termux.md +127 -0
- package/docs/themes.md +295 -0
- package/docs/tmux.md +61 -0
- package/docs/tui.md +918 -0
- package/docs/usage.md +277 -0
- package/docs/windows.md +17 -0
- package/examples/README.md +25 -0
- package/examples/extensions/README.md +208 -0
- package/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/border-status-editor.ts +150 -0
- package/examples/extensions/built-in-tool-renderer.ts +249 -0
- package/examples/extensions/claude-rules.ts +86 -0
- package/examples/extensions/commands.ts +72 -0
- package/examples/extensions/confirm-destructive.ts +59 -0
- package/examples/extensions/custom-compaction.ts +127 -0
- package/examples/extensions/custom-footer.ts +64 -0
- package/examples/extensions/custom-header.ts +73 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/examples/extensions/dirty-repo-guard.ts +56 -0
- package/examples/extensions/doom-overlay/README.md +46 -0
- package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/examples/extensions/doom-overlay/doom-component.ts +132 -0
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/examples/extensions/doom-overlay/index.ts +74 -0
- package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/examples/extensions/dynamic-resources/index.ts +15 -0
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/file-trigger.ts +41 -0
- package/examples/extensions/git-checkpoint.ts +53 -0
- package/examples/extensions/github-issue-autocomplete.ts +185 -0
- package/examples/extensions/handoff.ts +191 -0
- package/examples/extensions/hello.ts +26 -0
- package/examples/extensions/hidden-thinking-label.ts +53 -0
- package/examples/extensions/inline-bash.ts +94 -0
- package/examples/extensions/input-transform.ts +43 -0
- package/examples/extensions/interactive-shell.ts +196 -0
- package/examples/extensions/mac-system-theme.ts +47 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/minimal-mode.ts +426 -0
- package/examples/extensions/modal-editor.ts +85 -0
- package/examples/extensions/model-status.ts +31 -0
- package/examples/extensions/notify.ts +55 -0
- package/examples/extensions/overlay-qa-tests.ts +1348 -0
- package/examples/extensions/overlay-test.ts +150 -0
- package/examples/extensions/permission-gate.ts +34 -0
- package/examples/extensions/pirate.ts +47 -0
- package/examples/extensions/plan-mode/README.md +65 -0
- package/examples/extensions/plan-mode/index.ts +340 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/preset.ts +430 -0
- package/examples/extensions/prompt-customizer.ts +97 -0
- package/examples/extensions/protected-paths.ts +30 -0
- package/examples/extensions/provider-payload.ts +18 -0
- package/examples/extensions/qna.ts +122 -0
- package/examples/extensions/question.ts +264 -0
- package/examples/extensions/questionnaire.ts +427 -0
- package/examples/extensions/rainbow-editor.ts +88 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/rpc-demo.ts +118 -0
- package/examples/extensions/sandbox/index.ts +321 -0
- package/examples/extensions/sandbox/package-lock.json +92 -0
- package/examples/extensions/sandbox/package.json +19 -0
- package/examples/extensions/send-user-message.ts +97 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/shutdown-command.ts +63 -0
- package/examples/extensions/snake.ts +343 -0
- package/examples/extensions/space-invaders.ts +560 -0
- package/examples/extensions/ssh.ts +220 -0
- package/examples/extensions/status-line.ts +32 -0
- package/examples/extensions/structured-output.ts +65 -0
- package/examples/extensions/subagent/README.md +172 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +126 -0
- package/examples/extensions/subagent/index.ts +987 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/summarize.ts +206 -0
- package/examples/extensions/system-prompt-header.ts +17 -0
- package/examples/extensions/tic-tac-toe.ts +1008 -0
- package/examples/extensions/timed-confirm.ts +70 -0
- package/examples/extensions/titlebar-spinner.ts +58 -0
- package/examples/extensions/todo.ts +297 -0
- package/examples/extensions/tool-override.ts +144 -0
- package/examples/extensions/tools.ts +141 -0
- package/examples/extensions/trigger-compact.ts +50 -0
- package/examples/extensions/truncated-tool.ts +195 -0
- package/examples/extensions/widget-placement.ts +9 -0
- package/examples/extensions/with-deps/index.ts +32 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/extensions/working-indicator.ts +123 -0
- package/examples/extensions/working-message-test.ts +25 -0
- package/examples/rpc-extension-ui.ts +632 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +62 -0
- package/examples/sdk/04-skills.ts +55 -0
- package/examples/sdk/05-tools.ts +44 -0
- package/examples/sdk/06-extensions.ts +90 -0
- package/examples/sdk/07-context-files.ts +42 -0
- package/examples/sdk/08-prompt-templates.ts +51 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +48 -0
- package/examples/sdk/10-settings.ts +53 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +73 -0
- package/examples/sdk/13-session-runtime.ts +67 -0
- package/examples/sdk/README.md +147 -0
- package/extensions/phi/init.ts +15 -1
- package/extensions/phi/keys.ts +186 -0
- package/extensions/phi/providers/alibaba.ts +126 -0
- package/extensions/phi/providers/opencode-go.ts +204 -0
- package/extensions/phi/setup.ts +692 -0
- package/extensions/phi/smart-router.ts +8 -0
- package/extensions/phi/web-search.ts +432 -186
- package/package.json +111 -106
- package/scripts/copy-assets.sh +0 -0
- package/scripts/migrate-sessions.sh +0 -0
|
@@ -12,24 +12,27 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
|
-
import { readFileSync } from "node:fs";
|
|
16
|
-
import { basename, dirname,
|
|
17
|
-
import { isContextOverflow, modelsAreEqual, resetApiProviders,
|
|
18
|
-
import { getDocsPath } from "../config.js";
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { basename, dirname, resolve } from "node:path";
|
|
17
|
+
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, } from "phi-code-ai";
|
|
19
18
|
import { theme } from "../modes/interactive/theme/theme.js";
|
|
20
19
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
21
20
|
import { sleep } from "../utils/sleep.js";
|
|
22
|
-
import {
|
|
21
|
+
import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
|
|
22
|
+
import { executeBashWithOperations } from "./bash-executor.js";
|
|
23
23
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
24
24
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
25
25
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
26
26
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
27
|
-
import { ExtensionRunner, wrapRegisteredTools,
|
|
27
|
+
import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
28
|
+
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
28
29
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
29
|
-
import { getLatestCompactionEntry } from "./session-manager.js";
|
|
30
|
-
import {
|
|
30
|
+
import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
|
|
31
|
+
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
31
32
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
32
|
-
import {
|
|
33
|
+
import { createLocalBashOperations } from "./tools/bash.js";
|
|
34
|
+
import { createAllToolDefinitions } from "./tools/index.js";
|
|
35
|
+
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
33
36
|
/**
|
|
34
37
|
* Parse a skill block from message text.
|
|
35
38
|
* Returns null if the text doesn't contain a skill block.
|
|
@@ -50,59 +53,66 @@ export function parseSkillBlock(text) {
|
|
|
50
53
|
// ============================================================================
|
|
51
54
|
/** Standard thinking levels */
|
|
52
55
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
|
|
53
|
-
/** Thinking levels including xhigh (for supported models) */
|
|
54
|
-
const THINKING_LEVELS_WITH_XHIGH = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
55
56
|
// ============================================================================
|
|
56
57
|
// AgentSession Class
|
|
57
58
|
// ============================================================================
|
|
58
59
|
export class AgentSession {
|
|
60
|
+
agent;
|
|
61
|
+
sessionManager;
|
|
62
|
+
settingsManager;
|
|
63
|
+
_scopedModels;
|
|
64
|
+
// Event subscription state
|
|
65
|
+
_unsubscribeAgent;
|
|
66
|
+
_eventListeners = [];
|
|
67
|
+
_agentEventQueue = Promise.resolve();
|
|
68
|
+
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
69
|
+
_steeringMessages = [];
|
|
70
|
+
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
71
|
+
_followUpMessages = [];
|
|
72
|
+
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
73
|
+
_pendingNextTurnMessages = [];
|
|
74
|
+
// Compaction state
|
|
75
|
+
_compactionAbortController = undefined;
|
|
76
|
+
_autoCompactionAbortController = undefined;
|
|
77
|
+
_overflowRecoveryAttempted = false;
|
|
78
|
+
// Branch summarization state
|
|
79
|
+
_branchSummaryAbortController = undefined;
|
|
80
|
+
// Retry state
|
|
81
|
+
_retryAbortController = undefined;
|
|
82
|
+
_retryAttempt = 0;
|
|
83
|
+
_retryPromise = undefined;
|
|
84
|
+
_retryResolve = undefined;
|
|
85
|
+
// Bash execution state
|
|
86
|
+
_bashAbortController = undefined;
|
|
87
|
+
_pendingBashMessages = [];
|
|
88
|
+
// Extension system
|
|
89
|
+
_extensionRunner;
|
|
90
|
+
_turnIndex = 0;
|
|
91
|
+
_resourceLoader;
|
|
92
|
+
_customTools;
|
|
93
|
+
_baseToolDefinitions = new Map();
|
|
94
|
+
_cwd;
|
|
95
|
+
_extensionRunnerRef;
|
|
96
|
+
_initialActiveToolNames;
|
|
97
|
+
_allowedToolNames;
|
|
98
|
+
_baseToolsOverride;
|
|
99
|
+
_sessionStartEvent;
|
|
100
|
+
_extensionUIContext;
|
|
101
|
+
_extensionCommandContextActions;
|
|
102
|
+
_extensionShutdownHandler;
|
|
103
|
+
_extensionErrorListener;
|
|
104
|
+
_extensionErrorUnsubscriber;
|
|
105
|
+
// Model registry for API key resolution
|
|
106
|
+
_modelRegistry;
|
|
107
|
+
// Tool registry for extension getTools/setTools
|
|
108
|
+
_toolRegistry = new Map();
|
|
109
|
+
_toolDefinitions = new Map();
|
|
110
|
+
_toolPromptSnippets = new Map();
|
|
111
|
+
_toolPromptGuidelines = new Map();
|
|
112
|
+
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
113
|
+
_baseSystemPrompt = "";
|
|
114
|
+
_baseSystemPromptOptions;
|
|
59
115
|
constructor(config) {
|
|
60
|
-
this._eventListeners = [];
|
|
61
|
-
this._agentEventQueue = Promise.resolve();
|
|
62
|
-
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
63
|
-
this._steeringMessages = [];
|
|
64
|
-
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
65
|
-
this._followUpMessages = [];
|
|
66
|
-
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
67
|
-
this._pendingNextTurnMessages = [];
|
|
68
|
-
// Compaction state
|
|
69
|
-
this._compactionAbortController = undefined;
|
|
70
|
-
this._autoCompactionAbortController = undefined;
|
|
71
|
-
this._overflowRecoveryAttempted = false;
|
|
72
|
-
// Branch summarization state
|
|
73
|
-
this._branchSummaryAbortController = undefined;
|
|
74
|
-
// Retry state
|
|
75
|
-
this._retryAbortController = undefined;
|
|
76
|
-
this._retryAttempt = 0;
|
|
77
|
-
this._retryPromise = undefined;
|
|
78
|
-
this._retryResolve = undefined;
|
|
79
|
-
// Bash execution state
|
|
80
|
-
this._bashAbortController = undefined;
|
|
81
|
-
this._pendingBashMessages = [];
|
|
82
|
-
// Extension system
|
|
83
|
-
this._extensionRunner = undefined;
|
|
84
|
-
this._turnIndex = 0;
|
|
85
|
-
this._baseToolRegistry = new Map();
|
|
86
|
-
// Tool registry for extension getTools/setTools
|
|
87
|
-
this._toolRegistry = new Map();
|
|
88
|
-
this._toolPromptSnippets = new Map();
|
|
89
|
-
this._toolPromptGuidelines = new Map();
|
|
90
|
-
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
91
|
-
this._baseSystemPrompt = "";
|
|
92
|
-
// Track last assistant message for auto-compaction check
|
|
93
|
-
this._lastAssistantMessage = undefined;
|
|
94
|
-
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
95
|
-
this._handleAgentEvent = (event) => {
|
|
96
|
-
// Create retry promise synchronously before queueing async processing.
|
|
97
|
-
// Agent.emit() calls this handler synchronously, and prompt() calls waitForRetry()
|
|
98
|
-
// as soon as agent.prompt() resolves. If _retryPromise is created only inside
|
|
99
|
-
// _processAgentEvent, slow earlier queued events can delay agent_end processing
|
|
100
|
-
// and waitForRetry() can miss the in-flight retry.
|
|
101
|
-
this._createRetryPromiseForAgentEnd(event);
|
|
102
|
-
this._agentEventQueue = this._agentEventQueue.then(() => this._processAgentEvent(event), () => this._processAgentEvent(event));
|
|
103
|
-
// Keep queue alive if an event handler fails
|
|
104
|
-
this._agentEventQueue.catch(() => { });
|
|
105
|
-
};
|
|
106
116
|
this.agent = config.agent;
|
|
107
117
|
this.sessionManager = config.sessionManager;
|
|
108
118
|
this.settingsManager = config.settingsManager;
|
|
@@ -113,10 +123,13 @@ export class AgentSession {
|
|
|
113
123
|
this._modelRegistry = config.modelRegistry;
|
|
114
124
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
115
125
|
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
126
|
+
this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
|
|
116
127
|
this._baseToolsOverride = config.baseToolsOverride;
|
|
128
|
+
this._sessionStartEvent = config.sessionStartEvent ?? { type: "session_start", reason: "startup" };
|
|
117
129
|
// Always subscribe to agent events for internal handling
|
|
118
130
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
119
131
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
132
|
+
this._installAgentToolHooks();
|
|
120
133
|
this._buildRuntime({
|
|
121
134
|
activeToolNames: this._initialActiveToolNames,
|
|
122
135
|
includeAllExtensionTools: true,
|
|
@@ -126,6 +139,79 @@ export class AgentSession {
|
|
|
126
139
|
get modelRegistry() {
|
|
127
140
|
return this._modelRegistry;
|
|
128
141
|
}
|
|
142
|
+
async _getRequiredRequestAuth(model) {
|
|
143
|
+
const result = await this._modelRegistry.getApiKeyAndHeaders(model);
|
|
144
|
+
if (!result.ok) {
|
|
145
|
+
if (result.error.startsWith("No API key found")) {
|
|
146
|
+
throw new Error(formatNoApiKeyFoundMessage(model.provider));
|
|
147
|
+
}
|
|
148
|
+
throw new Error(result.error);
|
|
149
|
+
}
|
|
150
|
+
if (result.apiKey) {
|
|
151
|
+
return { apiKey: result.apiKey, headers: result.headers };
|
|
152
|
+
}
|
|
153
|
+
const isOAuth = this._modelRegistry.isUsingOAuth(model);
|
|
154
|
+
if (isOAuth) {
|
|
155
|
+
throw new Error(`Authentication failed for "${model.provider}". ` +
|
|
156
|
+
`Credentials may have expired or network is unavailable. ` +
|
|
157
|
+
`Run '/login ${model.provider}' to re-authenticate.`);
|
|
158
|
+
}
|
|
159
|
+
throw new Error(formatNoApiKeyFoundMessage(model.provider));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Install tool hooks once on the Agent instance.
|
|
163
|
+
*
|
|
164
|
+
* The callbacks read `this._extensionRunner` at execution time, so extension reload swaps in the
|
|
165
|
+
* new runner without reinstalling hooks. Extension-specific tool wrappers are still used to adapt
|
|
166
|
+
* registered tool execution to the extension context. Tool call and tool result interception now
|
|
167
|
+
* happens here instead of in wrappers.
|
|
168
|
+
*/
|
|
169
|
+
_installAgentToolHooks() {
|
|
170
|
+
this.agent.beforeToolCall = async ({ toolCall, args }) => {
|
|
171
|
+
const runner = this._extensionRunner;
|
|
172
|
+
if (!runner.hasHandlers("tool_call")) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
await this._agentEventQueue;
|
|
176
|
+
try {
|
|
177
|
+
return await runner.emitToolCall({
|
|
178
|
+
type: "tool_call",
|
|
179
|
+
toolName: toolCall.name,
|
|
180
|
+
toolCallId: toolCall.id,
|
|
181
|
+
input: args,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
if (err instanceof Error) {
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
throw new Error(`Extension failed, blocking execution: ${String(err)}`);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
this.agent.afterToolCall = async ({ toolCall, args, result, isError }) => {
|
|
192
|
+
const runner = this._extensionRunner;
|
|
193
|
+
if (!runner.hasHandlers("tool_result")) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
const hookResult = await runner.emitToolResult({
|
|
197
|
+
type: "tool_result",
|
|
198
|
+
toolName: toolCall.name,
|
|
199
|
+
toolCallId: toolCall.id,
|
|
200
|
+
input: args,
|
|
201
|
+
content: result.content,
|
|
202
|
+
details: result.details,
|
|
203
|
+
isError,
|
|
204
|
+
});
|
|
205
|
+
if (!hookResult) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
content: hookResult.content,
|
|
210
|
+
details: hookResult.details,
|
|
211
|
+
isError: hookResult.isError ?? isError,
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
}
|
|
129
215
|
// =========================================================================
|
|
130
216
|
// Event Subscription
|
|
131
217
|
// =========================================================================
|
|
@@ -135,6 +221,27 @@ export class AgentSession {
|
|
|
135
221
|
l(event);
|
|
136
222
|
}
|
|
137
223
|
}
|
|
224
|
+
_emitQueueUpdate() {
|
|
225
|
+
this._emit({
|
|
226
|
+
type: "queue_update",
|
|
227
|
+
steering: [...this._steeringMessages],
|
|
228
|
+
followUp: [...this._followUpMessages],
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// Track last assistant message for auto-compaction check
|
|
232
|
+
_lastAssistantMessage = undefined;
|
|
233
|
+
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
234
|
+
_handleAgentEvent = (event) => {
|
|
235
|
+
// Create retry promise synchronously before queueing async processing.
|
|
236
|
+
// Agent.emit() calls this handler synchronously, and prompt() calls waitForRetry()
|
|
237
|
+
// as soon as agent.prompt() resolves. If _retryPromise is created only inside
|
|
238
|
+
// _processAgentEvent, slow earlier queued events can delay agent_end processing
|
|
239
|
+
// and waitForRetry() can miss the in-flight retry.
|
|
240
|
+
this._createRetryPromiseForAgentEnd(event);
|
|
241
|
+
this._agentEventQueue = this._agentEventQueue.then(() => this._processAgentEvent(event), () => this._processAgentEvent(event));
|
|
242
|
+
// Keep queue alive if an event handler fails
|
|
243
|
+
this._agentEventQueue.catch(() => { });
|
|
244
|
+
};
|
|
138
245
|
_createRetryPromiseForAgentEnd(event) {
|
|
139
246
|
if (event.type !== "agent_end" || this._retryPromise) {
|
|
140
247
|
return;
|
|
@@ -171,12 +278,14 @@ export class AgentSession {
|
|
|
171
278
|
const steeringIndex = this._steeringMessages.indexOf(messageText);
|
|
172
279
|
if (steeringIndex !== -1) {
|
|
173
280
|
this._steeringMessages.splice(steeringIndex, 1);
|
|
281
|
+
this._emitQueueUpdate();
|
|
174
282
|
}
|
|
175
283
|
else {
|
|
176
284
|
// Check follow-up queue
|
|
177
285
|
const followUpIndex = this._followUpMessages.indexOf(messageText);
|
|
178
286
|
if (followUpIndex !== -1) {
|
|
179
287
|
this._followUpMessages.splice(followUpIndex, 1);
|
|
288
|
+
this._emitQueueUpdate();
|
|
180
289
|
}
|
|
181
290
|
}
|
|
182
291
|
}
|
|
@@ -215,7 +324,6 @@ export class AgentSession {
|
|
|
215
324
|
attempt: this._retryAttempt,
|
|
216
325
|
});
|
|
217
326
|
this._retryAttempt = 0;
|
|
218
|
-
this._resolveRetry();
|
|
219
327
|
}
|
|
220
328
|
}
|
|
221
329
|
}
|
|
@@ -229,6 +337,7 @@ export class AgentSession {
|
|
|
229
337
|
if (didRetry)
|
|
230
338
|
return; // Retry was initiated, don't proceed to compaction
|
|
231
339
|
}
|
|
340
|
+
this._resolveRetry();
|
|
232
341
|
await this._checkCompaction(msg);
|
|
233
342
|
}
|
|
234
343
|
}
|
|
@@ -261,10 +370,22 @@ export class AgentSession {
|
|
|
261
370
|
}
|
|
262
371
|
return undefined;
|
|
263
372
|
}
|
|
373
|
+
_replaceMessageInPlace(target, replacement) {
|
|
374
|
+
// Agent-core stores the finalized message object in its state before emitting message_end.
|
|
375
|
+
// SessionManager persistence happens later in _processAgentEvent() with event.message.
|
|
376
|
+
// Mutating this object in place keeps agent state, later turn/agent events, listeners,
|
|
377
|
+
// and the eventual SessionManager.appendMessage(event.message) persistence in sync.
|
|
378
|
+
if (target === replacement) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const targetRecord = target;
|
|
382
|
+
for (const key of Object.keys(targetRecord)) {
|
|
383
|
+
delete targetRecord[key];
|
|
384
|
+
}
|
|
385
|
+
Object.assign(targetRecord, replacement);
|
|
386
|
+
}
|
|
264
387
|
/** Emit extension events based on agent events */
|
|
265
388
|
async _emitExtensionEvent(event) {
|
|
266
|
-
if (!this._extensionRunner)
|
|
267
|
-
return;
|
|
268
389
|
if (event.type === "agent_start") {
|
|
269
390
|
this._turnIndex = 0;
|
|
270
391
|
await this._extensionRunner.emit({ type: "agent_start" });
|
|
@@ -310,7 +431,10 @@ export class AgentSession {
|
|
|
310
431
|
type: "message_end",
|
|
311
432
|
message: event.message,
|
|
312
433
|
};
|
|
313
|
-
await this._extensionRunner.
|
|
434
|
+
const replacement = await this._extensionRunner.emitMessageEnd(extensionEvent);
|
|
435
|
+
if (replacement) {
|
|
436
|
+
this._replaceMessageInPlace(event.message, replacement);
|
|
437
|
+
}
|
|
314
438
|
}
|
|
315
439
|
else if (event.type === "tool_execution_start") {
|
|
316
440
|
const extensionEvent = {
|
|
@@ -382,8 +506,10 @@ export class AgentSession {
|
|
|
382
506
|
* Call this when completely done with the session.
|
|
383
507
|
*/
|
|
384
508
|
dispose() {
|
|
509
|
+
this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().");
|
|
385
510
|
this._disconnectFromAgent();
|
|
386
511
|
this._eventListeners = [];
|
|
512
|
+
cleanupSessionResources(this.sessionId);
|
|
387
513
|
}
|
|
388
514
|
// =========================================================================
|
|
389
515
|
// Read-only State Access
|
|
@@ -420,15 +546,19 @@ export class AgentSession {
|
|
|
420
546
|
return this.agent.state.tools.map((t) => t.name);
|
|
421
547
|
}
|
|
422
548
|
/**
|
|
423
|
-
* Get all configured tools with name, description, and
|
|
549
|
+
* Get all configured tools with name, description, parameter schema, and source metadata.
|
|
424
550
|
*/
|
|
425
551
|
getAllTools() {
|
|
426
|
-
return Array.from(this.
|
|
427
|
-
name:
|
|
428
|
-
description:
|
|
429
|
-
parameters:
|
|
552
|
+
return Array.from(this._toolDefinitions.values()).map(({ definition, sourceInfo }) => ({
|
|
553
|
+
name: definition.name,
|
|
554
|
+
description: definition.description,
|
|
555
|
+
parameters: definition.parameters,
|
|
556
|
+
sourceInfo,
|
|
430
557
|
}));
|
|
431
558
|
}
|
|
559
|
+
getToolDefinition(name) {
|
|
560
|
+
return this._toolDefinitions.get(name)?.definition;
|
|
561
|
+
}
|
|
432
562
|
/**
|
|
433
563
|
* Set active tools by name.
|
|
434
564
|
* Only tools in the registry can be enabled. Unknown tool names are ignored.
|
|
@@ -445,10 +575,10 @@ export class AgentSession {
|
|
|
445
575
|
validToolNames.push(name);
|
|
446
576
|
}
|
|
447
577
|
}
|
|
448
|
-
this.agent.
|
|
578
|
+
this.agent.state.tools = tools;
|
|
449
579
|
// Rebuild base system prompt with new tool set
|
|
450
580
|
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
451
|
-
this.agent.
|
|
581
|
+
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
452
582
|
}
|
|
453
583
|
/** Whether compaction or branch summarization is currently running */
|
|
454
584
|
get isCompacting() {
|
|
@@ -462,11 +592,11 @@ export class AgentSession {
|
|
|
462
592
|
}
|
|
463
593
|
/** Current steering mode */
|
|
464
594
|
get steeringMode() {
|
|
465
|
-
return this.agent.
|
|
595
|
+
return this.agent.steeringMode;
|
|
466
596
|
}
|
|
467
597
|
/** Current follow-up mode */
|
|
468
598
|
get followUpMode() {
|
|
469
|
-
return this.agent.
|
|
599
|
+
return this.agent.followUpMode;
|
|
470
600
|
}
|
|
471
601
|
/** Current session file path, or undefined if sessions are disabled */
|
|
472
602
|
get sessionFile() {
|
|
@@ -533,7 +663,7 @@ export class AgentSession {
|
|
|
533
663
|
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined;
|
|
534
664
|
const loadedSkills = this._resourceLoader.getSkills().skills;
|
|
535
665
|
const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
|
|
536
|
-
|
|
666
|
+
this._baseSystemPromptOptions = {
|
|
537
667
|
cwd: this._cwd,
|
|
538
668
|
skills: loadedSkills,
|
|
539
669
|
contextFiles: loadedContextFiles,
|
|
@@ -542,7 +672,8 @@ export class AgentSession {
|
|
|
542
672
|
selectedTools: validToolNames,
|
|
543
673
|
toolSnippets,
|
|
544
674
|
promptGuidelines,
|
|
545
|
-
}
|
|
675
|
+
};
|
|
676
|
+
return buildSystemPrompt(this._baseSystemPromptOptions);
|
|
546
677
|
}
|
|
547
678
|
// =========================================================================
|
|
548
679
|
// Prompting
|
|
@@ -558,92 +689,92 @@ export class AgentSession {
|
|
|
558
689
|
*/
|
|
559
690
|
async prompt(text, options) {
|
|
560
691
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (this._extensionRunner?.hasHandlers("input")) {
|
|
574
|
-
const inputResult = await this._extensionRunner.emitInput(currentText, currentImages, options?.source ?? "interactive");
|
|
575
|
-
if (inputResult.action === "handled") {
|
|
576
|
-
return;
|
|
692
|
+
const preflightResult = options?.preflightResult;
|
|
693
|
+
let messages;
|
|
694
|
+
try {
|
|
695
|
+
// Handle extension commands first (execute immediately, even during streaming)
|
|
696
|
+
// Extension commands manage their own LLM interaction via pi.sendMessage()
|
|
697
|
+
if (expandPromptTemplates && text.startsWith("/")) {
|
|
698
|
+
const handled = await this._tryExecuteExtensionCommand(text);
|
|
699
|
+
if (handled) {
|
|
700
|
+
// Extension command executed, no prompt to send
|
|
701
|
+
preflightResult?.(true);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
577
704
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
705
|
+
// Emit input event for extension interception (before skill/template expansion)
|
|
706
|
+
let currentText = text;
|
|
707
|
+
let currentImages = options?.images;
|
|
708
|
+
if (this._extensionRunner.hasHandlers("input")) {
|
|
709
|
+
const inputResult = await this._extensionRunner.emitInput(currentText, currentImages, options?.source ?? "interactive");
|
|
710
|
+
if (inputResult.action === "handled") {
|
|
711
|
+
preflightResult?.(true);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (inputResult.action === "transform") {
|
|
715
|
+
currentText = inputResult.text;
|
|
716
|
+
currentImages = inputResult.images ?? currentImages;
|
|
717
|
+
}
|
|
581
718
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
588
|
-
}
|
|
589
|
-
// If streaming, queue via steer() or followUp() based on option
|
|
590
|
-
if (this.isStreaming) {
|
|
591
|
-
if (!options?.streamingBehavior) {
|
|
592
|
-
throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
|
|
719
|
+
// Expand skill commands (/skill:name args) and prompt templates (/template args)
|
|
720
|
+
let expandedText = currentText;
|
|
721
|
+
if (expandPromptTemplates) {
|
|
722
|
+
expandedText = this._expandSkillCommand(expandedText);
|
|
723
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
593
724
|
}
|
|
594
|
-
|
|
595
|
-
|
|
725
|
+
// If streaming, queue via steer() or followUp() based on option
|
|
726
|
+
if (this.isStreaming) {
|
|
727
|
+
if (!options?.streamingBehavior) {
|
|
728
|
+
throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
|
|
729
|
+
}
|
|
730
|
+
if (options.streamingBehavior === "followUp") {
|
|
731
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
await this._queueSteer(expandedText, currentImages);
|
|
735
|
+
}
|
|
736
|
+
preflightResult?.(true);
|
|
737
|
+
return;
|
|
596
738
|
}
|
|
597
|
-
|
|
598
|
-
|
|
739
|
+
// Flush any pending bash messages before the new prompt
|
|
740
|
+
this._flushPendingBashMessages();
|
|
741
|
+
// Validate model
|
|
742
|
+
if (!this.model) {
|
|
743
|
+
throw new Error(formatNoModelSelectedMessage());
|
|
744
|
+
}
|
|
745
|
+
if (!this._modelRegistry.hasConfiguredAuth(this.model)) {
|
|
746
|
+
const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
|
|
747
|
+
if (isOAuth) {
|
|
748
|
+
throw new Error(`Authentication failed for "${this.model.provider}". ` +
|
|
749
|
+
`Credentials may have expired or network is unavailable. ` +
|
|
750
|
+
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
751
|
+
}
|
|
752
|
+
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
753
|
+
}
|
|
754
|
+
// Check if we need to compact before sending (catches aborted responses)
|
|
755
|
+
const lastAssistant = this._findLastAssistantMessage();
|
|
756
|
+
if (lastAssistant) {
|
|
757
|
+
await this._checkCompaction(lastAssistant, false);
|
|
758
|
+
}
|
|
759
|
+
// Build messages array (custom message if any, then user message)
|
|
760
|
+
messages = [];
|
|
761
|
+
// Add user message
|
|
762
|
+
const userContent = [{ type: "text", text: expandedText }];
|
|
763
|
+
if (currentImages) {
|
|
764
|
+
userContent.push(...currentImages);
|
|
765
|
+
}
|
|
766
|
+
messages.push({
|
|
767
|
+
role: "user",
|
|
768
|
+
content: userContent,
|
|
769
|
+
timestamp: Date.now(),
|
|
770
|
+
});
|
|
771
|
+
// Inject any pending "nextTurn" messages as context alongside the user message
|
|
772
|
+
for (const msg of this._pendingNextTurnMessages) {
|
|
773
|
+
messages.push(msg);
|
|
599
774
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
this._flushPendingBashMessages();
|
|
604
|
-
// Validate model
|
|
605
|
-
if (!this.model) {
|
|
606
|
-
throw new Error("No model selected.\n\n" +
|
|
607
|
-
`Use /login or set an API key environment variable. See ${join(getDocsPath(), "providers.md")}\n\n` +
|
|
608
|
-
"Then use /model to select a model.");
|
|
609
|
-
}
|
|
610
|
-
// Validate API key
|
|
611
|
-
const apiKey = await this._modelRegistry.getApiKey(this.model);
|
|
612
|
-
if (!apiKey) {
|
|
613
|
-
const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
|
|
614
|
-
if (isOAuth) {
|
|
615
|
-
throw new Error(`Authentication failed for "${this.model.provider}". ` +
|
|
616
|
-
`Credentials may have expired or network is unavailable. ` +
|
|
617
|
-
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
618
|
-
}
|
|
619
|
-
throw new Error(`No API key found for ${this.model.provider}.\n\n` +
|
|
620
|
-
`Use /login or set an API key environment variable. See ${join(getDocsPath(), "providers.md")}`);
|
|
621
|
-
}
|
|
622
|
-
// Check if we need to compact before sending (catches aborted responses)
|
|
623
|
-
const lastAssistant = this._findLastAssistantMessage();
|
|
624
|
-
if (lastAssistant) {
|
|
625
|
-
await this._checkCompaction(lastAssistant, false);
|
|
626
|
-
}
|
|
627
|
-
// Build messages array (custom message if any, then user message)
|
|
628
|
-
const messages = [];
|
|
629
|
-
// Add user message
|
|
630
|
-
const userContent = [{ type: "text", text: expandedText }];
|
|
631
|
-
if (currentImages) {
|
|
632
|
-
userContent.push(...currentImages);
|
|
633
|
-
}
|
|
634
|
-
messages.push({
|
|
635
|
-
role: "user",
|
|
636
|
-
content: userContent,
|
|
637
|
-
timestamp: Date.now(),
|
|
638
|
-
});
|
|
639
|
-
// Inject any pending "nextTurn" messages as context alongside the user message
|
|
640
|
-
for (const msg of this._pendingNextTurnMessages) {
|
|
641
|
-
messages.push(msg);
|
|
642
|
-
}
|
|
643
|
-
this._pendingNextTurnMessages = [];
|
|
644
|
-
// Emit before_agent_start extension event
|
|
645
|
-
if (this._extensionRunner) {
|
|
646
|
-
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPrompt);
|
|
775
|
+
this._pendingNextTurnMessages = [];
|
|
776
|
+
// Emit before_agent_start extension event
|
|
777
|
+
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPrompt, this._baseSystemPromptOptions);
|
|
647
778
|
// Add all custom messages from extensions
|
|
648
779
|
if (result?.messages) {
|
|
649
780
|
for (const msg of result.messages) {
|
|
@@ -659,13 +790,21 @@ export class AgentSession {
|
|
|
659
790
|
}
|
|
660
791
|
// Apply extension-modified system prompt, or reset to base
|
|
661
792
|
if (result?.systemPrompt) {
|
|
662
|
-
this.agent.
|
|
793
|
+
this.agent.state.systemPrompt = result.systemPrompt;
|
|
663
794
|
}
|
|
664
795
|
else {
|
|
665
796
|
// Ensure we're using the base prompt (in case previous turn had modifications)
|
|
666
|
-
this.agent.
|
|
797
|
+
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
667
798
|
}
|
|
668
799
|
}
|
|
800
|
+
catch (error) {
|
|
801
|
+
preflightResult?.(false);
|
|
802
|
+
throw error;
|
|
803
|
+
}
|
|
804
|
+
if (!messages) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
preflightResult?.(true);
|
|
669
808
|
await this.agent.prompt(messages);
|
|
670
809
|
await this.waitForRetry();
|
|
671
810
|
}
|
|
@@ -673,8 +812,6 @@ export class AgentSession {
|
|
|
673
812
|
* Try to execute an extension command. Returns true if command was found and executed.
|
|
674
813
|
*/
|
|
675
814
|
async _tryExecuteExtensionCommand(text) {
|
|
676
|
-
if (!this._extensionRunner)
|
|
677
|
-
return false;
|
|
678
815
|
// Parse command name and args
|
|
679
816
|
const spaceIndex = text.indexOf(" ");
|
|
680
817
|
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
@@ -720,7 +857,7 @@ export class AgentSession {
|
|
|
720
857
|
}
|
|
721
858
|
catch (err) {
|
|
722
859
|
// Emit error like extension commands do
|
|
723
|
-
this._extensionRunner
|
|
860
|
+
this._extensionRunner.emitError({
|
|
724
861
|
extensionPath: skill.filePath,
|
|
725
862
|
event: "skill_expansion",
|
|
726
863
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -729,8 +866,9 @@ export class AgentSession {
|
|
|
729
866
|
}
|
|
730
867
|
}
|
|
731
868
|
/**
|
|
732
|
-
* Queue a steering message
|
|
733
|
-
* Delivered after current
|
|
869
|
+
* Queue a steering message while the agent is running.
|
|
870
|
+
* Delivered after the current assistant turn finishes executing its tool calls,
|
|
871
|
+
* before the next LLM call.
|
|
734
872
|
* Expands skill commands and prompt templates. Errors on extension commands.
|
|
735
873
|
* @param images Optional image attachments to include with the message
|
|
736
874
|
* @throws Error if text is an extension command
|
|
@@ -767,6 +905,7 @@ export class AgentSession {
|
|
|
767
905
|
*/
|
|
768
906
|
async _queueSteer(text, images) {
|
|
769
907
|
this._steeringMessages.push(text);
|
|
908
|
+
this._emitQueueUpdate();
|
|
770
909
|
const content = [{ type: "text", text }];
|
|
771
910
|
if (images) {
|
|
772
911
|
content.push(...images);
|
|
@@ -782,6 +921,7 @@ export class AgentSession {
|
|
|
782
921
|
*/
|
|
783
922
|
async _queueFollowUp(text, images) {
|
|
784
923
|
this._followUpMessages.push(text);
|
|
924
|
+
this._emitQueueUpdate();
|
|
785
925
|
const content = [{ type: "text", text }];
|
|
786
926
|
if (images) {
|
|
787
927
|
content.push(...images);
|
|
@@ -796,8 +936,6 @@ export class AgentSession {
|
|
|
796
936
|
* Throw an error if the text is an extension command.
|
|
797
937
|
*/
|
|
798
938
|
_throwIfExtensionCommand(text) {
|
|
799
|
-
if (!this._extensionRunner)
|
|
800
|
-
return;
|
|
801
939
|
const spaceIndex = text.indexOf(" ");
|
|
802
940
|
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
803
941
|
const command = this._extensionRunner.getCommand(commandName);
|
|
@@ -841,7 +979,7 @@ export class AgentSession {
|
|
|
841
979
|
await this.agent.prompt(appMessage);
|
|
842
980
|
}
|
|
843
981
|
else {
|
|
844
|
-
this.agent.
|
|
982
|
+
this.agent.state.messages.push(appMessage);
|
|
845
983
|
this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
|
|
846
984
|
this._emit({ type: "message_start", message: appMessage });
|
|
847
985
|
this._emit({ type: "message_end", message: appMessage });
|
|
@@ -895,6 +1033,7 @@ export class AgentSession {
|
|
|
895
1033
|
this._steeringMessages = [];
|
|
896
1034
|
this._followUpMessages = [];
|
|
897
1035
|
this.agent.clearAllQueues();
|
|
1036
|
+
this._emitQueueUpdate();
|
|
898
1037
|
return { steering, followUp };
|
|
899
1038
|
}
|
|
900
1039
|
/** Number of pending messages (includes both steering and follow-up) */
|
|
@@ -920,60 +1059,10 @@ export class AgentSession {
|
|
|
920
1059
|
this.agent.abort();
|
|
921
1060
|
await this.agent.waitForIdle();
|
|
922
1061
|
}
|
|
923
|
-
/**
|
|
924
|
-
* Start a new session, optionally with initial messages and parent tracking.
|
|
925
|
-
* Clears all messages and starts a new session.
|
|
926
|
-
* Listeners are preserved and will continue receiving events.
|
|
927
|
-
* @param options.parentSession - Optional parent session path for tracking
|
|
928
|
-
* @param options.setup - Optional callback to initialize session (e.g., append messages)
|
|
929
|
-
* @returns true if completed, false if cancelled by extension
|
|
930
|
-
*/
|
|
931
|
-
async newSession(options) {
|
|
932
|
-
const previousSessionFile = this.sessionFile;
|
|
933
|
-
// Emit session_before_switch event with reason "new" (can be cancelled)
|
|
934
|
-
if (this._extensionRunner?.hasHandlers("session_before_switch")) {
|
|
935
|
-
const result = (await this._extensionRunner.emit({
|
|
936
|
-
type: "session_before_switch",
|
|
937
|
-
reason: "new",
|
|
938
|
-
}));
|
|
939
|
-
if (result?.cancel) {
|
|
940
|
-
return false;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
this._disconnectFromAgent();
|
|
944
|
-
await this.abort();
|
|
945
|
-
this.agent.reset();
|
|
946
|
-
this.sessionManager.newSession({ parentSession: options?.parentSession });
|
|
947
|
-
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
948
|
-
this._steeringMessages = [];
|
|
949
|
-
this._followUpMessages = [];
|
|
950
|
-
this._pendingNextTurnMessages = [];
|
|
951
|
-
this.sessionManager.appendThinkingLevelChange(this.thinkingLevel);
|
|
952
|
-
// Run setup callback if provided (e.g., to append initial messages)
|
|
953
|
-
if (options?.setup) {
|
|
954
|
-
await options.setup(this.sessionManager);
|
|
955
|
-
// Sync agent state with session manager after setup
|
|
956
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
957
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
958
|
-
}
|
|
959
|
-
this._reconnectToAgent();
|
|
960
|
-
// Emit session_switch event with reason "new" to extensions
|
|
961
|
-
if (this._extensionRunner) {
|
|
962
|
-
await this._extensionRunner.emit({
|
|
963
|
-
type: "session_switch",
|
|
964
|
-
reason: "new",
|
|
965
|
-
previousSessionFile,
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
// Emit session event to custom tools
|
|
969
|
-
return true;
|
|
970
|
-
}
|
|
971
1062
|
// =========================================================================
|
|
972
1063
|
// Model Management
|
|
973
1064
|
// =========================================================================
|
|
974
1065
|
async _emitModelSelect(nextModel, previousModel, source) {
|
|
975
|
-
if (!this._extensionRunner)
|
|
976
|
-
return;
|
|
977
1066
|
if (modelsAreEqual(previousModel, nextModel))
|
|
978
1067
|
return;
|
|
979
1068
|
await this._extensionRunner.emit({
|
|
@@ -985,17 +1074,16 @@ export class AgentSession {
|
|
|
985
1074
|
}
|
|
986
1075
|
/**
|
|
987
1076
|
* Set model directly.
|
|
988
|
-
* Validates
|
|
989
|
-
* @throws Error if no
|
|
1077
|
+
* Validates that auth is configured, saves to session and settings.
|
|
1078
|
+
* @throws Error if no auth is configured for the model
|
|
990
1079
|
*/
|
|
991
1080
|
async setModel(model) {
|
|
992
|
-
|
|
993
|
-
if (!apiKey) {
|
|
1081
|
+
if (!this._modelRegistry.hasConfiguredAuth(model)) {
|
|
994
1082
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
995
1083
|
}
|
|
996
1084
|
const previousModel = this.model;
|
|
997
1085
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
998
|
-
this.agent.
|
|
1086
|
+
this.agent.state.model = model;
|
|
999
1087
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1000
1088
|
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1001
1089
|
// Re-clamp thinking level for new model's capabilities
|
|
@@ -1014,27 +1102,8 @@ export class AgentSession {
|
|
|
1014
1102
|
}
|
|
1015
1103
|
return this._cycleAvailableModel(direction);
|
|
1016
1104
|
}
|
|
1017
|
-
async _getScopedModelsWithApiKey() {
|
|
1018
|
-
const apiKeysByProvider = new Map();
|
|
1019
|
-
const result = [];
|
|
1020
|
-
for (const scoped of this._scopedModels) {
|
|
1021
|
-
const provider = scoped.model.provider;
|
|
1022
|
-
let apiKey;
|
|
1023
|
-
if (apiKeysByProvider.has(provider)) {
|
|
1024
|
-
apiKey = apiKeysByProvider.get(provider);
|
|
1025
|
-
}
|
|
1026
|
-
else {
|
|
1027
|
-
apiKey = await this._modelRegistry.getApiKeyForProvider(provider);
|
|
1028
|
-
apiKeysByProvider.set(provider, apiKey);
|
|
1029
|
-
}
|
|
1030
|
-
if (apiKey) {
|
|
1031
|
-
result.push(scoped);
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
return result;
|
|
1035
|
-
}
|
|
1036
1105
|
async _cycleScopedModel(direction) {
|
|
1037
|
-
const scopedModels =
|
|
1106
|
+
const scopedModels = this._scopedModels.filter((scoped) => this._modelRegistry.hasConfiguredAuth(scoped.model));
|
|
1038
1107
|
if (scopedModels.length <= 1)
|
|
1039
1108
|
return undefined;
|
|
1040
1109
|
const currentModel = this.model;
|
|
@@ -1046,7 +1115,7 @@ export class AgentSession {
|
|
|
1046
1115
|
const next = scopedModels[nextIndex];
|
|
1047
1116
|
const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
|
|
1048
1117
|
// Apply model
|
|
1049
|
-
this.agent.
|
|
1118
|
+
this.agent.state.model = next.model;
|
|
1050
1119
|
this.sessionManager.appendModelChange(next.model.provider, next.model.id);
|
|
1051
1120
|
this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
|
|
1052
1121
|
// Apply thinking level.
|
|
@@ -1068,12 +1137,8 @@ export class AgentSession {
|
|
|
1068
1137
|
const len = availableModels.length;
|
|
1069
1138
|
const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
|
|
1070
1139
|
const nextModel = availableModels[nextIndex];
|
|
1071
|
-
const apiKey = await this._modelRegistry.getApiKey(nextModel);
|
|
1072
|
-
if (!apiKey) {
|
|
1073
|
-
throw new Error(`No API key for ${nextModel.provider}/${nextModel.id}`);
|
|
1074
|
-
}
|
|
1075
1140
|
const thinkingLevel = this._getThinkingLevelForModelSwitch();
|
|
1076
|
-
this.agent.
|
|
1141
|
+
this.agent.state.model = nextModel;
|
|
1077
1142
|
this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
|
|
1078
1143
|
this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
|
|
1079
1144
|
// Re-clamp thinking level for new model's capabilities
|
|
@@ -1093,13 +1158,20 @@ export class AgentSession {
|
|
|
1093
1158
|
const availableLevels = this.getAvailableThinkingLevels();
|
|
1094
1159
|
const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
|
|
1095
1160
|
// Only persist if actually changing
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1161
|
+
const previousLevel = this.agent.state.thinkingLevel;
|
|
1162
|
+
const isChanging = effectiveLevel !== previousLevel;
|
|
1163
|
+
this.agent.state.thinkingLevel = effectiveLevel;
|
|
1098
1164
|
if (isChanging) {
|
|
1099
1165
|
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
1100
1166
|
if (this.supportsThinking() || effectiveLevel !== "off") {
|
|
1101
1167
|
this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
|
|
1102
1168
|
}
|
|
1169
|
+
this._emit({ type: "thinking_level_changed", level: effectiveLevel });
|
|
1170
|
+
void this._extensionRunner.emit({
|
|
1171
|
+
type: "thinking_level_select",
|
|
1172
|
+
level: effectiveLevel,
|
|
1173
|
+
previousLevel,
|
|
1174
|
+
});
|
|
1103
1175
|
}
|
|
1104
1176
|
}
|
|
1105
1177
|
/**
|
|
@@ -1121,15 +1193,9 @@ export class AgentSession {
|
|
|
1121
1193
|
* The provider will clamp to what the specific model supports internally.
|
|
1122
1194
|
*/
|
|
1123
1195
|
getAvailableThinkingLevels() {
|
|
1124
|
-
if (!this.
|
|
1125
|
-
return
|
|
1126
|
-
return this.
|
|
1127
|
-
}
|
|
1128
|
-
/**
|
|
1129
|
-
* Check if current model supports xhigh thinking level.
|
|
1130
|
-
*/
|
|
1131
|
-
supportsXhighThinking() {
|
|
1132
|
-
return this.model ? supportsXhigh(this.model) : false;
|
|
1196
|
+
if (!this.model)
|
|
1197
|
+
return THINKING_LEVELS;
|
|
1198
|
+
return getSupportedThinkingLevels(this.model);
|
|
1133
1199
|
}
|
|
1134
1200
|
/**
|
|
1135
1201
|
* Check if current model supports thinking/reasoning.
|
|
@@ -1146,24 +1212,8 @@ export class AgentSession {
|
|
|
1146
1212
|
}
|
|
1147
1213
|
return this.thinkingLevel;
|
|
1148
1214
|
}
|
|
1149
|
-
_clampThinkingLevel(level,
|
|
1150
|
-
|
|
1151
|
-
const available = new Set(availableLevels);
|
|
1152
|
-
const requestedIndex = ordered.indexOf(level);
|
|
1153
|
-
if (requestedIndex === -1) {
|
|
1154
|
-
return availableLevels[0] ?? "off";
|
|
1155
|
-
}
|
|
1156
|
-
for (let i = requestedIndex; i < ordered.length; i++) {
|
|
1157
|
-
const candidate = ordered[i];
|
|
1158
|
-
if (available.has(candidate))
|
|
1159
|
-
return candidate;
|
|
1160
|
-
}
|
|
1161
|
-
for (let i = requestedIndex - 1; i >= 0; i--) {
|
|
1162
|
-
const candidate = ordered[i];
|
|
1163
|
-
if (available.has(candidate))
|
|
1164
|
-
return candidate;
|
|
1165
|
-
}
|
|
1166
|
-
return availableLevels[0] ?? "off";
|
|
1215
|
+
_clampThinkingLevel(level, _availableLevels) {
|
|
1216
|
+
return this.model ? clampThinkingLevel(this.model, level) : "off";
|
|
1167
1217
|
}
|
|
1168
1218
|
// =========================================================================
|
|
1169
1219
|
// Queue Mode Management
|
|
@@ -1173,7 +1223,7 @@ export class AgentSession {
|
|
|
1173
1223
|
* Saves to settings.
|
|
1174
1224
|
*/
|
|
1175
1225
|
setSteeringMode(mode) {
|
|
1176
|
-
this.agent.
|
|
1226
|
+
this.agent.steeringMode = mode;
|
|
1177
1227
|
this.settingsManager.setSteeringMode(mode);
|
|
1178
1228
|
}
|
|
1179
1229
|
/**
|
|
@@ -1181,7 +1231,7 @@ export class AgentSession {
|
|
|
1181
1231
|
* Saves to settings.
|
|
1182
1232
|
*/
|
|
1183
1233
|
setFollowUpMode(mode) {
|
|
1184
|
-
this.agent.
|
|
1234
|
+
this.agent.followUpMode = mode;
|
|
1185
1235
|
this.settingsManager.setFollowUpMode(mode);
|
|
1186
1236
|
}
|
|
1187
1237
|
// =========================================================================
|
|
@@ -1196,14 +1246,12 @@ export class AgentSession {
|
|
|
1196
1246
|
this._disconnectFromAgent();
|
|
1197
1247
|
await this.abort();
|
|
1198
1248
|
this._compactionAbortController = new AbortController();
|
|
1249
|
+
this._emit({ type: "compaction_start", reason: "manual" });
|
|
1199
1250
|
try {
|
|
1200
1251
|
if (!this.model) {
|
|
1201
|
-
throw new Error(
|
|
1202
|
-
}
|
|
1203
|
-
const apiKey = await this._modelRegistry.getApiKey(this.model);
|
|
1204
|
-
if (!apiKey) {
|
|
1205
|
-
throw new Error(`No API key for ${this.model.provider}`);
|
|
1252
|
+
throw new Error(formatNoModelSelectedMessage());
|
|
1206
1253
|
}
|
|
1254
|
+
const { apiKey, headers } = await this._getRequiredRequestAuth(this.model);
|
|
1207
1255
|
const pathEntries = this.sessionManager.getBranch();
|
|
1208
1256
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1209
1257
|
const preparation = prepareCompaction(pathEntries, settings);
|
|
@@ -1217,7 +1265,7 @@ export class AgentSession {
|
|
|
1217
1265
|
}
|
|
1218
1266
|
let extensionCompaction;
|
|
1219
1267
|
let fromExtension = false;
|
|
1220
|
-
if (this._extensionRunner
|
|
1268
|
+
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1221
1269
|
const result = (await this._extensionRunner.emit({
|
|
1222
1270
|
type: "session_before_compact",
|
|
1223
1271
|
preparation,
|
|
@@ -1246,7 +1294,7 @@ export class AgentSession {
|
|
|
1246
1294
|
}
|
|
1247
1295
|
else {
|
|
1248
1296
|
// Generate compaction result
|
|
1249
|
-
const result = await compact(preparation, this.model, apiKey, customInstructions, this._compactionAbortController.signal);
|
|
1297
|
+
const result = await compact(preparation, this.model, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel);
|
|
1250
1298
|
summary = result.summary;
|
|
1251
1299
|
firstKeptEntryId = result.firstKeptEntryId;
|
|
1252
1300
|
tokensBefore = result.tokensBefore;
|
|
@@ -1258,7 +1306,7 @@ export class AgentSession {
|
|
|
1258
1306
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
|
|
1259
1307
|
const newEntries = this.sessionManager.getEntries();
|
|
1260
1308
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1261
|
-
this.agent.
|
|
1309
|
+
this.agent.state.messages = sessionContext.messages;
|
|
1262
1310
|
// Get the saved compaction entry for the extension event
|
|
1263
1311
|
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
1264
1312
|
if (this._extensionRunner && savedCompactionEntry) {
|
|
@@ -1268,12 +1316,33 @@ export class AgentSession {
|
|
|
1268
1316
|
fromExtension,
|
|
1269
1317
|
});
|
|
1270
1318
|
}
|
|
1271
|
-
|
|
1319
|
+
const compactionResult = {
|
|
1272
1320
|
summary,
|
|
1273
1321
|
firstKeptEntryId,
|
|
1274
1322
|
tokensBefore,
|
|
1275
1323
|
details,
|
|
1276
1324
|
};
|
|
1325
|
+
this._emit({
|
|
1326
|
+
type: "compaction_end",
|
|
1327
|
+
reason: "manual",
|
|
1328
|
+
result: compactionResult,
|
|
1329
|
+
aborted: false,
|
|
1330
|
+
willRetry: false,
|
|
1331
|
+
});
|
|
1332
|
+
return compactionResult;
|
|
1333
|
+
}
|
|
1334
|
+
catch (error) {
|
|
1335
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1336
|
+
const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
|
|
1337
|
+
this._emit({
|
|
1338
|
+
type: "compaction_end",
|
|
1339
|
+
reason: "manual",
|
|
1340
|
+
result: undefined,
|
|
1341
|
+
aborted,
|
|
1342
|
+
willRetry: false,
|
|
1343
|
+
errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
|
|
1344
|
+
});
|
|
1345
|
+
throw error;
|
|
1277
1346
|
}
|
|
1278
1347
|
finally {
|
|
1279
1348
|
this._compactionAbortController = undefined;
|
|
@@ -1329,7 +1398,8 @@ export class AgentSession {
|
|
|
1329
1398
|
if (sameModel && isContextOverflow(assistantMessage, contextWindow)) {
|
|
1330
1399
|
if (this._overflowRecoveryAttempted) {
|
|
1331
1400
|
this._emit({
|
|
1332
|
-
type: "
|
|
1401
|
+
type: "compaction_end",
|
|
1402
|
+
reason: "overflow",
|
|
1333
1403
|
result: undefined,
|
|
1334
1404
|
aborted: false,
|
|
1335
1405
|
willRetry: false,
|
|
@@ -1342,7 +1412,7 @@ export class AgentSession {
|
|
|
1342
1412
|
// but we don't want it in context for the retry)
|
|
1343
1413
|
const messages = this.agent.state.messages;
|
|
1344
1414
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
1345
|
-
this.agent.
|
|
1415
|
+
this.agent.state.messages = messages.slice(0, -1);
|
|
1346
1416
|
}
|
|
1347
1417
|
await this._runAutoCompaction("overflow", true);
|
|
1348
1418
|
return;
|
|
@@ -1379,27 +1449,46 @@ export class AgentSession {
|
|
|
1379
1449
|
*/
|
|
1380
1450
|
async _runAutoCompaction(reason, willRetry) {
|
|
1381
1451
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1382
|
-
this._emit({ type: "
|
|
1452
|
+
this._emit({ type: "compaction_start", reason });
|
|
1383
1453
|
this._autoCompactionAbortController = new AbortController();
|
|
1384
1454
|
try {
|
|
1385
1455
|
if (!this.model) {
|
|
1386
|
-
this._emit({
|
|
1456
|
+
this._emit({
|
|
1457
|
+
type: "compaction_end",
|
|
1458
|
+
reason,
|
|
1459
|
+
result: undefined,
|
|
1460
|
+
aborted: false,
|
|
1461
|
+
willRetry: false,
|
|
1462
|
+
});
|
|
1387
1463
|
return;
|
|
1388
1464
|
}
|
|
1389
|
-
const
|
|
1390
|
-
if (!apiKey) {
|
|
1391
|
-
this._emit({
|
|
1465
|
+
const authResult = await this._modelRegistry.getApiKeyAndHeaders(this.model);
|
|
1466
|
+
if (!authResult.ok || !authResult.apiKey) {
|
|
1467
|
+
this._emit({
|
|
1468
|
+
type: "compaction_end",
|
|
1469
|
+
reason,
|
|
1470
|
+
result: undefined,
|
|
1471
|
+
aborted: false,
|
|
1472
|
+
willRetry: false,
|
|
1473
|
+
});
|
|
1392
1474
|
return;
|
|
1393
1475
|
}
|
|
1476
|
+
const { apiKey, headers } = authResult;
|
|
1394
1477
|
const pathEntries = this.sessionManager.getBranch();
|
|
1395
1478
|
const preparation = prepareCompaction(pathEntries, settings);
|
|
1396
1479
|
if (!preparation) {
|
|
1397
|
-
this._emit({
|
|
1480
|
+
this._emit({
|
|
1481
|
+
type: "compaction_end",
|
|
1482
|
+
reason,
|
|
1483
|
+
result: undefined,
|
|
1484
|
+
aborted: false,
|
|
1485
|
+
willRetry: false,
|
|
1486
|
+
});
|
|
1398
1487
|
return;
|
|
1399
1488
|
}
|
|
1400
1489
|
let extensionCompaction;
|
|
1401
1490
|
let fromExtension = false;
|
|
1402
|
-
if (this._extensionRunner
|
|
1491
|
+
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1403
1492
|
const extensionResult = (await this._extensionRunner.emit({
|
|
1404
1493
|
type: "session_before_compact",
|
|
1405
1494
|
preparation,
|
|
@@ -1408,7 +1497,13 @@ export class AgentSession {
|
|
|
1408
1497
|
signal: this._autoCompactionAbortController.signal,
|
|
1409
1498
|
}));
|
|
1410
1499
|
if (extensionResult?.cancel) {
|
|
1411
|
-
this._emit({
|
|
1500
|
+
this._emit({
|
|
1501
|
+
type: "compaction_end",
|
|
1502
|
+
reason,
|
|
1503
|
+
result: undefined,
|
|
1504
|
+
aborted: true,
|
|
1505
|
+
willRetry: false,
|
|
1506
|
+
});
|
|
1412
1507
|
return;
|
|
1413
1508
|
}
|
|
1414
1509
|
if (extensionResult?.compaction) {
|
|
@@ -1429,20 +1524,26 @@ export class AgentSession {
|
|
|
1429
1524
|
}
|
|
1430
1525
|
else {
|
|
1431
1526
|
// Generate compaction result
|
|
1432
|
-
const compactResult = await compact(preparation, this.model, apiKey, undefined, this._autoCompactionAbortController.signal);
|
|
1527
|
+
const compactResult = await compact(preparation, this.model, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel);
|
|
1433
1528
|
summary = compactResult.summary;
|
|
1434
1529
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
1435
1530
|
tokensBefore = compactResult.tokensBefore;
|
|
1436
1531
|
details = compactResult.details;
|
|
1437
1532
|
}
|
|
1438
1533
|
if (this._autoCompactionAbortController.signal.aborted) {
|
|
1439
|
-
this._emit({
|
|
1534
|
+
this._emit({
|
|
1535
|
+
type: "compaction_end",
|
|
1536
|
+
reason,
|
|
1537
|
+
result: undefined,
|
|
1538
|
+
aborted: true,
|
|
1539
|
+
willRetry: false,
|
|
1540
|
+
});
|
|
1440
1541
|
return;
|
|
1441
1542
|
}
|
|
1442
1543
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension);
|
|
1443
1544
|
const newEntries = this.sessionManager.getEntries();
|
|
1444
1545
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1445
|
-
this.agent.
|
|
1546
|
+
this.agent.state.messages = sessionContext.messages;
|
|
1446
1547
|
// Get the saved compaction entry for the extension event
|
|
1447
1548
|
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
1448
1549
|
if (this._extensionRunner && savedCompactionEntry) {
|
|
@@ -1458,12 +1559,12 @@ export class AgentSession {
|
|
|
1458
1559
|
tokensBefore,
|
|
1459
1560
|
details,
|
|
1460
1561
|
};
|
|
1461
|
-
this._emit({ type: "
|
|
1562
|
+
this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
|
|
1462
1563
|
if (willRetry) {
|
|
1463
1564
|
const messages = this.agent.state.messages;
|
|
1464
1565
|
const lastMsg = messages[messages.length - 1];
|
|
1465
1566
|
if (lastMsg?.role === "assistant" && lastMsg.stopReason === "error") {
|
|
1466
|
-
this.agent.
|
|
1567
|
+
this.agent.state.messages = messages.slice(0, -1);
|
|
1467
1568
|
}
|
|
1468
1569
|
setTimeout(() => {
|
|
1469
1570
|
this.agent.continue().catch(() => { });
|
|
@@ -1480,7 +1581,8 @@ export class AgentSession {
|
|
|
1480
1581
|
catch (error) {
|
|
1481
1582
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
1482
1583
|
this._emit({
|
|
1483
|
-
type: "
|
|
1584
|
+
type: "compaction_end",
|
|
1585
|
+
reason,
|
|
1484
1586
|
result: undefined,
|
|
1485
1587
|
aborted: false,
|
|
1486
1588
|
willRetry: false,
|
|
@@ -1516,14 +1618,12 @@ export class AgentSession {
|
|
|
1516
1618
|
if (bindings.onError !== undefined) {
|
|
1517
1619
|
this._extensionErrorListener = bindings.onError;
|
|
1518
1620
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
await this.extendResourcesFromExtensions("startup");
|
|
1523
|
-
}
|
|
1621
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
1622
|
+
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
1623
|
+
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
1524
1624
|
}
|
|
1525
1625
|
async extendResourcesFromExtensions(reason) {
|
|
1526
|
-
if (!this._extensionRunner
|
|
1626
|
+
if (!this._extensionRunner.hasHandlers("resources_discover")) {
|
|
1527
1627
|
return;
|
|
1528
1628
|
}
|
|
1529
1629
|
const { skillPaths, promptPaths, themePaths } = await this._extensionRunner.emitResourcesDiscover(this._cwd, reason);
|
|
@@ -1537,7 +1637,7 @@ export class AgentSession {
|
|
|
1537
1637
|
};
|
|
1538
1638
|
this._resourceLoader.extendResources(extensionPaths);
|
|
1539
1639
|
this._baseSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames());
|
|
1540
|
-
this.agent.
|
|
1640
|
+
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
1541
1641
|
}
|
|
1542
1642
|
buildExtensionResourcePaths(entries) {
|
|
1543
1643
|
return entries.map((entry) => {
|
|
@@ -1570,37 +1670,36 @@ export class AgentSession {
|
|
|
1570
1670
|
? runner.onError(this._extensionErrorListener)
|
|
1571
1671
|
: undefined;
|
|
1572
1672
|
}
|
|
1673
|
+
_refreshCurrentModelFromRegistry() {
|
|
1674
|
+
const currentModel = this.model;
|
|
1675
|
+
if (!currentModel) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const refreshedModel = this._modelRegistry.find(currentModel.provider, currentModel.id);
|
|
1679
|
+
if (!refreshedModel || refreshedModel === currentModel) {
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
this.agent.state.model = refreshedModel;
|
|
1683
|
+
}
|
|
1573
1684
|
_bindExtensionCore(runner) {
|
|
1574
|
-
const normalizeLocation = (source) => {
|
|
1575
|
-
if (source === "user" || source === "project" || source === "path") {
|
|
1576
|
-
return source;
|
|
1577
|
-
}
|
|
1578
|
-
return undefined;
|
|
1579
|
-
};
|
|
1580
|
-
const reservedBuiltins = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
|
|
1581
1685
|
const getCommands = () => {
|
|
1582
|
-
const extensionCommands = runner
|
|
1583
|
-
.
|
|
1584
|
-
.filter(({ command }) => !reservedBuiltins.has(command.name))
|
|
1585
|
-
.map(({ command, extensionPath }) => ({
|
|
1586
|
-
name: command.name,
|
|
1686
|
+
const extensionCommands = runner.getRegisteredCommands().map((command) => ({
|
|
1687
|
+
name: command.invocationName,
|
|
1587
1688
|
description: command.description,
|
|
1588
1689
|
source: "extension",
|
|
1589
|
-
|
|
1690
|
+
sourceInfo: command.sourceInfo,
|
|
1590
1691
|
}));
|
|
1591
1692
|
const templates = this.promptTemplates.map((template) => ({
|
|
1592
1693
|
name: template.name,
|
|
1593
1694
|
description: template.description,
|
|
1594
1695
|
source: "prompt",
|
|
1595
|
-
|
|
1596
|
-
path: template.filePath,
|
|
1696
|
+
sourceInfo: template.sourceInfo,
|
|
1597
1697
|
}));
|
|
1598
1698
|
const skills = this._resourceLoader.getSkills().skills.map((skill) => ({
|
|
1599
1699
|
name: `skill:${skill.name}`,
|
|
1600
1700
|
description: skill.description,
|
|
1601
1701
|
source: "skill",
|
|
1602
|
-
|
|
1603
|
-
path: skill.filePath,
|
|
1702
|
+
sourceInfo: skill.sourceInfo,
|
|
1604
1703
|
}));
|
|
1605
1704
|
return [...extensionCommands, ...templates, ...skills];
|
|
1606
1705
|
};
|
|
@@ -1627,7 +1726,7 @@ export class AgentSession {
|
|
|
1627
1726
|
this.sessionManager.appendCustomEntry(customType, data);
|
|
1628
1727
|
},
|
|
1629
1728
|
setSessionName: (name) => {
|
|
1630
|
-
this.
|
|
1729
|
+
this.setSessionName(name);
|
|
1631
1730
|
},
|
|
1632
1731
|
getSessionName: () => {
|
|
1633
1732
|
return this.sessionManager.getSessionName();
|
|
@@ -1641,8 +1740,7 @@ export class AgentSession {
|
|
|
1641
1740
|
refreshTools: () => this._refreshToolRegistry(),
|
|
1642
1741
|
getCommands,
|
|
1643
1742
|
setModel: async (model) => {
|
|
1644
|
-
|
|
1645
|
-
if (!key)
|
|
1743
|
+
if (!this.modelRegistry.hasConfiguredAuth(model))
|
|
1646
1744
|
return false;
|
|
1647
1745
|
await this.setModel(model);
|
|
1648
1746
|
return true;
|
|
@@ -1652,6 +1750,7 @@ export class AgentSession {
|
|
|
1652
1750
|
}, {
|
|
1653
1751
|
getModel: () => this.model,
|
|
1654
1752
|
isIdle: () => !this.isStreaming,
|
|
1753
|
+
getSignal: () => this.agent.signal,
|
|
1655
1754
|
abort: () => this.abort(),
|
|
1656
1755
|
hasPendingMessages: () => this.pendingMessageCount > 0,
|
|
1657
1756
|
shutdown: () => {
|
|
@@ -1671,46 +1770,80 @@ export class AgentSession {
|
|
|
1671
1770
|
})();
|
|
1672
1771
|
},
|
|
1673
1772
|
getSystemPrompt: () => this.systemPrompt,
|
|
1773
|
+
}, {
|
|
1774
|
+
registerProvider: (name, config) => {
|
|
1775
|
+
this._modelRegistry.registerProvider(name, config);
|
|
1776
|
+
this._refreshCurrentModelFromRegistry();
|
|
1777
|
+
},
|
|
1778
|
+
unregisterProvider: (name) => {
|
|
1779
|
+
this._modelRegistry.unregisterProvider(name);
|
|
1780
|
+
this._refreshCurrentModelFromRegistry();
|
|
1781
|
+
},
|
|
1674
1782
|
});
|
|
1675
1783
|
}
|
|
1676
1784
|
_refreshToolRegistry(options) {
|
|
1677
1785
|
const previousRegistryNames = new Set(this._toolRegistry.keys());
|
|
1678
1786
|
const previousActiveToolNames = this.getActiveToolNames();
|
|
1679
|
-
const
|
|
1787
|
+
const allowedToolNames = this._allowedToolNames;
|
|
1788
|
+
const isAllowedTool = (name) => !allowedToolNames || allowedToolNames.has(name);
|
|
1789
|
+
const registeredTools = this._extensionRunner.getAllRegisteredTools();
|
|
1680
1790
|
const allCustomTools = [
|
|
1681
1791
|
...registeredTools,
|
|
1682
|
-
...this._customTools.map((
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1792
|
+
...this._customTools.map((definition) => ({
|
|
1793
|
+
definition,
|
|
1794
|
+
sourceInfo: createSyntheticSourceInfo(`<sdk:${definition.name}>`, { source: "sdk" }),
|
|
1795
|
+
})),
|
|
1796
|
+
].filter((tool) => isAllowedTool(tool.definition.name));
|
|
1797
|
+
const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries())
|
|
1798
|
+
.filter(([name]) => isAllowedTool(name))
|
|
1799
|
+
.map(([name, definition]) => [
|
|
1800
|
+
name,
|
|
1801
|
+
{
|
|
1802
|
+
definition,
|
|
1803
|
+
sourceInfo: createSyntheticSourceInfo(`<builtin:${name}>`, { source: "builtin" }),
|
|
1804
|
+
},
|
|
1805
|
+
]));
|
|
1806
|
+
for (const tool of allCustomTools) {
|
|
1807
|
+
definitionRegistry.set(tool.definition.name, {
|
|
1808
|
+
definition: tool.definition,
|
|
1809
|
+
sourceInfo: tool.sourceInfo,
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
this._toolDefinitions = definitionRegistry;
|
|
1813
|
+
this._toolPromptSnippets = new Map(Array.from(definitionRegistry.values())
|
|
1814
|
+
.map(({ definition }) => {
|
|
1815
|
+
const snippet = this._normalizePromptSnippet(definition.promptSnippet);
|
|
1816
|
+
return snippet ? [definition.name, snippet] : undefined;
|
|
1688
1817
|
})
|
|
1689
1818
|
.filter((entry) => entry !== undefined));
|
|
1690
|
-
this._toolPromptGuidelines = new Map(
|
|
1691
|
-
.map((
|
|
1692
|
-
const guidelines = this._normalizePromptGuidelines(
|
|
1693
|
-
return guidelines.length > 0 ? [
|
|
1819
|
+
this._toolPromptGuidelines = new Map(Array.from(definitionRegistry.values())
|
|
1820
|
+
.map(({ definition }) => {
|
|
1821
|
+
const guidelines = this._normalizePromptGuidelines(definition.promptGuidelines);
|
|
1822
|
+
return guidelines.length > 0 ? [definition.name, guidelines] : undefined;
|
|
1694
1823
|
})
|
|
1695
1824
|
.filter((entry) => entry !== undefined));
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1825
|
+
const runner = this._extensionRunner;
|
|
1826
|
+
const wrappedExtensionTools = wrapRegisteredTools(allCustomTools, runner);
|
|
1827
|
+
const wrappedBuiltInTools = wrapRegisteredTools(Array.from(this._baseToolDefinitions.values())
|
|
1828
|
+
.filter((definition) => isAllowedTool(definition.name))
|
|
1829
|
+
.map((definition) => ({
|
|
1830
|
+
definition,
|
|
1831
|
+
sourceInfo: createSyntheticSourceInfo(`<builtin:${definition.name}>`, { source: "builtin" }),
|
|
1832
|
+
})), runner);
|
|
1833
|
+
const toolRegistry = new Map(wrappedBuiltInTools.map((tool) => [tool.name, tool]));
|
|
1700
1834
|
for (const tool of wrappedExtensionTools) {
|
|
1701
1835
|
toolRegistry.set(tool.name, tool);
|
|
1702
1836
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1837
|
+
this._toolRegistry = toolRegistry;
|
|
1838
|
+
const nextActiveToolNames = (options?.activeToolNames ? [...options.activeToolNames] : [...previousActiveToolNames]).filter((name) => isAllowedTool(name));
|
|
1839
|
+
if (allowedToolNames) {
|
|
1840
|
+
for (const toolName of this._toolRegistry.keys()) {
|
|
1841
|
+
if (allowedToolNames.has(toolName)) {
|
|
1842
|
+
nextActiveToolNames.push(toolName);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1709
1845
|
}
|
|
1710
|
-
|
|
1711
|
-
? [...options.activeToolNames]
|
|
1712
|
-
: [...previousActiveToolNames];
|
|
1713
|
-
if (options?.includeAllExtensionTools) {
|
|
1846
|
+
else if (options?.includeAllExtensionTools) {
|
|
1714
1847
|
for (const tool of wrappedExtensionTools) {
|
|
1715
1848
|
nextActiveToolNames.push(tool.name);
|
|
1716
1849
|
}
|
|
@@ -1727,32 +1860,29 @@ export class AgentSession {
|
|
|
1727
1860
|
_buildRuntime(options) {
|
|
1728
1861
|
const autoResizeImages = this.settingsManager.getImageAutoResize();
|
|
1729
1862
|
const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
|
|
1730
|
-
const
|
|
1731
|
-
|
|
1732
|
-
|
|
1863
|
+
const shellPath = this.settingsManager.getShellPath();
|
|
1864
|
+
const baseToolDefinitions = this._baseToolsOverride
|
|
1865
|
+
? Object.fromEntries(Object.entries(this._baseToolsOverride).map(([name, tool]) => [
|
|
1866
|
+
name,
|
|
1867
|
+
createToolDefinitionFromAgentTool(tool),
|
|
1868
|
+
]))
|
|
1869
|
+
: createAllToolDefinitions(this._cwd, {
|
|
1733
1870
|
read: { autoResizeImages },
|
|
1734
|
-
bash: { commandPrefix: shellCommandPrefix },
|
|
1871
|
+
bash: { commandPrefix: shellCommandPrefix, shellPath },
|
|
1735
1872
|
});
|
|
1736
|
-
this.
|
|
1873
|
+
this._baseToolDefinitions = new Map(Object.entries(baseToolDefinitions).map(([name, tool]) => [name, tool]));
|
|
1737
1874
|
const extensionsResult = this._resourceLoader.getExtensions();
|
|
1738
1875
|
if (options.flagValues) {
|
|
1739
1876
|
for (const [name, value] of options.flagValues) {
|
|
1740
1877
|
extensionsResult.runtime.flagValues.set(name, value);
|
|
1741
1878
|
}
|
|
1742
1879
|
}
|
|
1743
|
-
|
|
1744
|
-
const hasCustomTools = this._customTools.length > 0;
|
|
1745
|
-
this._extensionRunner =
|
|
1746
|
-
hasExtensions || hasCustomTools
|
|
1747
|
-
? new ExtensionRunner(extensionsResult.extensions, extensionsResult.runtime, this._cwd, this.sessionManager, this._modelRegistry)
|
|
1748
|
-
: undefined;
|
|
1880
|
+
this._extensionRunner = new ExtensionRunner(extensionsResult.extensions, extensionsResult.runtime, this._cwd, this.sessionManager, this._modelRegistry);
|
|
1749
1881
|
if (this._extensionRunnerRef) {
|
|
1750
1882
|
this._extensionRunnerRef.current = this._extensionRunner;
|
|
1751
1883
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
this._applyExtensionBindings(this._extensionRunner);
|
|
1755
|
-
}
|
|
1884
|
+
this._bindExtensionCore(this._extensionRunner);
|
|
1885
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
1756
1886
|
const defaultActiveToolNames = this._baseToolsOverride
|
|
1757
1887
|
? Object.keys(this._baseToolsOverride)
|
|
1758
1888
|
: ["read", "bash", "edit", "write"];
|
|
@@ -1763,9 +1893,9 @@ export class AgentSession {
|
|
|
1763
1893
|
});
|
|
1764
1894
|
}
|
|
1765
1895
|
async reload() {
|
|
1766
|
-
const previousFlagValues = this._extensionRunner
|
|
1767
|
-
await this._extensionRunner
|
|
1768
|
-
this.settingsManager.reload();
|
|
1896
|
+
const previousFlagValues = this._extensionRunner.getFlagValues();
|
|
1897
|
+
await emitSessionShutdownEvent(this._extensionRunner, { type: "session_shutdown", reason: "reload" });
|
|
1898
|
+
await this.settingsManager.reload();
|
|
1769
1899
|
resetApiProviders();
|
|
1770
1900
|
await this._resourceLoader.reload();
|
|
1771
1901
|
this._buildRuntime({
|
|
@@ -1777,8 +1907,8 @@ export class AgentSession {
|
|
|
1777
1907
|
this._extensionCommandContextActions ||
|
|
1778
1908
|
this._extensionShutdownHandler ||
|
|
1779
1909
|
this._extensionErrorListener;
|
|
1780
|
-
if (
|
|
1781
|
-
await this._extensionRunner.emit({ type: "session_start" });
|
|
1910
|
+
if (hasBindings) {
|
|
1911
|
+
await this._extensionRunner.emit({ type: "session_start", reason: "reload" });
|
|
1782
1912
|
await this.extendResourcesFromExtensions("reload");
|
|
1783
1913
|
}
|
|
1784
1914
|
}
|
|
@@ -1797,8 +1927,8 @@ export class AgentSession {
|
|
|
1797
1927
|
if (isContextOverflow(message, contextWindow))
|
|
1798
1928
|
return false;
|
|
1799
1929
|
const err = message.errorMessage;
|
|
1800
|
-
// Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection errors, fetch failed, terminated, retry delay exceeded
|
|
1801
|
-
return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server
|
|
1930
|
+
// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, request ended without sending chunks, HTTP/2 closed before response, terminated, retry delay exceeded
|
|
1931
|
+
return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(err);
|
|
1802
1932
|
}
|
|
1803
1933
|
/**
|
|
1804
1934
|
* Handle retryable errors with exponential backoff.
|
|
@@ -1841,7 +1971,7 @@ export class AgentSession {
|
|
|
1841
1971
|
// Remove error message from agent state (keep in session for history)
|
|
1842
1972
|
const messages = this.agent.state.messages;
|
|
1843
1973
|
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
1844
|
-
this.agent.
|
|
1974
|
+
this.agent.state.messages = messages.slice(0, -1);
|
|
1845
1975
|
}
|
|
1846
1976
|
// Wait with exponential backoff (abortable)
|
|
1847
1977
|
this._retryAbortController = new AbortController();
|
|
@@ -1884,9 +2014,11 @@ export class AgentSession {
|
|
|
1884
2014
|
* Returns immediately if no retry is in progress.
|
|
1885
2015
|
*/
|
|
1886
2016
|
async waitForRetry() {
|
|
1887
|
-
if (this._retryPromise) {
|
|
1888
|
-
|
|
2017
|
+
if (!this._retryPromise) {
|
|
2018
|
+
return;
|
|
1889
2019
|
}
|
|
2020
|
+
await this._retryPromise;
|
|
2021
|
+
await this.agent.waitForIdle();
|
|
1890
2022
|
}
|
|
1891
2023
|
/** Whether auto-retry is currently in progress */
|
|
1892
2024
|
get isRetrying() {
|
|
@@ -1917,17 +2049,13 @@ export class AgentSession {
|
|
|
1917
2049
|
this._bashAbortController = new AbortController();
|
|
1918
2050
|
// Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
|
|
1919
2051
|
const prefix = this.settingsManager.getShellCommandPrefix();
|
|
2052
|
+
const shellPath = this.settingsManager.getShellPath();
|
|
1920
2053
|
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
1921
2054
|
try {
|
|
1922
|
-
const result = options?.operations
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
})
|
|
1927
|
-
: await executeBashCommand(resolvedCommand, {
|
|
1928
|
-
onChunk,
|
|
1929
|
-
signal: this._bashAbortController.signal,
|
|
1930
|
-
});
|
|
2055
|
+
const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
|
|
2056
|
+
onChunk,
|
|
2057
|
+
signal: this._bashAbortController.signal,
|
|
2058
|
+
});
|
|
1931
2059
|
this.recordBashResult(command, result, options);
|
|
1932
2060
|
return result;
|
|
1933
2061
|
}
|
|
@@ -1958,7 +2086,7 @@ export class AgentSession {
|
|
|
1958
2086
|
}
|
|
1959
2087
|
else {
|
|
1960
2088
|
// Add to agent state immediately
|
|
1961
|
-
this.agent.
|
|
2089
|
+
this.agent.state.messages.push(bashMessage);
|
|
1962
2090
|
// Save to session
|
|
1963
2091
|
this.sessionManager.appendMessage(bashMessage);
|
|
1964
2092
|
}
|
|
@@ -1986,7 +2114,7 @@ export class AgentSession {
|
|
|
1986
2114
|
return;
|
|
1987
2115
|
for (const bashMessage of this._pendingBashMessages) {
|
|
1988
2116
|
// Add to agent state
|
|
1989
|
-
this.agent.
|
|
2117
|
+
this.agent.state.messages.push(bashMessage);
|
|
1990
2118
|
// Save to session
|
|
1991
2119
|
this.sessionManager.appendMessage(bashMessage);
|
|
1992
2120
|
}
|
|
@@ -1995,129 +2123,12 @@ export class AgentSession {
|
|
|
1995
2123
|
// =========================================================================
|
|
1996
2124
|
// Session Management
|
|
1997
2125
|
// =========================================================================
|
|
1998
|
-
/**
|
|
1999
|
-
* Switch to a different session file.
|
|
2000
|
-
* Aborts current operation, loads messages, restores model/thinking.
|
|
2001
|
-
* Listeners are preserved and will continue receiving events.
|
|
2002
|
-
* @returns true if switch completed, false if cancelled by extension
|
|
2003
|
-
*/
|
|
2004
|
-
async switchSession(sessionPath) {
|
|
2005
|
-
const previousSessionFile = this.sessionManager.getSessionFile();
|
|
2006
|
-
// Emit session_before_switch event (can be cancelled)
|
|
2007
|
-
if (this._extensionRunner?.hasHandlers("session_before_switch")) {
|
|
2008
|
-
const result = (await this._extensionRunner.emit({
|
|
2009
|
-
type: "session_before_switch",
|
|
2010
|
-
reason: "resume",
|
|
2011
|
-
targetSessionFile: sessionPath,
|
|
2012
|
-
}));
|
|
2013
|
-
if (result?.cancel) {
|
|
2014
|
-
return false;
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
this._disconnectFromAgent();
|
|
2018
|
-
await this.abort();
|
|
2019
|
-
this._steeringMessages = [];
|
|
2020
|
-
this._followUpMessages = [];
|
|
2021
|
-
this._pendingNextTurnMessages = [];
|
|
2022
|
-
// Set new session
|
|
2023
|
-
this.sessionManager.setSessionFile(sessionPath);
|
|
2024
|
-
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
2025
|
-
// Reload messages
|
|
2026
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2027
|
-
// Emit session_switch event to extensions
|
|
2028
|
-
if (this._extensionRunner) {
|
|
2029
|
-
await this._extensionRunner.emit({
|
|
2030
|
-
type: "session_switch",
|
|
2031
|
-
reason: "resume",
|
|
2032
|
-
previousSessionFile,
|
|
2033
|
-
});
|
|
2034
|
-
}
|
|
2035
|
-
// Emit session event to custom tools
|
|
2036
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
2037
|
-
// Restore model if saved
|
|
2038
|
-
if (sessionContext.model) {
|
|
2039
|
-
const previousModel = this.model;
|
|
2040
|
-
const availableModels = await this._modelRegistry.getAvailable();
|
|
2041
|
-
const match = availableModels.find((m) => m.provider === sessionContext.model.provider && m.id === sessionContext.model.modelId);
|
|
2042
|
-
if (match) {
|
|
2043
|
-
this.agent.setModel(match);
|
|
2044
|
-
await this._emitModelSelect(match, previousModel, "restore");
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
const hasThinkingEntry = this.sessionManager.getBranch().some((entry) => entry.type === "thinking_level_change");
|
|
2048
|
-
const defaultThinkingLevel = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
2049
|
-
if (hasThinkingEntry) {
|
|
2050
|
-
// Restore thinking level if saved (setThinkingLevel clamps to model capabilities)
|
|
2051
|
-
this.setThinkingLevel(sessionContext.thinkingLevel);
|
|
2052
|
-
}
|
|
2053
|
-
else {
|
|
2054
|
-
const availableLevels = this.getAvailableThinkingLevels();
|
|
2055
|
-
const effectiveLevel = availableLevels.includes(defaultThinkingLevel)
|
|
2056
|
-
? defaultThinkingLevel
|
|
2057
|
-
: this._clampThinkingLevel(defaultThinkingLevel, availableLevels);
|
|
2058
|
-
this.agent.setThinkingLevel(effectiveLevel);
|
|
2059
|
-
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
2060
|
-
}
|
|
2061
|
-
this._reconnectToAgent();
|
|
2062
|
-
return true;
|
|
2063
|
-
}
|
|
2064
2126
|
/**
|
|
2065
2127
|
* Set a display name for the current session.
|
|
2066
2128
|
*/
|
|
2067
2129
|
setSessionName(name) {
|
|
2068
2130
|
this.sessionManager.appendSessionInfo(name);
|
|
2069
|
-
|
|
2070
|
-
/**
|
|
2071
|
-
* Create a fork from a specific entry.
|
|
2072
|
-
* Emits before_fork/fork session events to extensions.
|
|
2073
|
-
*
|
|
2074
|
-
* @param entryId ID of the entry to fork from
|
|
2075
|
-
* @returns Object with:
|
|
2076
|
-
* - selectedText: The text of the selected user message (for editor pre-fill)
|
|
2077
|
-
* - cancelled: True if an extension cancelled the fork
|
|
2078
|
-
*/
|
|
2079
|
-
async fork(entryId) {
|
|
2080
|
-
const previousSessionFile = this.sessionFile;
|
|
2081
|
-
const selectedEntry = this.sessionManager.getEntry(entryId);
|
|
2082
|
-
if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
|
|
2083
|
-
throw new Error("Invalid entry ID for forking");
|
|
2084
|
-
}
|
|
2085
|
-
const selectedText = this._extractUserMessageText(selectedEntry.message.content);
|
|
2086
|
-
let skipConversationRestore = false;
|
|
2087
|
-
// Emit session_before_fork event (can be cancelled)
|
|
2088
|
-
if (this._extensionRunner?.hasHandlers("session_before_fork")) {
|
|
2089
|
-
const result = (await this._extensionRunner.emit({
|
|
2090
|
-
type: "session_before_fork",
|
|
2091
|
-
entryId,
|
|
2092
|
-
}));
|
|
2093
|
-
if (result?.cancel) {
|
|
2094
|
-
return { selectedText, cancelled: true };
|
|
2095
|
-
}
|
|
2096
|
-
skipConversationRestore = result?.skipConversationRestore ?? false;
|
|
2097
|
-
}
|
|
2098
|
-
// Clear pending messages (bound to old session state)
|
|
2099
|
-
this._pendingNextTurnMessages = [];
|
|
2100
|
-
if (!selectedEntry.parentId) {
|
|
2101
|
-
this.sessionManager.newSession({ parentSession: previousSessionFile });
|
|
2102
|
-
}
|
|
2103
|
-
else {
|
|
2104
|
-
this.sessionManager.createBranchedSession(selectedEntry.parentId);
|
|
2105
|
-
}
|
|
2106
|
-
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
2107
|
-
// Reload messages from entries (works for both file and in-memory mode)
|
|
2108
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2109
|
-
// Emit session_fork event to extensions (after fork completes)
|
|
2110
|
-
if (this._extensionRunner) {
|
|
2111
|
-
await this._extensionRunner.emit({
|
|
2112
|
-
type: "session_fork",
|
|
2113
|
-
previousSessionFile,
|
|
2114
|
-
});
|
|
2115
|
-
}
|
|
2116
|
-
// Emit session event to custom tools (with reason "fork")
|
|
2117
|
-
if (!skipConversationRestore) {
|
|
2118
|
-
this.agent.replaceMessages(sessionContext.messages);
|
|
2119
|
-
}
|
|
2120
|
-
return { selectedText, cancelled: false };
|
|
2131
|
+
this._emit({ type: "session_info_changed", name: this.sessionManager.getSessionName() });
|
|
2121
2132
|
}
|
|
2122
2133
|
// =========================================================================
|
|
2123
2134
|
// Tree Navigation
|
|
@@ -2165,120 +2176,117 @@ export class AgentSession {
|
|
|
2165
2176
|
};
|
|
2166
2177
|
// Set up abort controller for summarization
|
|
2167
2178
|
this._branchSummaryAbortController = new AbortController();
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
customInstructions
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
replaceInstructions
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
label
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
// Run default summarizer if needed
|
|
2196
|
-
let summaryText;
|
|
2197
|
-
let summaryDetails;
|
|
2198
|
-
if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
|
|
2199
|
-
const model = this.model;
|
|
2200
|
-
const apiKey = await this._modelRegistry.getApiKey(model);
|
|
2201
|
-
if (!apiKey) {
|
|
2202
|
-
throw new Error(`No API key for ${model.provider}`);
|
|
2203
|
-
}
|
|
2204
|
-
const branchSummarySettings = this.settingsManager.getBranchSummarySettings();
|
|
2205
|
-
const result = await generateBranchSummary(entriesToSummarize, {
|
|
2206
|
-
model,
|
|
2207
|
-
apiKey,
|
|
2208
|
-
signal: this._branchSummaryAbortController.signal,
|
|
2209
|
-
customInstructions,
|
|
2210
|
-
replaceInstructions,
|
|
2211
|
-
reserveTokens: branchSummarySettings.reserveTokens,
|
|
2212
|
-
});
|
|
2213
|
-
this._branchSummaryAbortController = undefined;
|
|
2214
|
-
if (result.aborted) {
|
|
2215
|
-
return { cancelled: true, aborted: true };
|
|
2179
|
+
try {
|
|
2180
|
+
let extensionSummary;
|
|
2181
|
+
let fromExtension = false;
|
|
2182
|
+
// Emit session_before_tree event
|
|
2183
|
+
if (this._extensionRunner.hasHandlers("session_before_tree")) {
|
|
2184
|
+
const result = (await this._extensionRunner.emit({
|
|
2185
|
+
type: "session_before_tree",
|
|
2186
|
+
preparation,
|
|
2187
|
+
signal: this._branchSummaryAbortController.signal,
|
|
2188
|
+
}));
|
|
2189
|
+
if (result?.cancel) {
|
|
2190
|
+
return { cancelled: true };
|
|
2191
|
+
}
|
|
2192
|
+
if (result?.summary && options.summarize) {
|
|
2193
|
+
extensionSummary = result.summary;
|
|
2194
|
+
fromExtension = true;
|
|
2195
|
+
}
|
|
2196
|
+
// Allow extensions to override instructions and label
|
|
2197
|
+
if (result?.customInstructions !== undefined) {
|
|
2198
|
+
customInstructions = result.customInstructions;
|
|
2199
|
+
}
|
|
2200
|
+
if (result?.replaceInstructions !== undefined) {
|
|
2201
|
+
replaceInstructions = result.replaceInstructions;
|
|
2202
|
+
}
|
|
2203
|
+
if (result?.label !== undefined) {
|
|
2204
|
+
label = result.label;
|
|
2205
|
+
}
|
|
2216
2206
|
}
|
|
2217
|
-
if
|
|
2218
|
-
|
|
2207
|
+
// Run default summarizer if needed
|
|
2208
|
+
let summaryText;
|
|
2209
|
+
let summaryDetails;
|
|
2210
|
+
if (options.summarize && entriesToSummarize.length > 0 && !extensionSummary) {
|
|
2211
|
+
const model = this.model;
|
|
2212
|
+
const { apiKey, headers } = await this._getRequiredRequestAuth(model);
|
|
2213
|
+
const branchSummarySettings = this.settingsManager.getBranchSummarySettings();
|
|
2214
|
+
const result = await generateBranchSummary(entriesToSummarize, {
|
|
2215
|
+
model,
|
|
2216
|
+
apiKey,
|
|
2217
|
+
headers,
|
|
2218
|
+
signal: this._branchSummaryAbortController.signal,
|
|
2219
|
+
customInstructions,
|
|
2220
|
+
replaceInstructions,
|
|
2221
|
+
reserveTokens: branchSummarySettings.reserveTokens,
|
|
2222
|
+
});
|
|
2223
|
+
if (result.aborted) {
|
|
2224
|
+
return { cancelled: true, aborted: true };
|
|
2225
|
+
}
|
|
2226
|
+
if (result.error) {
|
|
2227
|
+
throw new Error(result.error);
|
|
2228
|
+
}
|
|
2229
|
+
summaryText = result.summary;
|
|
2230
|
+
summaryDetails = {
|
|
2231
|
+
readFiles: result.readFiles || [],
|
|
2232
|
+
modifiedFiles: result.modifiedFiles || [],
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
else if (extensionSummary) {
|
|
2236
|
+
summaryText = extensionSummary.summary;
|
|
2237
|
+
summaryDetails = extensionSummary.details;
|
|
2238
|
+
}
|
|
2239
|
+
// Determine the new leaf position based on target type
|
|
2240
|
+
let newLeafId;
|
|
2241
|
+
let editorText;
|
|
2242
|
+
if (targetEntry.type === "message" && targetEntry.message.role === "user") {
|
|
2243
|
+
// User message: leaf = parent (null if root), text goes to editor
|
|
2244
|
+
newLeafId = targetEntry.parentId;
|
|
2245
|
+
editorText = this._extractUserMessageText(targetEntry.message.content);
|
|
2246
|
+
}
|
|
2247
|
+
else if (targetEntry.type === "custom_message") {
|
|
2248
|
+
// Custom message: leaf = parent (null if root), text goes to editor
|
|
2249
|
+
newLeafId = targetEntry.parentId;
|
|
2250
|
+
editorText =
|
|
2251
|
+
typeof targetEntry.content === "string"
|
|
2252
|
+
? targetEntry.content
|
|
2253
|
+
: targetEntry.content
|
|
2254
|
+
.filter((c) => c.type === "text")
|
|
2255
|
+
.map((c) => c.text)
|
|
2256
|
+
.join("");
|
|
2219
2257
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
summaryText
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
newLeafId = targetEntry.parentId;
|
|
2236
|
-
editorText = this._extractUserMessageText(targetEntry.message.content);
|
|
2237
|
-
}
|
|
2238
|
-
else if (targetEntry.type === "custom_message") {
|
|
2239
|
-
// Custom message: leaf = parent (null if root), text goes to editor
|
|
2240
|
-
newLeafId = targetEntry.parentId;
|
|
2241
|
-
editorText =
|
|
2242
|
-
typeof targetEntry.content === "string"
|
|
2243
|
-
? targetEntry.content
|
|
2244
|
-
: targetEntry.content
|
|
2245
|
-
.filter((c) => c.type === "text")
|
|
2246
|
-
.map((c) => c.text)
|
|
2247
|
-
.join("");
|
|
2248
|
-
}
|
|
2249
|
-
else {
|
|
2250
|
-
// Non-user message: leaf = selected node
|
|
2251
|
-
newLeafId = targetId;
|
|
2252
|
-
}
|
|
2253
|
-
// Switch leaf (with or without summary)
|
|
2254
|
-
// Summary is attached at the navigation target position (newLeafId), not the old branch
|
|
2255
|
-
let summaryEntry;
|
|
2256
|
-
if (summaryText) {
|
|
2257
|
-
// Create summary at target position (can be null for root)
|
|
2258
|
-
const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
|
|
2259
|
-
summaryEntry = this.sessionManager.getEntry(summaryId);
|
|
2260
|
-
// Attach label to the summary entry
|
|
2261
|
-
if (label) {
|
|
2262
|
-
this.sessionManager.appendLabelChange(summaryId, label);
|
|
2258
|
+
else {
|
|
2259
|
+
// Non-user message: leaf = selected node
|
|
2260
|
+
newLeafId = targetId;
|
|
2261
|
+
}
|
|
2262
|
+
// Switch leaf (with or without summary)
|
|
2263
|
+
// Summary is attached at the navigation target position (newLeafId), not the old branch
|
|
2264
|
+
let summaryEntry;
|
|
2265
|
+
if (summaryText) {
|
|
2266
|
+
// Create summary at target position (can be null for root)
|
|
2267
|
+
const summaryId = this.sessionManager.branchWithSummary(newLeafId, summaryText, summaryDetails, fromExtension);
|
|
2268
|
+
summaryEntry = this.sessionManager.getEntry(summaryId);
|
|
2269
|
+
// Attach label to the summary entry
|
|
2270
|
+
if (label) {
|
|
2271
|
+
this.sessionManager.appendLabelChange(summaryId, label);
|
|
2272
|
+
}
|
|
2263
2273
|
}
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
// Emit session_tree event
|
|
2281
|
-
if (this._extensionRunner) {
|
|
2274
|
+
else if (newLeafId === null) {
|
|
2275
|
+
// No summary, navigating to root - reset leaf
|
|
2276
|
+
this.sessionManager.resetLeaf();
|
|
2277
|
+
}
|
|
2278
|
+
else {
|
|
2279
|
+
// No summary, navigating to non-root
|
|
2280
|
+
this.sessionManager.branch(newLeafId);
|
|
2281
|
+
}
|
|
2282
|
+
// Attach label to target entry when not summarizing (no summary entry to label)
|
|
2283
|
+
if (label && !summaryText) {
|
|
2284
|
+
this.sessionManager.appendLabelChange(targetId, label);
|
|
2285
|
+
}
|
|
2286
|
+
// Update agent state
|
|
2287
|
+
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2288
|
+
this.agent.state.messages = sessionContext.messages;
|
|
2289
|
+
// Emit session_tree event
|
|
2282
2290
|
await this._extensionRunner.emit({
|
|
2283
2291
|
type: "session_tree",
|
|
2284
2292
|
newLeafId: this.sessionManager.getLeafId(),
|
|
@@ -2286,10 +2294,12 @@ export class AgentSession {
|
|
|
2286
2294
|
summaryEntry,
|
|
2287
2295
|
fromExtension: summaryText ? fromExtension : undefined,
|
|
2288
2296
|
});
|
|
2297
|
+
// Emit to custom tools
|
|
2298
|
+
return { editorText, cancelled: false, summaryEntry };
|
|
2299
|
+
}
|
|
2300
|
+
finally {
|
|
2301
|
+
this._branchSummaryAbortController = undefined;
|
|
2289
2302
|
}
|
|
2290
|
-
// Emit to custom tools
|
|
2291
|
-
this._branchSummaryAbortController = undefined;
|
|
2292
|
-
return { editorText, cancelled: false, summaryEntry };
|
|
2293
2303
|
}
|
|
2294
2304
|
/**
|
|
2295
2305
|
* Get all user messages from session for fork selector.
|
|
@@ -2361,6 +2371,7 @@ export class AgentSession {
|
|
|
2361
2371
|
total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
|
|
2362
2372
|
},
|
|
2363
2373
|
cost: totalCost,
|
|
2374
|
+
contextUsage: this.getContextUsage(),
|
|
2364
2375
|
};
|
|
2365
2376
|
}
|
|
2366
2377
|
getContextUsage() {
|
|
@@ -2412,19 +2423,48 @@ export class AgentSession {
|
|
|
2412
2423
|
async exportToHtml(outputPath) {
|
|
2413
2424
|
const themeName = this.settingsManager.getTheme();
|
|
2414
2425
|
// Create tool renderer if we have an extension runner (for custom tool HTML rendering)
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
});
|
|
2421
|
-
}
|
|
2426
|
+
const toolRenderer = createToolHtmlRenderer({
|
|
2427
|
+
getToolDefinition: (name) => this.getToolDefinition(name),
|
|
2428
|
+
theme,
|
|
2429
|
+
cwd: this.sessionManager.getCwd(),
|
|
2430
|
+
});
|
|
2422
2431
|
return await exportSessionToHtml(this.sessionManager, this.state, {
|
|
2423
2432
|
outputPath,
|
|
2424
2433
|
themeName,
|
|
2425
2434
|
toolRenderer,
|
|
2426
2435
|
});
|
|
2427
2436
|
}
|
|
2437
|
+
/**
|
|
2438
|
+
* Export the current session branch to a JSONL file.
|
|
2439
|
+
* Writes the session header followed by all entries on the current branch path.
|
|
2440
|
+
* @param outputPath Target file path. If omitted, generates a timestamped file in cwd.
|
|
2441
|
+
* @returns The resolved output file path.
|
|
2442
|
+
*/
|
|
2443
|
+
exportToJsonl(outputPath) {
|
|
2444
|
+
const filePath = resolve(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`);
|
|
2445
|
+
const dir = dirname(filePath);
|
|
2446
|
+
if (!existsSync(dir)) {
|
|
2447
|
+
mkdirSync(dir, { recursive: true });
|
|
2448
|
+
}
|
|
2449
|
+
const header = {
|
|
2450
|
+
type: "session",
|
|
2451
|
+
version: CURRENT_SESSION_VERSION,
|
|
2452
|
+
id: this.sessionManager.getSessionId(),
|
|
2453
|
+
timestamp: new Date().toISOString(),
|
|
2454
|
+
cwd: this.sessionManager.getCwd(),
|
|
2455
|
+
};
|
|
2456
|
+
const branchEntries = this.sessionManager.getBranch();
|
|
2457
|
+
const lines = [JSON.stringify(header)];
|
|
2458
|
+
// Re-chain parentIds to form a linear sequence
|
|
2459
|
+
let prevId = null;
|
|
2460
|
+
for (const entry of branchEntries) {
|
|
2461
|
+
const linear = { ...entry, parentId: prevId };
|
|
2462
|
+
lines.push(JSON.stringify(linear));
|
|
2463
|
+
prevId = entry.id;
|
|
2464
|
+
}
|
|
2465
|
+
writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
2466
|
+
return filePath;
|
|
2467
|
+
}
|
|
2428
2468
|
// =========================================================================
|
|
2429
2469
|
// Utilities
|
|
2430
2470
|
// =========================================================================
|
|
@@ -2459,11 +2499,17 @@ export class AgentSession {
|
|
|
2459
2499
|
// =========================================================================
|
|
2460
2500
|
// Extension System
|
|
2461
2501
|
// =========================================================================
|
|
2502
|
+
createReplacedSessionContext() {
|
|
2503
|
+
const context = Object.defineProperties({}, Object.getOwnPropertyDescriptors(this._extensionRunner.createCommandContext()));
|
|
2504
|
+
context.sendMessage = (message, options) => this.sendCustomMessage(message, options);
|
|
2505
|
+
context.sendUserMessage = (content, options) => this.sendUserMessage(content, options);
|
|
2506
|
+
return context;
|
|
2507
|
+
}
|
|
2462
2508
|
/**
|
|
2463
2509
|
* Check if extensions have handlers for a specific event type.
|
|
2464
2510
|
*/
|
|
2465
2511
|
hasExtensionHandlers(eventType) {
|
|
2466
|
-
return this._extensionRunner
|
|
2512
|
+
return this._extensionRunner.hasHandlers(eventType);
|
|
2467
2513
|
}
|
|
2468
2514
|
/**
|
|
2469
2515
|
* Get the extension runner (for setting UI context and error handlers).
|