@astralyn/pi 0.87.1 → 0.87.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 +14 -0
- package/dist/bundle/chunks/{chunk-CDMUKIXY.js → chunk-NBIUJDV6.js} +1 -1
- package/dist/bundle/chunks/{chunk-772GXEFD.js → chunk-RFLBIA5Z.js} +69 -69
- package/dist/bundle/chunks/{virtual-modules-LQO2MG3T.js → virtual-modules-GOHLE5JL.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +9 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +158 -157
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +14 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +437 -434
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/background/types.d.ts +2 -0
- package/dist/core/background/types.d.ts.map +1 -1
- package/dist/core/background/types.js.map +1 -1
- package/dist/core/extensions/distribution-api.d.ts +41 -0
- package/dist/core/extensions/distribution-api.d.ts.map +1 -0
- package/dist/core/extensions/distribution-api.js +12 -0
- package/dist/core/extensions/distribution-api.js.map +1 -0
- 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 +1 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +0 -2
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +15 -45
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +1 -11
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/virtual-modules.js +2 -1
- package/dist/core/extensions/virtual-modules.js.map +1 -1
- package/dist/core/keybinding-registry.d.ts +11 -0
- package/dist/core/keybinding-registry.d.ts.map +1 -0
- package/dist/core/keybinding-registry.js +24 -0
- package/dist/core/keybinding-registry.js.map +1 -0
- package/dist/core/keybindings.d.ts +4 -64
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -41
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +70 -2
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +2 -26
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +5 -11
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/shell-execution.d.ts +1 -0
- package/dist/core/tools/shell-execution.d.ts.map +1 -1
- package/dist/core/tools/shell-execution.js +21 -0
- package/dist/core/tools/shell-execution.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +11 -48
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/extensions/background/index.d.ts +2 -0
- package/dist/extensions/background/index.d.ts.map +1 -1
- package/dist/extensions/background/index.js +17 -0
- package/dist/extensions/background/index.js.map +1 -1
- package/dist/extensions/background/keybindings.d.ts +10 -0
- package/dist/extensions/background/keybindings.d.ts.map +1 -0
- package/dist/extensions/background/keybindings.js +14 -0
- package/dist/extensions/background/keybindings.js.map +1 -0
- package/dist/extensions/background/manager.d.ts +2 -0
- package/dist/extensions/background/manager.d.ts.map +1 -1
- package/dist/extensions/background/manager.js +1 -0
- package/dist/extensions/background/manager.js.map +1 -1
- package/dist/extensions/btw/controller.d.ts +1 -0
- package/dist/extensions/btw/controller.d.ts.map +1 -1
- package/dist/extensions/btw/controller.js +5 -5
- package/dist/extensions/btw/controller.js.map +1 -1
- package/dist/extensions/btw/index.js +1 -2
- package/dist/extensions/btw/index.js.map +1 -1
- package/dist/extensions/btw/keybindings.d.ts +10 -0
- package/dist/extensions/btw/keybindings.d.ts.map +1 -0
- package/dist/extensions/btw/keybindings.js +8 -0
- package/dist/extensions/btw/keybindings.js.map +1 -0
- package/dist/extensions/btw/panel.d.ts +1 -0
- package/dist/extensions/btw/panel.d.ts.map +1 -1
- package/dist/extensions/btw/panel.js +1 -0
- package/dist/extensions/btw/panel.js.map +1 -1
- package/dist/extensions/list-keybindings.d.ts +8 -0
- package/dist/extensions/list-keybindings.d.ts.map +1 -0
- package/dist/extensions/list-keybindings.js +8 -0
- package/dist/extensions/list-keybindings.js.map +1 -0
- package/dist/extensions/provider/compat-fields.d.ts.map +1 -1
- package/dist/extensions/provider/compat-fields.js +23 -3
- package/dist/extensions/provider/compat-fields.js.map +1 -1
- package/dist/extensions/provider/connection.d.ts.map +1 -1
- package/dist/extensions/provider/connection.js +1 -1
- package/dist/extensions/provider/connection.js.map +1 -1
- package/dist/extensions/provider/constants.d.ts +2 -0
- package/dist/extensions/provider/constants.d.ts.map +1 -1
- package/dist/extensions/provider/constants.js +2 -0
- package/dist/extensions/provider/constants.js.map +1 -1
- package/dist/extensions/provider/keybindings.d.ts +9 -0
- package/dist/extensions/provider/keybindings.d.ts.map +1 -0
- package/dist/extensions/provider/keybindings.js +12 -0
- package/dist/extensions/provider/keybindings.js.map +1 -0
- package/dist/extensions/provider/probe.d.ts +8 -4
- package/dist/extensions/provider/probe.d.ts.map +1 -1
- package/dist/extensions/provider/probe.js +169 -11
- package/dist/extensions/provider/probe.js.map +1 -1
- package/dist/extensions/provider/ui/builtin-data.d.ts +1 -0
- package/dist/extensions/provider/ui/builtin-data.d.ts.map +1 -1
- package/dist/extensions/provider/ui/builtin-data.js +1 -0
- package/dist/extensions/provider/ui/builtin-data.js.map +1 -1
- package/dist/extensions/provider/ui/compat.d.ts +1 -0
- package/dist/extensions/provider/ui/compat.d.ts.map +1 -1
- package/dist/extensions/provider/ui/compat.js +1 -0
- package/dist/extensions/provider/ui/compat.js.map +1 -1
- package/dist/extensions/provider/ui/dictionary.d.ts +1 -0
- package/dist/extensions/provider/ui/dictionary.d.ts.map +1 -1
- package/dist/extensions/provider/ui/dictionary.js +1 -0
- package/dist/extensions/provider/ui/dictionary.js.map +1 -1
- package/dist/extensions/provider/ui/editor.d.ts +1 -0
- package/dist/extensions/provider/ui/editor.d.ts.map +1 -1
- package/dist/extensions/provider/ui/editor.js +1 -0
- package/dist/extensions/provider/ui/editor.js.map +1 -1
- package/dist/extensions/provider/ui/fetch-models.d.ts +5 -3
- package/dist/extensions/provider/ui/fetch-models.d.ts.map +1 -1
- package/dist/extensions/provider/ui/fetch-models.js +28 -3
- package/dist/extensions/provider/ui/fetch-models.js.map +1 -1
- package/dist/extensions/provider/ui/model-fields.d.ts +1 -0
- package/dist/extensions/provider/ui/model-fields.d.ts.map +1 -1
- package/dist/extensions/provider/ui/model-fields.js +1 -0
- package/dist/extensions/provider/ui/model-fields.js.map +1 -1
- package/dist/extensions/provider/ui/model-options.d.ts +1 -0
- package/dist/extensions/provider/ui/model-options.d.ts.map +1 -1
- package/dist/extensions/provider/ui/model-options.js +1 -0
- package/dist/extensions/provider/ui/model-options.js.map +1 -1
- package/dist/extensions/provider/ui/provider-fields.d.ts +1 -0
- package/dist/extensions/provider/ui/provider-fields.d.ts.map +1 -1
- package/dist/extensions/provider/ui/provider-fields.js +1 -0
- package/dist/extensions/provider/ui/provider-fields.js.map +1 -1
- package/dist/extensions/question/dialog.d.ts +1 -0
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js +1 -0
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/keybindings.d.ts +2 -0
- package/dist/extensions/question/keybindings.d.ts.map +1 -0
- package/dist/extensions/question/keybindings.js +3 -0
- package/dist/extensions/question/keybindings.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/editor-host.d.ts +43 -0
- package/dist/modes/interactive/editor-host.d.ts.map +1 -0
- package/dist/modes/interactive/editor-host.js +83 -0
- package/dist/modes/interactive/editor-host.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -11
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +300 -360
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tool-chat.d.ts +21 -0
- package/dist/modes/interactive/tool-chat.d.ts.map +1 -0
- package/dist/modes/interactive/tool-chat.js +58 -0
- package/dist/modes/interactive/tool-chat.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +0 -2
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/path-identity.d.ts +12 -0
- package/dist/utils/path-identity.d.ts.map +1 -0
- package/dist/utils/path-identity.js +63 -0
- package/dist/utils/path-identity.js.map +1 -0
- package/dist/utils/shell.d.ts +0 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +0 -21
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +2 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +2 -0
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/docs/bundled/extensions/provider.md +7 -3
- package/docs/extensions.md +15 -5
- package/examples/extensions/README.md +34 -14
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +2 -2
- package/examples/extensions/bookmark.ts +1 -1
- package/examples/extensions/border-status-editor.ts +6 -1
- package/examples/extensions/built-in-tool-renderer.ts +2 -2
- package/examples/extensions/claude-rules.ts +1 -1
- package/examples/extensions/commands.ts +1 -1
- package/examples/extensions/confirm-destructive.ts +1 -1
- package/examples/extensions/custom-compaction.ts +2 -2
- package/examples/extensions/custom-footer.ts +1 -1
- package/examples/extensions/custom-header.ts +2 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +6 -6
- 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 +3 -3
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/dirty-repo-guard.ts +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/dynamic-resources/dynamic.json +3 -1
- package/examples/extensions/dynamic-resources/index.ts +1 -1
- package/examples/extensions/dynamic-tools.ts +1 -1
- package/examples/extensions/entry-renderer.ts +1 -1
- package/examples/extensions/event-bus.ts +1 -1
- package/examples/extensions/file-trigger.ts +1 -1
- package/examples/extensions/git-checkpoint.ts +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +1 -1
- package/examples/extensions/github-issue-autocomplete.ts +1 -1
- package/examples/extensions/gondolin/index.ts +9 -7
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +2 -2
- package/examples/extensions/hello.ts +1 -1
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/inline-bash.ts +1 -1
- package/examples/extensions/input-transform-streaming.ts +1 -1
- package/examples/extensions/input-transform.ts +1 -1
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +1 -1
- package/examples/extensions/message-renderer.ts +1 -1
- package/examples/extensions/minimal-mode.ts +2 -2
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/notify.ts +1 -1
- package/examples/extensions/overlay-qa-tests.ts +3 -3
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/permission-gate.ts +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/plan-mode/index.ts +1 -1
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/project-trust.ts +3 -3
- package/examples/extensions/prompt-customizer.ts +1 -1
- package/examples/extensions/protected-paths.ts +1 -1
- package/examples/extensions/provider-payload.ts +1 -1
- package/examples/extensions/qna.ts +2 -2
- package/examples/extensions/question.ts +1 -1
- package/examples/extensions/questionnaire.ts +1 -1
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +2 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/send-user-message.ts +1 -1
- package/examples/extensions/session-name.ts +1 -1
- package/examples/extensions/shutdown-command.ts +1 -1
- package/examples/extensions/snake.ts +1 -1
- package/examples/extensions/space-invaders.ts +1 -1
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/status-line.ts +1 -1
- package/examples/extensions/structured-output.ts +1 -1
- package/examples/extensions/summarize.ts +2 -3
- package/examples/extensions/system-prompt-header.ts +1 -1
- package/examples/extensions/tic-tac-toe.ts +1 -1
- package/examples/extensions/timed-confirm.ts +1 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/todo.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/trigger-compact.ts +1 -1
- package/examples/extensions/truncated-tool.ts +4 -4
- package/examples/extensions/widget-placement.ts +1 -1
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/working-indicator.ts +1 -1
- package/examples/extensions/working-message-test.ts +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -251,6 +251,19 @@ export class AgentSession {
|
|
|
251
251
|
retryBackgroundNotifications() {
|
|
252
252
|
this._backgroundHost.retry();
|
|
253
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Run a lifecycle operation with delivery paused across its asynchronous gaps.
|
|
256
|
+
* Lifecycle methods are thin shells over upstream bodies so those keep upstream's shape.
|
|
257
|
+
*/
|
|
258
|
+
async _withBackgroundPaused(operation) {
|
|
259
|
+
const resumeBackground = this.pauseBackgroundNotifications();
|
|
260
|
+
try {
|
|
261
|
+
return await operation();
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
resumeBackground();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
254
267
|
get modelRuntime() {
|
|
255
268
|
return this._modelRuntime;
|
|
256
269
|
}
|
|
@@ -643,28 +656,36 @@ export class AgentSession {
|
|
|
643
656
|
this._resolveIdleWait = undefined;
|
|
644
657
|
resolve();
|
|
645
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Background completions stay paused until runs requested by settled handlers have started,
|
|
661
|
+
* so a deferred prompt never races a delivery for the same idle gap.
|
|
662
|
+
*/
|
|
646
663
|
async _emitAgentSettled() {
|
|
664
|
+
return this._withBackgroundPaused(() => this._emitAgentSettledBody());
|
|
665
|
+
}
|
|
666
|
+
async _emitAgentSettledBody() {
|
|
647
667
|
this._cacheWarmer?.onAgentSettled();
|
|
648
|
-
// Background completions stay paused until runs requested by settled handlers have started,
|
|
649
|
-
// so a deferred prompt never races a delivery for the same idle gap.
|
|
650
|
-
const resumeBackground = this.pauseBackgroundNotifications();
|
|
651
668
|
this._isAgentRunActive = false;
|
|
669
|
+
this._isEmittingAgentSettled = true;
|
|
652
670
|
try {
|
|
653
|
-
this.
|
|
671
|
+
await this._extensionRunner.emit({ type: "agent_settled" });
|
|
672
|
+
this._emit({ type: "agent_settled" });
|
|
673
|
+
}
|
|
674
|
+
finally {
|
|
675
|
+
this._isEmittingAgentSettled = false;
|
|
676
|
+
}
|
|
677
|
+
const deferred = this._deferredSettledActions.splice(0);
|
|
678
|
+
if (deferred.length > 0) {
|
|
654
679
|
try {
|
|
655
|
-
|
|
656
|
-
|
|
680
|
+
for (const action of deferred)
|
|
681
|
+
await action();
|
|
657
682
|
}
|
|
658
683
|
finally {
|
|
659
|
-
this.
|
|
684
|
+
this._resolveIdleWaitIfIdle();
|
|
660
685
|
}
|
|
661
|
-
|
|
662
|
-
await action();
|
|
663
|
-
}
|
|
664
|
-
finally {
|
|
665
|
-
this._resolveIdleWaitIfIdle();
|
|
666
|
-
resumeBackground();
|
|
686
|
+
return;
|
|
667
687
|
}
|
|
688
|
+
this._resolveIdleWaitIfIdle();
|
|
668
689
|
}
|
|
669
690
|
/**
|
|
670
691
|
* Persist model/custom messages and consume queued context only after the append succeeds.
|
|
@@ -1334,138 +1355,132 @@ export class AgentSession {
|
|
|
1334
1355
|
this.retryBackgroundNotifications();
|
|
1335
1356
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
1336
1357
|
const preflightResult = options?.preflightResult;
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1358
|
+
let messages;
|
|
1359
|
+
let resumeBackground;
|
|
1360
|
+
try {
|
|
1361
|
+
// Handle extension commands first (execute immediately, even during streaming)
|
|
1362
|
+
// Extension commands manage their own LLM interaction via pi.sendMessage()
|
|
1363
|
+
if (expandPromptTemplates && text.startsWith("/")) {
|
|
1364
|
+
const handled = await this._tryExecuteExtensionCommand(text);
|
|
1365
|
+
if (handled) {
|
|
1366
|
+
// Extension command executed, no prompt to send
|
|
1342
1367
|
preflightResult?.(true);
|
|
1343
1368
|
return;
|
|
1344
1369
|
}
|
|
1345
1370
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1371
|
+
// Observer commands above may stay open indefinitely; only real model input owns
|
|
1372
|
+
// the preflight pause, released once the user message is established.
|
|
1373
|
+
resumeBackground = this.pauseBackgroundNotifications();
|
|
1374
|
+
if (this._compactionAbortController !== undefined) {
|
|
1375
|
+
throw new Error("Cannot submit a prompt while compaction is in progress. Wait for compaction to finish and retry.");
|
|
1349
1376
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
if (expandPromptTemplates) {
|
|
1368
|
-
expandedText = this._expandSkillCommand(expandedText);
|
|
1369
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1370
|
-
}
|
|
1371
|
-
// If streaming, queue via steer() or followUp() based on option
|
|
1372
|
-
if (this.isStreaming) {
|
|
1373
|
-
if (!options?.streamingBehavior) {
|
|
1374
|
-
throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
|
|
1375
|
-
}
|
|
1376
|
-
if (options.streamingBehavior === "followUp") {
|
|
1377
|
-
await this._queueFollowUp(expandedText, currentImages);
|
|
1378
|
-
}
|
|
1379
|
-
else {
|
|
1380
|
-
await this._queueSteer(expandedText, currentImages);
|
|
1381
|
-
}
|
|
1382
|
-
preflightResult?.(true);
|
|
1383
|
-
return;
|
|
1377
|
+
// Emit input event for extension interception (before skill/template expansion)
|
|
1378
|
+
const processedInput = await this._runInputHandlers(text, options?.images, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
|
|
1379
|
+
if (!processedInput) {
|
|
1380
|
+
preflightResult?.(true);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
const { text: currentText, images: currentImages } = processedInput;
|
|
1384
|
+
// Expand skill commands (/skill:name args) and prompt templates (/template args)
|
|
1385
|
+
let expandedText = currentText;
|
|
1386
|
+
if (expandPromptTemplates) {
|
|
1387
|
+
expandedText = this._expandSkillCommand(expandedText);
|
|
1388
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1389
|
+
}
|
|
1390
|
+
// If streaming, queue via steer() or followUp() based on option
|
|
1391
|
+
if (this.isStreaming) {
|
|
1392
|
+
if (!options?.streamingBehavior) {
|
|
1393
|
+
throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
|
|
1384
1394
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
this._flushPendingCustomMessages();
|
|
1388
|
-
// Validate model
|
|
1389
|
-
if (!this.model) {
|
|
1390
|
-
throw new Error(formatNoModelSelectedMessage());
|
|
1395
|
+
if (options.streamingBehavior === "followUp") {
|
|
1396
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
1391
1397
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
if (!hasConfiguredAuth) {
|
|
1395
|
-
const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
|
|
1396
|
-
if (isOAuth) {
|
|
1397
|
-
throw new Error(`Authentication failed for "${this.model.provider}". ` +
|
|
1398
|
-
`Credentials may have expired or network is unavailable. ` +
|
|
1399
|
-
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
1400
|
-
}
|
|
1401
|
-
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
1398
|
+
else {
|
|
1399
|
+
await this._queueSteer(expandedText, currentImages);
|
|
1402
1400
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1401
|
+
preflightResult?.(true);
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
// Flush any pending bash and custom messages before the new prompt
|
|
1405
|
+
this._flushPendingBashMessages();
|
|
1406
|
+
this._flushPendingCustomMessages();
|
|
1407
|
+
// Validate model
|
|
1408
|
+
if (!this.model) {
|
|
1409
|
+
throw new Error(formatNoModelSelectedMessage());
|
|
1410
|
+
}
|
|
1411
|
+
const hasConfiguredAuth = this._modelRuntime.hasConfiguredAuth(this.model.provider) ||
|
|
1412
|
+
(await this._modelRuntime.checkAuth(this.model.provider)) !== undefined;
|
|
1413
|
+
if (!hasConfiguredAuth) {
|
|
1414
|
+
const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
|
|
1415
|
+
if (isOAuth) {
|
|
1416
|
+
throw new Error(`Authentication failed for "${this.model.provider}". ` +
|
|
1417
|
+
`Credentials may have expired or network is unavailable. ` +
|
|
1418
|
+
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
1408
1419
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1420
|
+
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
1421
|
+
}
|
|
1422
|
+
// Check if we need to compact before sending (catches aborted responses).
|
|
1423
|
+
// The user's new prompt is sent below, so do not call agent.continue() here.
|
|
1424
|
+
const lastAssistant = this._findLastAssistantMessage();
|
|
1425
|
+
if (lastAssistant) {
|
|
1426
|
+
await this._checkCompaction(lastAssistant, false);
|
|
1427
|
+
}
|
|
1428
|
+
// Emit before_agent_start before normalizing images so extension-driven model
|
|
1429
|
+
// selection determines the resize profile used for the request and history.
|
|
1430
|
+
const selectedToolsBefore = this._baseSystemPromptOptions.selectedTools;
|
|
1431
|
+
const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptOptions);
|
|
1432
|
+
// Handlers may edit event.systemPromptOptions.selectedTools or call setActiveTools(),
|
|
1433
|
+
// which updates the live loadout instead. An explicit edit wins; otherwise the live
|
|
1434
|
+
// loadout is authoritative, so a setActiveTools() call is not undone here.
|
|
1435
|
+
const handlerEditedTools = result.systemPromptOptions.selectedTools.length !== selectedToolsBefore.length ||
|
|
1436
|
+
result.systemPromptOptions.selectedTools.some((name, index) => name !== selectedToolsBefore[index]);
|
|
1437
|
+
if (!handlerEditedTools)
|
|
1438
|
+
result.systemPromptOptions.selectedTools = this.getActiveToolNames();
|
|
1439
|
+
const normalized = await this._normalizePromptImages(currentImages);
|
|
1440
|
+
const userText = normalized.hints.length > 0 ? `${expandedText}\n\n${normalized.hints.join("\n")}` : expandedText;
|
|
1441
|
+
// Build messages only after hooks and image normalization have completed.
|
|
1442
|
+
messages = [];
|
|
1443
|
+
const userContent = [{ type: "text", text: userText }];
|
|
1444
|
+
userContent.push(...normalized.images);
|
|
1445
|
+
messages.push({
|
|
1446
|
+
role: "user",
|
|
1447
|
+
content: userContent,
|
|
1448
|
+
timestamp: Date.now(),
|
|
1449
|
+
});
|
|
1450
|
+
// Inject any pending "nextTurn" messages as context alongside the user message.
|
|
1451
|
+
// They stay queued until _persistMessage() confirms the append.
|
|
1452
|
+
for (const msg of this._pendingNextTurnMessages) {
|
|
1453
|
+
messages.push(msg);
|
|
1454
|
+
}
|
|
1455
|
+
for (const msg of result.messages) {
|
|
1426
1456
|
messages.push({
|
|
1427
|
-
role: "
|
|
1428
|
-
|
|
1457
|
+
role: "custom",
|
|
1458
|
+
customType: msg.customType,
|
|
1459
|
+
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1460
|
+
content: msg.content ?? [],
|
|
1461
|
+
display: msg.display,
|
|
1462
|
+
details: msg.details,
|
|
1429
1463
|
timestamp: Date.now(),
|
|
1430
1464
|
});
|
|
1431
|
-
// Inject any pending "nextTurn" messages as context alongside the user message.
|
|
1432
|
-
// They stay queued until _persistMessage() confirms the append.
|
|
1433
|
-
for (const msg of this._pendingNextTurnMessages) {
|
|
1434
|
-
messages.push(msg);
|
|
1435
|
-
}
|
|
1436
|
-
for (const msg of result.messages) {
|
|
1437
|
-
messages.push({
|
|
1438
|
-
role: "custom",
|
|
1439
|
-
customType: msg.customType,
|
|
1440
|
-
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1441
|
-
content: msg.content ?? [],
|
|
1442
|
-
display: msg.display,
|
|
1443
|
-
details: msg.details,
|
|
1444
|
-
timestamp: Date.now(),
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
|
|
1448
|
-
this._runSystemPromptOptions = result.systemPromptOptions;
|
|
1449
|
-
if (updateMessage)
|
|
1450
|
-
messages.unshift(updateMessage);
|
|
1451
|
-
}
|
|
1452
|
-
catch (error) {
|
|
1453
|
-
preflightResult?.(false);
|
|
1454
|
-
throw error;
|
|
1455
1465
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
await this._runAgentPrompt(messages);
|
|
1466
|
+
const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
|
|
1467
|
+
this._runSystemPromptOptions = result.systemPromptOptions;
|
|
1468
|
+
if (updateMessage)
|
|
1469
|
+
messages.unshift(updateMessage);
|
|
1470
|
+
}
|
|
1471
|
+
catch (error) {
|
|
1472
|
+
preflightResult?.(false);
|
|
1473
|
+
throw error;
|
|
1465
1474
|
}
|
|
1466
1475
|
finally {
|
|
1467
|
-
|
|
1476
|
+
// Delivery is timer-scheduled, so a completion cannot start before the run below does.
|
|
1477
|
+
resumeBackground?.();
|
|
1468
1478
|
}
|
|
1479
|
+
if (!messages) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
preflightResult?.(true);
|
|
1483
|
+
await this._runAgentPrompt(messages);
|
|
1469
1484
|
}
|
|
1470
1485
|
/**
|
|
1471
1486
|
* Try to execute an extension command. Returns true if command was found and executed.
|
|
@@ -2005,137 +2020,134 @@ export class AgentSession {
|
|
|
2005
2020
|
* @param customInstructions Optional instructions for the compaction summary
|
|
2006
2021
|
*/
|
|
2007
2022
|
async compact(customInstructions) {
|
|
2008
|
-
|
|
2023
|
+
return this._withBackgroundPaused(() => this._compactBody(customInstructions));
|
|
2024
|
+
}
|
|
2025
|
+
async _compactBody(customInstructions) {
|
|
2026
|
+
await this.abort();
|
|
2027
|
+
this._compactionAbortController = new AbortController();
|
|
2028
|
+
this._emit({ type: "compaction_start", reason: "manual" });
|
|
2029
|
+
let fromExtension = false;
|
|
2030
|
+
let cancelledByExtension = false;
|
|
2009
2031
|
try {
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
const
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
2024
|
-
if (!preparation) {
|
|
2025
|
-
// Check why we can't compact
|
|
2026
|
-
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
2027
|
-
if (lastEntry?.type === "compaction") {
|
|
2028
|
-
throw new Error("Already compacted");
|
|
2029
|
-
}
|
|
2030
|
-
throw new Error("Nothing to compact (session too small)");
|
|
2031
|
-
}
|
|
2032
|
-
let extensionCompaction;
|
|
2033
|
-
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2034
|
-
const result = (await this._extensionRunner.emit({
|
|
2035
|
-
type: "session_before_compact",
|
|
2036
|
-
preparation,
|
|
2037
|
-
branchEntries: pathEntries,
|
|
2038
|
-
customInstructions,
|
|
2039
|
-
reason: "manual",
|
|
2040
|
-
willRetry: false,
|
|
2041
|
-
signal: this._compactionAbortController.signal,
|
|
2042
|
-
}));
|
|
2043
|
-
if (result?.cancel) {
|
|
2044
|
-
cancelledByExtension = true;
|
|
2045
|
-
throw new Error("Compaction cancelled");
|
|
2046
|
-
}
|
|
2047
|
-
if (result?.compaction) {
|
|
2048
|
-
extensionCompaction = result.compaction;
|
|
2049
|
-
fromExtension = true;
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
let summary;
|
|
2053
|
-
let firstKeptEntryId;
|
|
2054
|
-
let tokensBefore;
|
|
2055
|
-
let usage;
|
|
2056
|
-
let details;
|
|
2057
|
-
if (extensionCompaction) {
|
|
2058
|
-
// Extension provided compaction content
|
|
2059
|
-
summary = extensionCompaction.summary;
|
|
2060
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2061
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
2062
|
-
usage = extensionCompaction.usage;
|
|
2063
|
-
details = extensionCompaction.details;
|
|
2064
|
-
}
|
|
2065
|
-
else {
|
|
2066
|
-
// Shared default summary generator, also used by automatic compaction.
|
|
2067
|
-
const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
|
|
2068
|
-
summary = result.summary;
|
|
2069
|
-
firstKeptEntryId = result.firstKeptEntryId;
|
|
2070
|
-
tokensBefore = result.tokensBefore;
|
|
2071
|
-
usage = result.usage;
|
|
2072
|
-
details = result.details;
|
|
2032
|
+
const model = this.model;
|
|
2033
|
+
if (!model) {
|
|
2034
|
+
throw new Error(formatNoModelSelectedMessage());
|
|
2035
|
+
}
|
|
2036
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
2037
|
+
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, this._compactionAbortController.signal);
|
|
2038
|
+
const pathEntries = this.sessionManager.getBranch();
|
|
2039
|
+
const preparation = prepareCompaction(pathEntries, settings);
|
|
2040
|
+
if (!preparation) {
|
|
2041
|
+
// Check why we can't compact
|
|
2042
|
+
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
2043
|
+
if (lastEntry?.type === "compaction") {
|
|
2044
|
+
throw new Error("Already compacted");
|
|
2073
2045
|
}
|
|
2074
|
-
|
|
2046
|
+
throw new Error("Nothing to compact (session too small)");
|
|
2047
|
+
}
|
|
2048
|
+
let extensionCompaction;
|
|
2049
|
+
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2050
|
+
const result = (await this._extensionRunner.emit({
|
|
2051
|
+
type: "session_before_compact",
|
|
2052
|
+
preparation,
|
|
2053
|
+
branchEntries: pathEntries,
|
|
2054
|
+
customInstructions,
|
|
2055
|
+
reason: "manual",
|
|
2056
|
+
willRetry: false,
|
|
2057
|
+
signal: this._compactionAbortController.signal,
|
|
2058
|
+
}));
|
|
2059
|
+
if (result?.cancel) {
|
|
2060
|
+
cancelledByExtension = true;
|
|
2075
2061
|
throw new Error("Compaction cancelled");
|
|
2076
2062
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
|
|
2081
|
-
// Get the saved compaction entry for the extension event
|
|
2082
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2083
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2084
|
-
await this._extensionRunner.emit({
|
|
2085
|
-
type: "session_compact",
|
|
2086
|
-
compactionEntry: savedCompactionEntry,
|
|
2087
|
-
fromExtension,
|
|
2088
|
-
reason: "manual",
|
|
2089
|
-
willRetry: false,
|
|
2090
|
-
});
|
|
2063
|
+
if (result?.compaction) {
|
|
2064
|
+
extensionCompaction = result.compaction;
|
|
2065
|
+
fromExtension = true;
|
|
2091
2066
|
}
|
|
2092
|
-
const compactionResult = {
|
|
2093
|
-
summary,
|
|
2094
|
-
firstKeptEntryId,
|
|
2095
|
-
tokensBefore,
|
|
2096
|
-
estimatedTokensAfter,
|
|
2097
|
-
usage,
|
|
2098
|
-
details,
|
|
2099
|
-
};
|
|
2100
|
-
// compaction_end listeners may submit queued prompts, so expose idle state before notifying them.
|
|
2101
|
-
this._clearManualCompactionState();
|
|
2102
|
-
this._emit({
|
|
2103
|
-
type: "compaction_end",
|
|
2104
|
-
reason: "manual",
|
|
2105
|
-
result: compactionResult,
|
|
2106
|
-
aborted: false,
|
|
2107
|
-
willRetry: false,
|
|
2108
|
-
});
|
|
2109
|
-
return compactionResult;
|
|
2110
2067
|
}
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2068
|
+
let summary;
|
|
2069
|
+
let firstKeptEntryId;
|
|
2070
|
+
let tokensBefore;
|
|
2071
|
+
let usage;
|
|
2072
|
+
let details;
|
|
2073
|
+
if (extensionCompaction) {
|
|
2074
|
+
// Extension provided compaction content
|
|
2075
|
+
summary = extensionCompaction.summary;
|
|
2076
|
+
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2077
|
+
tokensBefore = extensionCompaction.tokensBefore;
|
|
2078
|
+
usage = extensionCompaction.usage;
|
|
2079
|
+
details = extensionCompaction.details;
|
|
2080
|
+
}
|
|
2081
|
+
else {
|
|
2082
|
+
// Shared default summary generator, also used by automatic compaction.
|
|
2083
|
+
const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
|
|
2084
|
+
summary = result.summary;
|
|
2085
|
+
firstKeptEntryId = result.firstKeptEntryId;
|
|
2086
|
+
tokensBefore = result.tokensBefore;
|
|
2087
|
+
usage = result.usage;
|
|
2088
|
+
details = result.details;
|
|
2089
|
+
}
|
|
2090
|
+
if (this._compactionAbortController.signal.aborted) {
|
|
2091
|
+
throw new Error("Compaction cancelled");
|
|
2092
|
+
}
|
|
2093
|
+
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2094
|
+
const newEntries = this.sessionManager.getEntries();
|
|
2095
|
+
this._refreshFinalizedContext();
|
|
2096
|
+
const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
|
|
2097
|
+
// Get the saved compaction entry for the extension event
|
|
2098
|
+
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2099
|
+
if (this._extensionRunner && savedCompactionEntry) {
|
|
2100
|
+
await this._extensionRunner.emit({
|
|
2101
|
+
type: "session_compact",
|
|
2102
|
+
compactionEntry: savedCompactionEntry,
|
|
2103
|
+
fromExtension,
|
|
2125
2104
|
reason: "manual",
|
|
2126
|
-
errorMessage,
|
|
2127
|
-
aborted,
|
|
2128
2105
|
willRetry: false,
|
|
2129
|
-
fromExtension,
|
|
2130
2106
|
});
|
|
2131
|
-
throw error;
|
|
2132
|
-
}
|
|
2133
|
-
finally {
|
|
2134
|
-
this._clearManualCompactionState();
|
|
2135
2107
|
}
|
|
2108
|
+
const compactionResult = {
|
|
2109
|
+
summary,
|
|
2110
|
+
firstKeptEntryId,
|
|
2111
|
+
tokensBefore,
|
|
2112
|
+
estimatedTokensAfter,
|
|
2113
|
+
usage,
|
|
2114
|
+
details,
|
|
2115
|
+
};
|
|
2116
|
+
// compaction_end listeners may submit queued prompts, so expose idle state before notifying them.
|
|
2117
|
+
this._clearManualCompactionState();
|
|
2118
|
+
this._emit({
|
|
2119
|
+
type: "compaction_end",
|
|
2120
|
+
reason: "manual",
|
|
2121
|
+
result: compactionResult,
|
|
2122
|
+
aborted: false,
|
|
2123
|
+
willRetry: false,
|
|
2124
|
+
});
|
|
2125
|
+
return compactionResult;
|
|
2126
|
+
}
|
|
2127
|
+
catch (error) {
|
|
2128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2129
|
+
const aborted = this._compactionAbortController.signal.aborted || cancelledByExtension;
|
|
2130
|
+
const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
|
|
2131
|
+
this._clearManualCompactionState();
|
|
2132
|
+
this._emit({
|
|
2133
|
+
type: "compaction_end",
|
|
2134
|
+
reason: "manual",
|
|
2135
|
+
result: undefined,
|
|
2136
|
+
aborted,
|
|
2137
|
+
willRetry: false,
|
|
2138
|
+
errorMessage,
|
|
2139
|
+
});
|
|
2140
|
+
await this._emitSessionCompactFailed({
|
|
2141
|
+
reason: "manual",
|
|
2142
|
+
errorMessage,
|
|
2143
|
+
aborted,
|
|
2144
|
+
willRetry: false,
|
|
2145
|
+
fromExtension,
|
|
2146
|
+
});
|
|
2147
|
+
throw error;
|
|
2136
2148
|
}
|
|
2137
2149
|
finally {
|
|
2138
|
-
|
|
2150
|
+
this._clearManualCompactionState();
|
|
2139
2151
|
}
|
|
2140
2152
|
}
|
|
2141
2153
|
/**
|
|
@@ -2298,140 +2310,137 @@ export class AgentSession {
|
|
|
2298
2310
|
* @returns Whether the post-run loop should call `agent.continue()`
|
|
2299
2311
|
*/
|
|
2300
2312
|
async _runAutoCompaction(reason, willRetry) {
|
|
2301
|
-
|
|
2313
|
+
return this._withBackgroundPaused(() => this._runAutoCompactionBody(reason, willRetry));
|
|
2314
|
+
}
|
|
2315
|
+
async _runAutoCompactionBody(reason, willRetry) {
|
|
2316
|
+
const model = this.model;
|
|
2317
|
+
const settings = this.settingsManager.getCompactionSettings(model);
|
|
2318
|
+
let abortController;
|
|
2319
|
+
let started = false;
|
|
2320
|
+
let fromExtension = false;
|
|
2321
|
+
let cancelledByExtension = false;
|
|
2302
2322
|
try {
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
let abortController;
|
|
2306
|
-
let started = false;
|
|
2307
|
-
let fromExtension = false;
|
|
2308
|
-
let cancelledByExtension = false;
|
|
2309
|
-
try {
|
|
2310
|
-
if (!model) {
|
|
2311
|
-
return false;
|
|
2312
|
-
}
|
|
2313
|
-
const pathEntries = this.sessionManager.getBranch();
|
|
2314
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
2315
|
-
if (!preparation) {
|
|
2316
|
-
return false;
|
|
2317
|
-
}
|
|
2318
|
-
abortController = new AbortController();
|
|
2319
|
-
this._autoCompactionAbortController = abortController;
|
|
2320
|
-
started = true;
|
|
2321
|
-
this._emit({ type: "compaction_start", reason });
|
|
2322
|
-
abortController.signal.throwIfAborted();
|
|
2323
|
-
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
|
|
2324
|
-
abortController.signal.throwIfAborted();
|
|
2325
|
-
let extensionCompaction;
|
|
2326
|
-
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2327
|
-
const extensionResult = (await this._extensionRunner.emit({
|
|
2328
|
-
type: "session_before_compact",
|
|
2329
|
-
preparation,
|
|
2330
|
-
branchEntries: pathEntries,
|
|
2331
|
-
customInstructions: undefined,
|
|
2332
|
-
reason,
|
|
2333
|
-
willRetry,
|
|
2334
|
-
signal: abortController.signal,
|
|
2335
|
-
}));
|
|
2336
|
-
if (extensionResult?.cancel) {
|
|
2337
|
-
cancelledByExtension = true;
|
|
2338
|
-
throw new Error("Compaction cancelled");
|
|
2339
|
-
}
|
|
2340
|
-
if (extensionResult?.compaction) {
|
|
2341
|
-
extensionCompaction = extensionResult.compaction;
|
|
2342
|
-
fromExtension = true;
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
abortController.signal.throwIfAborted();
|
|
2346
|
-
let summary;
|
|
2347
|
-
let firstKeptEntryId;
|
|
2348
|
-
let tokensBefore;
|
|
2349
|
-
let usage;
|
|
2350
|
-
let details;
|
|
2351
|
-
if (extensionCompaction) {
|
|
2352
|
-
// Extension provided compaction content
|
|
2353
|
-
summary = extensionCompaction.summary;
|
|
2354
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2355
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
2356
|
-
usage = extensionCompaction.usage;
|
|
2357
|
-
details = extensionCompaction.details;
|
|
2358
|
-
}
|
|
2359
|
-
else {
|
|
2360
|
-
// Shared default summary generator, also used by manual compaction.
|
|
2361
|
-
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, abortController.signal, env, reason);
|
|
2362
|
-
summary = compactResult.summary;
|
|
2363
|
-
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
2364
|
-
tokensBefore = compactResult.tokensBefore;
|
|
2365
|
-
usage = compactResult.usage;
|
|
2366
|
-
details = compactResult.details;
|
|
2367
|
-
}
|
|
2368
|
-
abortController.signal.throwIfAborted();
|
|
2369
|
-
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2370
|
-
const newEntries = this.sessionManager.getEntries();
|
|
2371
|
-
this._refreshFinalizedContext();
|
|
2372
|
-
const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
|
|
2373
|
-
// Get the saved compaction entry for the extension event
|
|
2374
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2375
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2376
|
-
await this._extensionRunner.emit({
|
|
2377
|
-
type: "session_compact",
|
|
2378
|
-
compactionEntry: savedCompactionEntry,
|
|
2379
|
-
fromExtension,
|
|
2380
|
-
reason,
|
|
2381
|
-
willRetry,
|
|
2382
|
-
});
|
|
2383
|
-
}
|
|
2384
|
-
const result = {
|
|
2385
|
-
summary,
|
|
2386
|
-
firstKeptEntryId,
|
|
2387
|
-
tokensBefore,
|
|
2388
|
-
estimatedTokensAfter,
|
|
2389
|
-
usage,
|
|
2390
|
-
details,
|
|
2391
|
-
};
|
|
2392
|
-
this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
|
|
2393
|
-
if (willRetry)
|
|
2394
|
-
return true;
|
|
2395
|
-
// Auto-compaction can complete while follow-up/steering/custom messages are waiting.
|
|
2396
|
-
// Continue once so queued messages are delivered.
|
|
2397
|
-
return this.agent.hasQueuedMessages();
|
|
2323
|
+
if (!model) {
|
|
2324
|
+
return false;
|
|
2398
2325
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
if (started) {
|
|
2403
|
-
const errorMessage = aborted
|
|
2404
|
-
? undefined
|
|
2405
|
-
: reason === "overflow"
|
|
2406
|
-
? `Context overflow recovery failed: ${message}`
|
|
2407
|
-
: `Auto-compaction failed: ${message}`;
|
|
2408
|
-
this._emit({
|
|
2409
|
-
type: "compaction_end",
|
|
2410
|
-
reason,
|
|
2411
|
-
result: undefined,
|
|
2412
|
-
aborted,
|
|
2413
|
-
willRetry: false,
|
|
2414
|
-
errorMessage,
|
|
2415
|
-
});
|
|
2416
|
-
await this._emitSessionCompactFailed({
|
|
2417
|
-
reason,
|
|
2418
|
-
errorMessage,
|
|
2419
|
-
aborted,
|
|
2420
|
-
willRetry: false,
|
|
2421
|
-
fromExtension,
|
|
2422
|
-
});
|
|
2423
|
-
}
|
|
2326
|
+
const pathEntries = this.sessionManager.getBranch();
|
|
2327
|
+
const preparation = prepareCompaction(pathEntries, settings);
|
|
2328
|
+
if (!preparation) {
|
|
2424
2329
|
return false;
|
|
2425
2330
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2331
|
+
abortController = new AbortController();
|
|
2332
|
+
this._autoCompactionAbortController = abortController;
|
|
2333
|
+
started = true;
|
|
2334
|
+
this._emit({ type: "compaction_start", reason });
|
|
2335
|
+
abortController.signal.throwIfAborted();
|
|
2336
|
+
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
|
|
2337
|
+
abortController.signal.throwIfAborted();
|
|
2338
|
+
let extensionCompaction;
|
|
2339
|
+
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2340
|
+
const extensionResult = (await this._extensionRunner.emit({
|
|
2341
|
+
type: "session_before_compact",
|
|
2342
|
+
preparation,
|
|
2343
|
+
branchEntries: pathEntries,
|
|
2344
|
+
customInstructions: undefined,
|
|
2345
|
+
reason,
|
|
2346
|
+
willRetry,
|
|
2347
|
+
signal: abortController.signal,
|
|
2348
|
+
}));
|
|
2349
|
+
if (extensionResult?.cancel) {
|
|
2350
|
+
cancelledByExtension = true;
|
|
2351
|
+
throw new Error("Compaction cancelled");
|
|
2429
2352
|
}
|
|
2430
|
-
|
|
2353
|
+
if (extensionResult?.compaction) {
|
|
2354
|
+
extensionCompaction = extensionResult.compaction;
|
|
2355
|
+
fromExtension = true;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
abortController.signal.throwIfAborted();
|
|
2359
|
+
let summary;
|
|
2360
|
+
let firstKeptEntryId;
|
|
2361
|
+
let tokensBefore;
|
|
2362
|
+
let usage;
|
|
2363
|
+
let details;
|
|
2364
|
+
if (extensionCompaction) {
|
|
2365
|
+
// Extension provided compaction content
|
|
2366
|
+
summary = extensionCompaction.summary;
|
|
2367
|
+
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2368
|
+
tokensBefore = extensionCompaction.tokensBefore;
|
|
2369
|
+
usage = extensionCompaction.usage;
|
|
2370
|
+
details = extensionCompaction.details;
|
|
2371
|
+
}
|
|
2372
|
+
else {
|
|
2373
|
+
// Shared default summary generator, also used by manual compaction.
|
|
2374
|
+
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, abortController.signal, env, reason);
|
|
2375
|
+
summary = compactResult.summary;
|
|
2376
|
+
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
2377
|
+
tokensBefore = compactResult.tokensBefore;
|
|
2378
|
+
usage = compactResult.usage;
|
|
2379
|
+
details = compactResult.details;
|
|
2380
|
+
}
|
|
2381
|
+
abortController.signal.throwIfAborted();
|
|
2382
|
+
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2383
|
+
const newEntries = this.sessionManager.getEntries();
|
|
2384
|
+
this._refreshFinalizedContext();
|
|
2385
|
+
const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
|
|
2386
|
+
// Get the saved compaction entry for the extension event
|
|
2387
|
+
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2388
|
+
if (this._extensionRunner && savedCompactionEntry) {
|
|
2389
|
+
await this._extensionRunner.emit({
|
|
2390
|
+
type: "session_compact",
|
|
2391
|
+
compactionEntry: savedCompactionEntry,
|
|
2392
|
+
fromExtension,
|
|
2393
|
+
reason,
|
|
2394
|
+
willRetry,
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
const result = {
|
|
2398
|
+
summary,
|
|
2399
|
+
firstKeptEntryId,
|
|
2400
|
+
tokensBefore,
|
|
2401
|
+
estimatedTokensAfter,
|
|
2402
|
+
usage,
|
|
2403
|
+
details,
|
|
2404
|
+
};
|
|
2405
|
+
this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
|
|
2406
|
+
if (willRetry)
|
|
2407
|
+
return true;
|
|
2408
|
+
// Auto-compaction can complete while follow-up/steering/custom messages are waiting.
|
|
2409
|
+
// Continue once so queued messages are delivered.
|
|
2410
|
+
return this.agent.hasQueuedMessages();
|
|
2411
|
+
}
|
|
2412
|
+
catch (error) {
|
|
2413
|
+
const message = error instanceof Error ? error.message : "compaction failed";
|
|
2414
|
+
const aborted = abortController?.signal.aborted === true || cancelledByExtension;
|
|
2415
|
+
if (started) {
|
|
2416
|
+
const errorMessage = aborted
|
|
2417
|
+
? undefined
|
|
2418
|
+
: reason === "overflow"
|
|
2419
|
+
? `Context overflow recovery failed: ${message}`
|
|
2420
|
+
: `Auto-compaction failed: ${message}`;
|
|
2421
|
+
this._emit({
|
|
2422
|
+
type: "compaction_end",
|
|
2423
|
+
reason,
|
|
2424
|
+
result: undefined,
|
|
2425
|
+
aborted,
|
|
2426
|
+
willRetry: false,
|
|
2427
|
+
errorMessage,
|
|
2428
|
+
});
|
|
2429
|
+
await this._emitSessionCompactFailed({
|
|
2430
|
+
reason,
|
|
2431
|
+
errorMessage,
|
|
2432
|
+
aborted,
|
|
2433
|
+
willRetry: false,
|
|
2434
|
+
fromExtension,
|
|
2435
|
+
});
|
|
2431
2436
|
}
|
|
2437
|
+
return false;
|
|
2432
2438
|
}
|
|
2433
2439
|
finally {
|
|
2434
|
-
|
|
2440
|
+
if (this._autoCompactionAbortController === abortController) {
|
|
2441
|
+
this._autoCompactionAbortController = undefined;
|
|
2442
|
+
}
|
|
2443
|
+
this._resolveIdleWaitIfIdle();
|
|
2435
2444
|
}
|
|
2436
2445
|
}
|
|
2437
2446
|
/**
|
|
@@ -2445,36 +2454,33 @@ export class AgentSession {
|
|
|
2445
2454
|
return this.settingsManager.getCompactionEnabled();
|
|
2446
2455
|
}
|
|
2447
2456
|
async bindExtensions(bindings) {
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
if (bindings.uiContext !== undefined) {
|
|
2454
|
-
this._extensionUIContext = bindings.uiContext;
|
|
2455
|
-
}
|
|
2456
|
-
if (bindings.mode !== undefined) {
|
|
2457
|
-
this._extensionMode = bindings.mode;
|
|
2458
|
-
}
|
|
2459
|
-
if (bindings.commandContextActions !== undefined) {
|
|
2460
|
-
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
2461
|
-
}
|
|
2462
|
-
if (bindings.abortHandler !== undefined) {
|
|
2463
|
-
this._extensionAbortHandler = bindings.abortHandler;
|
|
2464
|
-
}
|
|
2465
|
-
if (bindings.shutdownHandler !== undefined) {
|
|
2466
|
-
this._extensionShutdownHandler = bindings.shutdownHandler;
|
|
2467
|
-
}
|
|
2468
|
-
if (bindings.onError !== undefined) {
|
|
2469
|
-
this._extensionErrorListener = bindings.onError;
|
|
2470
|
-
}
|
|
2471
|
-
this._applyExtensionBindings(this._extensionRunner);
|
|
2472
|
-
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
2473
|
-
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
2457
|
+
return this._withBackgroundPaused(() => this._bindExtensionsBody(bindings));
|
|
2458
|
+
}
|
|
2459
|
+
async _bindExtensionsBody(bindings) {
|
|
2460
|
+
if (bindings.backgroundEnabled !== undefined) {
|
|
2461
|
+
this._backgroundHost.setEnabled(bindings.backgroundEnabled);
|
|
2474
2462
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2463
|
+
if (bindings.uiContext !== undefined) {
|
|
2464
|
+
this._extensionUIContext = bindings.uiContext;
|
|
2465
|
+
}
|
|
2466
|
+
if (bindings.mode !== undefined) {
|
|
2467
|
+
this._extensionMode = bindings.mode;
|
|
2468
|
+
}
|
|
2469
|
+
if (bindings.commandContextActions !== undefined) {
|
|
2470
|
+
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
2471
|
+
}
|
|
2472
|
+
if (bindings.abortHandler !== undefined) {
|
|
2473
|
+
this._extensionAbortHandler = bindings.abortHandler;
|
|
2477
2474
|
}
|
|
2475
|
+
if (bindings.shutdownHandler !== undefined) {
|
|
2476
|
+
this._extensionShutdownHandler = bindings.shutdownHandler;
|
|
2477
|
+
}
|
|
2478
|
+
if (bindings.onError !== undefined) {
|
|
2479
|
+
this._extensionErrorListener = bindings.onError;
|
|
2480
|
+
}
|
|
2481
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
2482
|
+
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
2483
|
+
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
2478
2484
|
}
|
|
2479
2485
|
async extendResourcesFromExtensions(reason) {
|
|
2480
2486
|
if (!this._extensionRunner.hasHandlers("resources_discover")) {
|
|
@@ -2766,37 +2772,34 @@ export class AgentSession {
|
|
|
2766
2772
|
});
|
|
2767
2773
|
}
|
|
2768
2774
|
async reload(options) {
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
}
|
|
2798
|
-
finally {
|
|
2799
|
-
resumeBackground();
|
|
2775
|
+
return this._withBackgroundPaused(() => this._reloadBody(options));
|
|
2776
|
+
}
|
|
2777
|
+
async _reloadBody(options) {
|
|
2778
|
+
const oldRunner = this._extensionRunner;
|
|
2779
|
+
const previousFlagValues = oldRunner.getFlagValues();
|
|
2780
|
+
this.background.close();
|
|
2781
|
+
await Promise.all([this.background.shutdown(), this.abort()]);
|
|
2782
|
+
await emitSessionShutdownEvent(oldRunner, { type: "session_shutdown", reason: "reload" });
|
|
2783
|
+
oldRunner.invalidate();
|
|
2784
|
+
await this.settingsManager.reload();
|
|
2785
|
+
this.syncQueueModesFromSettings();
|
|
2786
|
+
resetApiProviders();
|
|
2787
|
+
await this._resourceLoader.reload();
|
|
2788
|
+
this._backgroundHost.replaceService();
|
|
2789
|
+
this._buildRuntime({
|
|
2790
|
+
activeToolNames: this.getActiveToolNames(),
|
|
2791
|
+
flagValues: previousFlagValues,
|
|
2792
|
+
includeAllExtensionTools: true,
|
|
2793
|
+
});
|
|
2794
|
+
const hasBindings = this.background.enabled ||
|
|
2795
|
+
this._extensionUIContext ||
|
|
2796
|
+
this._extensionCommandContextActions ||
|
|
2797
|
+
this._extensionShutdownHandler ||
|
|
2798
|
+
this._extensionErrorListener;
|
|
2799
|
+
if (hasBindings) {
|
|
2800
|
+
await options?.beforeSessionStart?.();
|
|
2801
|
+
await this._extensionRunner.emit({ type: "session_start", reason: "reload" });
|
|
2802
|
+
await this.extendResourcesFromExtensions("reload");
|
|
2800
2803
|
}
|
|
2801
2804
|
}
|
|
2802
2805
|
// =========================================================================
|
|
@@ -3340,7 +3343,7 @@ export class AgentSession {
|
|
|
3340
3343
|
if (!model) {
|
|
3341
3344
|
throw new Error("No model selected");
|
|
3342
3345
|
}
|
|
3343
|
-
const { model: requestModel, apiKey, headers, env
|
|
3346
|
+
const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
|
|
3344
3347
|
return generateBugReportSummary({
|
|
3345
3348
|
messages: this.messages,
|
|
3346
3349
|
hint: options.hint,
|