@astralyn/pi 0.87.0 → 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 +31 -0
- package/dist/bundle/chunks/{anthropic-messages-R2LYJUDD.js → anthropic-messages-XWFTZP2N.js} +1 -1
- package/dist/bundle/chunks/{chunk-INDBRWNK.js → chunk-NBIUJDV6.js} +1 -1
- package/dist/bundle/chunks/{chunk-RKLIHRPH.js → chunk-RFLBIA5Z.js} +82 -82
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{openai-completions-JZCEEJ43.js → openai-completions-SHS3CAEL.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-CG7NJ5WO.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/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +14 -4
- package/dist/cli/args.js.map +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 +30 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +514 -443
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/background/service.d.ts +9 -0
- package/dist/core/background/service.d.ts.map +1 -1
- package/dist/core/background/service.js +13 -1
- package/dist/core/background/service.js.map +1 -1
- package/dist/core/background/session.d.ts +5 -0
- package/dist/core/background/session.d.ts.map +1 -1
- package/dist/core/background/session.js +2 -1
- package/dist/core/background/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/compaction/compaction.js +9 -9
- package/dist/core/compaction/compaction.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/model-resolver.js +1 -1
- package/dist/core/model-resolver.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/background.md +3 -3
- package/docs/bundled/extensions/provider.md +7 -3
- package/docs/bundled/extensions/subagent.md +1 -1
- package/docs/compaction.md +1 -1
- package/docs/extensions.md +15 -5
- package/docs/sdk.md +1 -1
- package/docs/usage.md +3 -0
- package/examples/README.md +11 -1
- 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/examples/rpc-client.ts +35 -0
- package/examples/rpc-extension-ui.ts +25 -5
- package/npm-shrinkwrap.json +43 -43
- package/package.json +8 -8
|
@@ -58,9 +58,6 @@ export function parseSkillBlock(text) {
|
|
|
58
58
|
userMessage: match[4]?.trim() || undefined,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
-
// ============================================================================
|
|
62
|
-
// Types
|
|
63
|
-
// ============================================================================
|
|
64
61
|
function withoutDeletedHeaders(headers) {
|
|
65
62
|
return headers
|
|
66
63
|
? Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] !== null))
|
|
@@ -89,6 +86,8 @@ export class AgentSession {
|
|
|
89
86
|
this._pendingNextTurnMessages = [];
|
|
90
87
|
/** Context-only custom messages queued during a run, flushed once the current turn's tool results are in. */
|
|
91
88
|
this._pendingCustomMessages = [];
|
|
89
|
+
/** Background completions queued for steering, awaiting the persistence that confirms delivery. */
|
|
90
|
+
this._queuedBackgroundCompletions = new Map();
|
|
92
91
|
// Compaction state
|
|
93
92
|
this._compactionAbortController = undefined;
|
|
94
93
|
this._autoCompactionAbortController = undefined;
|
|
@@ -122,6 +121,12 @@ export class AgentSession {
|
|
|
122
121
|
// Preserve host delivery identity before extension message_end transforms.
|
|
123
122
|
// Content/details may be replaced, but a persisted replacement is still an ack.
|
|
124
123
|
const acknowledgeBackground = event.type === "message_end" ? this._backgroundHost.messageAcknowledgement(event.message) : undefined;
|
|
124
|
+
// A queued completion is tracked by identity too, for the same reason.
|
|
125
|
+
const queuedBackgroundCompletion = event.type === "message_start" || event.type === "message_end"
|
|
126
|
+
? this._queuedBackgroundCompletions.get(event.message)
|
|
127
|
+
: undefined;
|
|
128
|
+
if (event.type === "message_start" && queuedBackgroundCompletion)
|
|
129
|
+
queuedBackgroundCompletion.drained = true;
|
|
125
130
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
126
131
|
// This ensures the UI sees the updated queue state
|
|
127
132
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
@@ -149,8 +154,10 @@ export class AgentSession {
|
|
|
149
154
|
this._emit(event.type === "agent_end" ? { ...event, willRetry: this._willRetryAfterAgentEnd(event) } : event);
|
|
150
155
|
// Handle session persistence
|
|
151
156
|
if (event.type === "message_end") {
|
|
152
|
-
if (this._persistMessage(event.message))
|
|
157
|
+
if (this._persistMessage(event.message)) {
|
|
153
158
|
acknowledgeBackground?.();
|
|
159
|
+
queuedBackgroundCompletion?.resolve();
|
|
160
|
+
}
|
|
154
161
|
if (event.message.role === "assistant") {
|
|
155
162
|
const assistantMsg = event.message;
|
|
156
163
|
this._lastAssistantMessage = assistantMsg;
|
|
@@ -178,6 +185,8 @@ export class AgentSession {
|
|
|
178
185
|
this._lastAssistantToolResults = event.toolResults;
|
|
179
186
|
this._flushPendingCustomMessages();
|
|
180
187
|
}
|
|
188
|
+
if (event.type === "agent_end")
|
|
189
|
+
this._failDrainedBackgroundCompletions();
|
|
181
190
|
};
|
|
182
191
|
this.agent = config.agent;
|
|
183
192
|
this.sessionManager = config.sessionManager;
|
|
@@ -185,16 +194,15 @@ export class AgentSession {
|
|
|
185
194
|
this._backgroundHost = new BackgroundSession({
|
|
186
195
|
manager: this.sessionManager,
|
|
187
196
|
role: config.executionRole ?? "main",
|
|
188
|
-
canDeliver: () =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const asides = this._pendingNextTurnMessages.slice();
|
|
196
|
-
return this._runAgentPrompt(asides.length ? [message, ...asides] : message);
|
|
197
|
+
canDeliver: () => {
|
|
198
|
+
if (this.model === undefined || this.isCompacting)
|
|
199
|
+
return false;
|
|
200
|
+
// A completion steered into an active run follows the interactive steering queue, which
|
|
201
|
+
// already preserves the order of queued user input. An idle delivery starts its own
|
|
202
|
+
// turn, so it still waits for the queues to drain.
|
|
203
|
+
return this.isStreaming || (this.pendingMessageCount === 0 && !this.agent.hasQueuedMessages());
|
|
197
204
|
},
|
|
205
|
+
deliver: (message) => this._deliverBackgroundCompletion(message),
|
|
198
206
|
onEntry: (entry) => this._emit({ type: "entry_appended", entry }),
|
|
199
207
|
onError: (event, error) => this._extensionRunner?.emitError({ extensionPath: "<background>", event, error }),
|
|
200
208
|
});
|
|
@@ -243,6 +251,19 @@ export class AgentSession {
|
|
|
243
251
|
retryBackgroundNotifications() {
|
|
244
252
|
this._backgroundHost.retry();
|
|
245
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
|
+
}
|
|
246
267
|
get modelRuntime() {
|
|
247
268
|
return this._modelRuntime;
|
|
248
269
|
}
|
|
@@ -635,28 +656,36 @@ export class AgentSession {
|
|
|
635
656
|
this._resolveIdleWait = undefined;
|
|
636
657
|
resolve();
|
|
637
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
|
+
*/
|
|
638
663
|
async _emitAgentSettled() {
|
|
664
|
+
return this._withBackgroundPaused(() => this._emitAgentSettledBody());
|
|
665
|
+
}
|
|
666
|
+
async _emitAgentSettledBody() {
|
|
639
667
|
this._cacheWarmer?.onAgentSettled();
|
|
640
|
-
// Background completions stay paused until runs requested by settled handlers have started,
|
|
641
|
-
// so a deferred prompt never races a delivery for the same idle gap.
|
|
642
|
-
const resumeBackground = this.pauseBackgroundNotifications();
|
|
643
668
|
this._isAgentRunActive = false;
|
|
669
|
+
this._isEmittingAgentSettled = true;
|
|
644
670
|
try {
|
|
645
|
-
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) {
|
|
646
679
|
try {
|
|
647
|
-
|
|
648
|
-
|
|
680
|
+
for (const action of deferred)
|
|
681
|
+
await action();
|
|
649
682
|
}
|
|
650
683
|
finally {
|
|
651
|
-
this.
|
|
684
|
+
this._resolveIdleWaitIfIdle();
|
|
652
685
|
}
|
|
653
|
-
|
|
654
|
-
await action();
|
|
655
|
-
}
|
|
656
|
-
finally {
|
|
657
|
-
this._resolveIdleWaitIfIdle();
|
|
658
|
-
resumeBackground();
|
|
686
|
+
return;
|
|
659
687
|
}
|
|
688
|
+
this._resolveIdleWaitIfIdle();
|
|
660
689
|
}
|
|
661
690
|
/**
|
|
662
691
|
* Persist model/custom messages and consume queued context only after the append succeeds.
|
|
@@ -881,6 +910,9 @@ export class AgentSession {
|
|
|
881
910
|
*/
|
|
882
911
|
dispose() {
|
|
883
912
|
this._backgroundHost.dispose();
|
|
913
|
+
for (const queued of [...this._queuedBackgroundCompletions.values()]) {
|
|
914
|
+
queued.reject(new Error("the session was disposed"));
|
|
915
|
+
}
|
|
884
916
|
try {
|
|
885
917
|
this.abortRetry();
|
|
886
918
|
this.abortCompaction();
|
|
@@ -1164,6 +1196,55 @@ export class AgentSession {
|
|
|
1164
1196
|
await this._emitAgentSettled();
|
|
1165
1197
|
}
|
|
1166
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Deliver a background completion through the same queueing path as interactive input:
|
|
1201
|
+
* steering into an active run lands the result right after the current tool batch, and an
|
|
1202
|
+
* idle session starts its own turn. The promise settles only once the message is persisted,
|
|
1203
|
+
* or once it can no longer be persisted, so the caller never releases its claim for a
|
|
1204
|
+
* message the transcript does not contain.
|
|
1205
|
+
*/
|
|
1206
|
+
_deliverBackgroundCompletion(message) {
|
|
1207
|
+
if (!this.isStreaming) {
|
|
1208
|
+
// Completion turns skip before_agent_start, but consume nextTurn context.
|
|
1209
|
+
const asides = this._pendingNextTurnMessages.slice();
|
|
1210
|
+
return this._runAgentPrompt(asides.length ? [message, ...asides] : message);
|
|
1211
|
+
}
|
|
1212
|
+
return new Promise((resolve, reject) => {
|
|
1213
|
+
const queued = {
|
|
1214
|
+
drained: false,
|
|
1215
|
+
resolve: () => {
|
|
1216
|
+
if (this._queuedBackgroundCompletions.get(message) !== queued)
|
|
1217
|
+
return;
|
|
1218
|
+
this._queuedBackgroundCompletions.delete(message);
|
|
1219
|
+
resolve();
|
|
1220
|
+
},
|
|
1221
|
+
reject: (reason) => {
|
|
1222
|
+
if (this._queuedBackgroundCompletions.get(message) !== queued)
|
|
1223
|
+
return;
|
|
1224
|
+
this._queuedBackgroundCompletions.delete(message);
|
|
1225
|
+
reject(reason);
|
|
1226
|
+
},
|
|
1227
|
+
};
|
|
1228
|
+
this._queuedBackgroundCompletions.set(message, queued);
|
|
1229
|
+
try {
|
|
1230
|
+
this.agent.steer(message);
|
|
1231
|
+
}
|
|
1232
|
+
catch (error) {
|
|
1233
|
+
queued.reject(error);
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Fail deliveries the owning run emitted but never persisted. A completion still sitting in
|
|
1239
|
+
* the steering queue when its run ends stays claimed: the next run delivers it, so failing
|
|
1240
|
+
* it here would persist the same message twice.
|
|
1241
|
+
*/
|
|
1242
|
+
_failDrainedBackgroundCompletions() {
|
|
1243
|
+
for (const queued of [...this._queuedBackgroundCompletions.values()]) {
|
|
1244
|
+
if (queued.drained)
|
|
1245
|
+
queued.reject(new Error("the completion message was not persisted"));
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1167
1248
|
async _handlePostAgentRun() {
|
|
1168
1249
|
const message = this._lastAssistantMessage;
|
|
1169
1250
|
const toolResults = this._lastAssistantToolResults;
|
|
@@ -1274,134 +1355,132 @@ export class AgentSession {
|
|
|
1274
1355
|
this.retryBackgroundNotifications();
|
|
1275
1356
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
1276
1357
|
const preflightResult = options?.preflightResult;
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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
|
|
1282
1367
|
preflightResult?.(true);
|
|
1283
1368
|
return;
|
|
1284
1369
|
}
|
|
1285
1370
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
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.");
|
|
1289
1376
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
if (expandPromptTemplates) {
|
|
1308
|
-
expandedText = this._expandSkillCommand(expandedText);
|
|
1309
|
-
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
1310
|
-
}
|
|
1311
|
-
// If streaming, queue via steer() or followUp() based on option
|
|
1312
|
-
if (this.isStreaming) {
|
|
1313
|
-
if (!options?.streamingBehavior) {
|
|
1314
|
-
throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
|
|
1315
|
-
}
|
|
1316
|
-
if (options.streamingBehavior === "followUp") {
|
|
1317
|
-
await this._queueFollowUp(expandedText, currentImages);
|
|
1318
|
-
}
|
|
1319
|
-
else {
|
|
1320
|
-
await this._queueSteer(expandedText, currentImages);
|
|
1321
|
-
}
|
|
1322
|
-
preflightResult?.(true);
|
|
1323
|
-
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.");
|
|
1324
1394
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
this._flushPendingCustomMessages();
|
|
1328
|
-
// Validate model
|
|
1329
|
-
if (!this.model) {
|
|
1330
|
-
throw new Error(formatNoModelSelectedMessage());
|
|
1395
|
+
if (options.streamingBehavior === "followUp") {
|
|
1396
|
+
await this._queueFollowUp(expandedText, currentImages);
|
|
1331
1397
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
if (!hasConfiguredAuth) {
|
|
1335
|
-
const isOAuth = this._modelRuntime.isUsingOAuth(this.model.provider);
|
|
1336
|
-
if (isOAuth) {
|
|
1337
|
-
throw new Error(`Authentication failed for "${this.model.provider}". ` +
|
|
1338
|
-
`Credentials may have expired or network is unavailable. ` +
|
|
1339
|
-
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
1340
|
-
}
|
|
1341
|
-
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
|
1398
|
+
else {
|
|
1399
|
+
await this._queueSteer(expandedText, currentImages);
|
|
1342
1400
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
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.`);
|
|
1348
1419
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
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) {
|
|
1366
1456
|
messages.push({
|
|
1367
|
-
role: "
|
|
1368
|
-
|
|
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,
|
|
1369
1463
|
timestamp: Date.now(),
|
|
1370
1464
|
});
|
|
1371
|
-
// Inject any pending "nextTurn" messages as context alongside the user message.
|
|
1372
|
-
// They stay queued until _persistMessage() confirms the append.
|
|
1373
|
-
for (const msg of this._pendingNextTurnMessages) {
|
|
1374
|
-
messages.push(msg);
|
|
1375
|
-
}
|
|
1376
|
-
for (const msg of result.messages) {
|
|
1377
|
-
messages.push({
|
|
1378
|
-
role: "custom",
|
|
1379
|
-
customType: msg.customType,
|
|
1380
|
-
// Untyped extensions can pass null/missing content; normalize at ingestion.
|
|
1381
|
-
content: msg.content ?? [],
|
|
1382
|
-
display: msg.display,
|
|
1383
|
-
details: msg.details,
|
|
1384
|
-
timestamp: Date.now(),
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
|
|
1388
|
-
this._runSystemPromptOptions = result.systemPromptOptions;
|
|
1389
|
-
if (updateMessage)
|
|
1390
|
-
messages.unshift(updateMessage);
|
|
1391
|
-
}
|
|
1392
|
-
catch (error) {
|
|
1393
|
-
preflightResult?.(false);
|
|
1394
|
-
throw error;
|
|
1395
|
-
}
|
|
1396
|
-
if (!messages) {
|
|
1397
|
-
return;
|
|
1398
1465
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
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;
|
|
1401
1474
|
}
|
|
1402
1475
|
finally {
|
|
1403
|
-
|
|
1476
|
+
// Delivery is timer-scheduled, so a completion cannot start before the run below does.
|
|
1477
|
+
resumeBackground?.();
|
|
1404
1478
|
}
|
|
1479
|
+
if (!messages) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
preflightResult?.(true);
|
|
1483
|
+
await this._runAgentPrompt(messages);
|
|
1405
1484
|
}
|
|
1406
1485
|
/**
|
|
1407
1486
|
* Try to execute an extension command. Returns true if command was found and executed.
|
|
@@ -1658,6 +1737,10 @@ export class AgentSession {
|
|
|
1658
1737
|
this._steeringMessages = [];
|
|
1659
1738
|
this._followUpMessages = [];
|
|
1660
1739
|
this.agent.clearAllQueues();
|
|
1740
|
+
// Clearing the queues drops queued completions; fail them so their claims retry.
|
|
1741
|
+
for (const queued of [...this._queuedBackgroundCompletions.values()]) {
|
|
1742
|
+
queued.reject(new Error("the completion message was dequeued before delivery"));
|
|
1743
|
+
}
|
|
1661
1744
|
this._emitQueueUpdate();
|
|
1662
1745
|
return { steering, followUp };
|
|
1663
1746
|
}
|
|
@@ -1937,137 +2020,134 @@ export class AgentSession {
|
|
|
1937
2020
|
* @param customInstructions Optional instructions for the compaction summary
|
|
1938
2021
|
*/
|
|
1939
2022
|
async compact(customInstructions) {
|
|
1940
|
-
|
|
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;
|
|
1941
2031
|
try {
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
const
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
1956
|
-
if (!preparation) {
|
|
1957
|
-
// Check why we can't compact
|
|
1958
|
-
const lastEntry = pathEntries[pathEntries.length - 1];
|
|
1959
|
-
if (lastEntry?.type === "compaction") {
|
|
1960
|
-
throw new Error("Already compacted");
|
|
1961
|
-
}
|
|
1962
|
-
throw new Error("Nothing to compact (session too small)");
|
|
1963
|
-
}
|
|
1964
|
-
let extensionCompaction;
|
|
1965
|
-
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
1966
|
-
const result = (await this._extensionRunner.emit({
|
|
1967
|
-
type: "session_before_compact",
|
|
1968
|
-
preparation,
|
|
1969
|
-
branchEntries: pathEntries,
|
|
1970
|
-
customInstructions,
|
|
1971
|
-
reason: "manual",
|
|
1972
|
-
willRetry: false,
|
|
1973
|
-
signal: this._compactionAbortController.signal,
|
|
1974
|
-
}));
|
|
1975
|
-
if (result?.cancel) {
|
|
1976
|
-
cancelledByExtension = true;
|
|
1977
|
-
throw new Error("Compaction cancelled");
|
|
1978
|
-
}
|
|
1979
|
-
if (result?.compaction) {
|
|
1980
|
-
extensionCompaction = result.compaction;
|
|
1981
|
-
fromExtension = true;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
let summary;
|
|
1985
|
-
let firstKeptEntryId;
|
|
1986
|
-
let tokensBefore;
|
|
1987
|
-
let usage;
|
|
1988
|
-
let details;
|
|
1989
|
-
if (extensionCompaction) {
|
|
1990
|
-
// Extension provided compaction content
|
|
1991
|
-
summary = extensionCompaction.summary;
|
|
1992
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
1993
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
1994
|
-
usage = extensionCompaction.usage;
|
|
1995
|
-
details = extensionCompaction.details;
|
|
1996
|
-
}
|
|
1997
|
-
else {
|
|
1998
|
-
// Shared default summary generator, also used by automatic compaction.
|
|
1999
|
-
const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
|
|
2000
|
-
summary = result.summary;
|
|
2001
|
-
firstKeptEntryId = result.firstKeptEntryId;
|
|
2002
|
-
tokensBefore = result.tokensBefore;
|
|
2003
|
-
usage = result.usage;
|
|
2004
|
-
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");
|
|
2005
2045
|
}
|
|
2006
|
-
|
|
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;
|
|
2007
2061
|
throw new Error("Compaction cancelled");
|
|
2008
2062
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
|
|
2013
|
-
// Get the saved compaction entry for the extension event
|
|
2014
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2015
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2016
|
-
await this._extensionRunner.emit({
|
|
2017
|
-
type: "session_compact",
|
|
2018
|
-
compactionEntry: savedCompactionEntry,
|
|
2019
|
-
fromExtension,
|
|
2020
|
-
reason: "manual",
|
|
2021
|
-
willRetry: false,
|
|
2022
|
-
});
|
|
2063
|
+
if (result?.compaction) {
|
|
2064
|
+
extensionCompaction = result.compaction;
|
|
2065
|
+
fromExtension = true;
|
|
2023
2066
|
}
|
|
2024
|
-
const compactionResult = {
|
|
2025
|
-
summary,
|
|
2026
|
-
firstKeptEntryId,
|
|
2027
|
-
tokensBefore,
|
|
2028
|
-
estimatedTokensAfter,
|
|
2029
|
-
usage,
|
|
2030
|
-
details,
|
|
2031
|
-
};
|
|
2032
|
-
// compaction_end listeners may submit queued prompts, so expose idle state before notifying them.
|
|
2033
|
-
this._clearManualCompactionState();
|
|
2034
|
-
this._emit({
|
|
2035
|
-
type: "compaction_end",
|
|
2036
|
-
reason: "manual",
|
|
2037
|
-
result: compactionResult,
|
|
2038
|
-
aborted: false,
|
|
2039
|
-
willRetry: false,
|
|
2040
|
-
});
|
|
2041
|
-
return compactionResult;
|
|
2042
2067
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
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,
|
|
2050
2104
|
reason: "manual",
|
|
2051
|
-
result: undefined,
|
|
2052
|
-
aborted,
|
|
2053
2105
|
willRetry: false,
|
|
2054
|
-
errorMessage,
|
|
2055
2106
|
});
|
|
2056
|
-
await this._emitSessionCompactFailed({
|
|
2057
|
-
reason: "manual",
|
|
2058
|
-
errorMessage,
|
|
2059
|
-
aborted,
|
|
2060
|
-
willRetry: false,
|
|
2061
|
-
fromExtension,
|
|
2062
|
-
});
|
|
2063
|
-
throw error;
|
|
2064
|
-
}
|
|
2065
|
-
finally {
|
|
2066
|
-
this._clearManualCompactionState();
|
|
2067
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;
|
|
2068
2148
|
}
|
|
2069
2149
|
finally {
|
|
2070
|
-
|
|
2150
|
+
this._clearManualCompactionState();
|
|
2071
2151
|
}
|
|
2072
2152
|
}
|
|
2073
2153
|
/**
|
|
@@ -2230,140 +2310,137 @@ export class AgentSession {
|
|
|
2230
2310
|
* @returns Whether the post-run loop should call `agent.continue()`
|
|
2231
2311
|
*/
|
|
2232
2312
|
async _runAutoCompaction(reason, willRetry) {
|
|
2233
|
-
|
|
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;
|
|
2234
2322
|
try {
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
let abortController;
|
|
2238
|
-
let started = false;
|
|
2239
|
-
let fromExtension = false;
|
|
2240
|
-
let cancelledByExtension = false;
|
|
2241
|
-
try {
|
|
2242
|
-
if (!model) {
|
|
2243
|
-
return false;
|
|
2244
|
-
}
|
|
2245
|
-
const pathEntries = this.sessionManager.getBranch();
|
|
2246
|
-
const preparation = prepareCompaction(pathEntries, settings);
|
|
2247
|
-
if (!preparation) {
|
|
2248
|
-
return false;
|
|
2249
|
-
}
|
|
2250
|
-
abortController = new AbortController();
|
|
2251
|
-
this._autoCompactionAbortController = abortController;
|
|
2252
|
-
started = true;
|
|
2253
|
-
this._emit({ type: "compaction_start", reason });
|
|
2254
|
-
abortController.signal.throwIfAborted();
|
|
2255
|
-
const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
|
|
2256
|
-
abortController.signal.throwIfAborted();
|
|
2257
|
-
let extensionCompaction;
|
|
2258
|
-
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2259
|
-
const extensionResult = (await this._extensionRunner.emit({
|
|
2260
|
-
type: "session_before_compact",
|
|
2261
|
-
preparation,
|
|
2262
|
-
branchEntries: pathEntries,
|
|
2263
|
-
customInstructions: undefined,
|
|
2264
|
-
reason,
|
|
2265
|
-
willRetry,
|
|
2266
|
-
signal: abortController.signal,
|
|
2267
|
-
}));
|
|
2268
|
-
if (extensionResult?.cancel) {
|
|
2269
|
-
cancelledByExtension = true;
|
|
2270
|
-
throw new Error("Compaction cancelled");
|
|
2271
|
-
}
|
|
2272
|
-
if (extensionResult?.compaction) {
|
|
2273
|
-
extensionCompaction = extensionResult.compaction;
|
|
2274
|
-
fromExtension = true;
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
abortController.signal.throwIfAborted();
|
|
2278
|
-
let summary;
|
|
2279
|
-
let firstKeptEntryId;
|
|
2280
|
-
let tokensBefore;
|
|
2281
|
-
let usage;
|
|
2282
|
-
let details;
|
|
2283
|
-
if (extensionCompaction) {
|
|
2284
|
-
// Extension provided compaction content
|
|
2285
|
-
summary = extensionCompaction.summary;
|
|
2286
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2287
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
2288
|
-
usage = extensionCompaction.usage;
|
|
2289
|
-
details = extensionCompaction.details;
|
|
2290
|
-
}
|
|
2291
|
-
else {
|
|
2292
|
-
// Shared default summary generator, also used by manual compaction.
|
|
2293
|
-
const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, abortController.signal, env, reason);
|
|
2294
|
-
summary = compactResult.summary;
|
|
2295
|
-
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
2296
|
-
tokensBefore = compactResult.tokensBefore;
|
|
2297
|
-
usage = compactResult.usage;
|
|
2298
|
-
details = compactResult.details;
|
|
2299
|
-
}
|
|
2300
|
-
abortController.signal.throwIfAborted();
|
|
2301
|
-
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
|
|
2302
|
-
const newEntries = this.sessionManager.getEntries();
|
|
2303
|
-
this._refreshFinalizedContext();
|
|
2304
|
-
const estimatedTokensAfter = estimateMessagesTokens(this.sessionManager.buildSessionProjection().messages);
|
|
2305
|
-
// Get the saved compaction entry for the extension event
|
|
2306
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2307
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2308
|
-
await this._extensionRunner.emit({
|
|
2309
|
-
type: "session_compact",
|
|
2310
|
-
compactionEntry: savedCompactionEntry,
|
|
2311
|
-
fromExtension,
|
|
2312
|
-
reason,
|
|
2313
|
-
willRetry,
|
|
2314
|
-
});
|
|
2315
|
-
}
|
|
2316
|
-
const result = {
|
|
2317
|
-
summary,
|
|
2318
|
-
firstKeptEntryId,
|
|
2319
|
-
tokensBefore,
|
|
2320
|
-
estimatedTokensAfter,
|
|
2321
|
-
usage,
|
|
2322
|
-
details,
|
|
2323
|
-
};
|
|
2324
|
-
this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
|
|
2325
|
-
if (willRetry)
|
|
2326
|
-
return true;
|
|
2327
|
-
// Auto-compaction can complete while follow-up/steering/custom messages are waiting.
|
|
2328
|
-
// Continue once so queued messages are delivered.
|
|
2329
|
-
return this.agent.hasQueuedMessages();
|
|
2323
|
+
if (!model) {
|
|
2324
|
+
return false;
|
|
2330
2325
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (started) {
|
|
2335
|
-
const errorMessage = aborted
|
|
2336
|
-
? undefined
|
|
2337
|
-
: reason === "overflow"
|
|
2338
|
-
? `Context overflow recovery failed: ${message}`
|
|
2339
|
-
: `Auto-compaction failed: ${message}`;
|
|
2340
|
-
this._emit({
|
|
2341
|
-
type: "compaction_end",
|
|
2342
|
-
reason,
|
|
2343
|
-
result: undefined,
|
|
2344
|
-
aborted,
|
|
2345
|
-
willRetry: false,
|
|
2346
|
-
errorMessage,
|
|
2347
|
-
});
|
|
2348
|
-
await this._emitSessionCompactFailed({
|
|
2349
|
-
reason,
|
|
2350
|
-
errorMessage,
|
|
2351
|
-
aborted,
|
|
2352
|
-
willRetry: false,
|
|
2353
|
-
fromExtension,
|
|
2354
|
-
});
|
|
2355
|
-
}
|
|
2326
|
+
const pathEntries = this.sessionManager.getBranch();
|
|
2327
|
+
const preparation = prepareCompaction(pathEntries, settings);
|
|
2328
|
+
if (!preparation) {
|
|
2356
2329
|
return false;
|
|
2357
2330
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
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");
|
|
2361
2352
|
}
|
|
2362
|
-
|
|
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
|
+
});
|
|
2363
2436
|
}
|
|
2437
|
+
return false;
|
|
2364
2438
|
}
|
|
2365
2439
|
finally {
|
|
2366
|
-
|
|
2440
|
+
if (this._autoCompactionAbortController === abortController) {
|
|
2441
|
+
this._autoCompactionAbortController = undefined;
|
|
2442
|
+
}
|
|
2443
|
+
this._resolveIdleWaitIfIdle();
|
|
2367
2444
|
}
|
|
2368
2445
|
}
|
|
2369
2446
|
/**
|
|
@@ -2377,36 +2454,33 @@ export class AgentSession {
|
|
|
2377
2454
|
return this.settingsManager.getCompactionEnabled();
|
|
2378
2455
|
}
|
|
2379
2456
|
async bindExtensions(bindings) {
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
if (bindings.uiContext !== undefined) {
|
|
2386
|
-
this._extensionUIContext = bindings.uiContext;
|
|
2387
|
-
}
|
|
2388
|
-
if (bindings.mode !== undefined) {
|
|
2389
|
-
this._extensionMode = bindings.mode;
|
|
2390
|
-
}
|
|
2391
|
-
if (bindings.commandContextActions !== undefined) {
|
|
2392
|
-
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
2393
|
-
}
|
|
2394
|
-
if (bindings.abortHandler !== undefined) {
|
|
2395
|
-
this._extensionAbortHandler = bindings.abortHandler;
|
|
2396
|
-
}
|
|
2397
|
-
if (bindings.shutdownHandler !== undefined) {
|
|
2398
|
-
this._extensionShutdownHandler = bindings.shutdownHandler;
|
|
2399
|
-
}
|
|
2400
|
-
if (bindings.onError !== undefined) {
|
|
2401
|
-
this._extensionErrorListener = bindings.onError;
|
|
2402
|
-
}
|
|
2403
|
-
this._applyExtensionBindings(this._extensionRunner);
|
|
2404
|
-
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
2405
|
-
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);
|
|
2406
2462
|
}
|
|
2407
|
-
|
|
2408
|
-
|
|
2463
|
+
if (bindings.uiContext !== undefined) {
|
|
2464
|
+
this._extensionUIContext = bindings.uiContext;
|
|
2465
|
+
}
|
|
2466
|
+
if (bindings.mode !== undefined) {
|
|
2467
|
+
this._extensionMode = bindings.mode;
|
|
2409
2468
|
}
|
|
2469
|
+
if (bindings.commandContextActions !== undefined) {
|
|
2470
|
+
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
2471
|
+
}
|
|
2472
|
+
if (bindings.abortHandler !== undefined) {
|
|
2473
|
+
this._extensionAbortHandler = bindings.abortHandler;
|
|
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");
|
|
2410
2484
|
}
|
|
2411
2485
|
async extendResourcesFromExtensions(reason) {
|
|
2412
2486
|
if (!this._extensionRunner.hasHandlers("resources_discover")) {
|
|
@@ -2698,37 +2772,34 @@ export class AgentSession {
|
|
|
2698
2772
|
});
|
|
2699
2773
|
}
|
|
2700
2774
|
async reload(options) {
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
}
|
|
2730
|
-
finally {
|
|
2731
|
-
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");
|
|
2732
2803
|
}
|
|
2733
2804
|
}
|
|
2734
2805
|
// =========================================================================
|
|
@@ -3272,7 +3343,7 @@ export class AgentSession {
|
|
|
3272
3343
|
if (!model) {
|
|
3273
3344
|
throw new Error("No model selected");
|
|
3274
3345
|
}
|
|
3275
|
-
const { model: requestModel, apiKey, headers, env
|
|
3346
|
+
const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
|
|
3276
3347
|
return generateBugReportSummary({
|
|
3277
3348
|
messages: this.messages,
|
|
3278
3349
|
hint: options.hint,
|