@code-yeongyu/senpi 2026.7.9 → 2026.7.10
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 +37 -1
- package/README.md +1 -1
- package/dist/cli/args.js +3 -3
- 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/core/agent-session.d.ts +18 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +153 -36
- package/dist/core/agent-session.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 +54 -79
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation.js +1 -1
- package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +1 -0
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/import-repro.d.ts.map +1 -1
- package/dist/core/extensions/builtin/import-repro.js +100 -6
- package/dist/core/extensions/builtin/import-repro.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/connection.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/connection.js +15 -0
- package/dist/core/extensions/builtin/mcp/connection.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/service.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/service.js +3 -1
- package/dist/core/extensions/builtin/mcp/service.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/runner.d.ts +4 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +30 -24
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +23 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.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 +20 -3
- package/dist/core/model-registry.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/resource-loader.d.ts +2 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +6 -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 +9 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +11 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +8 -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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -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 +1 -0
- package/dist/main.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 +272 -24
- package/dist/modes/interactive/components/config-selector.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 +5 -2
- 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 +16 -6
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +10 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +10 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +261 -37
- package/dist/modes/interactive/interactive-mode.js.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 +6 -6
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +3 -3
- package/dist/modes/rpc/rpc-client.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 +64 -17
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/docs/extensions.md +33 -9
- package/docs/models.md +4 -4
- package/docs/packages.md +7 -10
- package/docs/rpc.md +13 -3
- package/docs/sdk.md +22 -0
- package/docs/settings.md +1 -0
- package/docs/usage.md +1 -1
- 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/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- 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/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/README.md +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +37 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-repo.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-repo.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-repo.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.d.ts +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.js +7 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl-storage.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts +18 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +72 -37
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/transform-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/transform-messages.js +4 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/transform-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +446 -195
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts +84 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js +72 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts +63 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js +54 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts +84 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js +72 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +138 -63
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +136 -76
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +77 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +72 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.d.ts +0 -44
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js +9 -45
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.d.ts +0 -44
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js +9 -45
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.d.ts +0 -44
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js +9 -45
- package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +22 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -8
- package/npm-shrinkwrap.json +0 -3479
|
@@ -89,6 +89,7 @@ export class AgentSession {
|
|
|
89
89
|
constructor(config) {
|
|
90
90
|
this._eventListeners = [];
|
|
91
91
|
this._agentEventQueue = Promise.resolve();
|
|
92
|
+
this._isAgentRunActive = false;
|
|
92
93
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
93
94
|
this._steeringMessages = [];
|
|
94
95
|
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
@@ -217,11 +218,11 @@ export class AgentSession {
|
|
|
217
218
|
};
|
|
218
219
|
}
|
|
219
220
|
async _emitBeforeToolCallHooks(toolCall, args) {
|
|
221
|
+
await this._agentEventQueue;
|
|
220
222
|
const runner = this._extensionRunner;
|
|
221
223
|
if (!runner.hasHandlers("tool_call")) {
|
|
222
224
|
return undefined;
|
|
223
225
|
}
|
|
224
|
-
await this._agentEventQueue;
|
|
225
226
|
try {
|
|
226
227
|
return await runner.emitToolCall({
|
|
227
228
|
type: "tool_call",
|
|
@@ -323,6 +324,47 @@ export class AgentSession {
|
|
|
323
324
|
this.abortBranchSummary();
|
|
324
325
|
this._incrementMessageRevision();
|
|
325
326
|
}
|
|
327
|
+
_getIdleWaitPromise() {
|
|
328
|
+
if (!this._idleWaitPromise) {
|
|
329
|
+
this._idleWaitPromise = new Promise((resolve) => {
|
|
330
|
+
this._resolveIdleWait = resolve;
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return this._idleWaitPromise;
|
|
334
|
+
}
|
|
335
|
+
_resolveIdleWaitIfIdle() {
|
|
336
|
+
if (this._isAgentRunActive || !this._resolveIdleWait) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const resolve = this._resolveIdleWait;
|
|
340
|
+
this._idleWaitPromise = undefined;
|
|
341
|
+
this._resolveIdleWait = undefined;
|
|
342
|
+
resolve();
|
|
343
|
+
}
|
|
344
|
+
async _emitAgentSettled() {
|
|
345
|
+
if (!this._isAgentRunActive) {
|
|
346
|
+
this._resolveIdleWaitIfIdle();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
this._isAgentRunActive = false;
|
|
350
|
+
try {
|
|
351
|
+
await this._extensionRunner.emit({ type: "agent_settled" });
|
|
352
|
+
this._emit({ type: "agent_settled" });
|
|
353
|
+
}
|
|
354
|
+
finally {
|
|
355
|
+
this._resolveIdleWaitIfIdle();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async _promptAgent(messages) {
|
|
359
|
+
this._isAgentRunActive = true;
|
|
360
|
+
try {
|
|
361
|
+
await this.agent.prompt(messages);
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
await this._emitAgentSettled();
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
326
368
|
_createRetryPromiseForAgentEnd(event) {
|
|
327
369
|
if (event.type !== "agent_end" || this._retryPromise) {
|
|
328
370
|
return;
|
|
@@ -348,6 +390,34 @@ export class AgentSession {
|
|
|
348
390
|
}
|
|
349
391
|
return undefined;
|
|
350
392
|
}
|
|
393
|
+
_agentEndAllowsQueuedContinuation(messages) {
|
|
394
|
+
let lastAssistantIndex = -1;
|
|
395
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
396
|
+
if (messages[index]?.role === "assistant") {
|
|
397
|
+
lastAssistantIndex = index;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (lastAssistantIndex === -1) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
const lastAssistant = messages[lastAssistantIndex];
|
|
405
|
+
if (lastAssistant?.role !== "assistant") {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
if (lastAssistant.stopReason === "aborted" || lastAssistant.stopReason === "error") {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
for (let index = lastAssistantIndex + 1; index < messages.length; index++) {
|
|
412
|
+
const message = messages[index];
|
|
413
|
+
if (message?.role === "toolResult" &&
|
|
414
|
+
message.isError &&
|
|
415
|
+
message.content.some((content) => content.type === "text" && /\babort(?:ed)?\b/i.test(content.text))) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
351
421
|
_willRetryAfterAgentEnd(messages) {
|
|
352
422
|
const settings = this.settingsManager.getRetrySettings();
|
|
353
423
|
if (!settings.enabled) {
|
|
@@ -430,6 +500,8 @@ export class AgentSession {
|
|
|
430
500
|
}
|
|
431
501
|
}
|
|
432
502
|
// Check auto-retry and auto-compaction after agent completes
|
|
503
|
+
let launchedContinuation = false;
|
|
504
|
+
const allowsQueuedContinuation = event.type === "agent_end" ? this._agentEndAllowsQueuedContinuation(event.messages) : false;
|
|
433
505
|
if (event.type === "agent_end" && this._lastAssistantMessage) {
|
|
434
506
|
const msg = this._lastAssistantMessage;
|
|
435
507
|
this._lastAssistantMessage = undefined;
|
|
@@ -440,10 +512,16 @@ export class AgentSession {
|
|
|
440
512
|
return; // Retry was initiated, don't proceed to compaction
|
|
441
513
|
}
|
|
442
514
|
this._resolveRetry();
|
|
443
|
-
await this._checkCompaction(msg);
|
|
515
|
+
launchedContinuation = await this._checkCompaction(msg);
|
|
444
516
|
}
|
|
445
517
|
if (event.type === "agent_end") {
|
|
446
518
|
this._flushPendingBashMessages();
|
|
519
|
+
if (!launchedContinuation && allowsQueuedContinuation && this.agent.hasQueuedMessages()) {
|
|
520
|
+
launchedContinuation = await this._continueAgentAfterCurrentRun();
|
|
521
|
+
}
|
|
522
|
+
if (!launchedContinuation) {
|
|
523
|
+
await this._emitAgentSettled();
|
|
524
|
+
}
|
|
447
525
|
}
|
|
448
526
|
}
|
|
449
527
|
/** Resolve the pending retry promise */
|
|
@@ -537,7 +615,16 @@ export class AgentSession {
|
|
|
537
615
|
};
|
|
538
616
|
const replacement = await this._extensionRunner.emitMessageEnd(extensionEvent);
|
|
539
617
|
if (replacement) {
|
|
540
|
-
|
|
618
|
+
// Untyped extension handlers can return messages with null/missing content;
|
|
619
|
+
// normalize so it never enters agent state or session history.
|
|
620
|
+
const normalized = (replacement.role === "user" ||
|
|
621
|
+
replacement.role === "assistant" ||
|
|
622
|
+
replacement.role === "toolResult" ||
|
|
623
|
+
replacement.role === "custom") &&
|
|
624
|
+
replacement.content == null
|
|
625
|
+
? { ...replacement, content: [] }
|
|
626
|
+
: replacement;
|
|
627
|
+
this._replaceMessageInPlace(event.message, normalized);
|
|
541
628
|
}
|
|
542
629
|
}
|
|
543
630
|
else if (event.type === "tool_execution_start") {
|
|
@@ -644,9 +731,13 @@ export class AgentSession {
|
|
|
644
731
|
get serviceTier() {
|
|
645
732
|
return this._currentServiceTier;
|
|
646
733
|
}
|
|
647
|
-
/** Whether
|
|
734
|
+
/** Whether the session is currently processing an agent run or post-run continuation. */
|
|
648
735
|
get isStreaming() {
|
|
649
|
-
return this.
|
|
736
|
+
return this._isAgentRunActive;
|
|
737
|
+
}
|
|
738
|
+
/** Whether the session has no active agent run, retry, auto-compaction, or queued continuation. */
|
|
739
|
+
get isIdle() {
|
|
740
|
+
return !this._isAgentRunActive;
|
|
650
741
|
}
|
|
651
742
|
/** Current effective system prompt (includes any per-turn extension modifications) */
|
|
652
743
|
get systemPrompt() {
|
|
@@ -987,7 +1078,8 @@ export class AgentSession {
|
|
|
987
1078
|
messages.push({
|
|
988
1079
|
role: "custom",
|
|
989
1080
|
customType: msg.customType,
|
|
990
|
-
content
|
|
1081
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1082
|
+
content: msg.content ?? [],
|
|
991
1083
|
display: msg.display,
|
|
992
1084
|
details: msg.details,
|
|
993
1085
|
timestamp: Date.now(),
|
|
@@ -1013,8 +1105,9 @@ export class AgentSession {
|
|
|
1013
1105
|
return;
|
|
1014
1106
|
}
|
|
1015
1107
|
preflightResult?.(true);
|
|
1016
|
-
await this.
|
|
1108
|
+
await this._promptAgent(messages);
|
|
1017
1109
|
await this.waitForRetry();
|
|
1110
|
+
await this.waitForIdle();
|
|
1018
1111
|
if (shouldWaitForSessionWork) {
|
|
1019
1112
|
await this._waitForSettledSessionWork();
|
|
1020
1113
|
}
|
|
@@ -1244,7 +1337,8 @@ export class AgentSession {
|
|
|
1244
1337
|
const appMessage = {
|
|
1245
1338
|
role: "custom",
|
|
1246
1339
|
customType: message.customType,
|
|
1247
|
-
content
|
|
1340
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1341
|
+
content: message.content ?? [],
|
|
1248
1342
|
display: message.display,
|
|
1249
1343
|
details: message.details,
|
|
1250
1344
|
timestamp: Date.now(),
|
|
@@ -1261,7 +1355,7 @@ export class AgentSession {
|
|
|
1261
1355
|
}
|
|
1262
1356
|
}
|
|
1263
1357
|
else if (options?.triggerTurn) {
|
|
1264
|
-
await this.
|
|
1358
|
+
await this._promptAgent(appMessage);
|
|
1265
1359
|
}
|
|
1266
1360
|
else {
|
|
1267
1361
|
this.agent.state.messages.push(appMessage);
|
|
@@ -1352,7 +1446,7 @@ export class AgentSession {
|
|
|
1352
1446
|
}
|
|
1353
1447
|
const abortPromise = (async () => {
|
|
1354
1448
|
this.agent.abort();
|
|
1355
|
-
await this.
|
|
1449
|
+
await this.waitForIdle();
|
|
1356
1450
|
})();
|
|
1357
1451
|
this._userAbortPromise = abortPromise;
|
|
1358
1452
|
try {
|
|
@@ -1364,6 +1458,12 @@ export class AgentSession {
|
|
|
1364
1458
|
}
|
|
1365
1459
|
}
|
|
1366
1460
|
}
|
|
1461
|
+
async waitForIdle() {
|
|
1462
|
+
if (this.isIdle) {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
await this._getIdleWaitPromise();
|
|
1466
|
+
}
|
|
1367
1467
|
// =========================================================================
|
|
1368
1468
|
// Model Management
|
|
1369
1469
|
// =========================================================================
|
|
@@ -1848,10 +1948,10 @@ export class AgentSession {
|
|
|
1848
1948
|
async _checkCompaction(assistantMessage, skipAbortedCheck = true, requestReason) {
|
|
1849
1949
|
const settings = this.settingsManager.getCompactionSettings();
|
|
1850
1950
|
if (!settings.enabled)
|
|
1851
|
-
return;
|
|
1951
|
+
return false;
|
|
1852
1952
|
// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
|
|
1853
1953
|
if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
|
|
1854
|
-
return;
|
|
1954
|
+
return false;
|
|
1855
1955
|
const contextWindow = this.model?.contextWindow ?? 0;
|
|
1856
1956
|
// Skip overflow check if the message came from a different model.
|
|
1857
1957
|
// This handles the case where user switched from a smaller-context model (e.g. opus)
|
|
@@ -1865,7 +1965,7 @@ export class AgentSession {
|
|
|
1865
1965
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
1866
1966
|
const assistantIsFromBeforeCompaction = compactionEntry !== null && assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime();
|
|
1867
1967
|
if (assistantIsFromBeforeCompaction) {
|
|
1868
|
-
return;
|
|
1968
|
+
return false;
|
|
1869
1969
|
}
|
|
1870
1970
|
// Case 1: Overflow - LLM returned context overflow error.
|
|
1871
1971
|
// If the saved assistant provider differs from the currently selected provider alias,
|
|
@@ -1877,8 +1977,7 @@ export class AgentSession {
|
|
|
1877
1977
|
if (isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction)) {
|
|
1878
1978
|
const willRetry = assistantMessage.stopReason !== "stop";
|
|
1879
1979
|
if (!willRetry) {
|
|
1880
|
-
await this._runAutoCompaction("overflow", false);
|
|
1881
|
-
return;
|
|
1980
|
+
return await this._runAutoCompaction("overflow", false);
|
|
1882
1981
|
}
|
|
1883
1982
|
if (this._overflowRecoveryAttempted) {
|
|
1884
1983
|
const errorMessage = "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.";
|
|
@@ -1893,7 +1992,7 @@ export class AgentSession {
|
|
|
1893
1992
|
if (requestReason === "pre_prompt") {
|
|
1894
1993
|
throw new Error(errorMessage);
|
|
1895
1994
|
}
|
|
1896
|
-
return;
|
|
1995
|
+
return false;
|
|
1897
1996
|
}
|
|
1898
1997
|
this._overflowRecoveryAttempted = true;
|
|
1899
1998
|
// Remove the error message from agent state (it IS saved to session for history,
|
|
@@ -1907,9 +2006,9 @@ export class AgentSession {
|
|
|
1907
2006
|
await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck, "overflow", willRetry);
|
|
1908
2007
|
}
|
|
1909
2008
|
else {
|
|
1910
|
-
await this._runAutoCompaction("overflow", willRetry);
|
|
2009
|
+
return await this._runAutoCompaction("overflow", willRetry);
|
|
1911
2010
|
}
|
|
1912
|
-
return;
|
|
2011
|
+
return false;
|
|
1913
2012
|
}
|
|
1914
2013
|
// Case 2: Threshold - context is getting large
|
|
1915
2014
|
// For error messages or all-zero usage messages, estimate from the last valid response.
|
|
@@ -1921,7 +2020,7 @@ export class AgentSession {
|
|
|
1921
2020
|
const messages = filterContextExcludedMessages(this.agent.state.messages);
|
|
1922
2021
|
const estimate = estimateContextTokens(messages);
|
|
1923
2022
|
if (estimate.lastUsageIndex === null)
|
|
1924
|
-
return; // No usage data at all
|
|
2023
|
+
return false; // No usage data at all
|
|
1925
2024
|
// Verify the usage source is post-compaction. Kept pre-compaction messages
|
|
1926
2025
|
// have stale usage reflecting the old (larger) context and would falsely
|
|
1927
2026
|
// trigger compaction right after one just finished.
|
|
@@ -1929,7 +2028,7 @@ export class AgentSession {
|
|
|
1929
2028
|
if (compactionEntry &&
|
|
1930
2029
|
usageMsg.role === "assistant" &&
|
|
1931
2030
|
usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
|
|
1932
|
-
return;
|
|
2031
|
+
return false;
|
|
1933
2032
|
}
|
|
1934
2033
|
contextTokens = estimate.tokens;
|
|
1935
2034
|
}
|
|
@@ -1941,9 +2040,10 @@ export class AgentSession {
|
|
|
1941
2040
|
await this._runPrePromptCompaction(assistantMessage, skipAbortedCheck);
|
|
1942
2041
|
}
|
|
1943
2042
|
else {
|
|
1944
|
-
await this._runAutoCompaction("threshold", false);
|
|
2043
|
+
return await this._runAutoCompaction("threshold", false);
|
|
1945
2044
|
}
|
|
1946
2045
|
}
|
|
2046
|
+
return false;
|
|
1947
2047
|
}
|
|
1948
2048
|
async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false) {
|
|
1949
2049
|
this._emit({ type: "compaction_start", reason });
|
|
@@ -2256,7 +2356,7 @@ export class AgentSession {
|
|
|
2256
2356
|
}, {
|
|
2257
2357
|
getModel: () => this.model,
|
|
2258
2358
|
getServiceTier: () => this.serviceTier,
|
|
2259
|
-
isIdle: () =>
|
|
2359
|
+
isIdle: () => this.isIdle,
|
|
2260
2360
|
isProjectTrusted: () => this.settingsManager.isProjectTrusted(),
|
|
2261
2361
|
getSignal: () => this.agent.signal,
|
|
2262
2362
|
abort: () => {
|
|
@@ -2593,6 +2693,7 @@ export class AgentSession {
|
|
|
2593
2693
|
const attempt = this._retryAttempt;
|
|
2594
2694
|
this._retryAttempt = 0;
|
|
2595
2695
|
this._retryAbortController = undefined;
|
|
2696
|
+
await this._emitAgentSettled();
|
|
2596
2697
|
this._emit({
|
|
2597
2698
|
type: "auto_retry_end",
|
|
2598
2699
|
success: false,
|
|
@@ -2949,28 +3050,44 @@ export class AgentSession {
|
|
|
2949
3050
|
return "";
|
|
2950
3051
|
}
|
|
2951
3052
|
/**
|
|
2952
|
-
* Get session statistics.
|
|
3053
|
+
* Get session statistics. Aggregates over ALL session entries (including
|
|
3054
|
+
* history that was compacted away), so token/cost totals reflect what was
|
|
3055
|
+
* actually billed across the session.
|
|
2953
3056
|
*/
|
|
2954
3057
|
getSessionStats() {
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3058
|
+
let userMessages = 0;
|
|
3059
|
+
let assistantMessages = 0;
|
|
3060
|
+
let toolResults = 0;
|
|
3061
|
+
let totalMessages = 0;
|
|
2959
3062
|
let toolCalls = 0;
|
|
2960
3063
|
let totalInput = 0;
|
|
2961
3064
|
let totalOutput = 0;
|
|
2962
3065
|
let totalCacheRead = 0;
|
|
2963
3066
|
let totalCacheWrite = 0;
|
|
2964
3067
|
let totalCost = 0;
|
|
2965
|
-
for (const
|
|
2966
|
-
if (
|
|
3068
|
+
for (const entry of this.sessionManager.getEntries()) {
|
|
3069
|
+
if (entry.type !== "message")
|
|
3070
|
+
continue;
|
|
3071
|
+
totalMessages++;
|
|
3072
|
+
const message = entry.message;
|
|
3073
|
+
if (message.role === "user") {
|
|
3074
|
+
userMessages++;
|
|
3075
|
+
}
|
|
3076
|
+
else if (message.role === "toolResult") {
|
|
3077
|
+
toolResults++;
|
|
3078
|
+
}
|
|
3079
|
+
else if (message.role === "assistant") {
|
|
3080
|
+
assistantMessages++;
|
|
2967
3081
|
const assistantMsg = message;
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
3082
|
+
if (Array.isArray(assistantMsg.content)) {
|
|
3083
|
+
toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
3084
|
+
}
|
|
3085
|
+
const usage = assistantMsg.usage;
|
|
3086
|
+
totalInput += usage.input;
|
|
3087
|
+
totalOutput += usage.output;
|
|
3088
|
+
totalCacheRead += usage.cacheRead;
|
|
3089
|
+
totalCacheWrite += usage.cacheWrite;
|
|
3090
|
+
totalCost += usage.cost.total;
|
|
2974
3091
|
}
|
|
2975
3092
|
}
|
|
2976
3093
|
return {
|
|
@@ -2980,7 +3097,7 @@ export class AgentSession {
|
|
|
2980
3097
|
assistantMessages,
|
|
2981
3098
|
toolCalls,
|
|
2982
3099
|
toolResults,
|
|
2983
|
-
totalMessages
|
|
3100
|
+
totalMessages,
|
|
2984
3101
|
tokens: {
|
|
2985
3102
|
input: totalInput,
|
|
2986
3103
|
output: totalOutput,
|