@oh-my-pi/pi-tui 18.2.10 → 18.3.0

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/THIRD-PARTY-NOTICES.txt +1 -1
  3. package/dist/types/apps/ps-data.d.ts +1 -1
  4. package/dist/types/apps/ps-top.d.ts +1 -1
  5. package/dist/types/chat/chat-transcript-builder.d.ts +1 -1
  6. package/dist/types/overlays/annotation-overlay.d.ts +30 -0
  7. package/dist/types/overlays/annotation-types.d.ts +84 -0
  8. package/dist/types/overlays/copy-selector.d.ts +30 -2
  9. package/dist/types/overlays/copy-targets.d.ts +2 -0
  10. package/dist/types/overlays/model-hub.d.ts +2 -2
  11. package/dist/types/overlays/oauth-selector.d.ts +3 -6
  12. package/dist/types/overlays/usage-dashboard.d.ts +2 -0
  13. package/dist/types/prompt/image-source.d.ts +9 -8
  14. package/dist/types/render/render-utils.d.ts +2 -0
  15. package/dist/types/status-line/context-usage.d.ts +3 -1
  16. package/dist/types/theme/mermaid-cache.d.ts +4 -6
  17. package/dist/types/tools/bash.d.ts +7 -0
  18. package/dist/types/tools/daemon.d.ts +46 -0
  19. package/dist/types/tools/irc.d.ts +30 -0
  20. package/dist/types/tools/line-ranges.d.ts +1 -1
  21. package/dist/types/tools/proc-render.d.ts +34 -0
  22. package/dist/types/tools/read.d.ts +4 -1
  23. package/dist/types/tools/task.d.ts +4 -0
  24. package/dist/types/tools/wait.d.ts +131 -0
  25. package/dist/types/tools/write.d.ts +4 -0
  26. package/dist/types/tools/xd-url.d.ts +8 -0
  27. package/dist/types/windows-altgr.d.ts +54 -0
  28. package/package.json +13 -10
  29. package/src/apps/debug/protocol-probe.ts +1 -1
  30. package/src/apps/ps-data.ts +1 -1
  31. package/src/apps/ps-top.ts +1 -1
  32. package/src/chat/chat-transcript-builder.ts +4 -4
  33. package/src/chat/tool-execution.ts +8 -8
  34. package/src/chat/transcript-render-helpers.ts +3 -3
  35. package/src/components/markdown.ts +1 -1
  36. package/src/desktop-notify.ts +1 -1
  37. package/src/overlays/annotation-overlay.ts +1145 -0
  38. package/src/overlays/annotation-types.ts +78 -0
  39. package/src/overlays/copy-selector.ts +45 -19
  40. package/src/overlays/copy-targets.ts +3 -3
  41. package/src/overlays/model-hub.ts +5 -4
  42. package/src/overlays/oauth-selector.ts +8 -12
  43. package/src/overlays/plan-review-overlay.ts +213 -40
  44. package/src/overlays/settings-defs.ts +1 -0
  45. package/src/overlays/usage-dashboard.ts +120 -25
  46. package/src/prompt/image-source.ts +9 -8
  47. package/src/prompt/welcome.ts +4 -1
  48. package/src/render/render-utils.ts +1 -1
  49. package/src/setup/scenes/sign-in.ts +1 -1
  50. package/src/status-line/component.ts +15 -4
  51. package/src/status-line/context-usage.ts +15 -3
  52. package/src/status-line/segments.ts +3 -3
  53. package/src/terminal-capabilities.ts +16 -7
  54. package/src/terminal.ts +8 -1
  55. package/src/theme/mermaid-cache.ts +83 -25
  56. package/src/tools/bash.ts +14 -1
  57. package/src/tools/daemon.ts +51 -0
  58. package/src/tools/index.ts +2 -2
  59. package/src/tools/irc.ts +33 -0
  60. package/src/tools/line-ranges.ts +13 -3
  61. package/src/tools/proc-render.ts +294 -0
  62. package/src/tools/read.ts +22 -1
  63. package/src/tools/task.ts +18 -0
  64. package/src/tools/wait.ts +595 -0
  65. package/src/tools/write.ts +76 -0
  66. package/src/tools/xd-url.ts +9 -0
  67. package/src/windows-altgr.ts +227 -0
  68. package/dist/types/tools/hub.d.ts +0 -342
  69. package/src/tools/hub.ts +0 -1492
@@ -0,0 +1,294 @@
1
+ import type { Component } from "../tui";
2
+ import { createCachedComponent, Ellipsis, renderStatusLine, renderTreeList, truncateToWidth } from "../render";
3
+ import {
4
+ cappedHeadLines,
5
+ formatBadge,
6
+ formatDuration,
7
+ formatErrorDetail,
8
+ formatStatusIcon,
9
+ PREVIEW_LIMITS,
10
+ TRUNCATE_LENGTHS,
11
+ replaceTabs,
12
+ type ToolUIColor,
13
+ } from "../render/render-utils";
14
+ import type { Theme } from "../theme/theme";
15
+ import type { RenderResultOptions } from "./renderer";
16
+ import type { AgentActivitySnapshot, CoordinationDetails, JobSnapshot } from "./wait";
17
+ import type { IrcDeliveryReceipt } from "./irc";
18
+ import type { DaemonSnapshot } from "./daemon";
19
+ import { styleTerminalRow } from "./terminal-output";
20
+
21
+ export interface ProcReadDetails {
22
+ jobs?: JobSnapshot[];
23
+ agents?: AgentActivitySnapshot[];
24
+ daemons?: DaemonSnapshot[];
25
+ job?: JobSnapshot;
26
+ daemon?: DaemonSnapshot;
27
+ log?: string;
28
+ terminalRows?: string[];
29
+ }
30
+
31
+ export type ProcWriteDetails =
32
+ | CoordinationDetails
33
+ | { action: "stop" | "stdin" | "mode"; daemon: DaemonSnapshot; input?: string; mode?: string };
34
+
35
+ /** Process operation selected by a write URL, independent of its content. */
36
+ export type ProcWriteAction = "stdin" | "mode" | "kill";
37
+
38
+ interface ToolResult {
39
+ content: Array<{ type: string; text?: string }>;
40
+ isError?: boolean;
41
+ }
42
+
43
+ function firstText(result: ToolResult): string {
44
+ return result.content.find(item => item.type === "text")?.text ?? "";
45
+ }
46
+
47
+ function safe(value: string): string {
48
+ return replaceTabs(value).replace(/\r/g, "");
49
+ }
50
+
51
+ function card(lines: (width: number, expanded: boolean) => string[], options: RenderResultOptions): Component {
52
+ return createCachedComponent(
53
+ () => Boolean(options.expanded),
54
+ (width, expanded) =>
55
+ lines(width, expanded)
56
+ .flatMap(line => line.split("\n"))
57
+ .map(line => truncateToWidth(safe(line), width, Ellipsis.Unicode)),
58
+ { paddingX: 1 },
59
+ );
60
+ }
61
+
62
+ function preview(body: string, expanded: boolean, theme: Theme, tone: "dim" | "toolOutput" = "dim"): string[] {
63
+ if (!body.trim()) return [];
64
+ const limit = expanded ? PREVIEW_LIMITS.EXPANDED_LINES : PREVIEW_LIMITS.COLLAPSED_LINES;
65
+ const shown = cappedHeadLines(
66
+ body.split("\n").filter(line => line.trim()),
67
+ limit,
68
+ );
69
+ const quote = theme.fg("dim", theme.md.quoteBorder);
70
+ const lines = shown.lines.map(
71
+ line =>
72
+ ` ${quote} ${theme.fg(tone, truncateToWidth(safe(line.trim()), TRUNCATE_LENGTHS.LINE, Ellipsis.Unicode))}`,
73
+ );
74
+ if (shown.hidden) lines.push(` ${quote} ${theme.fg("dim", `… +${shown.hidden} more lines`)}`);
75
+ return lines;
76
+ }
77
+
78
+ function receiptColor(outcome: IrcDeliveryReceipt["outcome"]): ToolUIColor {
79
+ return outcome === "failed" ? "error" : outcome === "revived" ? "warning" : "success";
80
+ }
81
+
82
+ export function renderAgentWrite(
83
+ to: string,
84
+ body: string,
85
+ result: ToolResult | undefined,
86
+ details: CoordinationDetails | undefined,
87
+ options: RenderResultOptions,
88
+ theme: Theme,
89
+ ): Component {
90
+ return card((width, expanded) => {
91
+ const recipient = safe(details?.to || to || "…");
92
+ const title = `IRC ${theme.nav.selected} ${recipient}`;
93
+ const receipts = details?.receipts ?? [];
94
+ const delivered = receipts.filter(receipt => receipt.outcome !== "failed").length;
95
+ const failed = receipts.length - delivered;
96
+ const error = result?.isError || (failed > 0 && delivered === 0);
97
+ const meta: string[] = [];
98
+ if (recipient === "all") meta.push("broadcast");
99
+ if (receipts.length === 1) meta.push(theme.fg(receiptColor(receipts[0]!.outcome), receipts[0]!.outcome));
100
+ else if (receipts.length > 1) {
101
+ if (delivered) meta.push(theme.fg("success", `${delivered} delivered`));
102
+ if (failed) meta.push(theme.fg("error", `${failed} failed`));
103
+ }
104
+ const header = renderStatusLine(
105
+ result === undefined
106
+ ? { icon: "pending", title, meta }
107
+ : error
108
+ ? { icon: "error", title, meta }
109
+ : { iconOverride: theme.styledSymbol("tool.irc", "accent"), title, meta },
110
+ theme,
111
+ );
112
+ if (result?.isError && receipts.length === 0)
113
+ return [header, formatErrorDetail(firstText(result) || "Message delivery failed.", theme)];
114
+ const lines = [header, ...preview(body, expanded, theme)];
115
+ if (result && receipts.length === 0)
116
+ lines.push(theme.fg("muted", firstText(result) || "No live peers to broadcast to."));
117
+ if (receipts.length > 1 || failed > 0) {
118
+ lines.push(
119
+ ...renderTreeList(
120
+ {
121
+ items: receipts,
122
+ expanded,
123
+ maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS,
124
+ itemType: "recipient",
125
+ renderItem: receipt =>
126
+ `${theme.fg("toolOutput", safe(receipt.to))} ${formatBadge(receipt.outcome, receiptColor(receipt.outcome), theme)}${receipt.error ? ` ${theme.fg("error", `${theme.format.dash} ${safe(receipt.error)}`)}` : ""}`,
127
+ },
128
+ theme,
129
+ ),
130
+ );
131
+ }
132
+ return lines.map(line => truncateToWidth(line, width, Ellipsis.Unicode));
133
+ }, options);
134
+ }
135
+
136
+ function daemonMeta(daemon: DaemonSnapshot, theme: Theme): string[] {
137
+ const stateColor =
138
+ daemon.state === "ready" || daemon.state === "running"
139
+ ? "success"
140
+ : daemon.state === "failed"
141
+ ? "error"
142
+ : "warning";
143
+ const meta = [theme.fg(stateColor, daemon.state)];
144
+ if (daemon.pid !== undefined) meta.push(`pid ${daemon.pid}`);
145
+ meta.push(
146
+ `${daemon.exitedAt === undefined ? "up" : "ran"} ${formatDuration(Math.max(0, (daemon.exitedAt ?? Date.now()) - daemon.startedAt))}`,
147
+ );
148
+ if (daemon.detached) meta.push("detached");
149
+ else if (daemon.persist) meta.push("persistent");
150
+ return meta;
151
+ }
152
+
153
+ function jobRow(job: JobSnapshot, theme: Theme): string {
154
+ const icon = formatStatusIcon(
155
+ job.status === "cancelled"
156
+ ? "aborted"
157
+ : job.status === "failed"
158
+ ? "error"
159
+ : job.status === "running"
160
+ ? "running"
161
+ : "done",
162
+ theme,
163
+ );
164
+ return `${icon} ${formatBadge(job.type, job.status === "failed" ? "error" : job.status === "cancelled" ? "warning" : "accent", theme)} ${theme.fg("toolOutput", safe(job.id))} ${theme.fg("dim", safe(job.label))} ${theme.fg("dim", formatDuration(job.durationMs))}`;
165
+ }
166
+
167
+ /** Render live and completed process writes with the URL-selected operation. */
168
+ export function renderProcWrite(
169
+ id: string,
170
+ action: ProcWriteAction,
171
+ content: string | undefined,
172
+ result: ToolResult | undefined,
173
+ details: ProcWriteDetails | undefined,
174
+ options: RenderResultOptions,
175
+ theme: Theme,
176
+ ): Component {
177
+ return card((_width, expanded) => {
178
+ const title = `Proc ${action} ${safe(id || "…")}`;
179
+ const daemon = details && "daemon" in details ? details.daemon : undefined;
180
+ const header = renderStatusLine(
181
+ {
182
+ icon:
183
+ result === undefined ? "pending" : result.isError ? "error" : action === "kill" ? "aborted" : "success",
184
+ title,
185
+ meta: daemon ? daemonMeta(daemon, theme) : action === "mode" && content ? [safe(content)] : [],
186
+ },
187
+ theme,
188
+ );
189
+ if (result?.isError) return [header, formatErrorDetail(firstText(result) || "Process operation failed.", theme)];
190
+ const lines = [header];
191
+ if (content && action === "stdin") lines.push(...preview(content, expanded, theme));
192
+ if (details && "op" in details && details.op === "cancel") {
193
+ const jobs = details.jobs ?? [];
194
+ const outcomes = details.cancelled ?? [];
195
+ lines.push(
196
+ ...renderTreeList(
197
+ {
198
+ items: outcomes,
199
+ expanded,
200
+ maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS,
201
+ itemType: "job",
202
+ renderItem: outcome => {
203
+ const job = jobs.find(item => item.id === outcome.id);
204
+ return `${job ? `${jobRow(job, theme)} ` : `${theme.fg("toolOutput", safe(outcome.id))} `}${formatBadge(outcome.status, outcome.status === "cancelled" ? "warning" : "error", theme)}`;
205
+ },
206
+ },
207
+ theme,
208
+ ),
209
+ );
210
+ }
211
+ return lines;
212
+ }, options);
213
+ }
214
+
215
+ export function renderProcRead(
216
+ id: string,
217
+ result: ToolResult | undefined,
218
+ details: ProcReadDetails | undefined,
219
+ options: RenderResultOptions,
220
+ theme: Theme,
221
+ ): Component {
222
+ return card((_width, expanded) => {
223
+ const title = id ? `Proc ${safe(id)}` : "Proc jobs & services";
224
+ const daemon = details?.daemon;
225
+ const header = renderStatusLine(
226
+ {
227
+ icon: result === undefined ? "pending" : result.isError ? "error" : "info",
228
+ title,
229
+ meta: daemon ? daemonMeta(daemon, theme) : [],
230
+ },
231
+ theme,
232
+ );
233
+ if (result?.isError) return [header, formatErrorDetail(firstText(result) || "Process read failed.", theme)];
234
+ if (!result) return [header];
235
+ if (details?.job)
236
+ return [
237
+ header,
238
+ jobRow(details.job, theme),
239
+ ...preview(
240
+ details.log ?? details.job.errorText ?? details.job.resultText ?? "",
241
+ expanded,
242
+ theme,
243
+ "toolOutput",
244
+ ),
245
+ ];
246
+ if (daemon) {
247
+ const output = details.terminalRows ?? (details.log ?? "").split("\n").filter(Boolean);
248
+ const limit = expanded ? PREVIEW_LIMITS.EXPANDED_LINES : PREVIEW_LIMITS.COLLAPSED_LINES;
249
+ const visible = output
250
+ .slice(-limit)
251
+ .map(
252
+ line =>
253
+ ` ${styleTerminalRow(truncateToWidth(safe(line), TRUNCATE_LENGTHS.LINE, Ellipsis.Unicode), theme.fg("toolOutput", ""))}`,
254
+ );
255
+ if (output.length > limit) visible.unshift(theme.fg("dim", ` … ${output.length - limit} earlier lines`));
256
+ return [header, ...visible];
257
+ }
258
+ if (id && !details?.jobs && !details?.daemons && !details?.agents) {
259
+ return [header, ...preview(firstText(result), expanded, theme, "toolOutput")];
260
+ }
261
+ const jobs = details?.jobs ?? [];
262
+ const services = details?.daemons ?? [];
263
+ const agents = details?.agents ?? [];
264
+ const meta = [
265
+ `${jobs.length} jobs`,
266
+ `${services.length} services`,
267
+ ...(agents.length ? [`${agents.length} agents`] : []),
268
+ ];
269
+ const listHeader = renderStatusLine({ icon: "info", title, meta }, theme);
270
+ const items = [
271
+ ...jobs.map(job => ({ label: jobRow(job, theme) })),
272
+ ...services.map(service => ({
273
+ label: `${formatBadge("service", "accent", theme)} ${theme.fg("toolOutput", safe(service.name))} ${formatBadge(service.state, service.state === "failed" ? "error" : service.state === "ready" || service.state === "running" ? "success" : "warning", theme)} ${daemonMeta(service, theme).slice(1).join(theme.sep.dot)}`,
274
+ })),
275
+ ...agents.map(agent => ({
276
+ label: `${formatBadge("agent", agent.live ? "accent" : "warning", theme)} ${theme.fg("toolOutput", safe(agent.id))} ${theme.fg("dim", formatDuration(agent.ageMs))}`,
277
+ })),
278
+ ];
279
+ return [
280
+ listHeader,
281
+ ...renderTreeList(
282
+ {
283
+ items,
284
+ expanded,
285
+ maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS,
286
+ itemType: "process",
287
+ renderItem: item => item.label,
288
+ },
289
+ theme,
290
+ ),
291
+ ...(items.length ? [] : [theme.fg("dim", "No background jobs or services.")]),
292
+ ];
293
+ }, options);
294
+ }
package/src/tools/read.ts CHANGED
@@ -4,7 +4,7 @@ import { LINE_RANGE_CHUNK_SOURCE, parseLineRanges } from "./line-ranges";
4
4
  import * as path from "node:path";
5
5
  import type { Component } from "../tui";
6
6
  import { Text } from "../components/text";
7
- import type { RenderResultOptions, ToolRenderer } from "./renderer";
7
+ import type { RenderResultOptions, ToolActivityContext, ToolActivitySummary, ToolRenderer } from "./renderer";
8
8
  import { getLanguageFromPath } from "../lang-from-path";
9
9
  import type { Theme } from "../theme/theme";
10
10
  import { fileHyperlink, renderCodeCell, renderMarkdownCell, renderStatusLine } from "../render";
@@ -16,6 +16,7 @@ import { formatBytes, sanitizeDisplayLines, shortenPath, wrapBrackets } from "..
16
16
 
17
17
  import type { OutputMeta } from "./output-meta";
18
18
  import type { TruncationResult } from "./streaming-output";
19
+ import { renderProcRead, type ProcReadDetails } from "./proc-render";
19
20
 
20
21
  /** Read result metadata retains truncation statistics, not a second copy of the body. */
21
22
  export type ReadTruncationStats = Omit<TruncationResult, "content">;
@@ -23,6 +24,7 @@ export type ReadTruncationStats = Omit<TruncationResult, "content">;
23
24
  /** Display metadata for file and URL reads. */
24
25
  export interface ReadToolDetails {
25
26
  kind?: "file" | "url";
27
+ proc?: ProcReadDetails;
26
28
  /** Filesystem hyperlink target resolved by the executing tool. */
27
29
  displayTarget?: string;
28
30
  truncation?: ReadTruncationStats;
@@ -91,6 +93,7 @@ const INTERNAL_SCHEMES_WITH_SELECTORS: Record<string, true> = {
91
93
  memory: true,
92
94
  omp: true,
93
95
  pr: true,
96
+ proc: true,
94
97
  rule: true,
95
98
  security: true,
96
99
  skill: true,
@@ -266,9 +269,19 @@ function formatReadPathLink(
266
269
 
267
270
  /** Render file, image, and URL reads in the transcript. */
268
271
  export const readToolRenderer = {
272
+ activitySummary(args: unknown, _context: ToolActivityContext): ToolActivitySummary {
273
+ const input = args as ReadRenderArgs | undefined;
274
+ const rawPath =
275
+ typeof input?.file_path === "string" ? input.file_path : typeof input?.path === "string" ? input.path : "";
276
+ if (/^proc:\/\//i.test(rawPath))
277
+ return { label: "Process", detail: rawPath.slice("proc://".length) || "jobs & services" };
278
+ return { label: "Read", detail: shortenPath(rawPath) };
279
+ },
269
280
  renderCall(args: ReadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
270
281
  const rawPath =
271
282
  typeof args.file_path === "string" ? args.file_path : typeof args.path === "string" ? args.path : "";
283
+ if (/^proc:\/\//i.test(rawPath))
284
+ return renderProcRead(rawPath.slice("proc://".length), undefined, undefined, _options, uiTheme);
272
285
  if (isReadableUrlPath(rawPath)) {
273
286
  return renderReadUrlCall({ path: rawPath, raw: args.raw }, _options, uiTheme);
274
287
  }
@@ -296,6 +309,14 @@ export const readToolRenderer = {
296
309
  const urlDetails = result.details as ReadUrlToolDetails | undefined;
297
310
  const baseRawPathForKind =
298
311
  typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
312
+ if (/^proc:\/\//i.test(baseRawPathForKind))
313
+ return renderProcRead(
314
+ baseRawPathForKind.slice("proc://".length),
315
+ result,
316
+ result.details?.proc,
317
+ options,
318
+ uiTheme,
319
+ );
299
320
  if (urlDetails?.kind === "url" || isReadableUrlPath(baseRawPathForKind)) {
300
321
  return renderReadUrlResult(
301
322
  result as {
package/src/tools/task.ts CHANGED
@@ -636,6 +636,18 @@ function renderDescriptionLines(description: string, prefix: string, width: numb
636
636
  return wrapTextWithAnsi(description, contentWidth).map(line => `${boundedPrefix}${theme.fg("dim", line)}`);
637
637
  }
638
638
 
639
+ /** `routed:` note from a `before_subagent_spawn` model replacement; empty when unrouted. */
640
+ function renderRouteLine(route: string | undefined, continuePrefix: string, maxWidth: number, theme: Theme): string[] {
641
+ if (!route) return [];
642
+ return [
643
+ truncateTaskRow(
644
+ `${continuePrefix}${theme.fg("dim", `routed: ${replaceTabs(sanitizeText(route))}`)}`,
645
+ maxWidth,
646
+ "",
647
+ ),
648
+ ];
649
+ }
650
+
639
651
  /**
640
652
  * Render streaming progress for a single agent.
641
653
  */
@@ -688,6 +700,7 @@ function renderAgentProgress(
688
700
  if (fullDescription && !row.descriptionShown) {
689
701
  lines.push(...renderDescriptionLines(fullDescription, continuePrefix, maxWidth, theme));
690
702
  }
703
+ lines.push(...renderRouteLine(progress.resolvedModelRoute, continuePrefix, maxWidth, theme));
691
704
 
692
705
  lines.push(...renderTaskSection(progress.assignment ?? progress.task, continuePrefix, expanded, theme));
693
706
 
@@ -1029,6 +1042,7 @@ function renderAgentResult(
1029
1042
  if (fullDescription && !description) {
1030
1043
  lines.push(...renderDescriptionLines(fullDescription, continuePrefix, maxWidth, theme));
1031
1044
  }
1045
+ lines.push(...renderRouteLine(result.resolvedModelRoute, continuePrefix, maxWidth, theme));
1032
1046
 
1033
1047
  lines.push(...renderTaskSection(result.assignment ?? result.task, continuePrefix, expanded, theme));
1034
1048
 
@@ -1858,6 +1872,8 @@ export interface AgentProgress {
1858
1872
  resolvedThinkingLevel?: ConfiguredThinkingLevel;
1859
1873
  /** True when {@link resolvedModel} is the target of an active retry fallback (not the originally configured model). Lets observer-only UIs (collab guests, Agent Hub rows with no live session) flag the fallback and keep the provider. */
1860
1874
  resolvedModelIsFallback?: boolean;
1875
+ /** Extension routing note (e.g. model-pools) explaining why {@link resolvedModel} was chosen. */
1876
+ resolvedModelRoute?: string;
1861
1877
  /** True when a live advisor was attached to this run's session, not merely enabled in settings. */
1862
1878
  advisor?: boolean;
1863
1879
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
@@ -1935,6 +1951,8 @@ export interface SingleResult {
1935
1951
  resolvedThinkingLevel?: ConfiguredThinkingLevel;
1936
1952
  /** True when {@link resolvedModel} is the target of an active retry fallback. Mirrors {@link AgentProgress.resolvedModelIsFallback} onto the settled result. */
1937
1953
  resolvedModelIsFallback?: boolean;
1954
+ /** Mirrors {@link AgentProgress.resolvedModelRoute} onto the settled result. */
1955
+ resolvedModelRoute?: string;
1938
1956
  /** Retains {@link AgentProgress.advisor} after the advised session is disposed. */
1939
1957
  advisor?: boolean;
1940
1958
  error?: string;