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

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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.2.11] - 2026-09-23
6
+
7
+ ### Added
8
+
9
+ - Added a Skills section to the settings.
10
+ - Enabled OSC 8 hyperlinks in rio.
11
+
12
+ ### Fixed
13
+
14
+ - Fixed scrolling startup release notes without requiring an initial Escape keypress.
15
+ - Improved usage quota display so names remain distinguishable and readable in narrow and multi-column dashboards.
16
+
5
17
  ## [18.2.10] - 2026-09-22
6
18
 
7
19
  ### Changed
@@ -96,6 +96,8 @@ export declare const TRUNCATE_LENGTHS: {
96
96
  };
97
97
  /** Human-readable key currently bound to tool-output expansion, e.g. `Ctrl+O`. */
98
98
  export declare function expandKeyHint(): string;
99
+ /** Keep both ends of a single-line label without splitting wide characters. */
100
+ export declare function truncateMiddleToWidth(text: string, maxWidth: number): string;
99
101
  /** Select a leading line window and count the lines hidden after it. */
100
102
  export declare function cappedHeadLines(lines: readonly string[], max: number): {
101
103
  lines: readonly string[];
@@ -237,6 +237,8 @@ export interface AgentProgress {
237
237
  resolvedThinkingLevel?: ConfiguredThinkingLevel;
238
238
  /** 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. */
239
239
  resolvedModelIsFallback?: boolean;
240
+ /** Extension routing note (e.g. model-pools) explaining why {@link resolvedModel} was chosen. */
241
+ resolvedModelRoute?: string;
240
242
  /** True when a live advisor was attached to this run's session, not merely enabled in settings. */
241
243
  advisor?: boolean;
242
244
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
@@ -313,6 +315,8 @@ export interface SingleResult {
313
315
  resolvedThinkingLevel?: ConfiguredThinkingLevel;
314
316
  /** True when {@link resolvedModel} is the target of an active retry fallback. Mirrors {@link AgentProgress.resolvedModelIsFallback} onto the settled result. */
315
317
  resolvedModelIsFallback?: boolean;
318
+ /** Mirrors {@link AgentProgress.resolvedModelRoute} onto the settled result. */
319
+ resolvedModelRoute?: string;
316
320
  /** Retains {@link AgentProgress.advisor} after the advised session is disposed. */
317
321
  advisor?: boolean;
318
322
  error?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "18.2.10",
4
+ "version": "18.2.11",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -37,14 +37,14 @@
37
37
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/omptype": "18.2.10",
41
- "@oh-my-pi/pi-agent-core": "18.2.10",
42
- "@oh-my-pi/pi-ai": "18.2.10",
43
- "@oh-my-pi/pi-catalog": "18.2.10",
44
- "@oh-my-pi/pi-natives": "18.2.10",
45
- "@oh-my-pi/pi-utils": "18.2.10",
46
- "@oh-my-pi/pi-wire": "18.2.10",
47
- "@oh-my-pi/snapcompact": "18.2.10"
40
+ "@oh-my-pi/omptype": "18.2.11",
41
+ "@oh-my-pi/pi-agent-core": "18.2.11",
42
+ "@oh-my-pi/pi-ai": "18.2.11",
43
+ "@oh-my-pi/pi-catalog": "18.2.11",
44
+ "@oh-my-pi/pi-natives": "18.2.11",
45
+ "@oh-my-pi/pi-utils": "18.2.11",
46
+ "@oh-my-pi/pi-wire": "18.2.11",
47
+ "@oh-my-pi/snapcompact": "18.2.11"
48
48
  },
49
49
  "devDependencies": {
50
50
  "kitty-vt-wasm": "^0.2.0"
@@ -64,6 +64,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
64
64
  "Power",
65
65
  "Agent",
66
66
  "Git",
67
+ "Skills",
67
68
  ],
68
69
  context: ["General", "Compaction", "Rules (TTSR)", "Experimental"],
69
70
  memory: ["General", "Auto-Learn", "Mnemopi", "Hindsight", "Sharpshooter"],
@@ -7,7 +7,16 @@
7
7
  */
8
8
  import * as os from "node:os";
9
9
  import { resolveUsedFraction, type UsageLimit, type UsageReport } from "@oh-my-pi/pi-ai";
10
- import { type Component, matchesKey, replaceTabs, routeSgrMouseInput, truncateToWidth, visibleWidth } from "../index";
10
+ import {
11
+ type Component,
12
+ matchesKey,
13
+ replaceTabs,
14
+ routeSgrMouseInput,
15
+ sliceWithWidth,
16
+ truncateToWidth,
17
+ visibleWidth,
18
+ wrapTextWithAnsi,
19
+ } from "../index";
11
20
  import { colorLuma, formatDuration, hexToRgb, rgbToHex, sanitizeText } from "@oh-my-pi/pi-utils";
12
21
  import { formatProviderName } from "../chrome/format";
13
22
  import {
@@ -19,6 +28,8 @@ import {
19
28
  import { colorToAnsi } from "../theme/color";
20
29
  import { ensureThemeSync, theme } from "../theme/theme";
21
30
  import { formatAbsoluteOnlyAmount } from "../prompt/usage-amounts";
31
+ import { truncateMiddleToWidth } from "../render/render-utils";
32
+ import { sanitizeDisplayLine } from "./extensions/display-text";
22
33
  import {
23
34
  matchesSelectCancel,
24
35
  matchesSelectDown,
@@ -333,6 +344,16 @@ export function formatActivityErrorDetail(error: string, homeDir = os.homedir())
333
344
  const CARD_MIN_WIDTH = 32;
334
345
  const CARD_GUTTER = 3;
335
346
  const CARD_MAX_WINDOWS = 4;
347
+ const CARD_MIN_BAR_WIDTH = 12;
348
+ const CARD_MAX_LABEL_LINES = 2;
349
+
350
+ interface CardRowLayout {
351
+ labelWidth: number;
352
+ resetWidth: number;
353
+ barWidth: number;
354
+ stacked: boolean;
355
+ labelHeights: number[];
356
+ }
336
357
 
337
358
  export class UsageDashboardComponent implements Component {
338
359
  #options: UsageDashboardOptions;
@@ -423,7 +444,7 @@ export class UsageDashboardComponent implements Component {
423
444
  return `${theme.fg(this.#statusColor(status), bar)}${theme.fg("dim", empty)}`;
424
445
  }
425
446
 
426
- #renderCardLines(card: ProviderCard, width: number): string[] {
447
+ #renderCardLines(card: ProviderCard, width: number, labels: string[][], layout: CardRowLayout): string[] {
427
448
  const lines: string[] = [];
428
449
  const cardStatus = card.unlimited ? "ok" : aggregateStatus(card.windows);
429
450
  const accountsText = card.accounts > 1 ? theme.fg("dim", `${card.accounts} accts`) : "";
@@ -457,35 +478,33 @@ export class UsageDashboardComponent implements Component {
457
478
  }
458
479
 
459
480
  const hidden = card.windows.length - CARD_MAX_WINDOWS;
460
- const visibleWindows = card.windows.slice(0, CARD_MAX_WINDOWS);
461
- // Fixed columns across every row of the card so bars all start and end
462
- // at the same x: label | bar | pct | reset. The reset column sizes to
463
- // the card's widest countdown instead of flexing per row.
464
- const resetWidth = visibleWindows.reduce(
465
- (max, window) => Math.max(max, window.resetMs !== undefined ? formatDuration(window.resetMs).length : 0),
466
- 0,
467
- );
468
- const labelWidth = Math.min(16, Math.max(6, width - 24));
469
- const barWidth = Math.max(5, width - 2 - labelWidth - 1 - 5 - (resetWidth > 0 ? resetWidth + 1 : 0));
470
- for (const window of visibleWindows) {
471
- const tagPlain = window.windowTag
472
- ? truncateToWidth(window.windowTag, Math.max(2, Math.floor(labelWidth / 2) - 1))
473
- : "";
474
- const baseWidth = tagPlain ? labelWidth - visibleWidth(tagPlain) - 1 : labelWidth;
475
- const basePlain = truncateToWidth(window.label, baseWidth).padEnd(baseWidth);
476
- const label = tagPlain
477
- ? `${theme.fg("muted", basePlain)} ${theme.fg("dim", tagPlain)}`
478
- : theme.fg("muted", basePlain);
481
+ const { labelWidth, resetWidth, barWidth, stacked, labelHeights } = layout;
482
+ const contentWidth = Math.max(1, width - 2);
483
+ for (let index = 0; index < Math.min(card.windows.length, CARD_MAX_WINDOWS); index++) {
484
+ const window = card.windows[index]!;
485
+ const labelLines = labels[index]!;
486
+ const label = labelLines[0] ?? "";
487
+ const prefix = stacked ? "" : `${label}${" ".repeat(labelWidth - visibleWidth(label))} `;
488
+ if (stacked) {
489
+ for (let line = 0; line < labelHeights[index]; line++) {
490
+ lines.push(` ${labelLines[line] ?? ""}`);
491
+ }
492
+ }
479
493
  if (window.fraction === undefined) {
480
494
  const text = theme.fg("dim", window.usedText ?? "no data");
481
- lines.push(truncateToWidth(` ${label} ${text}`, width));
495
+ for (const line of wrapTextWithAnsi(`${prefix}${text}`, contentWidth)) lines.push(` ${line}`);
482
496
  continue;
483
497
  }
484
498
  const freePct = Math.max(0, Math.round((1 - window.fraction) * 100));
485
499
  const pctText = theme.fg(this.#statusColor(window.status), `${freePct}%`.padStart(5));
486
500
  const resetPlain = window.resetMs !== undefined ? formatDuration(window.resetMs) : "";
487
501
  const resetText = resetWidth > 0 ? ` ${theme.fg("dim", resetPlain.padStart(resetWidth))}` : "";
488
- lines.push(` ${label} ${this.#miniBar(window.fraction, window.status, barWidth)}${pctText}${resetText}`);
502
+ for (const line of wrapTextWithAnsi(
503
+ `${prefix}${this.#miniBar(window.fraction, window.status, barWidth)}${pctText}${resetText}`,
504
+ contentWidth,
505
+ )) {
506
+ lines.push(` ${line}`);
507
+ }
489
508
  }
490
509
  if (hidden > 0) lines.push(` ${theme.fg("dim", `+${hidden} more`)}`);
491
510
  return lines;
@@ -499,7 +518,62 @@ export class UsageDashboardComponent implements Component {
499
518
  const cardWidth = Math.floor((innerWidth - (columns - 1) * CARD_GUTTER) / columns);
500
519
  const lines: string[] = [];
501
520
  for (let start = 0; start < active.length; start += columns) {
502
- const rowCards = active.slice(start, start + columns).map(card => this.#renderCardLines(card, cardWidth));
521
+ const cards = active.slice(start, start + columns);
522
+ const windows = cards.map(card => card.windows.slice(0, CARD_MAX_WINDOWS));
523
+ const labels = windows.map(rows =>
524
+ rows.map(window => {
525
+ const label = theme.fg("muted", sanitizeDisplayLine(window.label));
526
+ const tag = window.windowTag ? sanitizeDisplayLine(window.windowTag) : "";
527
+ return tag ? `${label} ${theme.fg("dim", tag)}` : label;
528
+ }),
529
+ );
530
+ // One geometry per grid row: labels, bars, and resets share columns,
531
+ // and every card stacks together when inline bars would be too short.
532
+ const labelWidth = labels.reduce(
533
+ (max, rows) => rows.reduce((width, label) => Math.max(width, visibleWidth(label)), max),
534
+ 0,
535
+ );
536
+ const resetWidth = windows.reduce(
537
+ (max, rows) =>
538
+ rows.reduce(
539
+ (width, window) =>
540
+ Math.max(width, window.resetMs !== undefined ? formatDuration(window.resetMs).length : 0),
541
+ max,
542
+ ),
543
+ 0,
544
+ );
545
+ const contentWidth = Math.max(1, cardWidth - 2);
546
+ const suffixWidth = 5 + (resetWidth > 0 ? resetWidth + 1 : 0);
547
+ const inlineBarWidth = contentWidth - labelWidth - 1 - suffixWidth;
548
+ const stacked = inlineBarWidth < CARD_MIN_BAR_WIDTH;
549
+ const labelLines = labels.map(rows =>
550
+ rows.map(label => {
551
+ if (!stacked) return [label];
552
+ // Bound wrapping work as well as height, keeping the suffix that
553
+ // distinguishes model-specific quota buckets.
554
+ const bounded = truncateMiddleToWidth(label, contentWidth * CARD_MAX_LABEL_LINES);
555
+ const lines = wrapTextWithAnsi(bounded, contentWidth);
556
+ if (lines.length <= CARD_MAX_LABEL_LINES) return lines;
557
+ // Word wrapping can waste enough cells to create a third line even
558
+ // when the label fits. Use a grapheme-safe column split in that case.
559
+ const first = sliceWithWidth(bounded, 0, contentWidth, true);
560
+ const rest = sliceWithWidth(bounded, first.width, visibleWidth(bounded) - first.width, true);
561
+ return [first.text, truncateMiddleToWidth(rest.text, contentWidth)];
562
+ }),
563
+ );
564
+ const labelHeights = Array.from({ length: CARD_MAX_WINDOWS }, (_, index) =>
565
+ Math.max(0, ...labelLines.map(rows => rows[index]?.length ?? 0)),
566
+ );
567
+ const layout: CardRowLayout = {
568
+ labelWidth,
569
+ resetWidth,
570
+ barWidth: Math.max(1, stacked ? contentWidth - suffixWidth : inlineBarWidth),
571
+ stacked,
572
+ labelHeights,
573
+ };
574
+ const rowCards = cards.map((card, index) =>
575
+ this.#renderCardLines(card, cardWidth, labelLines[index]!, layout),
576
+ );
503
577
  const height = Math.max(...rowCards.map(card => card.length));
504
578
  for (let lineIdx = 0; lineIdx < height; lineIdx++) {
505
579
  const segments = rowCards.map(card => {
@@ -185,10 +185,13 @@ export class WelcomeComponent implements Component {
185
185
  this.#requestRender();
186
186
  this.#animTimer = setInterval(() => {
187
187
  const elapsed = performance.now() - (this.#animStart ?? 0);
188
+ const requestRender = this.#requestRender;
188
189
  if (elapsed >= INTRO_MS) {
189
190
  this.#stopAnimation();
190
191
  }
191
- this.#requestRender?.();
192
+ // Stopping clears the callback, but the settled frame must still paint
193
+ // so an oversized startup header can retire into native scrollback.
194
+ requestRender?.();
192
195
  }, INTRO_TICK_MS);
193
196
  }
194
197
 
@@ -211,7 +211,7 @@ export function expandKeyHint(): string {
211
211
  // Text Truncation Utilities
212
212
  // =============================================================================
213
213
  /** Keep both ends of a single-line label without splitting wide characters. */
214
- function truncateMiddleToWidth(text: string, maxWidth: number): string {
214
+ export function truncateMiddleToWidth(text: string, maxWidth: number): string {
215
215
  const width = visibleWidth(text);
216
216
  if (width <= maxWidth) return text;
217
217
  if (maxWidth <= 1) return maxWidth === 1 ? "…" : "";
@@ -55,6 +55,18 @@ const BRAND_FADE_MS = 450;
55
55
  /** Repaint cadence while the brand fade is in flight (rust omp's `FADE_FRAME`). */
56
56
  const BRAND_FADE_FRAME_MS = 40;
57
57
 
58
+ /**
59
+ * Providers whose subscription quota is a single monthly bucket, so their
60
+ * `monthly`/`30d` window is the one the usage segment must show. Providers that
61
+ * merely report a monthly side-counter (GitHub Copilot's premium requests) stay
62
+ * out: their monthly row is not the session quota.
63
+ */
64
+ const MONTHLY_SUBSCRIPTION_PROVIDERS: Record<string, true> = {
65
+ "alibaba-token-plan": true,
66
+ cursor: true,
67
+ "opencode-go": true,
68
+ };
69
+
58
70
  /** A displayable limit after provider, account, model, and window filtering. */
59
71
  interface UsageWindowCandidate {
60
72
  id?: string;
@@ -1842,6 +1854,8 @@ export class StatusLineComponent<TSession extends StatusLineSession = StatusLine
1842
1854
  const activeModelId = normalizeUsageScopeValue(context.modelId);
1843
1855
  const activeAntigravityCounter =
1844
1856
  context.provider === "google-antigravity" ? getAntigravityCounterKeyForModel(context.modelId) : undefined;
1857
+ const monthlySubscriptionProvider =
1858
+ context.provider !== undefined && MONTHLY_SUBSCRIPTION_PROVIDERS[context.provider] === true;
1845
1859
  const scopeGroups = new Map<string, UsageScopeGroup>();
1846
1860
  for (const report of reports) {
1847
1861
  if (!report || typeof report !== "object") continue;
@@ -1904,10 +1918,7 @@ export class StatusLineComponent<TSession extends StatusLineSession = StatusLine
1904
1918
  : undefined;
1905
1919
  const windowClass =
1906
1920
  subscriptionWindow ??
1907
- ((context.provider === "cursor" || context.provider === "opencode-go") &&
1908
- (windowId === "monthly" || windowId === "30d")
1909
- ? "monthly"
1910
- : undefined);
1921
+ (monthlySubscriptionProvider && (windowId === "monthly" || windowId === "30d") ? "monthly" : undefined);
1911
1922
  if (!windowClass) continue;
1912
1923
 
1913
1924
  const modelId = normalizeUsageScopeValue("modelId" in scope ? scope.modelId : undefined);
@@ -881,9 +881,9 @@ const usageSegment: StatusLineSegment = {
881
881
  parts.push(formatQuotaWindow(ctx, "7d", u.sevenDay.percent, u.sevenDay.resetHours, "h", "round"));
882
882
  }
883
883
  if (u.monthly) {
884
- // Cursor and OpenCode Go (normalize gates monthly to those providers).
885
- // Both floor used percents upstream (Cursor's dashboard shows 1.88 →
886
- // "1% used"; OpenCode's endpoint already emits floored integers).
884
+ // Monthly-subscription providers only (the normalizer gates the class).
885
+ // Cursor and QwenCloud floor used percents upstream (Cursor's dashboard
886
+ // shows 1.88 → "1% used"; OpenCode's endpoint emits floored integers).
887
887
  parts.push(formatQuotaWindow(ctx, "mo", u.monthly.percent, u.monthly.resetHours, "h", "floor"));
888
888
  }
889
889
  if (u.resetCredits) {
@@ -678,11 +678,20 @@ const KNOWN_TERMINALS = Object.freeze({
678
678
  // OSC 66 text sizing are unverified, so those stay on conservative defaults;
679
679
  // synchronized output is left to the runtime DECRQM probe.
680
680
  otty: new TerminalInfo("otty", ImageProtocol.Kitty, true, true, NotifyProtocol.Osc99),
681
- // rio ships the Kitty graphics protocol — direct placement plus U=1 Unicode
682
- // placeholders verified by the reporter (#12205). Everything unproven stays
683
- // conservative: hyperlinks, DECCARA, screen-to-scrollback, and notifications
684
- // keep the base defaults until verified in that terminal.
685
- rio: new TerminalInfo("rio", ImageProtocol.Kitty, true, false),
681
+ // rio publishes its supported escape sequences at
682
+ // rioterm.com/docs/escape-sequence-support: OSC 8 hyperlinks are opened via
683
+ // the hints system (Alt+click on Windows/Linux) — live-verified on
684
+ // rio 0.5.28/Win11 26100 where an OSC 8 span Alt+click opened the target
685
+ // URL in the browser, and an OSC 52 clipboard query round-tripped. Kitty
686
+ // graphics direct placement plus U=1 Unicode placeholders verified by the
687
+ // reporter (#12205). Notifications stay on BEL: rio parses OSC 9/777 into
688
+ // its own notifier, but its Windows path raises an unpackaged WinRT toast
689
+ // with AppId "Rio" that Windows silently drops without a registered AUMID
690
+ // shortcut — observed as a no-op live — so flipping it would only remove
691
+ // the D-Bus fallback for Linux rio users. DECCARA, screen-to-scrollback,
692
+ // OSC 99, and OSC 66 text sizing are outside rio's supported set and keep
693
+ // the conservative defaults.
694
+ rio: new TerminalInfo("rio", ImageProtocol.Kitty, true, true),
686
695
  });
687
696
 
688
697
  /** Resolve terminal identity from environment markers used by common emulators. */
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;