@earendil-works/pi-coding-agent 0.80.2 → 0.80.5
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 +115 -1
- package/README.md +3 -2
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +4 -2
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +13 -25
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/core/agent-session.d.ts +22 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +133 -50
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +13 -5
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/cache-stats.d.ts +49 -0
- package/dist/core/cache-stats.d.ts.map +1 -0
- package/dist/core/cache-stats.js +101 -0
- package/dist/core/cache-stats.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +1 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +56 -81
- package/dist/core/compaction/compaction.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/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +38 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +39 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +28 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -6
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +3 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +16 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +10 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +15 -18
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +68 -26
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +0 -10
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +8 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +21 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +104 -72
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +27 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts +1 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -3
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/timings.d.ts +4 -2
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +24 -14
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +20 -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 +2 -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 +12 -25
- package/dist/core/tools/read.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +2 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -4
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +23 -15
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +34 -3
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +280 -25
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-entry.js +52 -0
- package/dist/modes/interactive/components/custom-entry.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +3 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +12 -3
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +4 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +3 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +17 -6
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +23 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
- package/dist/modes/interactive/components/status-indicator.js +60 -0
- package/dist/modes/interactive/components/status-indicator.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +6 -2
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +19 -6
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +21 -10
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +417 -196
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +21 -6
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +17 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +20 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +26 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +2 -2
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +71 -17
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +10 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/image-convert.d.ts +1 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +21 -15
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +41 -0
- package/dist/utils/mime.js.map +1 -1
- package/docs/extensions.md +88 -17
- package/docs/keybindings.md +1 -1
- package/docs/models.md +4 -4
- package/docs/packages.md +2 -2
- package/docs/providers.md +2 -1
- package/docs/rpc.md +71 -3
- package/docs/sdk.md +50 -0
- package/docs/session-format.md +18 -8
- package/docs/settings.md +11 -0
- package/docs/usage.md +3 -3
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/custom-compaction.ts +1 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/entry-renderer.ts +41 -0
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +1 -1
- package/examples/extensions/qna.ts +1 -1
- package/examples/extensions/question.ts +1 -0
- package/examples/extensions/reload-runtime.ts +2 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +1 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +8 -5
|
@@ -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 { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
17
|
+
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
18
18
|
import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
|
|
19
19
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
20
20
|
import { resolvePath } from "../utils/paths.js";
|
|
@@ -72,6 +72,9 @@ export class AgentSession {
|
|
|
72
72
|
// Event subscription state
|
|
73
73
|
_unsubscribeAgent;
|
|
74
74
|
_eventListeners = [];
|
|
75
|
+
_isAgentRunActive = false;
|
|
76
|
+
_idleWaitPromise;
|
|
77
|
+
_resolveIdleWait;
|
|
75
78
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
76
79
|
_steeringMessages = [];
|
|
77
80
|
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
@@ -120,6 +123,7 @@ export class AgentSession {
|
|
|
120
123
|
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
121
124
|
_baseSystemPrompt = "";
|
|
122
125
|
_baseSystemPromptOptions;
|
|
126
|
+
_systemPromptOverride;
|
|
123
127
|
constructor(config) {
|
|
124
128
|
this.agent = config.agent;
|
|
125
129
|
this.sessionManager = config.sessionManager;
|
|
@@ -139,6 +143,7 @@ export class AgentSession {
|
|
|
139
143
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
140
144
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
141
145
|
this._installAgentToolHooks();
|
|
146
|
+
this._installAgentNextTurnRefresh();
|
|
142
147
|
this._buildRuntime({
|
|
143
148
|
activeToolNames: this._initialActiveToolNames,
|
|
144
149
|
includeAllExtensionTools: true,
|
|
@@ -227,6 +232,26 @@ export class AgentSession {
|
|
|
227
232
|
};
|
|
228
233
|
};
|
|
229
234
|
}
|
|
235
|
+
_installAgentNextTurnRefresh() {
|
|
236
|
+
const previousPrepareNextTurnWithContext = this.agent.prepareNextTurnWithContext ??
|
|
237
|
+
(this.agent.prepareNextTurn
|
|
238
|
+
? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
|
|
239
|
+
: undefined);
|
|
240
|
+
this.agent.prepareNextTurnWithContext = async (turn, signal) => {
|
|
241
|
+
const previousSnapshot = await previousPrepareNextTurnWithContext?.(turn, signal);
|
|
242
|
+
const previousContext = previousSnapshot?.context ?? turn.context;
|
|
243
|
+
return {
|
|
244
|
+
...previousSnapshot,
|
|
245
|
+
context: {
|
|
246
|
+
...previousContext,
|
|
247
|
+
systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
|
|
248
|
+
tools: this.agent.state.tools.slice(),
|
|
249
|
+
},
|
|
250
|
+
model: this.agent.state.model,
|
|
251
|
+
thinkingLevel: this.agent.state.thinkingLevel,
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
}
|
|
230
255
|
// =========================================================================
|
|
231
256
|
// Event Subscription
|
|
232
257
|
// =========================================================================
|
|
@@ -243,6 +268,33 @@ export class AgentSession {
|
|
|
243
268
|
followUp: [...this._followUpMessages],
|
|
244
269
|
});
|
|
245
270
|
}
|
|
271
|
+
_getIdleWaitPromise() {
|
|
272
|
+
if (!this._idleWaitPromise) {
|
|
273
|
+
this._idleWaitPromise = new Promise((resolve) => {
|
|
274
|
+
this._resolveIdleWait = resolve;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return this._idleWaitPromise;
|
|
278
|
+
}
|
|
279
|
+
_resolveIdleWaitIfIdle() {
|
|
280
|
+
if (this._isAgentRunActive || !this._resolveIdleWait) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const resolve = this._resolveIdleWait;
|
|
284
|
+
this._idleWaitPromise = undefined;
|
|
285
|
+
this._resolveIdleWait = undefined;
|
|
286
|
+
resolve();
|
|
287
|
+
}
|
|
288
|
+
async _emitAgentSettled() {
|
|
289
|
+
this._isAgentRunActive = false;
|
|
290
|
+
try {
|
|
291
|
+
await this._extensionRunner.emit({ type: "agent_settled" });
|
|
292
|
+
this._emit({ type: "agent_settled" });
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
this._resolveIdleWaitIfIdle();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
246
298
|
// Track last assistant message for auto-compaction check
|
|
247
299
|
_lastAssistantMessage = undefined;
|
|
248
300
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
@@ -404,7 +456,16 @@ export class AgentSession {
|
|
|
404
456
|
};
|
|
405
457
|
const replacement = await this._extensionRunner.emitMessageEnd(extensionEvent);
|
|
406
458
|
if (replacement) {
|
|
407
|
-
|
|
459
|
+
// Untyped extension handlers can return messages with null/missing content;
|
|
460
|
+
// normalize so it never enters agent state or session history.
|
|
461
|
+
const normalized = (replacement.role === "user" ||
|
|
462
|
+
replacement.role === "assistant" ||
|
|
463
|
+
replacement.role === "toolResult" ||
|
|
464
|
+
replacement.role === "custom") &&
|
|
465
|
+
replacement.content == null
|
|
466
|
+
? { ...replacement, content: [] }
|
|
467
|
+
: replacement;
|
|
468
|
+
this._replaceMessageInPlace(event.message, normalized);
|
|
408
469
|
}
|
|
409
470
|
}
|
|
410
471
|
else if (event.type === "tool_execution_start") {
|
|
@@ -507,9 +568,13 @@ export class AgentSession {
|
|
|
507
568
|
get thinkingLevel() {
|
|
508
569
|
return this.agent.state.thinkingLevel;
|
|
509
570
|
}
|
|
510
|
-
/** Whether
|
|
571
|
+
/** Whether the session is currently processing an agent run or post-run continuation. */
|
|
511
572
|
get isStreaming() {
|
|
512
|
-
return this.
|
|
573
|
+
return this._isAgentRunActive;
|
|
574
|
+
}
|
|
575
|
+
/** Whether the session has no active agent run, retry, auto-compaction, or queued continuation. */
|
|
576
|
+
get isIdle() {
|
|
577
|
+
return !this._isAgentRunActive;
|
|
513
578
|
}
|
|
514
579
|
/** Current effective system prompt (includes any per-turn extension modifications) */
|
|
515
580
|
get systemPrompt() {
|
|
@@ -560,7 +625,7 @@ export class AgentSession {
|
|
|
560
625
|
this.agent.state.tools = tools;
|
|
561
626
|
// Rebuild base system prompt with new tool set
|
|
562
627
|
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
563
|
-
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
628
|
+
this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
|
|
564
629
|
}
|
|
565
630
|
/** Whether compaction or branch summarization is currently running */
|
|
566
631
|
get isCompacting() {
|
|
@@ -661,6 +726,7 @@ export class AgentSession {
|
|
|
661
726
|
// Prompting
|
|
662
727
|
// =========================================================================
|
|
663
728
|
async _runAgentPrompt(messages) {
|
|
729
|
+
this._isAgentRunActive = true;
|
|
664
730
|
try {
|
|
665
731
|
await this.agent.prompt(messages);
|
|
666
732
|
while (await this._handlePostAgentRun()) {
|
|
@@ -668,7 +734,9 @@ export class AgentSession {
|
|
|
668
734
|
}
|
|
669
735
|
}
|
|
670
736
|
finally {
|
|
737
|
+
this._systemPromptOverride = undefined;
|
|
671
738
|
this._flushPendingBashMessages();
|
|
739
|
+
await this._emitAgentSettled();
|
|
672
740
|
}
|
|
673
741
|
}
|
|
674
742
|
async _handlePostAgentRun() {
|
|
@@ -769,18 +837,11 @@ export class AgentSession {
|
|
|
769
837
|
}
|
|
770
838
|
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
771
839
|
}
|
|
772
|
-
// Check if we need to compact before sending (catches aborted responses)
|
|
840
|
+
// Check if we need to compact before sending (catches aborted responses).
|
|
841
|
+
// The user's new prompt is sent below, so do not call agent.continue() here.
|
|
773
842
|
const lastAssistant = this._findLastAssistantMessage();
|
|
774
|
-
if (lastAssistant
|
|
775
|
-
|
|
776
|
-
await this.agent.continue();
|
|
777
|
-
while (await this._handlePostAgentRun()) {
|
|
778
|
-
await this.agent.continue();
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
finally {
|
|
782
|
-
this._flushPendingBashMessages();
|
|
783
|
-
}
|
|
843
|
+
if (lastAssistant) {
|
|
844
|
+
await this._checkCompaction(lastAssistant, false);
|
|
784
845
|
}
|
|
785
846
|
// Build messages array (custom message if any, then user message)
|
|
786
847
|
messages = [];
|
|
@@ -807,7 +868,8 @@ export class AgentSession {
|
|
|
807
868
|
messages.push({
|
|
808
869
|
role: "custom",
|
|
809
870
|
customType: msg.customType,
|
|
810
|
-
content
|
|
871
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
872
|
+
content: msg.content ?? [],
|
|
811
873
|
display: msg.display,
|
|
812
874
|
details: msg.details,
|
|
813
875
|
timestamp: Date.now(),
|
|
@@ -815,11 +877,13 @@ export class AgentSession {
|
|
|
815
877
|
}
|
|
816
878
|
}
|
|
817
879
|
// Apply extension-modified system prompt, or reset to base
|
|
818
|
-
if (result?.systemPrompt) {
|
|
880
|
+
if (result?.systemPrompt !== undefined) {
|
|
881
|
+
this._systemPromptOverride = result.systemPrompt;
|
|
819
882
|
this.agent.state.systemPrompt = result.systemPrompt;
|
|
820
883
|
}
|
|
821
884
|
else {
|
|
822
885
|
// Ensure we're using the base prompt (in case previous turn had modifications)
|
|
886
|
+
this._systemPromptOverride = undefined;
|
|
823
887
|
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
824
888
|
}
|
|
825
889
|
}
|
|
@@ -984,7 +1048,8 @@ export class AgentSession {
|
|
|
984
1048
|
const appMessage = {
|
|
985
1049
|
role: "custom",
|
|
986
1050
|
customType: message.customType,
|
|
987
|
-
content
|
|
1051
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1052
|
+
content: message.content ?? [],
|
|
988
1053
|
display: message.display,
|
|
989
1054
|
details: message.details,
|
|
990
1055
|
timestamp: Date.now(),
|
|
@@ -1082,7 +1147,13 @@ export class AgentSession {
|
|
|
1082
1147
|
async abort() {
|
|
1083
1148
|
this.abortRetry();
|
|
1084
1149
|
this.agent.abort();
|
|
1085
|
-
await this.
|
|
1150
|
+
await this.waitForIdle();
|
|
1151
|
+
}
|
|
1152
|
+
async waitForIdle() {
|
|
1153
|
+
if (this.isIdle) {
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
await this._getIdleWaitPromise();
|
|
1086
1157
|
}
|
|
1087
1158
|
// =========================================================================
|
|
1088
1159
|
// Model Management
|
|
@@ -1767,7 +1838,11 @@ export class AgentSession {
|
|
|
1767
1838
|
});
|
|
1768
1839
|
},
|
|
1769
1840
|
appendEntry: (customType, data) => {
|
|
1770
|
-
this.sessionManager.appendCustomEntry(customType, data);
|
|
1841
|
+
const entryId = this.sessionManager.appendCustomEntry(customType, data);
|
|
1842
|
+
const entry = this.sessionManager.getEntry(entryId);
|
|
1843
|
+
if (entry) {
|
|
1844
|
+
this._emit({ type: "entry_appended", entry });
|
|
1845
|
+
}
|
|
1771
1846
|
},
|
|
1772
1847
|
setSessionName: (name) => {
|
|
1773
1848
|
this.setSessionName(name);
|
|
@@ -1793,7 +1868,7 @@ export class AgentSession {
|
|
|
1793
1868
|
setThinkingLevel: (level) => this.setThinkingLevel(level),
|
|
1794
1869
|
}, {
|
|
1795
1870
|
getModel: () => this.model,
|
|
1796
|
-
isIdle: () =>
|
|
1871
|
+
isIdle: () => this.isIdle,
|
|
1797
1872
|
isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
|
|
1798
1873
|
getSignal: () => this.agent.signal,
|
|
1799
1874
|
abort: () => {
|
|
@@ -1970,25 +2045,15 @@ export class AgentSession {
|
|
|
1970
2045
|
// =========================================================================
|
|
1971
2046
|
// Auto-Retry
|
|
1972
2047
|
// =========================================================================
|
|
1973
|
-
_isNonRetryableProviderLimitError(errorMessage) {
|
|
1974
|
-
return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorMessage);
|
|
1975
|
-
}
|
|
1976
2048
|
/**
|
|
1977
2049
|
* Check if an error is retryable (overloaded, rate limit, server errors).
|
|
1978
2050
|
* Context overflow errors are NOT retryable (handled by compaction instead).
|
|
1979
2051
|
*/
|
|
1980
2052
|
_isRetryableError(message) {
|
|
1981
|
-
|
|
2053
|
+
// Context overflow is handled by compaction, not retry.
|
|
2054
|
+
if (isContextOverflow(message, this.model?.contextWindow ?? 0))
|
|
1982
2055
|
return false;
|
|
1983
|
-
|
|
1984
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
1985
|
-
if (isContextOverflow(message, contextWindow))
|
|
1986
|
-
return false;
|
|
1987
|
-
const err = message.errorMessage;
|
|
1988
|
-
if (this._isNonRetryableProviderLimitError(err))
|
|
1989
|
-
return false;
|
|
1990
|
-
// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded
|
|
1991
|
-
return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(err);
|
|
2056
|
+
return isRetryableAssistantError(message);
|
|
1992
2057
|
}
|
|
1993
2058
|
/**
|
|
1994
2059
|
* Prepare a retryable error for continuation with exponential backoff.
|
|
@@ -2154,7 +2219,9 @@ export class AgentSession {
|
|
|
2154
2219
|
*/
|
|
2155
2220
|
setSessionName(name) {
|
|
2156
2221
|
this.sessionManager.appendSessionInfo(name);
|
|
2157
|
-
|
|
2222
|
+
const event = { type: "session_info_changed", name: this.sessionManager.getSessionName() };
|
|
2223
|
+
this._emit(event);
|
|
2224
|
+
void this._extensionRunner.emit(event);
|
|
2158
2225
|
}
|
|
2159
2226
|
// =========================================================================
|
|
2160
2227
|
// Tree Navigation
|
|
@@ -2359,28 +2426,44 @@ export class AgentSession {
|
|
|
2359
2426
|
return "";
|
|
2360
2427
|
}
|
|
2361
2428
|
/**
|
|
2362
|
-
* Get session statistics.
|
|
2429
|
+
* Get session statistics. Aggregates over ALL session entries (including
|
|
2430
|
+
* history that was compacted away), so token/cost totals reflect what was
|
|
2431
|
+
* actually billed across the session.
|
|
2363
2432
|
*/
|
|
2364
2433
|
getSessionStats() {
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2434
|
+
let userMessages = 0;
|
|
2435
|
+
let assistantMessages = 0;
|
|
2436
|
+
let toolResults = 0;
|
|
2437
|
+
let totalMessages = 0;
|
|
2369
2438
|
let toolCalls = 0;
|
|
2370
2439
|
let totalInput = 0;
|
|
2371
2440
|
let totalOutput = 0;
|
|
2372
2441
|
let totalCacheRead = 0;
|
|
2373
2442
|
let totalCacheWrite = 0;
|
|
2374
2443
|
let totalCost = 0;
|
|
2375
|
-
for (const
|
|
2376
|
-
if (
|
|
2444
|
+
for (const entry of this.sessionManager.getEntries()) {
|
|
2445
|
+
if (entry.type !== "message")
|
|
2446
|
+
continue;
|
|
2447
|
+
totalMessages++;
|
|
2448
|
+
const message = entry.message;
|
|
2449
|
+
if (message.role === "user") {
|
|
2450
|
+
userMessages++;
|
|
2451
|
+
}
|
|
2452
|
+
else if (message.role === "toolResult") {
|
|
2453
|
+
toolResults++;
|
|
2454
|
+
}
|
|
2455
|
+
else if (message.role === "assistant") {
|
|
2456
|
+
assistantMessages++;
|
|
2377
2457
|
const assistantMsg = message;
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2458
|
+
if (Array.isArray(assistantMsg.content)) {
|
|
2459
|
+
toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
2460
|
+
}
|
|
2461
|
+
const usage = assistantMsg.usage;
|
|
2462
|
+
totalInput += usage.input;
|
|
2463
|
+
totalOutput += usage.output;
|
|
2464
|
+
totalCacheRead += usage.cacheRead;
|
|
2465
|
+
totalCacheWrite += usage.cacheWrite;
|
|
2466
|
+
totalCost += usage.cost.total;
|
|
2384
2467
|
}
|
|
2385
2468
|
}
|
|
2386
2469
|
return {
|
|
@@ -2390,7 +2473,7 @@ export class AgentSession {
|
|
|
2390
2473
|
assistantMessages,
|
|
2391
2474
|
toolCalls,
|
|
2392
2475
|
toolResults,
|
|
2393
|
-
totalMessages
|
|
2476
|
+
totalMessages,
|
|
2394
2477
|
tokens: {
|
|
2395
2478
|
input: totalInput,
|
|
2396
2479
|
output: totalOutput,
|