@oh-my-pi/pi-coding-agent 15.12.3 → 15.12.4
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 +43 -1
- package/dist/cli.js +1120 -870
- package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/run-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/update-notes.d.ts +1 -1
- package/dist/types/cli/args.d.ts +0 -1
- package/dist/types/cli/models-cli.d.ts +49 -0
- package/dist/types/commands/launch.d.ts +0 -3
- package/dist/types/commands/models.d.ts +33 -0
- package/dist/types/commands/token.d.ts +25 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-overview.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +1 -1
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +1 -1
- package/dist/types/commit/agentic/tools/schemas.d.ts +1 -1
- package/dist/types/commit/agentic/tools/split-commit.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +1 -1
- package/dist/types/config/model-registry.d.ts +7 -0
- package/dist/types/config/models-config-schema.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/edit/hashline/params.d.ts +1 -1
- package/dist/types/edit/modes/apply-patch.d.ts +1 -1
- package/dist/types/edit/modes/patch.d.ts +1 -1
- package/dist/types/edit/modes/replace.d.ts +1 -1
- package/dist/types/extensibility/custom-commands/types.d.ts +2 -2
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +2 -2
- package/dist/types/extensibility/hooks/types.d.ts +2 -2
- package/dist/types/goals/tools/goal-tool.d.ts +1 -1
- package/dist/types/lsp/types.d.ts +1 -1
- package/dist/types/mcp/manager.d.ts +8 -0
- package/dist/types/mnemopi/config.d.ts +28 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -2
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/logout-account-selector.d.ts +8 -0
- package/dist/types/modes/components/status-line/component.d.ts +9 -5
- package/dist/types/modes/components/status-line/types.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +0 -17
- package/dist/types/modes/interactive-mode.d.ts +0 -3
- package/dist/types/modes/types.d.ts +0 -5
- package/dist/types/session/agent-session.d.ts +14 -33
- package/dist/types/session/agent-storage.d.ts +2 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/messages.d.ts +8 -10
- package/dist/types/session/session-manager.d.ts +15 -0
- package/dist/types/session/session-storage.d.ts +5 -0
- package/dist/types/slash-commands/helpers/logout.d.ts +15 -0
- package/dist/types/task/types.d.ts +1 -1
- package/dist/types/tools/ask.d.ts +1 -1
- package/dist/types/tools/ast-edit.d.ts +1 -1
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +202 -0
- package/dist/types/tools/browser/cmux/rpc.d.ts +70 -0
- package/dist/types/tools/browser/cmux/socket-client.d.ts +19 -0
- package/dist/types/tools/browser/registry.d.ts +16 -3
- package/dist/types/tools/browser/render.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +16 -4
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/checkpoint.d.ts +1 -1
- package/dist/types/tools/debug.d.ts +1 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/gh.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/inspect-image.d.ts +1 -1
- package/dist/types/tools/irc.d.ts +1 -1
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/memory-recall.d.ts +1 -1
- package/dist/types/tools/memory-reflect.d.ts +1 -1
- package/dist/types/tools/memory-retain.d.ts +1 -1
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/render-mermaid.d.ts +1 -1
- package/dist/types/tools/resolve.d.ts +1 -1
- package/dist/types/tools/review.d.ts +1 -1
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +1 -1
- package/dist/types/tools/ssh.d.ts +1 -1
- package/dist/types/tools/todo.d.ts +1 -1
- package/dist/types/tools/tts.d.ts +1 -1
- package/dist/types/tools/write.d.ts +1 -1
- package/dist/types/utils/clipboard.d.ts +4 -3
- package/dist/types/utils/image-loading.d.ts +18 -1
- package/dist/types/utils/thinking-display.d.ts +17 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/package.json +14 -14
- package/src/autoresearch/storage.ts +2 -1
- package/src/autoresearch/tools/init-experiment.ts +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/autoresearch/tools/run-experiment.ts +1 -1
- package/src/autoresearch/tools/update-notes.ts +1 -1
- package/src/cli/args.ts +0 -8
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/models-cli.ts +427 -0
- package/src/cli-commands.ts +2 -0
- package/src/collab/host.ts +9 -12
- package/src/commands/launch.ts +0 -3
- package/src/commands/models.ts +61 -0
- package/src/commands/token.ts +89 -0
- package/src/commit/agentic/tools/analyze-file.ts +1 -1
- package/src/commit/agentic/tools/git-file-diff.ts +1 -1
- package/src/commit/agentic/tools/git-hunk.ts +1 -1
- package/src/commit/agentic/tools/git-overview.ts +1 -1
- package/src/commit/agentic/tools/propose-changelog.ts +1 -1
- package/src/commit/agentic/tools/propose-commit.ts +1 -1
- package/src/commit/agentic/tools/recent-commits.ts +1 -1
- package/src/commit/agentic/tools/schemas.ts +1 -1
- package/src/commit/agentic/tools/split-commit.ts +1 -1
- package/src/commit/analysis/summary.ts +1 -1
- package/src/commit/changelog/generate.ts +1 -1
- package/src/commit/shared-llm.ts +1 -1
- package/src/config/model-registry.ts +15 -12
- package/src/config/model-resolver.ts +2 -2
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +18 -0
- package/src/edit/hashline/params.ts +1 -1
- package/src/edit/modes/apply-patch.ts +1 -1
- package/src/edit/modes/patch.ts +1 -1
- package/src/edit/modes/replace.ts +1 -1
- package/src/eval/agent-bridge.ts +1 -1
- package/src/eval/completion-bridge.ts +1 -1
- package/src/export/html/template.js +24 -2
- package/src/export/html/tool-views.generated.js +2 -2
- package/src/extensibility/custom-commands/loader.ts +1 -1
- package/src/extensibility/custom-commands/types.ts +2 -2
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +2 -2
- package/src/extensibility/extensions/types.ts +2 -2
- package/src/extensibility/hooks/loader.ts +1 -1
- package/src/extensibility/hooks/types.ts +2 -2
- package/src/extensibility/skills.ts +18 -3
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +5 -2
- package/src/lsp/types.ts +1 -1
- package/src/main.ts +0 -25
- package/src/mcp/config-writer.ts +7 -3
- package/src/mcp/manager.ts +11 -0
- package/src/memories/index.ts +3 -1
- package/src/memories/storage.ts +2 -1
- package/src/mnemopi/config.ts +95 -11
- package/src/modes/acp/acp-agent.ts +5 -48
- package/src/modes/acp/acp-event-mapper.ts +5 -1
- package/src/modes/components/agent-hub.ts +2 -1
- package/src/modes/components/assistant-message.ts +8 -7
- package/src/modes/components/index.ts +1 -0
- package/src/modes/components/logout-account-selector.ts +130 -0
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/status-line/component.ts +54 -157
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +2 -1
- package/src/modes/controllers/command-controller.ts +0 -12
- package/src/modes/controllers/event-controller.ts +23 -62
- package/src/modes/controllers/input-controller.ts +53 -30
- package/src/modes/controllers/mcp-command-controller.ts +44 -3
- package/src/modes/controllers/selector-controller.ts +56 -10
- package/src/modes/controllers/streaming-reveal.ts +4 -3
- package/src/modes/interactive-mode.ts +2 -8
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +0 -5
- package/src/modes/utils/ui-helpers.ts +2 -1
- package/src/prompts/system/empty-stop-retry.md +4 -6
- package/src/sdk.ts +15 -19
- package/src/session/agent-session.ts +125 -234
- package/src/session/agent-storage.ts +18 -9
- package/src/session/history-storage.ts +2 -1
- package/src/session/indexed-session-storage.ts +7 -0
- package/src/session/messages.ts +9 -11
- package/src/session/session-dump-format.ts +4 -2
- package/src/session/session-manager.ts +116 -0
- package/src/session/session-storage.ts +20 -0
- package/src/slash-commands/builtin-registry.ts +15 -1
- package/src/slash-commands/helpers/logout.ts +88 -0
- package/src/task/types.ts +1 -1
- package/src/tools/ask.ts +1 -1
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/ast-grep.ts +1 -1
- package/src/tools/bash.ts +1 -1
- package/src/tools/browser/cmux/cmux-tab.ts +1264 -0
- package/src/tools/browser/cmux/rpc.ts +156 -0
- package/src/tools/browser/cmux/socket-client.ts +309 -0
- package/src/tools/browser/registry.ts +37 -3
- package/src/tools/browser/render.ts +6 -1
- package/src/tools/browser/tab-protocol.ts +2 -0
- package/src/tools/browser/tab-supervisor.ts +189 -18
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +16 -1
- package/src/tools/checkpoint.ts +1 -1
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval.ts +11 -6
- package/src/tools/fetch.ts +13 -2
- package/src/tools/find.ts +1 -1
- package/src/tools/gh.ts +1 -1
- package/src/tools/github-cache.ts +2 -1
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -1
- package/src/tools/inspect-image.ts +3 -1
- package/src/tools/irc.ts +1 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-recall.ts +1 -1
- package/src/tools/memory-reflect.ts +1 -1
- package/src/tools/memory-retain.ts +1 -1
- package/src/tools/read.ts +8 -2
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/search-tool-bm25.ts +1 -1
- package/src/tools/search.ts +1 -1
- package/src/tools/ssh.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/tools/tts.ts +1 -1
- package/src/tools/write.ts +1 -1
- package/src/utils/clipboard.ts +35 -18
- package/src/utils/image-loading.ts +35 -4
- package/src/utils/thinking-display.ts +37 -0
- package/src/web/search/index.ts +1 -1
- package/dist/types/cli/list-models.d.ts +0 -30
- package/src/cli/list-models.ts +0 -194
|
@@ -253,7 +253,7 @@ import {
|
|
|
253
253
|
type CustomMessage,
|
|
254
254
|
convertToLlm,
|
|
255
255
|
type PythonExecutionMessage,
|
|
256
|
-
|
|
256
|
+
readQueueChipText,
|
|
257
257
|
SILENT_ABORT_MARKER,
|
|
258
258
|
SKILL_PROMPT_MESSAGE_TYPE,
|
|
259
259
|
stripImagesFromMessage,
|
|
@@ -862,18 +862,42 @@ function extractPermissionLocations(
|
|
|
862
862
|
// AgentSession Class
|
|
863
863
|
// ============================================================================
|
|
864
864
|
|
|
865
|
-
/** Internal record stored in the steering/followUp display queues. The optional
|
|
866
|
-
* `tag` is set only by `enqueueCustomMessageDisplay` (used for skill-prompt
|
|
867
|
-
* custom messages queued during streaming) and is matched by the custom-role
|
|
868
|
-
* `message_start` dequeue branch; user-message pushes leave it undefined and
|
|
869
|
-
* rely on the existing text-equality match. `images` carries the original
|
|
870
|
-
* (pre-normalization) image blocks so queue restoration (Esc / Alt+Up) can
|
|
871
|
-
* hand them back to the editor instead of dropping them. */
|
|
872
|
-
type QueuedDisplayEntry = { text: string; tag?: string; images?: ImageContent[] };
|
|
873
|
-
|
|
874
865
|
/** Entry returned by {@link AgentSession.clearQueue} / {@link AgentSession.popLastQueuedMessage}. */
|
|
875
866
|
export type RestoredQueuedMessage = { text: string; images?: ImageContent[] };
|
|
876
867
|
|
|
868
|
+
function queuedTextContent(message: AgentMessage): string | undefined {
|
|
869
|
+
if (!("content" in message)) return undefined;
|
|
870
|
+
const content = message.content;
|
|
871
|
+
if (typeof content === "string") return content;
|
|
872
|
+
return content.find((part): part is TextContent => part.type === "text")?.text;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function queuedImageContent(message: AgentMessage): ImageContent[] | undefined {
|
|
876
|
+
if (!("content" in message) || typeof message.content === "string") return undefined;
|
|
877
|
+
const images = message.content.filter(
|
|
878
|
+
(part): part is ImageContent =>
|
|
879
|
+
part.type === "image" && typeof part.data === "string" && typeof part.mimeType === "string",
|
|
880
|
+
);
|
|
881
|
+
return images.length > 0 ? images : undefined;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function isDisplayableQueuedMessage(message: AgentMessage): boolean {
|
|
885
|
+
return !(message.role === "custom" && message.display === false);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function queueChipText(message: AgentMessage): string {
|
|
889
|
+
if (message.role === "custom") {
|
|
890
|
+
return readQueueChipText(message.details) ?? queuedTextContent(message) ?? "";
|
|
891
|
+
}
|
|
892
|
+
const text = queuedTextContent(message) ?? "";
|
|
893
|
+
if (text) return text;
|
|
894
|
+
return queuedImageContent(message) ? "[Image]" : "";
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function toRestoredQueuedMessage(message: AgentMessage): RestoredQueuedMessage {
|
|
898
|
+
return { text: queueChipText(message), images: queuedImageContent(message) };
|
|
899
|
+
}
|
|
900
|
+
|
|
877
901
|
export class AgentSession {
|
|
878
902
|
readonly agent: Agent;
|
|
879
903
|
readonly sessionManager: SessionManager;
|
|
@@ -904,15 +928,6 @@ export class AgentSession {
|
|
|
904
928
|
#eventListeners: AgentSessionEventListener[] = [];
|
|
905
929
|
#commandMetadataChangedListeners: CommandMetadataChangedListener[] = [];
|
|
906
930
|
|
|
907
|
-
/** Tracks pending steering messages for UI display. Removed when delivered.
|
|
908
|
-
* Entry shape: `{ text }` for plain-text steers (user-message dequeue
|
|
909
|
-
* matches by `.text`); `{ text, tag }` for queued custom messages (skill
|
|
910
|
-
* invocations dispatched while streaming) — the custom-role dequeue
|
|
911
|
-
* matches by `.tag` so duplicate-args queued skills cannot collide. */
|
|
912
|
-
#steeringMessages: QueuedDisplayEntry[] = [];
|
|
913
|
-
/** Tracks pending follow-up messages for UI display. Removed when delivered.
|
|
914
|
-
* See `#steeringMessages` for entry shape. */
|
|
915
|
-
#followUpMessages: QueuedDisplayEntry[] = [];
|
|
916
931
|
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
917
932
|
#pendingNextTurnMessages: CustomMessage[] = [];
|
|
918
933
|
#scheduledHiddenNextTurnGeneration: number | undefined = undefined;
|
|
@@ -1058,10 +1073,6 @@ export class AgentSession {
|
|
|
1058
1073
|
* without producing an aborted message_end). */
|
|
1059
1074
|
#planCompactAbortPending = false;
|
|
1060
1075
|
|
|
1061
|
-
/** Monotonic counter for `enqueueCustomMessageDisplay` tag generation;
|
|
1062
|
-
* combined with `Date.now()` so tags stay unique even across rapid
|
|
1063
|
-
* same-tick enqueues. */
|
|
1064
|
-
#customDisplayTagCounter = 0;
|
|
1065
1076
|
#postPromptTasks = new Set<Promise<unknown>>();
|
|
1066
1077
|
#postPromptTasksPromise: Promise<void> | undefined = undefined;
|
|
1067
1078
|
#postPromptTasksResolve: (() => void) | undefined = undefined;
|
|
@@ -1471,28 +1482,6 @@ export class AgentSession {
|
|
|
1471
1482
|
this.#planCompactAbortPending = false;
|
|
1472
1483
|
}
|
|
1473
1484
|
|
|
1474
|
-
/** Register a compact display string for a custom message that the caller is
|
|
1475
|
-
* about to dispatch via `promptCustomMessage` / `sendCustomMessage`.
|
|
1476
|
-
* Returns a stable tag the caller MUST embed in
|
|
1477
|
-
* `CustomMessage.details.__pendingDisplayTag` so the agent-side
|
|
1478
|
-
* `message_start` handler can remove the matching display entry when the
|
|
1479
|
-
* queued message is consumed.
|
|
1480
|
-
*
|
|
1481
|
-
* Does NOT push to the agent's steering/followUp queue — that happens
|
|
1482
|
-
* separately inside `sendCustomMessage`. */
|
|
1483
|
-
enqueueCustomMessageDisplay(text: string, mode: "steer" | "followUp"): string {
|
|
1484
|
-
const tag = `omp-cmd-${Date.now()}-${++this.#customDisplayTagCounter}`;
|
|
1485
|
-
const displayText = text.trim();
|
|
1486
|
-
if (!displayText) return tag;
|
|
1487
|
-
const entry: QueuedDisplayEntry = { text: displayText, tag };
|
|
1488
|
-
if (mode === "steer") {
|
|
1489
|
-
this.#steeringMessages.push(entry);
|
|
1490
|
-
} else {
|
|
1491
|
-
this.#followUpMessages.push(entry);
|
|
1492
|
-
}
|
|
1493
|
-
return tag;
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
1485
|
getAsyncJobSnapshot(options?: { recentLimit?: number }): AsyncJobSnapshot | null {
|
|
1497
1486
|
const manager = this.#asyncJobManager;
|
|
1498
1487
|
if (!manager) return null;
|
|
@@ -1614,45 +1603,6 @@ export class AgentSession {
|
|
|
1614
1603
|
|
|
1615
1604
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
1616
1605
|
#handleAgentEvent = async (event: AgentEvent): Promise<void> => {
|
|
1617
|
-
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
1618
|
-
// This ensures the UI sees the updated queue state
|
|
1619
|
-
if (event.type === "message_start" && event.message.role === "user") {
|
|
1620
|
-
const messageText = this.#getUserMessageText(event.message);
|
|
1621
|
-
if (messageText) {
|
|
1622
|
-
// Check steering queue first (match by .text on tagged records)
|
|
1623
|
-
const steeringIndex = this.#steeringMessages.findIndex(e => e.text === messageText);
|
|
1624
|
-
if (steeringIndex !== -1) {
|
|
1625
|
-
this.#steeringMessages.splice(steeringIndex, 1);
|
|
1626
|
-
} else {
|
|
1627
|
-
// Check follow-up queue
|
|
1628
|
-
const followUpIndex = this.#followUpMessages.findIndex(e => e.text === messageText);
|
|
1629
|
-
if (followUpIndex !== -1) {
|
|
1630
|
-
this.#followUpMessages.splice(followUpIndex, 1);
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
// Tag-based dequeue for custom messages (skills queued via promptCustomMessage).
|
|
1637
|
-
// The InputController attached a stable tag via CustomMessage.details when it
|
|
1638
|
-
// registered the display chip; pull it back here to remove the matching entry
|
|
1639
|
-
// from the pending bar atomically with the agent's queue consumption. Match by
|
|
1640
|
-
// tag (not text) — two queued skills with identical args cannot collide.
|
|
1641
|
-
if (event.type === "message_start" && event.message.role === "custom") {
|
|
1642
|
-
const tag = readPendingDisplayTag(event.message.details);
|
|
1643
|
-
if (tag) {
|
|
1644
|
-
const steerIdx = this.#steeringMessages.findIndex(e => e.tag === tag);
|
|
1645
|
-
if (steerIdx !== -1) {
|
|
1646
|
-
this.#steeringMessages.splice(steerIdx, 1);
|
|
1647
|
-
} else {
|
|
1648
|
-
const followUpIdx = this.#followUpMessages.findIndex(e => e.tag === tag);
|
|
1649
|
-
if (followUpIdx !== -1) {
|
|
1650
|
-
this.#followUpMessages.splice(followUpIdx, 1);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
1606
|
// Plan-mode → compaction transition: stamp `SILENT_ABORT_MARKER` on the
|
|
1657
1607
|
// persisted message BEFORE the obfuscator's display-side copy below.
|
|
1658
1608
|
// Invariant (must hold across refactors): this branch precedes the
|
|
@@ -2620,18 +2570,6 @@ export class AgentSession {
|
|
|
2620
2570
|
|
|
2621
2571
|
return Array.from(candidates);
|
|
2622
2572
|
}
|
|
2623
|
-
/** Extract text content from a message */
|
|
2624
|
-
#getUserMessageText(message: Message): string {
|
|
2625
|
-
if (message.role !== "user") return "";
|
|
2626
|
-
const content = message.content;
|
|
2627
|
-
if (typeof content === "string") return content;
|
|
2628
|
-
const textBlocks = content.filter(c => c.type === "text");
|
|
2629
|
-
const text = textBlocks.map(c => (c as TextContent).text).join("");
|
|
2630
|
-
if (text.length > 0) return text;
|
|
2631
|
-
const hasImages = content.some(c => c.type === "image");
|
|
2632
|
-
return hasImages ? "[Image]" : "";
|
|
2633
|
-
}
|
|
2634
|
-
|
|
2635
2573
|
/** Find the last assistant message in agent state (including aborted ones) */
|
|
2636
2574
|
#findLastAssistantMessage(): AssistantMessage | undefined {
|
|
2637
2575
|
const messages = this.agent.state.messages;
|
|
@@ -3340,6 +3278,10 @@ export class AgentSession {
|
|
|
3340
3278
|
return this.agent.state.isStreaming || this.#promptInFlightCount > 0;
|
|
3341
3279
|
}
|
|
3342
3280
|
|
|
3281
|
+
get isAborting(): boolean {
|
|
3282
|
+
return this.agent.isAborting;
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3343
3285
|
/** Wait until streaming and deferred recovery work are fully settled. */
|
|
3344
3286
|
async waitForIdle(): Promise<void> {
|
|
3345
3287
|
await this.agent.waitForIdle();
|
|
@@ -4527,13 +4469,17 @@ export class AgentSession {
|
|
|
4527
4469
|
};
|
|
4528
4470
|
}
|
|
4529
4471
|
|
|
4472
|
+
#normalizeImagesForModel(images: ImageContent[] | undefined): Promise<ImageContent[] | undefined> {
|
|
4473
|
+
return normalizeModelContextImages(images, { model: this.model });
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4530
4476
|
async #normalizeMessageContentImages(
|
|
4531
4477
|
content: string | (TextContent | ImageContent)[],
|
|
4532
4478
|
): Promise<string | (TextContent | ImageContent)[]> {
|
|
4533
4479
|
if (typeof content === "string") return content;
|
|
4534
4480
|
const images = content.filter((part): part is ImageContent => part.type === "image");
|
|
4535
4481
|
if (images.length === 0) return content;
|
|
4536
|
-
const normalizedImages = await
|
|
4482
|
+
const normalizedImages = await this.#normalizeImagesForModel(images);
|
|
4537
4483
|
if (!normalizedImages) return content;
|
|
4538
4484
|
let imageIndex = 0;
|
|
4539
4485
|
return content.map(part => (part.type === "image" ? normalizedImages[imageIndex++]! : part));
|
|
@@ -4646,9 +4592,9 @@ export class AgentSession {
|
|
|
4646
4592
|
throw new AgentBusyError();
|
|
4647
4593
|
}
|
|
4648
4594
|
if (options.streamingBehavior === "followUp") {
|
|
4649
|
-
await this.#
|
|
4595
|
+
await this.#queueUserMessage(expandedText, options?.images, "followUp");
|
|
4650
4596
|
} else {
|
|
4651
|
-
await this.#
|
|
4597
|
+
await this.#queueUserMessage(expandedText, options?.images, "steer");
|
|
4652
4598
|
}
|
|
4653
4599
|
// Steer/follow-up the keyword notices alongside the queued user message.
|
|
4654
4600
|
for (const notice of keywordNotices) {
|
|
@@ -4661,7 +4607,7 @@ export class AgentSession {
|
|
|
4661
4607
|
const hasPendingUserDirective = this.#toolChoiceQueue.inspect().includes("user-force");
|
|
4662
4608
|
const eagerTodoPrelude =
|
|
4663
4609
|
!options?.synthetic && !hasPendingUserDirective ? this.#createEagerTodoPrelude(expandedText) : undefined;
|
|
4664
|
-
const normalizedImages = await
|
|
4610
|
+
const normalizedImages = await this.#normalizeImagesForModel(options?.images);
|
|
4665
4611
|
|
|
4666
4612
|
const userContent: (TextContent | ImageContent)[] = [{ type: "text", text: expandedText }];
|
|
4667
4613
|
if (normalizedImages) {
|
|
@@ -4699,7 +4645,7 @@ export class AgentSession {
|
|
|
4699
4645
|
|
|
4700
4646
|
async promptCustomMessage<T = unknown>(
|
|
4701
4647
|
message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
|
|
4702
|
-
options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice"
|
|
4648
|
+
options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice"> & { queueChipText?: string },
|
|
4703
4649
|
): Promise<void> {
|
|
4704
4650
|
const textContent =
|
|
4705
4651
|
typeof message.content === "string"
|
|
@@ -4723,7 +4669,10 @@ export class AgentSession {
|
|
|
4723
4669
|
if (!options?.streamingBehavior) {
|
|
4724
4670
|
throw new AgentBusyError();
|
|
4725
4671
|
}
|
|
4726
|
-
await this.sendCustomMessage(message, {
|
|
4672
|
+
await this.sendCustomMessage(message, {
|
|
4673
|
+
deliverAs: options.streamingBehavior,
|
|
4674
|
+
queueChipText: options.queueChipText,
|
|
4675
|
+
});
|
|
4727
4676
|
for (const notice of keywordNotices) {
|
|
4728
4677
|
await this.sendCustomMessage(notice, { deliverAs: options.streamingBehavior });
|
|
4729
4678
|
}
|
|
@@ -5060,7 +5009,7 @@ export class AgentSession {
|
|
|
5060
5009
|
}
|
|
5061
5010
|
|
|
5062
5011
|
const expandedText = expandPromptTemplate(text, [...this.#promptTemplates]);
|
|
5063
|
-
await this.#
|
|
5012
|
+
await this.#queueUserMessage(expandedText, images, "steer");
|
|
5064
5013
|
}
|
|
5065
5014
|
|
|
5066
5015
|
/**
|
|
@@ -5072,73 +5021,47 @@ export class AgentSession {
|
|
|
5072
5021
|
}
|
|
5073
5022
|
|
|
5074
5023
|
const expandedText = expandPromptTemplate(text, [...this.#promptTemplates]);
|
|
5075
|
-
await this.#
|
|
5024
|
+
await this.#queueUserMessage(expandedText, images, "followUp");
|
|
5076
5025
|
}
|
|
5077
5026
|
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
const
|
|
5084
|
-
this.#steeringMessages.push({ text: displayText, images });
|
|
5027
|
+
async #queueUserMessage(
|
|
5028
|
+
text: string,
|
|
5029
|
+
images: ImageContent[] | undefined,
|
|
5030
|
+
mode: "steer" | "followUp",
|
|
5031
|
+
): Promise<void> {
|
|
5032
|
+
const normalizedImages = await this.#normalizeImagesForModel(images);
|
|
5085
5033
|
const content: (TextContent | ImageContent)[] = [{ type: "text", text }];
|
|
5086
|
-
if (normalizedImages
|
|
5034
|
+
if (normalizedImages?.length) {
|
|
5087
5035
|
content.push(...normalizedImages);
|
|
5088
5036
|
}
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
shouldContinue: () => this.#canAutoContinueForFollowUp() && this.agent.hasQueuedMessages(),
|
|
5037
|
+
if (mode === "followUp") {
|
|
5038
|
+
this.agent.followUp({
|
|
5039
|
+
role: "user",
|
|
5040
|
+
content,
|
|
5041
|
+
attribution: "user",
|
|
5042
|
+
timestamp: Date.now(),
|
|
5043
|
+
});
|
|
5044
|
+
} else {
|
|
5045
|
+
this.agent.steer({
|
|
5046
|
+
role: "user",
|
|
5047
|
+
content,
|
|
5048
|
+
steering: true,
|
|
5049
|
+
attribution: "user",
|
|
5050
|
+
timestamp: Date.now(),
|
|
5104
5051
|
});
|
|
5105
5052
|
}
|
|
5053
|
+
this.#scheduleIdleQueueDrain();
|
|
5106
5054
|
}
|
|
5107
5055
|
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
const normalizedImages = await normalizeModelContextImages(images);
|
|
5113
|
-
const displayText = text || (images && images.length > 0 ? "[Image]" : "");
|
|
5114
|
-
this.#followUpMessages.push({ text: displayText, images });
|
|
5115
|
-
const content: (TextContent | ImageContent)[] = [{ type: "text", text }];
|
|
5116
|
-
if (normalizedImages && normalizedImages.length > 0) {
|
|
5117
|
-
content.push(...normalizedImages);
|
|
5118
|
-
}
|
|
5119
|
-
this.agent.followUp({
|
|
5120
|
-
role: "user",
|
|
5121
|
-
content,
|
|
5122
|
-
attribution: "user",
|
|
5123
|
-
timestamp: Date.now(),
|
|
5056
|
+
#scheduleIdleQueueDrain(): void {
|
|
5057
|
+
if (!this.#canAutoContinueForFollowUp()) return;
|
|
5058
|
+
this.#scheduleAgentContinue({
|
|
5059
|
+
shouldContinue: () => this.#canAutoContinueForFollowUp() && this.agent.hasQueuedMessages(),
|
|
5124
5060
|
});
|
|
5125
|
-
// When fully idle AND the session is in a resumable assistant-ended state,
|
|
5126
|
-
// schedule an immediate continue so the queued follow-up is delivered
|
|
5127
|
-
// without waiting for the next user turn. We gate on isStreaming (model
|
|
5128
|
-
// actively producing), isRetrying (auto-retry backoff is sleeping between
|
|
5129
|
-
// attempts, #retryPromise set), and the last message being assistant —
|
|
5130
|
-
// agent.continue() only dequeues follow-ups from an assistant-ended state;
|
|
5131
|
-
// resuming from user/toolResult state runs an extra model call on the
|
|
5132
|
-
// stale prompt before draining the queue.
|
|
5133
|
-
if (this.#canAutoContinueForFollowUp()) {
|
|
5134
|
-
this.#scheduleAgentContinue({
|
|
5135
|
-
shouldContinue: () => this.#canAutoContinueForFollowUp() && this.agent.hasQueuedMessages(),
|
|
5136
|
-
});
|
|
5137
|
-
}
|
|
5138
5061
|
}
|
|
5139
5062
|
|
|
5140
5063
|
/**
|
|
5141
|
-
* Gate for idle-path
|
|
5064
|
+
* Gate for idle-path queued-message auto-continue. See `#scheduleIdleQueueDrain` for rationale.
|
|
5142
5065
|
*/
|
|
5143
5066
|
#canAutoContinueForFollowUp(): boolean {
|
|
5144
5067
|
if (this.isStreaming) return false;
|
|
@@ -5247,14 +5170,24 @@ export class AgentSession {
|
|
|
5247
5170
|
*/
|
|
5248
5171
|
async sendCustomMessage<T = unknown>(
|
|
5249
5172
|
message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
|
|
5250
|
-
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
5173
|
+
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn"; queueChipText?: string },
|
|
5251
5174
|
): Promise<void> {
|
|
5175
|
+
const details =
|
|
5176
|
+
options?.queueChipText && options.deliverAs !== "nextTurn"
|
|
5177
|
+
? ({
|
|
5178
|
+
...((message.details && typeof message.details === "object" ? message.details : {}) as Record<
|
|
5179
|
+
string,
|
|
5180
|
+
unknown
|
|
5181
|
+
>),
|
|
5182
|
+
__queueChipText: options.queueChipText,
|
|
5183
|
+
} as T)
|
|
5184
|
+
: message.details;
|
|
5252
5185
|
const appMessage: CustomMessage<T> = {
|
|
5253
5186
|
role: "custom",
|
|
5254
5187
|
customType: message.customType,
|
|
5255
5188
|
content: message.content,
|
|
5256
5189
|
display: message.display,
|
|
5257
|
-
details
|
|
5190
|
+
details,
|
|
5258
5191
|
attribution: message.attribution ?? "agent",
|
|
5259
5192
|
timestamp: Date.now(),
|
|
5260
5193
|
};
|
|
@@ -5270,14 +5203,7 @@ export class AgentSession {
|
|
|
5270
5203
|
} else {
|
|
5271
5204
|
this.agent.steer(normalizedAppMessage);
|
|
5272
5205
|
}
|
|
5273
|
-
|
|
5274
|
-
// awaited, so the turn may have ended in between, leaving the message
|
|
5275
|
-
// queued on an idle agent. Mirror #queueSteer's idle-path delivery.
|
|
5276
|
-
if (this.#canAutoContinueForFollowUp()) {
|
|
5277
|
-
this.#scheduleAgentContinue({
|
|
5278
|
-
shouldContinue: () => this.#canAutoContinueForFollowUp() && this.agent.hasQueuedMessages(),
|
|
5279
|
-
});
|
|
5280
|
-
}
|
|
5206
|
+
this.#scheduleIdleQueueDrain();
|
|
5281
5207
|
return;
|
|
5282
5208
|
}
|
|
5283
5209
|
|
|
@@ -5352,11 +5278,11 @@ export class AgentSession {
|
|
|
5352
5278
|
}
|
|
5353
5279
|
|
|
5354
5280
|
if (options?.deliverAs === "followUp") {
|
|
5355
|
-
await this.#
|
|
5281
|
+
await this.#queueUserMessage(text, images, "followUp");
|
|
5356
5282
|
return;
|
|
5357
5283
|
}
|
|
5358
5284
|
if (options?.deliverAs === "steer") {
|
|
5359
|
-
await this.#
|
|
5285
|
+
await this.#queueUserMessage(text, images, "steer");
|
|
5360
5286
|
return;
|
|
5361
5287
|
}
|
|
5362
5288
|
|
|
@@ -5367,57 +5293,37 @@ export class AgentSession {
|
|
|
5367
5293
|
});
|
|
5368
5294
|
}
|
|
5369
5295
|
|
|
5370
|
-
/**
|
|
5371
|
-
* Clear queued messages and return them (text plus any attached images).
|
|
5372
|
-
* Useful for restoring to editor when user aborts. The internal entry
|
|
5373
|
-
* arrays are handed out as-is — a `tag` (if any) is inert once the record
|
|
5374
|
-
* leaves the queue.
|
|
5375
|
-
*/
|
|
5296
|
+
/** Clear queued messages and return them (text plus any attached images). */
|
|
5376
5297
|
clearQueue(): { steering: RestoredQueuedMessage[]; followUp: RestoredQueuedMessage[] } {
|
|
5377
|
-
const steering = this
|
|
5378
|
-
const followUp = this
|
|
5379
|
-
this.#steeringMessages = [];
|
|
5380
|
-
this.#followUpMessages = [];
|
|
5298
|
+
const steering = this.agent.peekSteeringQueue().map(toRestoredQueuedMessage);
|
|
5299
|
+
const followUp = this.agent.peekFollowUpQueue().map(toRestoredQueuedMessage);
|
|
5381
5300
|
this.agent.clearAllQueues();
|
|
5382
5301
|
return { steering, followUp };
|
|
5383
5302
|
}
|
|
5384
5303
|
|
|
5385
|
-
/** Number of pending messages (includes steering, follow-up, and next-turn messages) */
|
|
5304
|
+
/** Number of pending displayable messages (includes steering, follow-up, and next-turn messages) */
|
|
5386
5305
|
get queuedMessageCount(): number {
|
|
5387
|
-
return
|
|
5306
|
+
return (
|
|
5307
|
+
this.agent.peekSteeringQueue().filter(isDisplayableQueuedMessage).length +
|
|
5308
|
+
this.agent.peekFollowUpQueue().filter(isDisplayableQueuedMessage).length +
|
|
5309
|
+
this.#pendingNextTurnMessages.length
|
|
5310
|
+
);
|
|
5388
5311
|
}
|
|
5389
5312
|
|
|
5390
|
-
/** Get pending messages (read-only). Returns the public text-only view;
|
|
5391
|
-
* internal `{text, tag?}` records are mapped to `.text` so callers
|
|
5392
|
-
* (`updatePendingMessagesDisplay`, `restoreQueuedMessagesToEditor`) see
|
|
5393
|
-
* the unchanged historical shape. */
|
|
5394
5313
|
getQueuedMessages(): { steering: readonly string[]; followUp: readonly string[] } {
|
|
5395
5314
|
return {
|
|
5396
|
-
steering: this
|
|
5397
|
-
followUp: this
|
|
5315
|
+
steering: this.agent.peekSteeringQueue().filter(isDisplayableQueuedMessage).map(queueChipText),
|
|
5316
|
+
followUp: this.agent.peekFollowUpQueue().filter(isDisplayableQueuedMessage).map(queueChipText),
|
|
5398
5317
|
};
|
|
5399
5318
|
}
|
|
5400
5319
|
|
|
5401
5320
|
/**
|
|
5402
5321
|
* Pop the last queued message (steering first, then follow-up).
|
|
5403
5322
|
* Used by dequeue keybinding to restore messages to editor one at a time.
|
|
5404
|
-
* Returns the popped entry's text and images; the tag (if any) dies with
|
|
5405
|
-
* the record — no orphan state can outlive the queue entry.
|
|
5406
5323
|
*/
|
|
5407
5324
|
popLastQueuedMessage(): RestoredQueuedMessage | undefined {
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
const entry = this.#steeringMessages.pop();
|
|
5411
|
-
this.agent.popLastSteer();
|
|
5412
|
-
return entry;
|
|
5413
|
-
}
|
|
5414
|
-
// Then from follow-up
|
|
5415
|
-
if (this.#followUpMessages.length > 0) {
|
|
5416
|
-
const entry = this.#followUpMessages.pop();
|
|
5417
|
-
this.agent.popLastFollowUp();
|
|
5418
|
-
return entry;
|
|
5419
|
-
}
|
|
5420
|
-
return undefined;
|
|
5325
|
+
const message = this.agent.popLastSteer() ?? this.agent.popLastFollowUp();
|
|
5326
|
+
return message ? toRestoredQueuedMessage(message) : undefined;
|
|
5421
5327
|
}
|
|
5422
5328
|
|
|
5423
5329
|
get skillsSettings(): SkillsSettings | undefined {
|
|
@@ -5499,19 +5405,6 @@ export class AgentSession {
|
|
|
5499
5405
|
}
|
|
5500
5406
|
}
|
|
5501
5407
|
|
|
5502
|
-
/**
|
|
5503
|
-
* Abort active work, then immediately resume the agent so queued steer/follow-up
|
|
5504
|
-
* messages drain instead of waiting for another natural turn boundary.
|
|
5505
|
-
*/
|
|
5506
|
-
async interruptAndFlushQueuedMessages(options?: { reason?: string }): Promise<void> {
|
|
5507
|
-
if (!this.agent.hasQueuedMessages()) return;
|
|
5508
|
-
await this.abort({ reason: options?.reason });
|
|
5509
|
-
if (!this.agent.hasQueuedMessages()) return;
|
|
5510
|
-
if (this.isCompacting || this.isGeneratingHandoff) return;
|
|
5511
|
-
await this.#maybeRestoreRetryFallbackPrimary();
|
|
5512
|
-
await this.agent.continue();
|
|
5513
|
-
}
|
|
5514
|
-
|
|
5515
5408
|
/**
|
|
5516
5409
|
* Start a new session, optionally with initial messages and parent tracking.
|
|
5517
5410
|
* Clears all messages and starts a new session.
|
|
@@ -5562,8 +5455,6 @@ export class AgentSession {
|
|
|
5562
5455
|
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
5563
5456
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
5564
5457
|
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
5565
|
-
this.#steeringMessages = [];
|
|
5566
|
-
this.#followUpMessages = [];
|
|
5567
5458
|
this.#pendingNextTurnMessages = [];
|
|
5568
5459
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
5569
5460
|
|
|
@@ -5678,7 +5569,10 @@ export class AgentSession {
|
|
|
5678
5569
|
|
|
5679
5570
|
/**
|
|
5680
5571
|
* Set model directly.
|
|
5681
|
-
* Validates
|
|
5572
|
+
* Validates that a credential source is configured (synchronously, without
|
|
5573
|
+
* refreshing OAuth or running command-backed key programs) and saves to the
|
|
5574
|
+
* active session. Persists settings only when requested. The concrete key is
|
|
5575
|
+
* resolved lazily per request, so switching never blocks the event loop.
|
|
5682
5576
|
* @throws Error if no API key available for the model
|
|
5683
5577
|
*/
|
|
5684
5578
|
async setModel(
|
|
@@ -5687,8 +5581,7 @@ export class AgentSession {
|
|
|
5687
5581
|
options?: { selector?: string; thinkingLevel?: ThinkingLevel; persist?: boolean },
|
|
5688
5582
|
): Promise<void> {
|
|
5689
5583
|
const previousEditMode = this.#resolveActiveEditMode();
|
|
5690
|
-
|
|
5691
|
-
if (!apiKey) {
|
|
5584
|
+
if (!this.#modelRegistry.hasConfiguredAuth(model)) {
|
|
5692
5585
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
5693
5586
|
}
|
|
5694
5587
|
|
|
@@ -5711,7 +5604,9 @@ export class AgentSession {
|
|
|
5711
5604
|
|
|
5712
5605
|
/**
|
|
5713
5606
|
* Set model temporarily (for this session only).
|
|
5714
|
-
* Validates
|
|
5607
|
+
* Validates that a credential source is configured (synchronously, without
|
|
5608
|
+
* refreshing OAuth or running command-backed key programs), saves to session
|
|
5609
|
+
* log but NOT to settings.
|
|
5715
5610
|
* @throws Error if no API key available for the model
|
|
5716
5611
|
*/
|
|
5717
5612
|
async setModelTemporary(
|
|
@@ -5720,8 +5615,7 @@ export class AgentSession {
|
|
|
5720
5615
|
options?: { ephemeral?: boolean },
|
|
5721
5616
|
): Promise<void> {
|
|
5722
5617
|
const previousEditMode = this.#resolveActiveEditMode();
|
|
5723
|
-
|
|
5724
|
-
if (!apiKey) {
|
|
5618
|
+
if (!this.#modelRegistry.hasConfiguredAuth(model)) {
|
|
5725
5619
|
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
5726
5620
|
}
|
|
5727
5621
|
|
|
@@ -6705,8 +6599,6 @@ export class AgentSession {
|
|
|
6705
6599
|
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
6706
6600
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
6707
6601
|
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
6708
|
-
this.#steeringMessages = [];
|
|
6709
|
-
this.#followUpMessages = [];
|
|
6710
6602
|
this.#pendingNextTurnMessages = [];
|
|
6711
6603
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
6712
6604
|
this.#todoReminderCount = 0;
|
|
@@ -6978,10 +6870,11 @@ export class AgentSession {
|
|
|
6978
6870
|
#isEmptyAssistantStop(assistantMessage: AssistantMessage): boolean {
|
|
6979
6871
|
switch (assistantMessage.stopReason) {
|
|
6980
6872
|
case "stop":
|
|
6873
|
+
// Reasoning/thinking-only turns are not actionable: they do not
|
|
6874
|
+
// answer the user and do not give the agent loop a tool call to run.
|
|
6981
6875
|
for (const content of assistantMessage.content) {
|
|
6982
6876
|
if (content.type === "toolCall") return false;
|
|
6983
6877
|
if (content.type === "text" && hasNonWhitespace(content.text)) return false;
|
|
6984
|
-
if (content.type === "thinking" && hasNonWhitespace(content.thinking)) return false;
|
|
6985
6878
|
}
|
|
6986
6879
|
return true;
|
|
6987
6880
|
case "toolUse":
|
|
@@ -7315,7 +7208,7 @@ export class AgentSession {
|
|
|
7315
7208
|
const candidate = this.#resolveContextPromotionConfiguredTarget(currentModel, availableModels);
|
|
7316
7209
|
if (!candidate) return undefined;
|
|
7317
7210
|
if (modelsAreEqual(candidate, currentModel)) return undefined;
|
|
7318
|
-
if (candidate.contextWindow <= contextWindow) return undefined;
|
|
7211
|
+
if (candidate.contextWindow == null || candidate.contextWindow <= contextWindow) return undefined;
|
|
7319
7212
|
const apiKey = await this.#modelRegistry.getApiKey(candidate, this.sessionId);
|
|
7320
7213
|
if (!apiKey) return undefined;
|
|
7321
7214
|
return candidate;
|
|
@@ -7626,7 +7519,7 @@ export class AgentSession {
|
|
|
7626
7519
|
addCandidate(this.#resolveRoleModelFull(role, availableModels, currentModel).model);
|
|
7627
7520
|
}
|
|
7628
7521
|
|
|
7629
|
-
const sortedByContext = [...availableModels].sort((a, b) => b.contextWindow - a.contextWindow);
|
|
7522
|
+
const sortedByContext = [...availableModels].sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0));
|
|
7630
7523
|
for (const model of sortedByContext) {
|
|
7631
7524
|
if (!seen.has(this.#getModelKey(model))) {
|
|
7632
7525
|
addCandidate(model);
|
|
@@ -9603,8 +9496,8 @@ export class AgentSession {
|
|
|
9603
9496
|
// the existing message objects is sufficient and avoids structured-clone failures for
|
|
9604
9497
|
// extension/custom metadata that is valid to persist but not cloneable.
|
|
9605
9498
|
const previousAgentMessages = [...this.agent.state.messages];
|
|
9606
|
-
const previousSteeringMessages = [...this
|
|
9607
|
-
const previousFollowUpMessages = [...this
|
|
9499
|
+
const previousSteeringMessages = [...this.agent.peekSteeringQueue()];
|
|
9500
|
+
const previousFollowUpMessages = [...this.agent.peekFollowUpQueue()];
|
|
9608
9501
|
const previousPendingNextTurnMessages = [...this.#pendingNextTurnMessages];
|
|
9609
9502
|
const previousScheduledHiddenNextTurnGeneration = this.#scheduledHiddenNextTurnGeneration;
|
|
9610
9503
|
const previousModel = this.model;
|
|
@@ -9621,8 +9514,7 @@ export class AgentSession {
|
|
|
9621
9514
|
? this.#getSessionDefaultSelectedMCPToolNames(previousSessionFile)
|
|
9622
9515
|
: undefined;
|
|
9623
9516
|
|
|
9624
|
-
this
|
|
9625
|
-
this.#followUpMessages = [];
|
|
9517
|
+
this.agent.clearAllQueues();
|
|
9626
9518
|
this.#pendingNextTurnMessages = [];
|
|
9627
9519
|
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
9628
9520
|
|
|
@@ -9763,8 +9655,7 @@ export class AgentSession {
|
|
|
9763
9655
|
this.#baseSystemPrompt = previousBaseSystemPrompt;
|
|
9764
9656
|
this.agent.setSystemPrompt(previousSystemPrompt);
|
|
9765
9657
|
this.agent.replaceMessages(previousAgentMessages);
|
|
9766
|
-
this
|
|
9767
|
-
this.#followUpMessages = previousFollowUpMessages;
|
|
9658
|
+
this.agent.replaceQueues(previousSteeringMessages, previousFollowUpMessages);
|
|
9768
9659
|
this.#pendingNextTurnMessages = previousPendingNextTurnMessages;
|
|
9769
9660
|
this.#scheduledHiddenNextTurnGeneration = previousScheduledHiddenNextTurnGeneration;
|
|
9770
9661
|
if (previousModel) {
|