@earendil-works/pi-coding-agent 0.85.1 → 0.86.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 +86 -0
- package/README.md +2 -1
- package/dist/bundle/chunks/anthropic-messages-MYU5ZMRF.js +6 -0
- package/dist/bundle/chunks/azure-openai-responses-HUSQ3GP2.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +47 -38
- package/dist/bundle/chunks/chunk-7IOYH2SY.js +2 -0
- package/dist/bundle/chunks/chunk-7YM6BE7Y.js +1561 -0
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +2 -0
- package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +42 -0
- package/dist/bundle/chunks/chunk-FUXEF6JQ.js +44 -0
- package/dist/bundle/chunks/{chunk-IDDQWTHI.js → chunk-RCZIEVGO.js} +10 -1
- package/dist/bundle/chunks/chunk-S7SZN6Z3.js +2 -0
- package/dist/bundle/chunks/chunk-WIUZI2CJ.js +11 -0
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/google-generative-ai-IMS5EK2Y.js +2 -0
- package/dist/bundle/chunks/google-vertex-O4YJEQUW.js +2 -0
- package/dist/bundle/chunks/jiti-loader-C3NU2SDH.js +21 -0
- package/dist/bundle/chunks/jiti-static-loader-E6ANQD5T.js +2 -0
- package/dist/bundle/chunks/mistral-conversations-NOSQHBEY.js +5 -0
- package/dist/bundle/chunks/{node-KG362ECQ.js → node-O3RIBJRH.js} +1 -1
- package/dist/bundle/chunks/openai-codex-responses-QDSOTBBO.js +10 -0
- package/dist/bundle/chunks/openai-completions-KQPOPEVM.js +7 -0
- package/dist/bundle/chunks/openai-responses-HWEUZ6WZ.js +2 -0
- package/dist/bundle/chunks/{pi-messages-PML4SPVJ.js → pi-messages-GFKBWJHZ.js} +1 -1
- package/dist/bundle/chunks/virtual-modules-XW5APVUE.js +2 -0
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/session-picker.d.ts +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +56 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +293 -155
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bug-report-upload.d.ts +12 -0
- package/dist/core/bug-report-upload.d.ts.map +1 -0
- package/dist/core/bug-report-upload.js +22 -0
- package/dist/core/bug-report-upload.js.map +1 -0
- package/dist/core/bug-report.d.ts +176 -0
- package/dist/core/bug-report.d.ts.map +1 -0
- package/dist/core/bug-report.js +292 -0
- package/dist/core/bug-report.js.map +1 -0
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js +12 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/cache-warmer.d.ts +102 -0
- package/dist/core/cache-warmer.d.ts.map +1 -0
- package/dist/core/cache-warmer.js +342 -0
- package/dist/core/cache-warmer.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -12
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/crash-log.d.ts +22 -0
- package/dist/core/crash-log.d.ts.map +1 -0
- package/dist/core/crash-log.js +71 -0
- package/dist/core/crash-log.js.map +1 -0
- package/dist/core/experimental.d.ts +0 -4
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +0 -4
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/jiti-loader.d.ts +2 -0
- package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
- package/dist/core/extensions/jiti-loader.js +4 -0
- package/dist/core/extensions/jiti-loader.js.map +1 -0
- package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
- package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
- package/dist/core/extensions/jiti-static-loader.js +4 -0
- package/dist/core/extensions/jiti-static-loader.js.map +1 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +38 -51
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +10 -7
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +85 -67
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +60 -50
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/virtual-modules.d.ts +3 -0
- package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
- package/dist/core/extensions/virtual-modules.js +38 -0
- package/dist/core/extensions/virtual-modules.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +1 -20
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +1 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +1 -0
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-config.d.ts +101 -20
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +25 -18
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +8 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +5 -3
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/provider-composer.d.ts +3 -2
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +2 -0
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/radius.d.ts +3 -0
- package/dist/core/radius.d.ts.map +1 -1
- package/dist/core/radius.js +6 -0
- package/dist/core/radius.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +60 -38
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +6 -2
- package/dist/core/session-export.d.ts.map +1 -1
- package/dist/core/session-export.js +14 -13
- package/dist/core/session-export.js.map +1 -1
- package/dist/core/session-manager.d.ts +29 -6
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +159 -96
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +20 -4
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +43 -7
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +49 -6
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +121 -92
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +10 -4
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +1 -2
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -2
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/renderers/bash.d.ts.map +1 -1
- package/dist/core/tools/renderers/bash.js +9 -1
- package/dist/core/tools/renderers/bash.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +1 -2
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/usage-totals.d.ts +1 -1
- package/dist/core/usage-totals.d.ts.map +1 -1
- package/dist/core/usage-totals.js +5 -1
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/extensions/llama/client.d.ts +2 -0
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +7 -3
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +20 -5
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +13 -9
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -2
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/bug-report.d.ts +14 -0
- package/dist/modes/interactive/bug-report.d.ts.map +1 -0
- package/dist/modes/interactive/bug-report.js +209 -0
- package/dist/modes/interactive/bug-report.js.map +1 -0
- package/dist/modes/interactive/chat-viewport.d.ts.map +1 -1
- package/dist/modes/interactive/chat-viewport.js +1 -1
- package/dist/modes/interactive/chat-viewport.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 +12 -5
- 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 +12 -5
- package/dist/modes/interactive/components/compaction-summary-message.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.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +2 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +6 -0
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +4 -0
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +5 -5
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +74 -48
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +11 -0
- package/dist/modes/interactive/components/settings-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 +11 -4
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +2 -2
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +7 -7
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +18 -9
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +2 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +220 -84
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/session-share.d.ts +2 -0
- package/dist/modes/interactive/session-share.d.ts.map +1 -1
- package/dist/modes/interactive/session-share.js +8 -4
- package/dist/modes/interactive/session-share.js.map +1 -1
- package/dist/modes/interactive/tui-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/tui-renderer.js +2 -2
- package/dist/modes/interactive/tui-renderer.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/clipboard-command.d.ts +7 -0
- package/dist/utils/clipboard-command.d.ts.map +1 -0
- package/dist/utils/clipboard-command.js +45 -0
- package/dist/utils/clipboard-command.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +52 -69
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +61 -125
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.d.ts.map +1 -0
- package/dist/utils/zip.js +60 -0
- package/dist/utils/zip.js.map +1 -0
- package/docs/compaction.md +39 -13
- package/docs/custom-provider.md +22 -13
- package/docs/development.md +4 -4
- package/docs/docs.json +4 -0
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +57 -43
- package/docs/json.md +4 -4
- package/docs/models.md +33 -2
- package/docs/providers.md +2 -2
- package/docs/sdk.md +4 -2
- package/docs/security.md +1 -1
- package/docs/session-format.md +82 -40
- package/docs/sessions.md +27 -0
- package/docs/settings.md +60 -1
- package/docs/termux.md +0 -1
- package/docs/tui.md +2 -2
- package/docs/usage.md +1 -0
- package/examples/extensions/README.md +0 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/prompt-customizer.ts +19 -67
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +435 -762
- package/package.json +17 -23
- package/dist/bundle/chunks/anthropic-messages-VWZZOSJQ.js +0 -6
- package/dist/bundle/chunks/azure-openai-responses-BJLAM6CD.js +0 -2
- package/dist/bundle/chunks/chunk-AXIIZGTV.js +0 -2
- package/dist/bundle/chunks/chunk-CLNPYIDP.js +0 -42
- package/dist/bundle/chunks/chunk-GMWCTUPB.js +0 -2
- package/dist/bundle/chunks/chunk-JVUZSMYM.js +0 -1547
- package/dist/bundle/chunks/chunk-OUXLLA64.js +0 -11
- package/dist/bundle/chunks/chunk-U6ZMQKGD.js +0 -50
- package/dist/bundle/chunks/chunk-UAQELI3K.js +0 -2
- package/dist/bundle/chunks/google-generative-ai-Q2K7FBDI.js +0 -2
- package/dist/bundle/chunks/google-vertex-7UJBF4S7.js +0 -2
- package/dist/bundle/chunks/mistral-conversations-4M6BZBX6.js +0 -5
- package/dist/bundle/chunks/openai-codex-responses-R6VYZTVY.js +0 -10
- package/dist/bundle/chunks/openai-completions-EKZT2IH2.js +0 -7
- package/dist/bundle/chunks/openai-responses-TFDINO6W.js +0 -2
- package/dist/utils/clipboard-native.d.ts +0 -11
- package/dist/utils/clipboard-native.d.ts.map +0 -1
- package/dist/utils/clipboard-native.js +0 -20
- package/dist/utils/clipboard-native.js.map +0 -1
- package/examples/extensions/kimi-deferred-tools.ts +0 -61
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { readFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname } from "node:path";
|
|
17
|
-
import { contentText } from "@earendil-works/pi-ai";
|
|
17
|
+
import { contentText, getCurrentSystemMessage, retryDelayMs } from "@earendil-works/pi-ai";
|
|
18
18
|
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
19
19
|
import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
|
|
20
20
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
@@ -22,6 +22,7 @@ import { sleep } from "../utils/sleep.js";
|
|
|
22
22
|
import { normalizeToolResultImages } from "../utils/tool-result-images.js";
|
|
23
23
|
import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
|
|
24
24
|
import { executeBashWithOperations } from "./bash-executor.js";
|
|
25
|
+
import { generateBugReportSummary } from "./bug-report.js";
|
|
25
26
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
26
27
|
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
|
|
27
28
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
@@ -33,7 +34,7 @@ import { expandPromptTemplate } from "./prompt-templates.js";
|
|
|
33
34
|
import { exportSessionToJsonl } from "./session-export.js";
|
|
34
35
|
import { getLatestCompactionEntry } from "./session-manager.js";
|
|
35
36
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
36
|
-
import { buildSystemPrompt } from "./system-prompt.js";
|
|
37
|
+
import { buildSystemPrompt, buildSystemPromptSections, diffSystemPromptSections, normalizeBuildSystemPromptOptions, } from "./system-prompt.js";
|
|
37
38
|
import { createLocalBashOperations } from "./tools/bash.js";
|
|
38
39
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
39
40
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
@@ -69,9 +70,6 @@ function estimateMessagesTokens(messages) {
|
|
|
69
70
|
return tokens;
|
|
70
71
|
}
|
|
71
72
|
// ============================================================================
|
|
72
|
-
// Constants
|
|
73
|
-
// ============================================================================
|
|
74
|
-
// ============================================================================
|
|
75
73
|
// AgentSession Class
|
|
76
74
|
// ============================================================================
|
|
77
75
|
export class AgentSession {
|
|
@@ -83,6 +81,7 @@ export class AgentSession {
|
|
|
83
81
|
_unsubscribeAgent;
|
|
84
82
|
_eventListeners = [];
|
|
85
83
|
_isAgentRunActive = false;
|
|
84
|
+
_agentRunAbortRequested = false;
|
|
86
85
|
_idleWaitPromise;
|
|
87
86
|
_resolveIdleWait;
|
|
88
87
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
@@ -126,15 +125,15 @@ export class AgentSession {
|
|
|
126
125
|
_extensionErrorListener;
|
|
127
126
|
_extensionErrorUnsubscriber;
|
|
128
127
|
_modelRuntime;
|
|
128
|
+
_cacheWarmer;
|
|
129
129
|
// Tool registry for extension getTools/setTools
|
|
130
130
|
_toolRegistry = new Map();
|
|
131
131
|
_toolDefinitions = new Map();
|
|
132
132
|
_toolPromptSnippets = new Map();
|
|
133
133
|
_toolPromptGuidelines = new Map();
|
|
134
|
-
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
135
|
-
_baseSystemPrompt = "";
|
|
136
134
|
_baseSystemPromptOptions;
|
|
137
|
-
|
|
135
|
+
/** Prompt options after before_agent_start mutations for the active run. */
|
|
136
|
+
_runSystemPromptOptions;
|
|
138
137
|
constructor(config) {
|
|
139
138
|
this.agent = config.agent;
|
|
140
139
|
this.sessionManager = config.sessionManager;
|
|
@@ -144,6 +143,10 @@ export class AgentSession {
|
|
|
144
143
|
this._customTools = config.customTools ?? [];
|
|
145
144
|
this._cwd = config.cwd;
|
|
146
145
|
this._modelRuntime = config.modelRuntime;
|
|
146
|
+
this._cacheWarmer = config.cacheWarmer;
|
|
147
|
+
if (this._cacheWarmer) {
|
|
148
|
+
this._cacheWarmer.onWarmed = (entry) => this._emit({ type: "entry_appended", entry });
|
|
149
|
+
}
|
|
147
150
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
148
151
|
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
149
152
|
this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
|
|
@@ -155,18 +158,21 @@ export class AgentSession {
|
|
|
155
158
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
156
159
|
this._installAgentToolHooks();
|
|
157
160
|
this._installAgentNextTurnRefresh();
|
|
161
|
+
this._installAgentForcedPromptProjection();
|
|
158
162
|
this._buildRuntime({
|
|
159
163
|
activeToolNames: this._initialActiveToolNames,
|
|
160
164
|
includeAllExtensionTools: true,
|
|
161
165
|
});
|
|
166
|
+
if (this._initialActiveToolNames === undefined)
|
|
167
|
+
this._restoreToolsFromTranscript();
|
|
162
168
|
}
|
|
163
169
|
get modelRuntime() {
|
|
164
170
|
return this._modelRuntime;
|
|
165
171
|
}
|
|
166
|
-
async _getRequiredRequestAuth(model) {
|
|
172
|
+
async _getRequiredRequestAuth(model, signal) {
|
|
167
173
|
let result;
|
|
168
174
|
try {
|
|
169
|
-
result = await this._modelRuntime.getAuth(model);
|
|
175
|
+
result = await this._modelRuntime.getAuth(model, { signal });
|
|
170
176
|
}
|
|
171
177
|
catch (error) {
|
|
172
178
|
const cause = error instanceof Error ? error.cause : undefined;
|
|
@@ -192,12 +198,12 @@ export class AgentSession {
|
|
|
192
198
|
}
|
|
193
199
|
throw new Error(formatNoApiKeyFoundMessage(model.provider));
|
|
194
200
|
}
|
|
195
|
-
async _getSummarizationRequestAuth(model) {
|
|
201
|
+
async _getSummarizationRequestAuth(model, signal) {
|
|
196
202
|
if (this.agent.streamFunction === streamSimple) {
|
|
197
|
-
return this._getRequiredRequestAuth(model);
|
|
203
|
+
return this._getRequiredRequestAuth(model, signal);
|
|
198
204
|
}
|
|
199
205
|
try {
|
|
200
|
-
const result = await this._modelRuntime.getAuth(model);
|
|
206
|
+
const result = await this._modelRuntime.getAuth(model, { signal });
|
|
201
207
|
if (!result)
|
|
202
208
|
return { model };
|
|
203
209
|
const requestModel = result.auth.baseUrl ? { ...model, baseUrl: result.auth.baseUrl } : model;
|
|
@@ -208,7 +214,9 @@ export class AgentSession {
|
|
|
208
214
|
env: result.env,
|
|
209
215
|
};
|
|
210
216
|
}
|
|
211
|
-
catch {
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (signal?.aborted)
|
|
219
|
+
throw error;
|
|
212
220
|
return { model };
|
|
213
221
|
}
|
|
214
222
|
}
|
|
@@ -273,7 +281,7 @@ export class AgentSession {
|
|
|
273
281
|
}
|
|
274
282
|
async _compactBeforeNextAssistantResponse(context) {
|
|
275
283
|
const model = this.model;
|
|
276
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
284
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
277
285
|
if (!model ||
|
|
278
286
|
model.contextWindow <= 0 ||
|
|
279
287
|
!shouldCompact(estimateContextTokens(context.messages).tokens, model.contextWindow, settings)) {
|
|
@@ -294,13 +302,25 @@ export class AgentSession {
|
|
|
294
302
|
const context = await this._compactBeforeNextAssistantResponse(turn.context);
|
|
295
303
|
const previousSnapshot = await previousPrepareNextTurnWithContext?.({ ...turn, context }, signal);
|
|
296
304
|
const nextContext = previousSnapshot?.context ?? context;
|
|
305
|
+
const runOptions = this._runSystemPromptOptions ?? this._baseSystemPromptOptions;
|
|
306
|
+
const options = normalizeBuildSystemPromptOptions({
|
|
307
|
+
...runOptions,
|
|
308
|
+
selectedTools: this.getActiveToolNames(),
|
|
309
|
+
toolSnippets: { ...this._baseSystemPromptOptions.toolSnippets, ...runOptions.toolSnippets },
|
|
310
|
+
toolGuidelines: { ...this._baseSystemPromptOptions.toolGuidelines, ...runOptions.toolGuidelines },
|
|
311
|
+
});
|
|
312
|
+
const updateMessage = this._preparePromptAndToolLoadout(options, nextContext.messages);
|
|
313
|
+
// Keep session.systemPrompt and ctx.getSystemPrompt() in step with what the provider sees.
|
|
314
|
+
this._runSystemPromptOptions = options;
|
|
297
315
|
return {
|
|
298
316
|
...previousSnapshot,
|
|
299
317
|
context: {
|
|
300
318
|
...nextContext,
|
|
301
|
-
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
302
319
|
tools: this.agent.state.tools.slice(),
|
|
303
320
|
},
|
|
321
|
+
messages: updateMessage
|
|
322
|
+
? [...(previousSnapshot?.messages ?? []), updateMessage]
|
|
323
|
+
: previousSnapshot?.messages,
|
|
304
324
|
model: this.agent.state.model,
|
|
305
325
|
thinkingLevel: this.agent.state.thinkingLevel,
|
|
306
326
|
};
|
|
@@ -345,6 +365,7 @@ export class AgentSession {
|
|
|
345
365
|
resolve();
|
|
346
366
|
}
|
|
347
367
|
async _emitAgentSettled() {
|
|
368
|
+
this._cacheWarmer?.onAgentSettled();
|
|
348
369
|
this._isAgentRunActive = false;
|
|
349
370
|
try {
|
|
350
371
|
await this._extensionRunner.emit({ type: "agent_settled" });
|
|
@@ -391,7 +412,8 @@ export class AgentSession {
|
|
|
391
412
|
// Persist as CustomMessageEntry
|
|
392
413
|
this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
|
|
393
414
|
}
|
|
394
|
-
else if (event.message.role === "
|
|
415
|
+
else if (event.message.role === "system" ||
|
|
416
|
+
event.message.role === "user" ||
|
|
395
417
|
event.message.role === "assistant" ||
|
|
396
418
|
event.message.role === "toolResult") {
|
|
397
419
|
// Regular LLM message - persist as SessionMessageEntry
|
|
@@ -427,6 +449,8 @@ export class AgentSession {
|
|
|
427
449
|
}
|
|
428
450
|
};
|
|
429
451
|
_willRetryAfterAgentEnd(event) {
|
|
452
|
+
if (this._agentRunAbortRequested)
|
|
453
|
+
return false;
|
|
430
454
|
const settings = this.settingsManager.getRetrySettings();
|
|
431
455
|
if (!settings.enabled || this._retryAttempt >= settings.maxRetries) {
|
|
432
456
|
return false;
|
|
@@ -595,6 +619,10 @@ export class AgentSession {
|
|
|
595
619
|
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().");
|
|
596
620
|
this._disconnectFromAgent();
|
|
597
621
|
this._eventListeners = [];
|
|
622
|
+
if (this._cacheWarmer) {
|
|
623
|
+
this._cacheWarmer.onWarmed = undefined;
|
|
624
|
+
this._cacheWarmer.cancel();
|
|
625
|
+
}
|
|
598
626
|
cleanupSessionResources(this.sessionId);
|
|
599
627
|
}
|
|
600
628
|
// =========================================================================
|
|
@@ -604,6 +632,15 @@ export class AgentSession {
|
|
|
604
632
|
get state() {
|
|
605
633
|
return this.agent.state;
|
|
606
634
|
}
|
|
635
|
+
/** Current cache-warming state and the policy inputs that produced it. */
|
|
636
|
+
get cacheWarmingStatus() {
|
|
637
|
+
return this._cacheWarmer?.status;
|
|
638
|
+
}
|
|
639
|
+
/** Persist the cache-warming mode and immediately reconcile active warming. */
|
|
640
|
+
setCacheWarmingMode(mode) {
|
|
641
|
+
this.settingsManager.setCacheWarmingMode(mode);
|
|
642
|
+
this._cacheWarmer?.onModeChanged();
|
|
643
|
+
}
|
|
607
644
|
/** Current model (may be undefined if not yet selected) */
|
|
608
645
|
get model() {
|
|
609
646
|
return this.agent.state.model;
|
|
@@ -620,9 +657,9 @@ export class AgentSession {
|
|
|
620
657
|
get isIdle() {
|
|
621
658
|
return !this._isAgentRunActive && !this.isCompacting;
|
|
622
659
|
}
|
|
623
|
-
/** Current effective system prompt
|
|
660
|
+
/** Current effective system prompt, including changes not yet sent to the model. */
|
|
624
661
|
get systemPrompt() {
|
|
625
|
-
return this.
|
|
662
|
+
return buildSystemPrompt(this._runSystemPromptOptions ?? this._baseSystemPromptOptions);
|
|
626
663
|
}
|
|
627
664
|
/** Current retry attempt (0 if not retrying) */
|
|
628
665
|
get retryAttempt() {
|
|
@@ -667,9 +704,7 @@ export class AgentSession {
|
|
|
667
704
|
}
|
|
668
705
|
}
|
|
669
706
|
this.agent.state.tools = tools;
|
|
670
|
-
|
|
671
|
-
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
672
|
-
this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
|
|
707
|
+
this._rebuildSystemPrompt(validToolNames);
|
|
673
708
|
}
|
|
674
709
|
/** Whether compaction or branch summarization is currently running */
|
|
675
710
|
get isCompacting() {
|
|
@@ -738,23 +773,17 @@ export class AgentSession {
|
|
|
738
773
|
_rebuildSystemPrompt(toolNames) {
|
|
739
774
|
const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
|
|
740
775
|
const toolSnippets = {};
|
|
741
|
-
const
|
|
742
|
-
for (const name of validToolNames) {
|
|
776
|
+
for (const name of this._toolRegistry.keys()) {
|
|
743
777
|
const snippet = this._toolPromptSnippets.get(name);
|
|
744
|
-
if (snippet)
|
|
778
|
+
if (snippet)
|
|
745
779
|
toolSnippets[name] = snippet;
|
|
746
|
-
}
|
|
747
|
-
const toolGuidelines = this._toolPromptGuidelines.get(name);
|
|
748
|
-
if (toolGuidelines) {
|
|
749
|
-
promptGuidelines.push(...toolGuidelines);
|
|
750
|
-
}
|
|
751
780
|
}
|
|
752
781
|
const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
|
|
753
782
|
const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
|
|
754
|
-
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") :
|
|
783
|
+
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : "";
|
|
755
784
|
const loadedSkills = this._resourceLoader.getSkills().skills;
|
|
756
785
|
const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
|
|
757
|
-
this._baseSystemPromptOptions = {
|
|
786
|
+
this._baseSystemPromptOptions = normalizeBuildSystemPromptOptions({
|
|
758
787
|
cwd: this._cwd,
|
|
759
788
|
skills: loadedSkills,
|
|
760
789
|
contextFiles: loadedContextFiles,
|
|
@@ -762,23 +791,87 @@ export class AgentSession {
|
|
|
762
791
|
appendSystemPrompt,
|
|
763
792
|
selectedTools: validToolNames,
|
|
764
793
|
toolSnippets,
|
|
765
|
-
|
|
794
|
+
toolGuidelines: Object.fromEntries(this._toolPromptGuidelines),
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Apply a prompt and tool loadout for the next request. Sets the executable tools and
|
|
799
|
+
* returns a system message patching the prompt sections the model currently has (replayed
|
|
800
|
+
* from `messages`), or undefined when the prompt is unchanged. Tool changes are declared by
|
|
801
|
+
* the agent loop before the request.
|
|
802
|
+
*
|
|
803
|
+
* A forced prompt does not affect the transcript: the structured sections are still diffed
|
|
804
|
+
* and persisted, and the forced text is projected onto the request by
|
|
805
|
+
* {@link _installAgentForcedPromptProjection}.
|
|
806
|
+
*/
|
|
807
|
+
_preparePromptAndToolLoadout(options, messages = this.agent.state.messages) {
|
|
808
|
+
options.selectedTools = [...new Set(options.selectedTools)].filter((name) => this._toolRegistry.has(name));
|
|
809
|
+
this.agent.state.tools = options.selectedTools.flatMap((name) => {
|
|
810
|
+
const tool = this._toolRegistry.get(name);
|
|
811
|
+
return tool ? [tool] : [];
|
|
812
|
+
});
|
|
813
|
+
const sections = diffSystemPromptSections(getCurrentSystemMessage(messages)?.sections ?? {}, buildSystemPromptSections(options));
|
|
814
|
+
return sections ? { role: "system", content: "", sections, timestamp: Date.now() } : undefined;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Send a forced prompt as the provider's leading system prompt without recording it.
|
|
818
|
+
*
|
|
819
|
+
* A `before_agent_start` handler that returns `systemPrompt` needs that exact text at the
|
|
820
|
+
* head of the request; a mid-conversation system message would leave the original prompt
|
|
821
|
+
* in place. The forced text is a rendering of the current prompt, so the transcript keeps
|
|
822
|
+
* its structured sections and the request is projected instead: the system messages
|
|
823
|
+
* collapse into one head holding the forced text and the current tools. Runs after the
|
|
824
|
+
* `context` extension handlers.
|
|
825
|
+
*/
|
|
826
|
+
_installAgentForcedPromptProjection() {
|
|
827
|
+
const previousTransformContext = this.agent.transformContext;
|
|
828
|
+
this.agent.transformContext = async (messages, signal) => {
|
|
829
|
+
const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
|
|
830
|
+
const forced = this._runSystemPromptOptions?.forceSystemPrompt;
|
|
831
|
+
if (forced === undefined)
|
|
832
|
+
return transformed;
|
|
833
|
+
const current = getCurrentSystemMessage(transformed);
|
|
834
|
+
const head = {
|
|
835
|
+
role: "system",
|
|
836
|
+
content: forced,
|
|
837
|
+
...(current?.toolsAdded ? { toolsAdded: current.toolsAdded } : {}),
|
|
838
|
+
timestamp: current?.timestamp ?? Date.now(),
|
|
839
|
+
};
|
|
840
|
+
return [head, ...transformed.filter((message) => message.role !== "system")];
|
|
766
841
|
};
|
|
767
|
-
|
|
842
|
+
}
|
|
843
|
+
/** Restore the active tool loadout declared by the session transcript, if it declares one. */
|
|
844
|
+
_restoreToolsFromTranscript() {
|
|
845
|
+
const current = getCurrentSystemMessage(this.sessionManager.buildSessionContext().messages);
|
|
846
|
+
if (!current)
|
|
847
|
+
return;
|
|
848
|
+
const toolNames = (current.toolsAdded ?? [])
|
|
849
|
+
.map((tool) => tool.name)
|
|
850
|
+
.filter((name) => this._toolRegistry.has(name));
|
|
851
|
+
this.agent.state.tools = toolNames.flatMap((name) => {
|
|
852
|
+
const registered = this._toolRegistry.get(name);
|
|
853
|
+
return registered ? [registered] : [];
|
|
854
|
+
});
|
|
855
|
+
this._rebuildSystemPrompt(toolNames);
|
|
768
856
|
}
|
|
769
857
|
// =========================================================================
|
|
770
858
|
// Prompting
|
|
771
859
|
// =========================================================================
|
|
772
860
|
async _runAgentPrompt(messages) {
|
|
861
|
+
this._agentRunAbortRequested = false;
|
|
773
862
|
this._isAgentRunActive = true;
|
|
774
863
|
try {
|
|
775
864
|
await this.agent.prompt(messages);
|
|
776
865
|
while (await this._handlePostAgentRun()) {
|
|
866
|
+
if (this._agentRunAbortRequested)
|
|
867
|
+
break;
|
|
777
868
|
await this.agent.continue();
|
|
778
869
|
}
|
|
779
870
|
}
|
|
780
871
|
finally {
|
|
781
|
-
this.
|
|
872
|
+
if (this._agentRunAbortRequested)
|
|
873
|
+
this._finishCancelledRetry();
|
|
874
|
+
this._runSystemPromptOptions = undefined;
|
|
782
875
|
this._flushPendingBashMessages();
|
|
783
876
|
this._flushPendingCustomMessages();
|
|
784
877
|
await this._emitAgentSettled();
|
|
@@ -787,11 +880,21 @@ export class AgentSession {
|
|
|
787
880
|
async _handlePostAgentRun() {
|
|
788
881
|
const msg = this._lastAssistantMessage;
|
|
789
882
|
this._lastAssistantMessage = undefined;
|
|
883
|
+
if (this._agentRunAbortRequested) {
|
|
884
|
+
this._finishCancelledRetry();
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
790
887
|
if (!msg) {
|
|
791
888
|
return false;
|
|
792
889
|
}
|
|
793
890
|
if (this._isRetryableError(msg) && (await this._prepareRetry(msg))) {
|
|
794
|
-
|
|
891
|
+
if (this._agentRunAbortRequested)
|
|
892
|
+
this._finishCancelledRetry();
|
|
893
|
+
return !this._agentRunAbortRequested;
|
|
894
|
+
}
|
|
895
|
+
if (this._agentRunAbortRequested) {
|
|
896
|
+
this._finishCancelledRetry();
|
|
897
|
+
return false;
|
|
795
898
|
}
|
|
796
899
|
if (msg.stopReason === "error" && this._retryAttempt > 0) {
|
|
797
900
|
this._emit({
|
|
@@ -803,11 +906,24 @@ export class AgentSession {
|
|
|
803
906
|
this._retryAttempt = 0;
|
|
804
907
|
}
|
|
805
908
|
if (await this._checkCompaction(msg)) {
|
|
806
|
-
return
|
|
909
|
+
return !this._agentRunAbortRequested;
|
|
807
910
|
}
|
|
808
911
|
// The agent loop drains both queues before emitting agent_end. Any messages
|
|
809
912
|
// here were queued by agent_end extension handlers and need a continuation.
|
|
810
|
-
return this.agent.hasQueuedMessages();
|
|
913
|
+
return !this._agentRunAbortRequested && this.agent.hasQueuedMessages();
|
|
914
|
+
}
|
|
915
|
+
async _runInputHandlers(text, images, source, streamingBehavior) {
|
|
916
|
+
if (!this._extensionRunner.hasHandlers("input")) {
|
|
917
|
+
return { text, images };
|
|
918
|
+
}
|
|
919
|
+
const inputResult = await this._extensionRunner.emitInput(text, images, source, streamingBehavior);
|
|
920
|
+
if (inputResult.action === "handled") {
|
|
921
|
+
return undefined;
|
|
922
|
+
}
|
|
923
|
+
if (inputResult.action === "transform") {
|
|
924
|
+
return { text: inputResult.text, images: inputResult.images ?? images };
|
|
925
|
+
}
|
|
926
|
+
return { text, images };
|
|
811
927
|
}
|
|
812
928
|
/**
|
|
813
929
|
* Send a prompt to the agent.
|
|
@@ -837,19 +953,12 @@ export class AgentSession {
|
|
|
837
953
|
throw new Error("Cannot submit a prompt while compaction is in progress. Wait for compaction to finish and retry.");
|
|
838
954
|
}
|
|
839
955
|
// Emit input event for extension interception (before skill/template expansion)
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
if (inputResult.action === "handled") {
|
|
845
|
-
preflightResult?.(true);
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
if (inputResult.action === "transform") {
|
|
849
|
-
currentText = inputResult.text;
|
|
850
|
-
currentImages = inputResult.images ?? currentImages;
|
|
851
|
-
}
|
|
956
|
+
const processedInput = await this._runInputHandlers(text, options?.images, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
|
|
957
|
+
if (!processedInput) {
|
|
958
|
+
preflightResult?.(true);
|
|
959
|
+
return;
|
|
852
960
|
}
|
|
961
|
+
const { text: currentText, images: currentImages } = processedInput;
|
|
853
962
|
// Expand skill commands (/skill:name args) and prompt templates (/template args)
|
|
854
963
|
let expandedText = currentText;
|
|
855
964
|
if (expandPromptTemplates) {
|
|
@@ -912,31 +1021,30 @@ export class AgentSession {
|
|
|
912
1021
|
}
|
|
913
1022
|
this._pendingNextTurnMessages = [];
|
|
914
1023
|
// Emit before_agent_start extension event
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
// Ensure we're using the base prompt (in case previous turn had modifications)
|
|
937
|
-
this._systemPromptOverride = undefined;
|
|
938
|
-
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
1024
|
+
const selectedToolsBefore = this._baseSystemPromptOptions.selectedTools;
|
|
1025
|
+
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptOptions);
|
|
1026
|
+
// Handlers may edit event.systemPromptOptions.selectedTools or call setActiveTools(),
|
|
1027
|
+
// which updates the live loadout instead. An explicit edit wins; otherwise the live
|
|
1028
|
+
// loadout is authoritative, so a setActiveTools() call is not undone here.
|
|
1029
|
+
const handlerEditedTools = result.systemPromptOptions.selectedTools.length !== selectedToolsBefore.length ||
|
|
1030
|
+
result.systemPromptOptions.selectedTools.some((name, index) => name !== selectedToolsBefore[index]);
|
|
1031
|
+
if (!handlerEditedTools)
|
|
1032
|
+
result.systemPromptOptions.selectedTools = this.getActiveToolNames();
|
|
1033
|
+
for (const msg of result.messages) {
|
|
1034
|
+
messages.push({
|
|
1035
|
+
role: "custom",
|
|
1036
|
+
customType: msg.customType,
|
|
1037
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1038
|
+
content: msg.content ?? [],
|
|
1039
|
+
display: msg.display,
|
|
1040
|
+
details: msg.details,
|
|
1041
|
+
timestamp: Date.now(),
|
|
1042
|
+
});
|
|
939
1043
|
}
|
|
1044
|
+
const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
|
|
1045
|
+
this._runSystemPromptOptions = result.systemPromptOptions;
|
|
1046
|
+
if (updateMessage)
|
|
1047
|
+
messages.unshift(updateMessage);
|
|
940
1048
|
}
|
|
941
1049
|
catch (error) {
|
|
942
1050
|
preflightResult?.(false);
|
|
@@ -1005,40 +1113,44 @@ export class AgentSession {
|
|
|
1005
1113
|
return text; // Return original on error
|
|
1006
1114
|
}
|
|
1007
1115
|
}
|
|
1116
|
+
async _queueUserInput(text, images, behavior, source) {
|
|
1117
|
+
if (text.startsWith("/")) {
|
|
1118
|
+
this._throwIfExtensionCommand(text);
|
|
1119
|
+
}
|
|
1120
|
+
const processedInput = await this._runInputHandlers(text, images, source, this.isStreaming ? behavior : undefined);
|
|
1121
|
+
if (!processedInput)
|
|
1122
|
+
return;
|
|
1123
|
+
let expandedText = this._expandSkillCommand(processedInput.text);
|
|
1124
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1125
|
+
if (behavior === "steer") {
|
|
1126
|
+
await this._queueSteer(expandedText, processedInput.images);
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
await this._queueFollowUp(expandedText, processedInput.images);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1008
1132
|
/**
|
|
1009
1133
|
* Queue a steering message while the agent is running.
|
|
1010
1134
|
* Delivered after the current assistant turn finishes executing its tool calls,
|
|
1011
1135
|
* before the next LLM call.
|
|
1012
1136
|
* Expands skill commands and prompt templates. Errors on extension commands.
|
|
1013
1137
|
* @param images Optional image attachments to include with the message
|
|
1138
|
+
* @param options Input source; defaults to interactive
|
|
1014
1139
|
* @throws Error if text is an extension command
|
|
1015
1140
|
*/
|
|
1016
|
-
async steer(text, images) {
|
|
1017
|
-
|
|
1018
|
-
if (text.startsWith("/")) {
|
|
1019
|
-
this._throwIfExtensionCommand(text);
|
|
1020
|
-
}
|
|
1021
|
-
// Expand skill commands and prompt templates
|
|
1022
|
-
let expandedText = this._expandSkillCommand(text);
|
|
1023
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1024
|
-
await this._queueSteer(expandedText, images);
|
|
1141
|
+
async steer(text, images, options) {
|
|
1142
|
+
await this._queueUserInput(text, images, "steer", options?.source ?? "interactive");
|
|
1025
1143
|
}
|
|
1026
1144
|
/**
|
|
1027
1145
|
* Queue a follow-up message to be processed after the agent finishes.
|
|
1028
1146
|
* Delivered only when agent has no more tool calls or steering messages.
|
|
1029
1147
|
* Expands skill commands and prompt templates. Errors on extension commands.
|
|
1030
1148
|
* @param images Optional image attachments to include with the message
|
|
1149
|
+
* @param options Input source; defaults to interactive
|
|
1031
1150
|
* @throws Error if text is an extension command
|
|
1032
1151
|
*/
|
|
1033
|
-
async followUp(text, images) {
|
|
1034
|
-
|
|
1035
|
-
if (text.startsWith("/")) {
|
|
1036
|
-
this._throwIfExtensionCommand(text);
|
|
1037
|
-
}
|
|
1038
|
-
// Expand skill commands and prompt templates
|
|
1039
|
-
let expandedText = this._expandSkillCommand(text);
|
|
1040
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1041
|
-
await this._queueFollowUp(expandedText, images);
|
|
1152
|
+
async followUp(text, images, options) {
|
|
1153
|
+
await this._queueUserInput(text, images, "followUp", options?.source ?? "interactive");
|
|
1042
1154
|
}
|
|
1043
1155
|
/**
|
|
1044
1156
|
* Internal: Queue a steering message (already expanded, no extension command check).
|
|
@@ -1220,6 +1332,9 @@ export class AgentSession {
|
|
|
1220
1332
|
* Abort current operation and wait for agent to become idle.
|
|
1221
1333
|
*/
|
|
1222
1334
|
async abort() {
|
|
1335
|
+
if (this._isAgentRunActive) {
|
|
1336
|
+
this._agentRunAbortRequested = true;
|
|
1337
|
+
}
|
|
1223
1338
|
this.abortRetry();
|
|
1224
1339
|
this.abortCompaction();
|
|
1225
1340
|
this.abortBranchSummary();
|
|
@@ -1476,13 +1591,15 @@ export class AgentSession {
|
|
|
1476
1591
|
this._compactionAbortController = new AbortController();
|
|
1477
1592
|
this._emit({ type: "compaction_start", reason: "manual" });
|
|
1478
1593
|
let fromExtension = false;
|
|
1594
|
+
let cancelledByExtension = false;
|
|
1479
1595
|
try {
|
|
1480
|
-
|
|
1596
|
+
const model = this.model;
|
|
1597
|
+
if (!model) {
|
|
1481
1598
|
throw new Error(formatNoModelSelectedMessage());
|
|
1482
1599
|
}
|
|
1483
|
-
const
|
|
1600
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
1601
|
+
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, this._compactionAbortController.signal);
|
|
1484
1602
|
const pathEntries = this.sessionManager.getBranch();
|
|
1485
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1486
1603
|
const preparation = prepareCompaction(pathEntries, settings);
|
|
1487
1604
|
if (!preparation) {
|
|
1488
1605
|
// Check why we can't compact
|
|
@@ -1504,6 +1621,7 @@ export class AgentSession {
|
|
|
1504
1621
|
signal: this._compactionAbortController.signal,
|
|
1505
1622
|
}));
|
|
1506
1623
|
if (result?.cancel) {
|
|
1624
|
+
cancelledByExtension = true;
|
|
1507
1625
|
throw new Error("Compaction cancelled");
|
|
1508
1626
|
}
|
|
1509
1627
|
if (result?.compaction) {
|
|
@@ -1573,7 +1691,7 @@ export class AgentSession {
|
|
|
1573
1691
|
}
|
|
1574
1692
|
catch (error) {
|
|
1575
1693
|
const message = error instanceof Error ? error.message : String(error);
|
|
1576
|
-
const aborted =
|
|
1694
|
+
const aborted = this._compactionAbortController.signal.aborted || cancelledByExtension;
|
|
1577
1695
|
const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
|
|
1578
1696
|
this._clearManualCompactionState();
|
|
1579
1697
|
this._emit({
|
|
@@ -1632,7 +1750,7 @@ export class AgentSession {
|
|
|
1632
1750
|
* @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
|
|
1633
1751
|
*/
|
|
1634
1752
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
1635
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1753
|
+
const settings = this.settingsManager.getCompactionSettings(this.model);
|
|
1636
1754
|
if (!settings.enabled)
|
|
1637
1755
|
return false;
|
|
1638
1756
|
// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
|
|
@@ -1737,22 +1855,28 @@ export class AgentSession {
|
|
|
1737
1855
|
* @returns Whether the post-run loop should call `agent.continue()`
|
|
1738
1856
|
*/
|
|
1739
1857
|
async _runAutoCompaction(reason, willRetry) {
|
|
1740
|
-
const
|
|
1858
|
+
const model = this.model;
|
|
1859
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
1860
|
+
let abortController;
|
|
1741
1861
|
let started = false;
|
|
1742
1862
|
let fromExtension = false;
|
|
1863
|
+
let cancelledByExtension = false;
|
|
1743
1864
|
try {
|
|
1744
|
-
if (!
|
|
1865
|
+
if (!model) {
|
|
1745
1866
|
return false;
|
|
1746
1867
|
}
|
|
1747
|
-
const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model);
|
|
1748
1868
|
const pathEntries = this.sessionManager.getBranch();
|
|
1749
1869
|
const preparation = prepareCompaction(pathEntries, settings);
|
|
1750
1870
|
if (!preparation) {
|
|
1751
1871
|
return false;
|
|
1752
1872
|
}
|
|
1753
|
-
|
|
1754
|
-
this._autoCompactionAbortController =
|
|
1873
|
+
abortController = new AbortController();
|
|
1874
|
+
this._autoCompactionAbortController = abortController;
|
|
1755
1875
|
started = true;
|
|
1876
|
+
this._emit({ type: "compaction_start", reason });
|
|
1877
|
+
abortController.signal.throwIfAborted();
|
|
1878
|
+
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
|
|
1879
|
+
abortController.signal.throwIfAborted();
|
|
1756
1880
|
let extensionCompaction;
|
|
1757
1881
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1758
1882
|
const extensionResult = (await this._extensionRunner.emit({
|
|
@@ -1762,29 +1886,18 @@ export class AgentSession {
|
|
|
1762
1886
|
customInstructions: undefined,
|
|
1763
1887
|
reason,
|
|
1764
1888
|
willRetry,
|
|
1765
|
-
signal:
|
|
1889
|
+
signal: abortController.signal,
|
|
1766
1890
|
}));
|
|
1767
1891
|
if (extensionResult?.cancel) {
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
reason,
|
|
1771
|
-
result: undefined,
|
|
1772
|
-
aborted: true,
|
|
1773
|
-
willRetry: false,
|
|
1774
|
-
});
|
|
1775
|
-
await this._emitSessionCompactFailed({
|
|
1776
|
-
reason,
|
|
1777
|
-
aborted: true,
|
|
1778
|
-
willRetry: false,
|
|
1779
|
-
fromExtension: false,
|
|
1780
|
-
});
|
|
1781
|
-
return false;
|
|
1892
|
+
cancelledByExtension = true;
|
|
1893
|
+
throw new Error("Compaction cancelled");
|
|
1782
1894
|
}
|
|
1783
1895
|
if (extensionResult?.compaction) {
|
|
1784
1896
|
extensionCompaction = extensionResult.compaction;
|
|
1785
1897
|
fromExtension = true;
|
|
1786
1898
|
}
|
|
1787
1899
|
}
|
|
1900
|
+
abortController.signal.throwIfAborted();
|
|
1788
1901
|
let summary;
|
|
1789
1902
|
let firstKeptEntryId;
|
|
1790
1903
|
let tokensBefore;
|
|
@@ -1800,29 +1913,14 @@ export class AgentSession {
|
|
|
1800
1913
|
}
|
|
1801
1914
|
else {
|
|
1802
1915
|
// Shared default summary generator, also used by manual compaction.
|
|
1803
|
-
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined,
|
|
1916
|
+
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, abortController.signal, env, reason);
|
|
1804
1917
|
summary = compactResult.summary;
|
|
1805
1918
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
1806
1919
|
tokensBefore = compactResult.tokensBefore;
|
|
1807
1920
|
usage = compactResult.usage;
|
|
1808
1921
|
details = compactResult.details;
|
|
1809
1922
|
}
|
|
1810
|
-
|
|
1811
|
-
this._emit({
|
|
1812
|
-
type: "compaction_end",
|
|
1813
|
-
reason,
|
|
1814
|
-
result: undefined,
|
|
1815
|
-
aborted: true,
|
|
1816
|
-
willRetry: false,
|
|
1817
|
-
});
|
|
1818
|
-
await this._emitSessionCompactFailed({
|
|
1819
|
-
reason,
|
|
1820
|
-
aborted: true,
|
|
1821
|
-
willRetry: false,
|
|
1822
|
-
fromExtension,
|
|
1823
|
-
});
|
|
1824
|
-
return false;
|
|
1825
|
-
}
|
|
1923
|
+
abortController.signal.throwIfAborted();
|
|
1826
1924
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
1827
1925
|
const newEntries = this.sessionManager.getEntries();
|
|
1828
1926
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
@@ -1865,23 +1963,26 @@ export class AgentSession {
|
|
|
1865
1963
|
return this.agent.hasQueuedMessages();
|
|
1866
1964
|
}
|
|
1867
1965
|
catch (error) {
|
|
1868
|
-
const
|
|
1966
|
+
const message = error instanceof Error ? error.message : "compaction failed";
|
|
1967
|
+
const aborted = abortController?.signal.aborted === true || cancelledByExtension;
|
|
1869
1968
|
if (started) {
|
|
1870
|
-
const
|
|
1871
|
-
?
|
|
1872
|
-
:
|
|
1969
|
+
const errorMessage = aborted
|
|
1970
|
+
? undefined
|
|
1971
|
+
: reason === "overflow"
|
|
1972
|
+
? `Context overflow recovery failed: ${message}`
|
|
1973
|
+
: `Auto-compaction failed: ${message}`;
|
|
1873
1974
|
this._emit({
|
|
1874
1975
|
type: "compaction_end",
|
|
1875
1976
|
reason,
|
|
1876
1977
|
result: undefined,
|
|
1877
|
-
aborted
|
|
1978
|
+
aborted,
|
|
1878
1979
|
willRetry: false,
|
|
1879
|
-
errorMessage
|
|
1980
|
+
errorMessage,
|
|
1880
1981
|
});
|
|
1881
1982
|
await this._emitSessionCompactFailed({
|
|
1882
1983
|
reason,
|
|
1883
|
-
errorMessage
|
|
1884
|
-
aborted
|
|
1984
|
+
errorMessage,
|
|
1985
|
+
aborted,
|
|
1885
1986
|
willRetry: false,
|
|
1886
1987
|
fromExtension,
|
|
1887
1988
|
});
|
|
@@ -1889,7 +1990,9 @@ export class AgentSession {
|
|
|
1889
1990
|
return false;
|
|
1890
1991
|
}
|
|
1891
1992
|
finally {
|
|
1892
|
-
this._autoCompactionAbortController
|
|
1993
|
+
if (this._autoCompactionAbortController === abortController) {
|
|
1994
|
+
this._autoCompactionAbortController = undefined;
|
|
1995
|
+
}
|
|
1893
1996
|
this._resolveIdleWaitIfIdle();
|
|
1894
1997
|
}
|
|
1895
1998
|
}
|
|
@@ -1940,8 +2043,7 @@ export class AgentSession {
|
|
|
1940
2043
|
themePaths: this.buildExtensionResourcePaths(themePaths),
|
|
1941
2044
|
};
|
|
1942
2045
|
this._resourceLoader.extendResources(extensionPaths);
|
|
1943
|
-
this.
|
|
1944
|
-
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
2046
|
+
this._rebuildSystemPrompt(this.getActiveToolNames());
|
|
1945
2047
|
}
|
|
1946
2048
|
buildExtensionResourcePaths(entries) {
|
|
1947
2049
|
return entries.map((entry) => {
|
|
@@ -2279,6 +2381,18 @@ export class AgentSession {
|
|
|
2279
2381
|
},
|
|
2280
2382
|
};
|
|
2281
2383
|
}
|
|
2384
|
+
_finishCancelledRetry() {
|
|
2385
|
+
if (this._retryAttempt === 0)
|
|
2386
|
+
return;
|
|
2387
|
+
const attempt = this._retryAttempt;
|
|
2388
|
+
this._retryAttempt = 0;
|
|
2389
|
+
this._emit({
|
|
2390
|
+
type: "auto_retry_end",
|
|
2391
|
+
success: false,
|
|
2392
|
+
attempt,
|
|
2393
|
+
finalError: "Retry cancelled",
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2282
2396
|
/**
|
|
2283
2397
|
* Prepare a retryable error for continuation with exponential backoff.
|
|
2284
2398
|
* @returns true if the caller should continue the agent, false otherwise
|
|
@@ -2294,7 +2408,7 @@ export class AgentSession {
|
|
|
2294
2408
|
this._retryAttempt--;
|
|
2295
2409
|
return false;
|
|
2296
2410
|
}
|
|
2297
|
-
const delayMs = settings
|
|
2411
|
+
const delayMs = retryDelayMs(settings, this._retryAttempt);
|
|
2298
2412
|
this._emit({
|
|
2299
2413
|
type: "auto_retry_start",
|
|
2300
2414
|
attempt: this._retryAttempt,
|
|
@@ -2314,14 +2428,7 @@ export class AgentSession {
|
|
|
2314
2428
|
}
|
|
2315
2429
|
catch {
|
|
2316
2430
|
// Aborted during sleep - emit end event so UI can clean up
|
|
2317
|
-
|
|
2318
|
-
this._retryAttempt = 0;
|
|
2319
|
-
this._emit({
|
|
2320
|
-
type: "auto_retry_end",
|
|
2321
|
-
success: false,
|
|
2322
|
-
attempt,
|
|
2323
|
-
finalError: "Retry cancelled",
|
|
2324
|
-
});
|
|
2431
|
+
this._finishCancelledRetry();
|
|
2325
2432
|
return false;
|
|
2326
2433
|
}
|
|
2327
2434
|
finally {
|
|
@@ -2472,6 +2579,9 @@ export class AgentSession {
|
|
|
2472
2579
|
if (this.isStreaming) {
|
|
2473
2580
|
throw new Error("Wait for the current response to finish before navigating the session tree.");
|
|
2474
2581
|
}
|
|
2582
|
+
if (this.isCompacting) {
|
|
2583
|
+
throw new Error("Wait for the current compaction or tree navigation to finish before navigating the session tree.");
|
|
2584
|
+
}
|
|
2475
2585
|
const oldLeafId = this.sessionManager.getLeafId();
|
|
2476
2586
|
// No-op if already at target
|
|
2477
2587
|
if (targetId === oldLeafId) {
|
|
@@ -2614,6 +2724,7 @@ export class AgentSession {
|
|
|
2614
2724
|
// Update agent state
|
|
2615
2725
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2616
2726
|
this.agent.state.messages = sessionContext.messages;
|
|
2727
|
+
this._restoreToolsFromTranscript();
|
|
2617
2728
|
// Emit session_tree event
|
|
2618
2729
|
await this._extensionRunner.emit({
|
|
2619
2730
|
type: "session_tree",
|
|
@@ -2661,7 +2772,10 @@ export class AgentSession {
|
|
|
2661
2772
|
let toolCalls = 0;
|
|
2662
2773
|
const usageTotals = createUsageTotals();
|
|
2663
2774
|
for (const entry of this.sessionManager.getEntries()) {
|
|
2664
|
-
if (
|
|
2775
|
+
if (entry.type === "usage") {
|
|
2776
|
+
addUsageToTotals(usageTotals, entry.usage);
|
|
2777
|
+
}
|
|
2778
|
+
else if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
|
|
2665
2779
|
addUsageToTotals(usageTotals, entry.usage);
|
|
2666
2780
|
}
|
|
2667
2781
|
if (entry.type !== "message")
|
|
@@ -2775,6 +2889,30 @@ export class AgentSession {
|
|
|
2775
2889
|
exportToJsonl(outputPath) {
|
|
2776
2890
|
return exportSessionToJsonl(this.sessionManager, outputPath);
|
|
2777
2891
|
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Ask the current model to describe what went wrong in this session for a bug report.
|
|
2894
|
+
* Used when the user declines to share the transcript itself.
|
|
2895
|
+
*/
|
|
2896
|
+
async summarizeForBugReport(options) {
|
|
2897
|
+
const model = this.model;
|
|
2898
|
+
if (!model) {
|
|
2899
|
+
throw new Error("No model selected");
|
|
2900
|
+
}
|
|
2901
|
+
const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
|
|
2902
|
+
return generateBugReportSummary({
|
|
2903
|
+
messages: this.messages,
|
|
2904
|
+
hint: options.hint,
|
|
2905
|
+
model: requestModel,
|
|
2906
|
+
apiKey,
|
|
2907
|
+
headers,
|
|
2908
|
+
env,
|
|
2909
|
+
signal: options.signal,
|
|
2910
|
+
thinkingLevel: this.thinkingLevel,
|
|
2911
|
+
streamFn: this.agent.streamFunction,
|
|
2912
|
+
retry: this.settingsManager.getRetrySettings(),
|
|
2913
|
+
sessionId: this.sessionId,
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2778
2916
|
// =========================================================================
|
|
2779
2917
|
// Utilities
|
|
2780
2918
|
// =========================================================================
|