@averyyy/pi-coding-agent 0.85.1-piclient.9 → 0.86.1-piclient.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +109 -0
- package/README.md +3 -1
- package/dist/bundle/chunks/anthropic-messages-MYU5ZMRF.js +6 -0
- package/dist/bundle/chunks/azure-openai-responses-QJ7SPP5M.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +47 -38
- package/dist/bundle/chunks/chunk-7L6OAVKC.js +1663 -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-UZNGGY6N.js +11 -0
- package/dist/bundle/chunks/chunk-XREANBOX.js +2 -0
- package/dist/bundle/chunks/devin-runtime.js +7 -2
- 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/meta.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-SG7PGUUZ.js +10 -0
- package/dist/bundle/chunks/openai-completions-CYGM3XXP.js +7 -0
- package/dist/bundle/chunks/openai-responses-AH7QTG23.js +2 -0
- package/dist/bundle/chunks/{pi-messages-PML4SPVJ.js → pi-messages-GFKBWJHZ.js} +1 -1
- package/dist/bundle/chunks/virtual-modules-SC5AFIK5.js +2 -0
- package/dist/bundle/cli-runtime.js +3 -0
- package/dist/bundle/cli.js +4 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +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 +362 -183
- 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/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +23 -18
- 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 +2 -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 +61 -41
- 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 +19 -5
- 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 +16 -0
- package/dist/modes/interactive/bug-report.d.ts.map +1 -0
- package/dist/modes/interactive/bug-report.js +206 -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-editor.d.ts +4 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +7 -2
- package/dist/modes/interactive/components/extension-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 +15 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +235 -86
- 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 +53 -81
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +111 -121
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/wsl.d.ts +3 -0
- package/dist/utils/wsl.d.ts.map +1 -0
- package/dist/utils/wsl.js +15 -0
- package/dist/utils/wsl.js.map +1 -0
- 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 +10 -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/package.json +17 -23
- package/dist/bundle/chunks/anthropic-messages-VWZZOSJQ.js +0 -6
- package/dist/bundle/chunks/azure-openai-responses-B467Y57F.js +0 -2
- package/dist/bundle/chunks/chunk-6WPJTKUF.js +0 -11
- 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-ORJJLIYM.js +0 -1648
- 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-MERYAUSE.js +0 -10
- package/dist/bundle/chunks/openai-completions-EKZT2IH2.js +0 -7
- package/dist/bundle/chunks/openai-responses-YZ6M7S4X.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 { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname } from "node:path";
|
|
17
|
-
import { contentText } from "@earendil-works/pi-ai";
|
|
17
|
+
import { contentText, getCurrentSystemMessage, normalizeContext, 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";
|
|
@@ -23,6 +23,7 @@ import { sleep } from "../utils/sleep.js";
|
|
|
23
23
|
import { normalizeToolResultImages } from "../utils/tool-result-images.js";
|
|
24
24
|
import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
|
|
25
25
|
import { executeBashWithOperations } from "./bash-executor.js";
|
|
26
|
+
import { generateBugReportSummary } from "./bug-report.js";
|
|
26
27
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
27
28
|
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
|
|
28
29
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
@@ -34,7 +35,7 @@ import { cancelPiServerOperations, compactPiServer, } from "./pi-server-client.j
|
|
|
34
35
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
35
36
|
import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
|
|
36
37
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
37
|
-
import { buildSystemPrompt } from "./system-prompt.js";
|
|
38
|
+
import { buildSystemPrompt, buildSystemPromptSections, diffSystemPromptSections, normalizeBuildSystemPromptOptions, } from "./system-prompt.js";
|
|
38
39
|
import { createLocalBashOperations } from "./tools/bash.js";
|
|
39
40
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
40
41
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
@@ -80,6 +81,26 @@ const AUTO_SESSION_NAME_PROMPT = "Name this coding session from the transcript b
|
|
|
80
81
|
function isPiServerMode() {
|
|
81
82
|
return process.env.PI_SERVER_MODE === "true";
|
|
82
83
|
}
|
|
84
|
+
/** Project the coding-agent session tree into the legacy pi-server wire tree. */
|
|
85
|
+
function buildPiServerSessionTree(entries, leafId) {
|
|
86
|
+
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
87
|
+
const usageIds = new Set(entries.filter((entry) => entry.type === "usage").map((entry) => entry.id));
|
|
88
|
+
const resolveParentId = (parentId) => {
|
|
89
|
+
let current = parentId;
|
|
90
|
+
const visited = new Set();
|
|
91
|
+
while (current !== null && usageIds.has(current)) {
|
|
92
|
+
if (visited.has(current))
|
|
93
|
+
throw new Error(`Usage entry parent cycle at ${current}`);
|
|
94
|
+
visited.add(current);
|
|
95
|
+
current = byId.get(current)?.parentId ?? null;
|
|
96
|
+
}
|
|
97
|
+
return current;
|
|
98
|
+
};
|
|
99
|
+
const legacyEntries = entries
|
|
100
|
+
.filter((entry) => entry.type !== "usage")
|
|
101
|
+
.map((entry) => ({ ...entry, parentId: resolveParentId(entry.parentId) }));
|
|
102
|
+
return { entries: legacyEntries, leafId: resolveParentId(leafId) };
|
|
103
|
+
}
|
|
83
104
|
function toProviderReasoning(thinkingLevel) {
|
|
84
105
|
return thinkingLevel === "off" ? undefined : thinkingLevel;
|
|
85
106
|
}
|
|
@@ -138,7 +159,6 @@ function sanitizeGeneratedSessionName(text) {
|
|
|
138
159
|
return undefined;
|
|
139
160
|
return title.length > AUTO_SESSION_NAME_MAX_CHARS ? title.slice(0, AUTO_SESSION_NAME_MAX_CHARS).trim() : title;
|
|
140
161
|
}
|
|
141
|
-
// ============================================================================
|
|
142
162
|
// AgentSession Class
|
|
143
163
|
// ============================================================================
|
|
144
164
|
export class AgentSession {
|
|
@@ -150,6 +170,7 @@ export class AgentSession {
|
|
|
150
170
|
_unsubscribeAgent;
|
|
151
171
|
_eventListeners = [];
|
|
152
172
|
_isAgentRunActive = false;
|
|
173
|
+
_agentRunAbortRequested = false;
|
|
153
174
|
_idleWaitPromise;
|
|
154
175
|
_resolveIdleWait;
|
|
155
176
|
_abortPromise;
|
|
@@ -207,15 +228,15 @@ export class AgentSession {
|
|
|
207
228
|
_autoSessionNameEnabled;
|
|
208
229
|
_autoSessionNameAttempted = false;
|
|
209
230
|
_modelRuntime;
|
|
231
|
+
_cacheWarmer;
|
|
210
232
|
// Tool registry for extension getTools/setTools
|
|
211
233
|
_toolRegistry = new Map();
|
|
212
234
|
_toolDefinitions = new Map();
|
|
213
235
|
_toolPromptSnippets = new Map();
|
|
214
236
|
_toolPromptGuidelines = new Map();
|
|
215
|
-
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
216
|
-
_baseSystemPrompt = "";
|
|
217
237
|
_baseSystemPromptOptions;
|
|
218
|
-
|
|
238
|
+
/** Prompt options after before_agent_start mutations for the active run. */
|
|
239
|
+
_runSystemPromptOptions;
|
|
219
240
|
constructor(config) {
|
|
220
241
|
this.agent = config.agent;
|
|
221
242
|
this.sessionManager = config.sessionManager;
|
|
@@ -225,6 +246,10 @@ export class AgentSession {
|
|
|
225
246
|
this._customTools = config.customTools ?? [];
|
|
226
247
|
this._cwd = config.cwd;
|
|
227
248
|
this._modelRuntime = config.modelRuntime;
|
|
249
|
+
this._cacheWarmer = config.cacheWarmer;
|
|
250
|
+
if (this._cacheWarmer) {
|
|
251
|
+
this._cacheWarmer.onWarmed = (entry) => this._emit({ type: "entry_appended", entry });
|
|
252
|
+
}
|
|
228
253
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
229
254
|
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
230
255
|
this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
|
|
@@ -240,10 +265,13 @@ export class AgentSession {
|
|
|
240
265
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
241
266
|
this._installAgentToolHooks();
|
|
242
267
|
this._installAgentNextTurnRefresh();
|
|
268
|
+
this._installAgentForcedPromptProjection();
|
|
243
269
|
this._buildRuntime({
|
|
244
270
|
activeToolNames: this._initialActiveToolNames,
|
|
245
271
|
includeAllExtensionTools: true,
|
|
246
272
|
});
|
|
273
|
+
if (this._initialActiveToolNames === undefined)
|
|
274
|
+
this._restoreToolsFromTranscript();
|
|
247
275
|
}
|
|
248
276
|
get modelRuntime() {
|
|
249
277
|
return this._modelRuntime;
|
|
@@ -258,10 +286,10 @@ export class AgentSession {
|
|
|
258
286
|
messageCount: snapshot.messages.length,
|
|
259
287
|
});
|
|
260
288
|
}
|
|
261
|
-
async _getRequiredRequestAuth(model) {
|
|
289
|
+
async _getRequiredRequestAuth(model, signal) {
|
|
262
290
|
let result;
|
|
263
291
|
try {
|
|
264
|
-
result = await this._modelRuntime.getAuth(model);
|
|
292
|
+
result = await this._modelRuntime.getAuth(model, { signal });
|
|
265
293
|
}
|
|
266
294
|
catch (error) {
|
|
267
295
|
const cause = error instanceof Error ? error.cause : undefined;
|
|
@@ -287,12 +315,12 @@ export class AgentSession {
|
|
|
287
315
|
}
|
|
288
316
|
throw new Error(formatNoApiKeyFoundMessage(model.provider));
|
|
289
317
|
}
|
|
290
|
-
async _getSummarizationRequestAuth(model) {
|
|
318
|
+
async _getSummarizationRequestAuth(model, signal) {
|
|
291
319
|
if (this.agent.streamFunction === streamSimple) {
|
|
292
|
-
return this._getRequiredRequestAuth(model);
|
|
320
|
+
return this._getRequiredRequestAuth(model, signal);
|
|
293
321
|
}
|
|
294
322
|
try {
|
|
295
|
-
const result = await this._modelRuntime.getAuth(model);
|
|
323
|
+
const result = await this._modelRuntime.getAuth(model, { signal });
|
|
296
324
|
if (!result)
|
|
297
325
|
return { model };
|
|
298
326
|
const requestModel = result.auth.baseUrl ? { ...model, baseUrl: result.auth.baseUrl } : model;
|
|
@@ -303,7 +331,9 @@ export class AgentSession {
|
|
|
303
331
|
env: result.env,
|
|
304
332
|
};
|
|
305
333
|
}
|
|
306
|
-
catch {
|
|
334
|
+
catch (error) {
|
|
335
|
+
if (signal?.aborted)
|
|
336
|
+
throw error;
|
|
307
337
|
return { model };
|
|
308
338
|
}
|
|
309
339
|
}
|
|
@@ -375,14 +405,26 @@ export class AgentSession {
|
|
|
375
405
|
const compactedContext = await this._compactBeforeNextProviderRequest(turn, signal);
|
|
376
406
|
const nextTurn = compactedContext ? { ...turn, context: compactedContext } : turn;
|
|
377
407
|
const previousSnapshot = await previousPrepareNextTurnWithContext?.(nextTurn, signal);
|
|
378
|
-
const
|
|
408
|
+
const nextContext = previousSnapshot?.context ?? nextTurn.context;
|
|
409
|
+
const runOptions = this._runSystemPromptOptions ?? this._baseSystemPromptOptions;
|
|
410
|
+
const options = normalizeBuildSystemPromptOptions({
|
|
411
|
+
...runOptions,
|
|
412
|
+
selectedTools: this.getActiveToolNames(),
|
|
413
|
+
toolSnippets: { ...this._baseSystemPromptOptions.toolSnippets, ...runOptions.toolSnippets },
|
|
414
|
+
toolGuidelines: { ...this._baseSystemPromptOptions.toolGuidelines, ...runOptions.toolGuidelines },
|
|
415
|
+
});
|
|
416
|
+
const updateMessage = this._preparePromptAndToolLoadout(options, nextContext.messages);
|
|
417
|
+
// Keep session.systemPrompt and ctx.getSystemPrompt() in step with what the provider sees.
|
|
418
|
+
this._runSystemPromptOptions = options;
|
|
379
419
|
return {
|
|
380
420
|
...previousSnapshot,
|
|
381
421
|
context: {
|
|
382
|
-
...
|
|
383
|
-
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
422
|
+
...nextContext,
|
|
384
423
|
tools: this.agent.state.tools.slice(),
|
|
385
424
|
},
|
|
425
|
+
messages: updateMessage
|
|
426
|
+
? [...(previousSnapshot?.messages ?? []), updateMessage]
|
|
427
|
+
: previousSnapshot?.messages,
|
|
386
428
|
model: this.agent.state.model,
|
|
387
429
|
thinkingLevel: this.agent.state.thinkingLevel,
|
|
388
430
|
};
|
|
@@ -427,6 +469,7 @@ export class AgentSession {
|
|
|
427
469
|
resolve();
|
|
428
470
|
}
|
|
429
471
|
async _emitAgentSettled() {
|
|
472
|
+
this._cacheWarmer?.onAgentSettled();
|
|
430
473
|
this._isAgentRunActive = false;
|
|
431
474
|
try {
|
|
432
475
|
await this._extensionRunner.emit({ type: "agent_settled" });
|
|
@@ -473,7 +516,8 @@ export class AgentSession {
|
|
|
473
516
|
// Persist as CustomMessageEntry
|
|
474
517
|
this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
|
|
475
518
|
}
|
|
476
|
-
else if (event.message.role === "
|
|
519
|
+
else if (event.message.role === "system" ||
|
|
520
|
+
event.message.role === "user" ||
|
|
477
521
|
event.message.role === "assistant" ||
|
|
478
522
|
event.message.role === "toolResult") {
|
|
479
523
|
// Regular LLM message - persist as SessionMessageEntry
|
|
@@ -509,6 +553,8 @@ export class AgentSession {
|
|
|
509
553
|
}
|
|
510
554
|
};
|
|
511
555
|
_willRetryAfterAgentEnd(event) {
|
|
556
|
+
if (this._agentRunAbortRequested)
|
|
557
|
+
return false;
|
|
512
558
|
const settings = this.settingsManager.getRetrySettings();
|
|
513
559
|
if (!settings.enabled || this._retryAttempt >= settings.maxRetries) {
|
|
514
560
|
return false;
|
|
@@ -733,6 +779,10 @@ export class AgentSession {
|
|
|
733
779
|
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().");
|
|
734
780
|
this._disconnectFromAgent();
|
|
735
781
|
this._eventListeners = [];
|
|
782
|
+
if (this._cacheWarmer) {
|
|
783
|
+
this._cacheWarmer.onWarmed = undefined;
|
|
784
|
+
this._cacheWarmer.cancel();
|
|
785
|
+
}
|
|
736
786
|
cleanupSessionResources(this.sessionId);
|
|
737
787
|
}
|
|
738
788
|
// =========================================================================
|
|
@@ -742,6 +792,15 @@ export class AgentSession {
|
|
|
742
792
|
get state() {
|
|
743
793
|
return this.agent.state;
|
|
744
794
|
}
|
|
795
|
+
/** Current cache-warming state and the policy inputs that produced it. */
|
|
796
|
+
get cacheWarmingStatus() {
|
|
797
|
+
return this._cacheWarmer?.status;
|
|
798
|
+
}
|
|
799
|
+
/** Persist the cache-warming mode and immediately reconcile active warming. */
|
|
800
|
+
setCacheWarmingMode(mode) {
|
|
801
|
+
this.settingsManager.setCacheWarmingMode(mode);
|
|
802
|
+
this._cacheWarmer?.onModeChanged();
|
|
803
|
+
}
|
|
745
804
|
/** Current model (may be undefined if not yet selected) */
|
|
746
805
|
get model() {
|
|
747
806
|
return this.agent.state.model;
|
|
@@ -766,9 +825,9 @@ export class AgentSession {
|
|
|
766
825
|
get isIdle() {
|
|
767
826
|
return !this._isAgentRunActive && !this.isCompacting;
|
|
768
827
|
}
|
|
769
|
-
/** Current effective system prompt
|
|
828
|
+
/** Current effective system prompt, including changes not yet sent to the model. */
|
|
770
829
|
get systemPrompt() {
|
|
771
|
-
return this.
|
|
830
|
+
return buildSystemPrompt(this._runSystemPromptOptions ?? this._baseSystemPromptOptions);
|
|
772
831
|
}
|
|
773
832
|
/** Current retry attempt (0 if not retrying) */
|
|
774
833
|
get retryAttempt() {
|
|
@@ -813,9 +872,7 @@ export class AgentSession {
|
|
|
813
872
|
}
|
|
814
873
|
}
|
|
815
874
|
this.agent.state.tools = tools;
|
|
816
|
-
|
|
817
|
-
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
818
|
-
this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
|
|
875
|
+
this._rebuildSystemPrompt(validToolNames);
|
|
819
876
|
}
|
|
820
877
|
/** Whether compaction or branch summarization is currently running */
|
|
821
878
|
get isCompacting() {
|
|
@@ -884,23 +941,17 @@ export class AgentSession {
|
|
|
884
941
|
_rebuildSystemPrompt(toolNames) {
|
|
885
942
|
const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
|
|
886
943
|
const toolSnippets = {};
|
|
887
|
-
const
|
|
888
|
-
for (const name of validToolNames) {
|
|
944
|
+
for (const name of this._toolRegistry.keys()) {
|
|
889
945
|
const snippet = this._toolPromptSnippets.get(name);
|
|
890
|
-
if (snippet)
|
|
946
|
+
if (snippet)
|
|
891
947
|
toolSnippets[name] = snippet;
|
|
892
|
-
}
|
|
893
|
-
const toolGuidelines = this._toolPromptGuidelines.get(name);
|
|
894
|
-
if (toolGuidelines) {
|
|
895
|
-
promptGuidelines.push(...toolGuidelines);
|
|
896
|
-
}
|
|
897
948
|
}
|
|
898
949
|
const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
|
|
899
950
|
const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
|
|
900
|
-
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") :
|
|
951
|
+
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : "";
|
|
901
952
|
const loadedSkills = this._resourceLoader.getSkills().skills;
|
|
902
953
|
const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
|
|
903
|
-
this._baseSystemPromptOptions = {
|
|
954
|
+
this._baseSystemPromptOptions = normalizeBuildSystemPromptOptions({
|
|
904
955
|
cwd: this._cwd,
|
|
905
956
|
skills: loadedSkills,
|
|
906
957
|
contextFiles: loadedContextFiles,
|
|
@@ -908,21 +959,81 @@ export class AgentSession {
|
|
|
908
959
|
appendSystemPrompt,
|
|
909
960
|
selectedTools: validToolNames,
|
|
910
961
|
toolSnippets,
|
|
911
|
-
|
|
962
|
+
toolGuidelines: Object.fromEntries(this._toolPromptGuidelines),
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Apply a prompt and tool loadout for the next request. Sets the executable tools and
|
|
967
|
+
* returns a system message patching the prompt sections the model currently has (replayed
|
|
968
|
+
* from `messages`), or undefined when the prompt is unchanged. Tool changes are declared by
|
|
969
|
+
* the agent loop before the request.
|
|
970
|
+
*
|
|
971
|
+
* A forced prompt does not affect the transcript: the structured sections are still diffed
|
|
972
|
+
* and persisted, and the forced text is projected onto the request by
|
|
973
|
+
* {@link _installAgentForcedPromptProjection}.
|
|
974
|
+
*/
|
|
975
|
+
_preparePromptAndToolLoadout(options, messages = this.agent.state.messages) {
|
|
976
|
+
options.selectedTools = [...new Set(options.selectedTools)].filter((name) => this._toolRegistry.has(name));
|
|
977
|
+
this.agent.state.tools = options.selectedTools.flatMap((name) => {
|
|
978
|
+
const tool = this._toolRegistry.get(name);
|
|
979
|
+
return tool ? [tool] : [];
|
|
980
|
+
});
|
|
981
|
+
const sections = diffSystemPromptSections(getCurrentSystemMessage(messages)?.sections ?? {}, buildSystemPromptSections(options));
|
|
982
|
+
return sections ? { role: "system", content: "", sections, timestamp: Date.now() } : undefined;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Send a forced prompt as the provider's leading system prompt without recording it.
|
|
986
|
+
*
|
|
987
|
+
* A `before_agent_start` handler that returns `systemPrompt` needs that exact text at the
|
|
988
|
+
* head of the request; a mid-conversation system message would leave the original prompt
|
|
989
|
+
* in place. The forced text is a rendering of the current prompt, so the transcript keeps
|
|
990
|
+
* its structured sections and the request is projected instead: the system messages
|
|
991
|
+
* collapse into one head holding the forced text and the current tools. Runs after the
|
|
992
|
+
* `context` extension handlers.
|
|
993
|
+
*/
|
|
994
|
+
_installAgentForcedPromptProjection() {
|
|
995
|
+
const previousTransformContext = this.agent.transformContext;
|
|
996
|
+
this.agent.transformContext = async (messages, signal) => {
|
|
997
|
+
const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
|
|
998
|
+
const forced = this._runSystemPromptOptions?.forceSystemPrompt;
|
|
999
|
+
if (forced === undefined)
|
|
1000
|
+
return transformed;
|
|
1001
|
+
const current = getCurrentSystemMessage(transformed);
|
|
1002
|
+
const head = {
|
|
1003
|
+
role: "system",
|
|
1004
|
+
content: forced,
|
|
1005
|
+
...(current?.toolsAdded ? { toolsAdded: current.toolsAdded } : {}),
|
|
1006
|
+
timestamp: current?.timestamp ?? Date.now(),
|
|
1007
|
+
};
|
|
1008
|
+
return [head, ...transformed.filter((message) => message.role !== "system")];
|
|
912
1009
|
};
|
|
913
|
-
|
|
1010
|
+
}
|
|
1011
|
+
/** Restore the active tool loadout declared by the session transcript, if it declares one. */
|
|
1012
|
+
_restoreToolsFromTranscript() {
|
|
1013
|
+
const current = getCurrentSystemMessage(this.sessionManager.buildSessionContext().messages);
|
|
1014
|
+
if (!current)
|
|
1015
|
+
return;
|
|
1016
|
+
const toolNames = (current.toolsAdded ?? [])
|
|
1017
|
+
.map((tool) => tool.name)
|
|
1018
|
+
.filter((name) => this._toolRegistry.has(name));
|
|
1019
|
+
this.agent.state.tools = toolNames.flatMap((name) => {
|
|
1020
|
+
const registered = this._toolRegistry.get(name);
|
|
1021
|
+
return registered ? [registered] : [];
|
|
1022
|
+
});
|
|
1023
|
+
this._rebuildSystemPrompt(toolNames);
|
|
914
1024
|
}
|
|
915
1025
|
// =========================================================================
|
|
916
1026
|
// Prompting
|
|
917
1027
|
// =========================================================================
|
|
918
1028
|
async _runAgentOperation(start) {
|
|
1029
|
+
this._agentRunAbortRequested = false;
|
|
919
1030
|
this._isAgentRunActive = true;
|
|
920
1031
|
try {
|
|
921
1032
|
await start();
|
|
922
1033
|
while (await this._handlePostAgentRun()) {
|
|
923
1034
|
// Esc cancels the current agent run. Keep messages queued during that
|
|
924
1035
|
// cancellation for the single deferred continuation in abort().
|
|
925
|
-
if (this._isAborting) {
|
|
1036
|
+
if (this._isAborting || this._agentRunAbortRequested) {
|
|
926
1037
|
break;
|
|
927
1038
|
}
|
|
928
1039
|
try {
|
|
@@ -942,12 +1053,14 @@ export class AgentSession {
|
|
|
942
1053
|
throw error;
|
|
943
1054
|
}
|
|
944
1055
|
}
|
|
945
|
-
if (!this._isAborting) {
|
|
1056
|
+
if (!this._isAborting && !this._agentRunAbortRequested) {
|
|
946
1057
|
await this._maybeGenerateSessionName();
|
|
947
1058
|
}
|
|
948
1059
|
}
|
|
949
1060
|
finally {
|
|
950
|
-
this.
|
|
1061
|
+
if (this._agentRunAbortRequested)
|
|
1062
|
+
this._finishCancelledRetry();
|
|
1063
|
+
this._runSystemPromptOptions = undefined;
|
|
951
1064
|
this._flushPendingBashMessages();
|
|
952
1065
|
this._flushPendingCustomMessages();
|
|
953
1066
|
await this._emitAgentSettled();
|
|
@@ -991,7 +1104,7 @@ export class AgentSession {
|
|
|
991
1104
|
return;
|
|
992
1105
|
this._sessionNameAbortController = new AbortController();
|
|
993
1106
|
try {
|
|
994
|
-
const stream = await this.agent.streamFunction(model, {
|
|
1107
|
+
const stream = await this.agent.streamFunction(model, normalizeContext({
|
|
995
1108
|
systemPrompt: this.systemPrompt,
|
|
996
1109
|
messages: [
|
|
997
1110
|
{
|
|
@@ -1001,7 +1114,7 @@ export class AgentSession {
|
|
|
1001
1114
|
},
|
|
1002
1115
|
],
|
|
1003
1116
|
tools: this.agent.state.tools,
|
|
1004
|
-
}, {
|
|
1117
|
+
}), {
|
|
1005
1118
|
maxTokens: 32,
|
|
1006
1119
|
reasoning: undefined,
|
|
1007
1120
|
sessionId: this.sessionId,
|
|
@@ -1030,6 +1143,10 @@ export class AgentSession {
|
|
|
1030
1143
|
async _handlePostAgentRun() {
|
|
1031
1144
|
const msg = this._lastAssistantMessage;
|
|
1032
1145
|
this._lastAssistantMessage = undefined;
|
|
1146
|
+
if (this._agentRunAbortRequested) {
|
|
1147
|
+
this._finishCancelledRetry();
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1033
1150
|
if (!msg) {
|
|
1034
1151
|
return false;
|
|
1035
1152
|
}
|
|
@@ -1040,7 +1157,13 @@ export class AgentSession {
|
|
|
1040
1157
|
return false;
|
|
1041
1158
|
}
|
|
1042
1159
|
if (this._isRetryableError(msg) && (await this._prepareRetry(msg))) {
|
|
1043
|
-
|
|
1160
|
+
if (this._agentRunAbortRequested)
|
|
1161
|
+
this._finishCancelledRetry();
|
|
1162
|
+
return !this._agentRunAbortRequested;
|
|
1163
|
+
}
|
|
1164
|
+
if (this._agentRunAbortRequested) {
|
|
1165
|
+
this._finishCancelledRetry();
|
|
1166
|
+
return false;
|
|
1044
1167
|
}
|
|
1045
1168
|
if (this._isAborting) {
|
|
1046
1169
|
return false;
|
|
@@ -1055,14 +1178,27 @@ export class AgentSession {
|
|
|
1055
1178
|
this._retryAttempt = 0;
|
|
1056
1179
|
}
|
|
1057
1180
|
if (await this._checkCompaction(msg)) {
|
|
1058
|
-
return
|
|
1181
|
+
return !this._agentRunAbortRequested;
|
|
1059
1182
|
}
|
|
1060
1183
|
if (isPiServerMode() && this._isTerminalAssistantFailure(msg)) {
|
|
1061
1184
|
this._detachPiServerTerminalAssistantFailure();
|
|
1062
1185
|
}
|
|
1063
1186
|
// The agent loop drains both queues before emitting agent_end. Any messages
|
|
1064
1187
|
// here were queued by agent_end extension handlers and need a continuation.
|
|
1065
|
-
return this.agent.hasQueuedMessages();
|
|
1188
|
+
return !this._agentRunAbortRequested && this.agent.hasQueuedMessages();
|
|
1189
|
+
}
|
|
1190
|
+
async _runInputHandlers(text, images, source, streamingBehavior) {
|
|
1191
|
+
if (!this._extensionRunner.hasHandlers("input")) {
|
|
1192
|
+
return { text, images };
|
|
1193
|
+
}
|
|
1194
|
+
const inputResult = await this._extensionRunner.emitInput(text, images, source, streamingBehavior);
|
|
1195
|
+
if (inputResult.action === "handled") {
|
|
1196
|
+
return undefined;
|
|
1197
|
+
}
|
|
1198
|
+
if (inputResult.action === "transform") {
|
|
1199
|
+
return { text: inputResult.text, images: inputResult.images ?? images };
|
|
1200
|
+
}
|
|
1201
|
+
return { text, images };
|
|
1066
1202
|
}
|
|
1067
1203
|
/**
|
|
1068
1204
|
* Send a prompt to the agent.
|
|
@@ -1092,19 +1228,12 @@ export class AgentSession {
|
|
|
1092
1228
|
throw new Error("Cannot submit a prompt while compaction is in progress. Wait for compaction to finish and retry.");
|
|
1093
1229
|
}
|
|
1094
1230
|
// Emit input event for extension interception (before skill/template expansion)
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
if (inputResult.action === "handled") {
|
|
1100
|
-
preflightResult?.(true);
|
|
1101
|
-
return;
|
|
1102
|
-
}
|
|
1103
|
-
if (inputResult.action === "transform") {
|
|
1104
|
-
currentText = inputResult.text;
|
|
1105
|
-
currentImages = inputResult.images ?? currentImages;
|
|
1106
|
-
}
|
|
1231
|
+
const processedInput = await this._runInputHandlers(text, options?.images, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
|
|
1232
|
+
if (!processedInput) {
|
|
1233
|
+
preflightResult?.(true);
|
|
1234
|
+
return;
|
|
1107
1235
|
}
|
|
1236
|
+
const { text: currentText, images: currentImages } = processedInput;
|
|
1108
1237
|
// Expand skill commands (/skill:name args) and prompt templates (/template args)
|
|
1109
1238
|
let expandedText = currentText;
|
|
1110
1239
|
if (expandPromptTemplates) {
|
|
@@ -1196,31 +1325,30 @@ export class AgentSession {
|
|
|
1196
1325
|
}
|
|
1197
1326
|
this._pendingNextTurnMessages = [];
|
|
1198
1327
|
// Emit before_agent_start extension event
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
else {
|
|
1220
|
-
// Ensure we're using the base prompt (in case previous turn had modifications)
|
|
1221
|
-
this._systemPromptOverride = undefined;
|
|
1222
|
-
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
1328
|
+
const selectedToolsBefore = this._baseSystemPromptOptions.selectedTools;
|
|
1329
|
+
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptOptions);
|
|
1330
|
+
// Handlers may edit event.systemPromptOptions.selectedTools or call setActiveTools(),
|
|
1331
|
+
// which updates the live loadout instead. An explicit edit wins; otherwise the live
|
|
1332
|
+
// loadout is authoritative, so a setActiveTools() call is not undone here.
|
|
1333
|
+
const handlerEditedTools = result.systemPromptOptions.selectedTools.length !== selectedToolsBefore.length ||
|
|
1334
|
+
result.systemPromptOptions.selectedTools.some((name, index) => name !== selectedToolsBefore[index]);
|
|
1335
|
+
if (!handlerEditedTools)
|
|
1336
|
+
result.systemPromptOptions.selectedTools = this.getActiveToolNames();
|
|
1337
|
+
for (const msg of result.messages) {
|
|
1338
|
+
messages.push({
|
|
1339
|
+
role: "custom",
|
|
1340
|
+
customType: msg.customType,
|
|
1341
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1342
|
+
content: msg.content ?? [],
|
|
1343
|
+
display: msg.display,
|
|
1344
|
+
details: msg.details,
|
|
1345
|
+
timestamp: Date.now(),
|
|
1346
|
+
});
|
|
1223
1347
|
}
|
|
1348
|
+
const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
|
|
1349
|
+
this._runSystemPromptOptions = result.systemPromptOptions;
|
|
1350
|
+
if (updateMessage)
|
|
1351
|
+
messages.unshift(updateMessage);
|
|
1224
1352
|
}
|
|
1225
1353
|
catch (error) {
|
|
1226
1354
|
preflightResult?.(false);
|
|
@@ -1289,40 +1417,44 @@ export class AgentSession {
|
|
|
1289
1417
|
return text; // Return original on error
|
|
1290
1418
|
}
|
|
1291
1419
|
}
|
|
1420
|
+
async _queueUserInput(text, images, behavior, source) {
|
|
1421
|
+
if (text.startsWith("/")) {
|
|
1422
|
+
this._throwIfExtensionCommand(text);
|
|
1423
|
+
}
|
|
1424
|
+
const processedInput = await this._runInputHandlers(text, images, source, this.isStreaming ? behavior : undefined);
|
|
1425
|
+
if (!processedInput)
|
|
1426
|
+
return;
|
|
1427
|
+
let expandedText = this._expandSkillCommand(processedInput.text);
|
|
1428
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1429
|
+
if (behavior === "steer") {
|
|
1430
|
+
await this._queueSteer(expandedText, processedInput.images);
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
await this._queueFollowUp(expandedText, processedInput.images);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1292
1436
|
/**
|
|
1293
1437
|
* Queue a steering message while the agent is running.
|
|
1294
1438
|
* Delivered after the current assistant turn finishes executing its tool calls,
|
|
1295
1439
|
* before the next LLM call.
|
|
1296
1440
|
* Expands skill commands and prompt templates. Errors on extension commands.
|
|
1297
1441
|
* @param images Optional image attachments to include with the message
|
|
1442
|
+
* @param options Input source; defaults to interactive
|
|
1298
1443
|
* @throws Error if text is an extension command
|
|
1299
1444
|
*/
|
|
1300
|
-
async steer(text, images) {
|
|
1301
|
-
|
|
1302
|
-
if (text.startsWith("/")) {
|
|
1303
|
-
this._throwIfExtensionCommand(text);
|
|
1304
|
-
}
|
|
1305
|
-
// Expand skill commands and prompt templates
|
|
1306
|
-
let expandedText = this._expandSkillCommand(text);
|
|
1307
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1308
|
-
await this._queueSteer(expandedText, images);
|
|
1445
|
+
async steer(text, images, options) {
|
|
1446
|
+
await this._queueUserInput(text, images, "steer", options?.source ?? "interactive");
|
|
1309
1447
|
}
|
|
1310
1448
|
/**
|
|
1311
1449
|
* Queue a follow-up message to be processed after the agent finishes.
|
|
1312
1450
|
* Delivered only when agent has no more tool calls or steering messages.
|
|
1313
1451
|
* Expands skill commands and prompt templates. Errors on extension commands.
|
|
1314
1452
|
* @param images Optional image attachments to include with the message
|
|
1453
|
+
* @param options Input source; defaults to interactive
|
|
1315
1454
|
* @throws Error if text is an extension command
|
|
1316
1455
|
*/
|
|
1317
|
-
async followUp(text, images) {
|
|
1318
|
-
|
|
1319
|
-
if (text.startsWith("/")) {
|
|
1320
|
-
this._throwIfExtensionCommand(text);
|
|
1321
|
-
}
|
|
1322
|
-
// Expand skill commands and prompt templates
|
|
1323
|
-
let expandedText = this._expandSkillCommand(text);
|
|
1324
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1325
|
-
await this._queueFollowUp(expandedText, images);
|
|
1456
|
+
async followUp(text, images, options) {
|
|
1457
|
+
await this._queueUserInput(text, images, "followUp", options?.source ?? "interactive");
|
|
1326
1458
|
}
|
|
1327
1459
|
/**
|
|
1328
1460
|
* Internal: Queue a steering message (already expanded, no extension command check).
|
|
@@ -1525,6 +1657,9 @@ export class AgentSession {
|
|
|
1525
1657
|
});
|
|
1526
1658
|
}
|
|
1527
1659
|
async _performAbort() {
|
|
1660
|
+
if (this._isAgentRunActive) {
|
|
1661
|
+
this._agentRunAbortRequested = true;
|
|
1662
|
+
}
|
|
1528
1663
|
this.abortRetry();
|
|
1529
1664
|
this.abortCompaction();
|
|
1530
1665
|
this.abortBranchSummary();
|
|
@@ -1550,6 +1685,11 @@ export class AgentSession {
|
|
|
1550
1685
|
if (cleanupFailure?.status === "rejected") {
|
|
1551
1686
|
throw cleanupFailure.reason;
|
|
1552
1687
|
}
|
|
1688
|
+
if (isPiServerMode()) {
|
|
1689
|
+
// The provider may persist a terminal assistant after agent.abort() without
|
|
1690
|
+
// routing through _handlePostAgentRun; keep the durable leaf resumable.
|
|
1691
|
+
this._detachPiServerTerminalAssistantFailure();
|
|
1692
|
+
}
|
|
1553
1693
|
}
|
|
1554
1694
|
catch (error) {
|
|
1555
1695
|
this._abortError = error instanceof Error ? error.message : String(error);
|
|
@@ -1572,6 +1712,9 @@ export class AgentSession {
|
|
|
1572
1712
|
* Messages queued while cancellation is in progress are sent in one deferred continuation.
|
|
1573
1713
|
*/
|
|
1574
1714
|
abort() {
|
|
1715
|
+
if (this._isAgentRunActive) {
|
|
1716
|
+
this._agentRunAbortRequested = true;
|
|
1717
|
+
}
|
|
1575
1718
|
if (this._abortPromise) {
|
|
1576
1719
|
return this._abortPromise;
|
|
1577
1720
|
}
|
|
@@ -1851,6 +1994,8 @@ export class AgentSession {
|
|
|
1851
1994
|
async compact(customInstructions) {
|
|
1852
1995
|
await this.abort();
|
|
1853
1996
|
this._compactionAbortController = new AbortController();
|
|
1997
|
+
let fromExtension = false;
|
|
1998
|
+
let cancelledByExtension = false;
|
|
1854
1999
|
let resolveCompactionCompletion;
|
|
1855
2000
|
const compactionCompletion = new Promise((resolve) => {
|
|
1856
2001
|
resolveCompactionCompletion = resolve;
|
|
@@ -1858,10 +2003,12 @@ export class AgentSession {
|
|
|
1858
2003
|
this._compactionCompletionPromise = compactionCompletion;
|
|
1859
2004
|
try {
|
|
1860
2005
|
this._emit({ type: "compaction_start", reason: "manual" });
|
|
1861
|
-
|
|
2006
|
+
const model = this.model;
|
|
2007
|
+
if (!model) {
|
|
1862
2008
|
throw new Error(formatNoModelSelectedMessage());
|
|
1863
2009
|
}
|
|
1864
|
-
const
|
|
2010
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
2011
|
+
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, this._compactionAbortController.signal);
|
|
1865
2012
|
if (isPiServerMode()) {
|
|
1866
2013
|
this._detachPiServerTerminalAssistantFailure();
|
|
1867
2014
|
const piServerResult = await compactPiServer(requestModel, {
|
|
@@ -1874,7 +2021,7 @@ export class AgentSession {
|
|
|
1874
2021
|
headers,
|
|
1875
2022
|
customInstructions,
|
|
1876
2023
|
settings: this.settingsManager.getCompactionSettings(),
|
|
1877
|
-
sessionTree:
|
|
2024
|
+
sessionTree: buildPiServerSessionTree(this.sessionManager.getEntries(), this.sessionManager.getLeafId()),
|
|
1878
2025
|
signal: this._compactionAbortController.signal,
|
|
1879
2026
|
reasoning: toProviderReasoning(this.thinkingLevel),
|
|
1880
2027
|
onHistoryReconciled: (snapshot) => this.reconcilePiServerHistory(snapshot),
|
|
@@ -1891,7 +2038,6 @@ export class AgentSession {
|
|
|
1891
2038
|
return result;
|
|
1892
2039
|
}
|
|
1893
2040
|
const pathEntries = this.sessionManager.getBranch();
|
|
1894
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
1895
2041
|
const preparation = prepareCompaction(pathEntries, settings);
|
|
1896
2042
|
if (!preparation) {
|
|
1897
2043
|
// Check why we can't compact
|
|
@@ -1902,7 +2048,6 @@ export class AgentSession {
|
|
|
1902
2048
|
throw new Error("Nothing to compact (session too small)");
|
|
1903
2049
|
}
|
|
1904
2050
|
let extensionCompaction;
|
|
1905
|
-
let fromExtension = false;
|
|
1906
2051
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1907
2052
|
const result = (await this._extensionRunner.emit({
|
|
1908
2053
|
type: "session_before_compact",
|
|
@@ -1914,6 +2059,7 @@ export class AgentSession {
|
|
|
1914
2059
|
signal: this._compactionAbortController.signal,
|
|
1915
2060
|
}));
|
|
1916
2061
|
if (result?.cancel) {
|
|
2062
|
+
cancelledByExtension = true;
|
|
1917
2063
|
throw new Error("Compaction cancelled");
|
|
1918
2064
|
}
|
|
1919
2065
|
if (result?.compaction) {
|
|
@@ -1983,8 +2129,10 @@ export class AgentSession {
|
|
|
1983
2129
|
}
|
|
1984
2130
|
catch (error) {
|
|
1985
2131
|
const message = error instanceof Error ? error.message : String(error);
|
|
1986
|
-
const aborted =
|
|
1987
|
-
|
|
2132
|
+
const aborted = this._compactionAbortController?.signal.aborted === true ||
|
|
2133
|
+
cancelledByExtension ||
|
|
2134
|
+
message === "Compaction cancelled" ||
|
|
2135
|
+
(error instanceof Error && error.name === "AbortError");
|
|
1988
2136
|
const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
|
|
1989
2137
|
this._clearManualCompactionState();
|
|
1990
2138
|
this._emit({
|
|
@@ -1995,6 +2143,13 @@ export class AgentSession {
|
|
|
1995
2143
|
willRetry: false,
|
|
1996
2144
|
errorMessage,
|
|
1997
2145
|
});
|
|
2146
|
+
await this._emitSessionCompactFailed({
|
|
2147
|
+
reason: "manual",
|
|
2148
|
+
errorMessage,
|
|
2149
|
+
aborted,
|
|
2150
|
+
willRetry: false,
|
|
2151
|
+
fromExtension,
|
|
2152
|
+
});
|
|
1998
2153
|
throw error;
|
|
1999
2154
|
}
|
|
2000
2155
|
finally {
|
|
@@ -2034,7 +2189,7 @@ export class AgentSession {
|
|
|
2034
2189
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true, externalSignal) {
|
|
2035
2190
|
if (externalSignal?.aborted)
|
|
2036
2191
|
return false;
|
|
2037
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
2192
|
+
const settings = this.settingsManager.getCompactionSettings(this.model);
|
|
2038
2193
|
if (!settings.enabled)
|
|
2039
2194
|
return false;
|
|
2040
2195
|
// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
|
|
@@ -2066,7 +2221,9 @@ export class AgentSession {
|
|
|
2066
2221
|
if (contextOverflow || recoverableLength) {
|
|
2067
2222
|
const willRetry = assistantMessage.stopReason !== "stop";
|
|
2068
2223
|
if (!willRetry) {
|
|
2069
|
-
return
|
|
2224
|
+
return externalSignal
|
|
2225
|
+
? await this._runAutoCompaction("overflow", false, undefined, undefined, externalSignal)
|
|
2226
|
+
: await this._runAutoCompaction("overflow", false);
|
|
2070
2227
|
}
|
|
2071
2228
|
if (this._overflowRecoveryAttempted) {
|
|
2072
2229
|
const errorMessage = contextOverflow
|
|
@@ -2125,7 +2282,9 @@ export class AgentSession {
|
|
|
2125
2282
|
contextTokens = directContextTokens;
|
|
2126
2283
|
}
|
|
2127
2284
|
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
2128
|
-
return
|
|
2285
|
+
return externalSignal
|
|
2286
|
+
? await this._runAutoCompaction("threshold", false, undefined, undefined, externalSignal)
|
|
2287
|
+
: await this._runAutoCompaction("threshold", false);
|
|
2129
2288
|
}
|
|
2130
2289
|
return false;
|
|
2131
2290
|
}
|
|
@@ -2133,27 +2292,28 @@ export class AgentSession {
|
|
|
2133
2292
|
* Internal: Run auto-compaction with events.
|
|
2134
2293
|
*/
|
|
2135
2294
|
async _runAutoCompaction(reason, willRetry, preparationOverride, preparationOptions, externalSignal) {
|
|
2136
|
-
const
|
|
2295
|
+
const model = this.model;
|
|
2296
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
2297
|
+
let abortController;
|
|
2137
2298
|
let started = false;
|
|
2138
2299
|
let fromExtension = false;
|
|
2300
|
+
let cancelledByExtension = false;
|
|
2139
2301
|
if (externalSignal?.aborted)
|
|
2140
2302
|
return false;
|
|
2141
|
-
const abortExternal = () =>
|
|
2303
|
+
const abortExternal = () => abortController?.abort();
|
|
2142
2304
|
externalSignal?.addEventListener("abort", abortExternal, { once: true });
|
|
2143
2305
|
try {
|
|
2144
|
-
if (!
|
|
2306
|
+
if (!model) {
|
|
2145
2307
|
return false;
|
|
2146
2308
|
}
|
|
2147
|
-
const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model);
|
|
2148
|
-
if (externalSignal?.aborted)
|
|
2149
|
-
return false;
|
|
2150
2309
|
if (isPiServerMode()) {
|
|
2151
|
-
|
|
2152
|
-
this._autoCompactionAbortController =
|
|
2153
|
-
if (externalSignal?.aborted)
|
|
2154
|
-
this._autoCompactionAbortController.abort();
|
|
2310
|
+
abortController = new AbortController();
|
|
2311
|
+
this._autoCompactionAbortController = abortController;
|
|
2155
2312
|
started = true;
|
|
2156
|
-
|
|
2313
|
+
this._emit({ type: "compaction_start", reason });
|
|
2314
|
+
abortController.signal.throwIfAborted();
|
|
2315
|
+
const { model: requestModel, apiKey, headers, } = await this._getSummarizationRequestAuth(model, abortController.signal);
|
|
2316
|
+
abortController.signal.throwIfAborted();
|
|
2157
2317
|
const piServerResult = await compactPiServer(requestModel, {
|
|
2158
2318
|
systemPrompt: this.systemPrompt,
|
|
2159
2319
|
messages: this.agent.state.messages,
|
|
@@ -2164,13 +2324,19 @@ export class AgentSession {
|
|
|
2164
2324
|
headers,
|
|
2165
2325
|
settings,
|
|
2166
2326
|
preparation: preparationOptions,
|
|
2167
|
-
sessionTree:
|
|
2168
|
-
signal,
|
|
2327
|
+
sessionTree: buildPiServerSessionTree(this.sessionManager.getEntries(), this.sessionManager.getLeafId()),
|
|
2328
|
+
signal: abortController.signal,
|
|
2169
2329
|
reasoning: toProviderReasoning(this.thinkingLevel),
|
|
2170
2330
|
onHistoryReconciled: (snapshot) => this.reconcilePiServerHistory(snapshot),
|
|
2171
2331
|
});
|
|
2172
|
-
if (externalSignal?.aborted || signal.aborted) {
|
|
2332
|
+
if (externalSignal?.aborted || abortController.signal.aborted) {
|
|
2173
2333
|
this._emit({ type: "compaction_end", reason, result: undefined, aborted: true, willRetry: false });
|
|
2334
|
+
await this._emitSessionCompactFailed({
|
|
2335
|
+
reason,
|
|
2336
|
+
aborted: true,
|
|
2337
|
+
willRetry: false,
|
|
2338
|
+
fromExtension,
|
|
2339
|
+
});
|
|
2174
2340
|
return false;
|
|
2175
2341
|
}
|
|
2176
2342
|
const result = await this._applyPiServerCompactionResult(piServerResult, reason, willRetry);
|
|
@@ -2179,17 +2345,17 @@ export class AgentSession {
|
|
|
2179
2345
|
return willRetry ? true : this.agent.hasQueuedMessages();
|
|
2180
2346
|
}
|
|
2181
2347
|
const pathEntries = this.sessionManager.getBranch();
|
|
2182
|
-
if (externalSignal?.aborted)
|
|
2183
|
-
return false;
|
|
2184
2348
|
const preparation = preparationOverride ?? prepareCompaction(pathEntries, settings);
|
|
2185
2349
|
if (!preparation) {
|
|
2186
2350
|
return false;
|
|
2187
2351
|
}
|
|
2188
|
-
|
|
2189
|
-
this._autoCompactionAbortController =
|
|
2190
|
-
if (externalSignal?.aborted)
|
|
2191
|
-
this._autoCompactionAbortController.abort();
|
|
2352
|
+
abortController = new AbortController();
|
|
2353
|
+
this._autoCompactionAbortController = abortController;
|
|
2192
2354
|
started = true;
|
|
2355
|
+
this._emit({ type: "compaction_start", reason });
|
|
2356
|
+
abortController.signal.throwIfAborted();
|
|
2357
|
+
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
|
|
2358
|
+
abortController.signal.throwIfAborted();
|
|
2193
2359
|
let extensionCompaction;
|
|
2194
2360
|
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2195
2361
|
const extensionResult = (await this._extensionRunner.emit({
|
|
@@ -2199,36 +2365,24 @@ export class AgentSession {
|
|
|
2199
2365
|
customInstructions: undefined,
|
|
2200
2366
|
reason,
|
|
2201
2367
|
willRetry,
|
|
2202
|
-
signal:
|
|
2368
|
+
signal: abortController.signal,
|
|
2203
2369
|
}));
|
|
2204
2370
|
if (extensionResult?.cancel) {
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
reason,
|
|
2208
|
-
result: undefined,
|
|
2209
|
-
aborted: true,
|
|
2210
|
-
willRetry: false,
|
|
2211
|
-
});
|
|
2212
|
-
await this._emitSessionCompactFailed({
|
|
2213
|
-
reason,
|
|
2214
|
-
aborted: true,
|
|
2215
|
-
willRetry: false,
|
|
2216
|
-
fromExtension: false,
|
|
2217
|
-
});
|
|
2218
|
-
return false;
|
|
2371
|
+
cancelledByExtension = true;
|
|
2372
|
+
throw new Error("Compaction cancelled");
|
|
2219
2373
|
}
|
|
2220
2374
|
if (extensionResult?.compaction) {
|
|
2221
2375
|
extensionCompaction = extensionResult.compaction;
|
|
2222
2376
|
fromExtension = true;
|
|
2223
2377
|
}
|
|
2224
2378
|
}
|
|
2379
|
+
abortController.signal.throwIfAborted();
|
|
2225
2380
|
let summary;
|
|
2226
2381
|
let firstKeptEntryId;
|
|
2227
2382
|
let tokensBefore;
|
|
2228
2383
|
let usage;
|
|
2229
2384
|
let details;
|
|
2230
2385
|
if (extensionCompaction) {
|
|
2231
|
-
// Extension provided compaction content
|
|
2232
2386
|
summary = extensionCompaction.summary;
|
|
2233
2387
|
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2234
2388
|
tokensBefore = extensionCompaction.tokensBefore;
|
|
@@ -2236,22 +2390,15 @@ export class AgentSession {
|
|
|
2236
2390
|
details = extensionCompaction.details;
|
|
2237
2391
|
}
|
|
2238
2392
|
else {
|
|
2239
|
-
|
|
2240
|
-
const compactResult = await compact(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
|
|
2393
|
+
const compactResult = await compact(preparation, requestModel, apiKey, headers, undefined, abortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
|
|
2241
2394
|
summary = compactResult.summary;
|
|
2242
2395
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
2243
2396
|
tokensBefore = compactResult.tokensBefore;
|
|
2244
2397
|
usage = compactResult.usage;
|
|
2245
2398
|
details = compactResult.details;
|
|
2246
2399
|
}
|
|
2247
|
-
if (externalSignal?.aborted ||
|
|
2248
|
-
this._emit({
|
|
2249
|
-
type: "compaction_end",
|
|
2250
|
-
reason,
|
|
2251
|
-
result: undefined,
|
|
2252
|
-
aborted: true,
|
|
2253
|
-
willRetry: false,
|
|
2254
|
-
});
|
|
2400
|
+
if (externalSignal?.aborted || abortController.signal.aborted) {
|
|
2401
|
+
this._emit({ type: "compaction_end", reason, result: undefined, aborted: true, willRetry: false });
|
|
2255
2402
|
await this._emitSessionCompactFailed({
|
|
2256
2403
|
reason,
|
|
2257
2404
|
aborted: true,
|
|
@@ -2260,12 +2407,12 @@ export class AgentSession {
|
|
|
2260
2407
|
});
|
|
2261
2408
|
return false;
|
|
2262
2409
|
}
|
|
2410
|
+
abortController.signal.throwIfAborted();
|
|
2263
2411
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2264
2412
|
const newEntries = this.sessionManager.getEntries();
|
|
2265
2413
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2266
2414
|
this.agent.state.messages = sessionContext.messages;
|
|
2267
2415
|
const estimatedTokensAfter = estimateMessagesTokens(sessionContext.messages);
|
|
2268
|
-
// Get the saved compaction entry for the extension event
|
|
2269
2416
|
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2270
2417
|
if (this._extensionRunner && savedCompactionEntry) {
|
|
2271
2418
|
await this._extensionRunner.emit({
|
|
@@ -2288,38 +2435,34 @@ export class AgentSession {
|
|
|
2288
2435
|
if (willRetry) {
|
|
2289
2436
|
const messages = this.agent.state.messages;
|
|
2290
2437
|
const lastMsg = messages[messages.length - 1];
|
|
2291
|
-
// The overflow response was persisted on message_end before _checkCompaction() removed it
|
|
2292
|
-
// from agent state. Rebuilding state from the new compaction can restore that kept entry,
|
|
2293
|
-
// leaving an assistant as the final message. agent.continue() rejects that state, so remove
|
|
2294
|
-
// the retriable error or truncated-length response again before continuing the interrupted turn.
|
|
2295
2438
|
if (lastMsg?.role === "assistant" && (lastMsg.stopReason === "error" || lastMsg.stopReason === "length")) {
|
|
2296
2439
|
this.agent.state.messages = messages.slice(0, -1);
|
|
2297
2440
|
}
|
|
2298
2441
|
return true;
|
|
2299
2442
|
}
|
|
2300
|
-
// Auto-compaction can complete while follow-up/steering/custom messages are waiting.
|
|
2301
|
-
// Continue once so queued messages are delivered.
|
|
2302
2443
|
return this.agent.hasQueuedMessages();
|
|
2303
2444
|
}
|
|
2304
2445
|
catch (error) {
|
|
2305
|
-
const
|
|
2446
|
+
const message = error instanceof Error ? error.message : "compaction failed";
|
|
2447
|
+
const aborted = externalSignal?.aborted === true || abortController?.signal.aborted === true || cancelledByExtension;
|
|
2306
2448
|
if (started) {
|
|
2449
|
+
const errorMessage = aborted
|
|
2450
|
+
? undefined
|
|
2451
|
+
: reason === "overflow"
|
|
2452
|
+
? `Context overflow recovery failed: ${message}`
|
|
2453
|
+
: `Auto-compaction failed: ${message}`;
|
|
2307
2454
|
this._emit({
|
|
2308
2455
|
type: "compaction_end",
|
|
2309
2456
|
reason,
|
|
2310
2457
|
result: undefined,
|
|
2311
|
-
aborted
|
|
2458
|
+
aborted,
|
|
2312
2459
|
willRetry: false,
|
|
2313
|
-
errorMessage
|
|
2314
|
-
? `Context overflow recovery failed: ${errorMessage}`
|
|
2315
|
-
: `Auto-compaction failed: ${errorMessage}`,
|
|
2460
|
+
errorMessage,
|
|
2316
2461
|
});
|
|
2317
2462
|
await this._emitSessionCompactFailed({
|
|
2318
2463
|
reason,
|
|
2319
|
-
errorMessage
|
|
2320
|
-
|
|
2321
|
-
: `Auto-compaction failed: ${errorMessage}`,
|
|
2322
|
-
aborted: false,
|
|
2464
|
+
errorMessage,
|
|
2465
|
+
aborted,
|
|
2323
2466
|
willRetry: false,
|
|
2324
2467
|
fromExtension,
|
|
2325
2468
|
});
|
|
@@ -2328,7 +2471,9 @@ export class AgentSession {
|
|
|
2328
2471
|
}
|
|
2329
2472
|
finally {
|
|
2330
2473
|
externalSignal?.removeEventListener("abort", abortExternal);
|
|
2331
|
-
this._autoCompactionAbortController
|
|
2474
|
+
if (this._autoCompactionAbortController === abortController) {
|
|
2475
|
+
this._autoCompactionAbortController = undefined;
|
|
2476
|
+
}
|
|
2332
2477
|
this._resolveIdleWaitIfIdle();
|
|
2333
2478
|
}
|
|
2334
2479
|
}
|
|
@@ -2343,7 +2488,7 @@ export class AgentSession {
|
|
|
2343
2488
|
return this.settingsManager.getCompactionEnabled();
|
|
2344
2489
|
}
|
|
2345
2490
|
_shouldCompactBeforeNextProviderRequest(turn) {
|
|
2346
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
2491
|
+
const settings = this.settingsManager.getCompactionSettings(this.model);
|
|
2347
2492
|
if (!settings.enabled || turn.toolResults.length === 0)
|
|
2348
2493
|
return false;
|
|
2349
2494
|
const contextWindow = this.model?.contextWindow ?? 0;
|
|
@@ -2369,7 +2514,6 @@ export class AgentSession {
|
|
|
2369
2514
|
return {
|
|
2370
2515
|
...baseContext,
|
|
2371
2516
|
messages: this.agent.state.messages.slice(),
|
|
2372
|
-
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
2373
2517
|
tools: this.agent.state.tools.slice(),
|
|
2374
2518
|
};
|
|
2375
2519
|
}
|
|
@@ -2377,7 +2521,7 @@ export class AgentSession {
|
|
|
2377
2521
|
if (signal?.aborted || !this._shouldCompactBeforeNextProviderRequest(turn)) {
|
|
2378
2522
|
return undefined;
|
|
2379
2523
|
}
|
|
2380
|
-
const settings = this.settingsManager.getCompactionSettings();
|
|
2524
|
+
const settings = this.settingsManager.getCompactionSettings(this.model);
|
|
2381
2525
|
const markerParentId = this.sessionManager.getLeafId();
|
|
2382
2526
|
const markerId = this._appendIntraTurnCompactionMarker();
|
|
2383
2527
|
const preparationOptions = { firstKeptEntryId: markerId };
|
|
@@ -2433,8 +2577,7 @@ export class AgentSession {
|
|
|
2433
2577
|
themePaths: this.buildExtensionResourcePaths(themePaths),
|
|
2434
2578
|
};
|
|
2435
2579
|
this._resourceLoader.extendResources(extensionPaths);
|
|
2436
|
-
this.
|
|
2437
|
-
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
2580
|
+
this._rebuildSystemPrompt(this.getActiveToolNames());
|
|
2438
2581
|
}
|
|
2439
2582
|
buildExtensionResourcePaths(entries) {
|
|
2440
2583
|
return entries.map((entry) => {
|
|
@@ -2786,6 +2929,18 @@ export class AgentSession {
|
|
|
2786
2929
|
},
|
|
2787
2930
|
};
|
|
2788
2931
|
}
|
|
2932
|
+
_finishCancelledRetry() {
|
|
2933
|
+
if (this._retryAttempt === 0)
|
|
2934
|
+
return;
|
|
2935
|
+
const attempt = this._retryAttempt;
|
|
2936
|
+
this._retryAttempt = 0;
|
|
2937
|
+
this._emit({
|
|
2938
|
+
type: "auto_retry_end",
|
|
2939
|
+
success: false,
|
|
2940
|
+
attempt,
|
|
2941
|
+
finalError: "Retry cancelled",
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2789
2944
|
/**
|
|
2790
2945
|
* Prepare a retryable error for continuation with exponential backoff.
|
|
2791
2946
|
* @returns true if the caller should continue the agent, false otherwise
|
|
@@ -2801,7 +2956,7 @@ export class AgentSession {
|
|
|
2801
2956
|
this._retryAttempt--;
|
|
2802
2957
|
return false;
|
|
2803
2958
|
}
|
|
2804
|
-
const delayMs = settings
|
|
2959
|
+
const delayMs = retryDelayMs(settings, this._retryAttempt);
|
|
2805
2960
|
this._emit({
|
|
2806
2961
|
type: "auto_retry_start",
|
|
2807
2962
|
attempt: this._retryAttempt,
|
|
@@ -2838,14 +2993,7 @@ export class AgentSession {
|
|
|
2838
2993
|
}
|
|
2839
2994
|
catch {
|
|
2840
2995
|
// Aborted during sleep - emit end event so UI can clean up
|
|
2841
|
-
|
|
2842
|
-
this._retryAttempt = 0;
|
|
2843
|
-
this._emit({
|
|
2844
|
-
type: "auto_retry_end",
|
|
2845
|
-
success: false,
|
|
2846
|
-
attempt,
|
|
2847
|
-
finalError: "Retry cancelled",
|
|
2848
|
-
});
|
|
2996
|
+
this._finishCancelledRetry();
|
|
2849
2997
|
return false;
|
|
2850
2998
|
}
|
|
2851
2999
|
finally {
|
|
@@ -3003,6 +3151,9 @@ export class AgentSession {
|
|
|
3003
3151
|
if (this.isStreaming) {
|
|
3004
3152
|
throw new Error("Wait for the current response to finish before navigating the session tree.");
|
|
3005
3153
|
}
|
|
3154
|
+
if (this.isCompacting) {
|
|
3155
|
+
throw new Error("Wait for the current compaction or tree navigation to finish before navigating the session tree.");
|
|
3156
|
+
}
|
|
3006
3157
|
const oldLeafId = this.sessionManager.getLeafId();
|
|
3007
3158
|
// No-op if already at target
|
|
3008
3159
|
if (targetId === oldLeafId) {
|
|
@@ -3150,6 +3301,7 @@ export class AgentSession {
|
|
|
3150
3301
|
// Update agent state
|
|
3151
3302
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
3152
3303
|
this.agent.state.messages = sessionContext.messages;
|
|
3304
|
+
this._restoreToolsFromTranscript();
|
|
3153
3305
|
// Emit session_tree event
|
|
3154
3306
|
await this._extensionRunner.emit({
|
|
3155
3307
|
type: "session_tree",
|
|
@@ -3201,7 +3353,10 @@ export class AgentSession {
|
|
|
3201
3353
|
let toolCalls = 0;
|
|
3202
3354
|
const usageTotals = createUsageTotals();
|
|
3203
3355
|
for (const entry of this.sessionManager.getEntries()) {
|
|
3204
|
-
if (
|
|
3356
|
+
if (entry.type === "usage") {
|
|
3357
|
+
addUsageToTotals(usageTotals, entry.usage);
|
|
3358
|
+
}
|
|
3359
|
+
else if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
|
|
3205
3360
|
addUsageToTotals(usageTotals, entry.usage);
|
|
3206
3361
|
}
|
|
3207
3362
|
if (entry.type !== "message")
|
|
@@ -3337,6 +3492,30 @@ export class AgentSession {
|
|
|
3337
3492
|
writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
3338
3493
|
return filePath;
|
|
3339
3494
|
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Ask the current model to describe what went wrong in this session for a bug report.
|
|
3497
|
+
* Used when the user declines to share the transcript itself.
|
|
3498
|
+
*/
|
|
3499
|
+
async summarizeForBugReport(options) {
|
|
3500
|
+
const model = this.model;
|
|
3501
|
+
if (!model) {
|
|
3502
|
+
throw new Error("No model selected");
|
|
3503
|
+
}
|
|
3504
|
+
const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
|
|
3505
|
+
return generateBugReportSummary({
|
|
3506
|
+
messages: this.messages,
|
|
3507
|
+
hint: options.hint,
|
|
3508
|
+
model: requestModel,
|
|
3509
|
+
apiKey,
|
|
3510
|
+
headers,
|
|
3511
|
+
env,
|
|
3512
|
+
signal: options.signal,
|
|
3513
|
+
thinkingLevel: this.thinkingLevel,
|
|
3514
|
+
streamFn: this.agent.streamFunction,
|
|
3515
|
+
retry: this.settingsManager.getRetrySettings(),
|
|
3516
|
+
sessionId: this.sessionId,
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3340
3519
|
// =========================================================================
|
|
3341
3520
|
// Utilities
|
|
3342
3521
|
// =========================================================================
|