@robota-sdk/agent-ui-web 3.3.0 → 3.3.1

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 (53) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/index.cjs +81 -81
  3. package/dist/index.d.cts +61 -61
  4. package/dist/index.d.ts +61 -61
  5. package/dist/index.js +81 -81
  6. package/dist/index.js.map +1 -1
  7. package/docs/SPEC.md +1 -1
  8. package/package.json +6 -6
  9. package/src/client/ws-session-client.ts +1 -1
  10. package/src/clipboard.ts +2 -2
  11. package/src/components/AgentActivityPanel.tsx +8 -8
  12. package/src/components/AgentSwitcherSheet.tsx +2 -2
  13. package/src/components/Composer.tsx +27 -27
  14. package/src/components/ConversationView.tsx +14 -14
  15. package/src/components/Dialog.tsx +6 -6
  16. package/src/components/DiffLines.tsx +2 -2
  17. package/src/components/ExecutionDetailSheet.tsx +1 -1
  18. package/src/components/HelpSheet.tsx +2 -2
  19. package/src/components/PermissionPrompt.tsx +22 -22
  20. package/src/components/PopupMenu.tsx +1 -1
  21. package/src/components/ProjectPanel.tsx +2 -2
  22. package/src/components/SessionMonitor.tsx +1 -1
  23. package/src/components/SessionSidebar.tsx +8 -8
  24. package/src/components/SessionSurface.tsx +18 -18
  25. package/src/components/SessionSurfaceChrome.tsx +8 -8
  26. package/src/components/SettingsGeneralSection.tsx +1 -1
  27. package/src/components/SettingsMcpSection.tsx +1 -1
  28. package/src/components/SettingsPermissionsSection.tsx +1 -1
  29. package/src/components/SettingsPluginsSection.tsx +1 -1
  30. package/src/components/SettingsProvidersSection.tsx +3 -3
  31. package/src/components/SettingsScreen.tsx +3 -3
  32. package/src/components/StatusControls.tsx +12 -12
  33. package/src/components/composer-attachments.ts +5 -5
  34. package/src/driver-labels.ts +3 -3
  35. package/src/execution-entry-status.ts +1 -1
  36. package/src/goal-view.ts +1 -1
  37. package/src/hooks/command-menu.ts +2 -2
  38. package/src/hooks/excluded-commands.ts +2 -2
  39. package/src/hooks/prompt-state.ts +4 -4
  40. package/src/hooks/server-message-handling.ts +11 -11
  41. package/src/hooks/session-client-types.ts +29 -29
  42. package/src/hooks/ui-intent-state.ts +6 -6
  43. package/src/hooks/use-agent-switcher-state.ts +2 -2
  44. package/src/hooks/use-media-query.ts +1 -1
  45. package/src/hooks/use-model-list.ts +1 -1
  46. package/src/hooks/use-project-panel.ts +1 -1
  47. package/src/hooks/use-schedules-state.ts +4 -4
  48. package/src/hooks/use-session-directory.ts +4 -4
  49. package/src/hooks/use-settings-state.ts +4 -4
  50. package/src/hooks/useSessionClient.ts +44 -44
  51. package/src/index.ts +5 -5
  52. package/src/schedule-view.ts +1 -1
  53. package/src/styles/surface.css +1 -1
@@ -30,7 +30,7 @@ function keepsSessionsLive(listing: TSessionListing): boolean {
30
30
  }
31
31
 
32
32
  /**
33
- * Issue #3280 §5: a desktop Reconnect reloads the page, which loses every bit of JS state — so the
33
+ * A desktop Reconnect reloads the page, which loses every bit of JS state — so the
34
34
  * session to go back to cannot live in a ref like `lastSwitchedIdRef` below. `sessionStorage` survives
35
35
  * the reload (and only that tab), so it carries the id across it. Namespaced so a page hosting other
36
36
  * state under the same origin does not collide.
@@ -74,7 +74,7 @@ function initialSidebarOpen(): boolean {
74
74
  }
75
75
 
76
76
  /**
77
- * #3189 — the host's session list beside the conversation: requests correlated by id (the latest
77
+ * The host's session list beside the conversation: requests correlated by id (the latest
78
78
  * wins), the start/switch commands, and whether the sidebar is shown.
79
79
  *
80
80
  * A host that keeps sessions live binds each connection to its own session and puts a new connection
@@ -120,7 +120,7 @@ export function useSessionDirectoryState(
120
120
  (): void => send({ type: 'new-session', requestId: nextRequestId('session_change') }),
121
121
  [send, browserNamespace],
122
122
  );
123
- // #3289 §1: rename or delete a row in the list — current or not. Both answer on their own
123
+ // Rename or delete a row in the list — current or not. Both answer on their own
124
124
  // correlated reply (`session_renamed_in_list`/`session_deleted`, or a `_failed` counterpart); the
125
125
  // reducer in `useSessionClient` refreshes the listing on success and raises a notice on failure.
126
126
  const renameSessionInList = useCallback(
@@ -138,7 +138,7 @@ export function useSessionDirectoryState(
138
138
  const lastSwitchedIdRef = useRef<string | null>(null);
139
139
  const restoreTargetRef = useRef<string | null>(null);
140
140
  const armRestore = useCallback((): void => {
141
- // A remembered id (issue #3280 §5, a desktop Reconnect's reload) wins over the same-page target:
141
+ // A remembered id (a desktop Reconnect's reload) wins over the same-page target:
142
142
  // on a fresh page, `lastSwitchedIdRef` is always null anyway, so this only ever adds a source.
143
143
  restoreTargetRef.current = readStoredRestoreId(browserNamespace) ?? lastSwitchedIdRef.current;
144
144
  }, [browserNamespace]);
@@ -24,12 +24,12 @@ function nextRequestId(prefix: string): string {
24
24
  }
25
25
 
26
26
  /**
27
- * #3282 §4a: the Settings screen's own correlated request/response state, independent from chat
27
+ * The Settings screen's own correlated request/response state, independent from chat
28
28
  * state — the same shape `usePersonalUsageState` already uses for `get-personal-usage-report`.
29
29
  *
30
30
  * `openSettings` re-fetches every time (opening from the gear, `/settings`, or reopening after a
31
31
  * close): the simplest correct way for a change made elsewhere — another typed command, another
32
- * client — to reach an open-then-reopened screen, per #3282 §4's "pick the simplest correct option."
32
+ * client — to reach an open-then-reopened screen.
33
33
  *
34
34
  * A failed UPDATE never touches `settingsSnapshot`, so a control bound to it shows the value it had
35
35
  * before the write — "a failed write leaves the control at its previous value" — while
@@ -39,7 +39,7 @@ function nextRequestId(prefix: string): string {
39
39
  export function useSettingsState(send: (msg: TClientMessage) => void): TSettingsState & {
40
40
  handleSettingsMessage: (msg: TServerMessage) => boolean;
41
41
  /**
42
- * #3282 §4b: re-fetch without touching `settingsOpen`/`settingsInitialSectionId` — for a change
42
+ * Re-fetch without touching `settingsOpen`/`settingsInitialSectionId` — for a change
43
43
  * made elsewhere while the screen is already open (e.g. "Add provider…"'s ask flow finishing).
44
44
  * Unlike `openSettings`, this never resets which section is showing.
45
45
  */
@@ -49,7 +49,7 @@ export function useSettingsState(send: (msg: TClientMessage) => void): TSettings
49
49
  const [settingsStatus, setSettingsStatus] = useState<IWsSessionState['settingsStatus']>('idle');
50
50
  const [settingsSnapshot, setSettingsSnapshot] = useState<ISettingsSnapshot | null>(null);
51
51
  const [settingsError, setSettingsError] = useState<string | null>(null);
52
- // #3282 §4 part b-2 / §4b: which section to land on THIS open — `/plugin` opens straight to
52
+ // Which section to land on THIS open — `/plugin` opens straight to
53
53
  // Plugins, "Manage providers…" straight to Providers & Models; everything else (the gear,
54
54
  // `/settings`) opens on the screen's own default (General).
55
55
  const [settingsInitialSectionId, setSettingsInitialSectionId] = useState<string | null>(null);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * GUI-005 — the transport-neutral session reducer for the GUI presentation layer. Reconstructs conversation
2
+ * The transport-neutral session reducer for the GUI presentation layer. Reconstructs conversation
3
3
  * state from the wire `TServerMessage` stream, independent of HOW the bytes arrive (a `TMakeSessionClient`
4
4
  * factory over `{onMessage, onStatusChange}`). The core is **generic over its status type** (default
5
5
  * `TConnectionStatus`) so a surface with extra connection states (e.g. the browser WebRTC surface) can widen it
@@ -83,7 +83,7 @@ function nextSilentCommandRequestId(): string {
83
83
  }
84
84
 
85
85
  /**
86
- * #3288: a finished turn's text and tool calls, in the order they actually happened — a running
86
+ * A finished turn's text and tool calls, in the order they actually happened — a running
87
87
  * turn accumulates these alongside (not instead of) the legacy `streamingText`/`activeTools` live
88
88
  * view, and `finishTurn` flushes them as SEPARATE conversation entries in order, instead of one
89
89
  * tools-block followed by one merged text block. Consecutive tool calls with no text between them
@@ -118,7 +118,7 @@ function pushToolStartSegment(segments: TTurnSegment[], tool: IActiveTool): TTur
118
118
  }
119
119
 
120
120
  /**
121
- * #3288: attribute a `tool_end` to its call by executionId first — two same-named parallel calls
121
+ * Attribute a `tool_end` to its call by executionId first — two same-named parallel calls
122
122
  * can finish out of start order, and matching by name+running alone would close whichever running
123
123
  * call of that name is found first, not the one that actually finished. Falls back to name+running
124
124
  * only when the event carries no executionId (legacy fixtures / hosts that predate it).
@@ -189,7 +189,7 @@ function applyToolEndToSegments(segments: TTurnSegment[], state: IToolState): TT
189
189
  }
190
190
 
191
191
  /**
192
- * #3288: the files an Edit/Write call touched during the turn, aggregated by path — the "Changed
192
+ * The files an Edit/Write call touched during the turn, aggregated by path — the "Changed
193
193
  * files" row's data. `diffLines` keeps the LATEST edit's diff (what a click on the file opens); the
194
194
  * +/- counts sum every edit to that path within the turn.
195
195
  */
@@ -221,7 +221,7 @@ function activeToolFromHistoricalState(state: IToolState): IActiveTool {
221
221
  }
222
222
 
223
223
  /**
224
- * #3288 §2: turns the server's chronological `IHistoryDisplaySegment[]` (a reload/reconnect/resume
224
+ * Turns the server's chronological `IHistoryDisplaySegment[]` (a reload/reconnect/resume
225
225
  * replay) into the SAME `TConversationEntry[]` shape a live turn's `finishTurn` produces — reusing its
226
226
  * exact segment-accumulation and "Changed files" logic, so the two paths cannot draw the turn/grouping
227
227
  * boundary differently. A `'text', role: 'user'` segment is both its own entry AND the turn boundary:
@@ -289,15 +289,15 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
289
289
  const [sessionName, setSessionName] = useState<string | null>(null);
290
290
  const [sessionNotices, setSessionNotices] = useState<readonly ISessionNotice[]>([]);
291
291
  const [commandCatalog, setCommandCatalog] = useState<TCommandCatalog | null>(null);
292
- // #3282 §4e: `send` (below) reads this on every `command` message to catch an excluded command —
292
+ // `send` (below) reads this on every `command` message to catch an excluded command —
293
293
  // a ref (not the state above) so `send`'s identity never changes as the catalog arrives or is
294
294
  // refreshed, matching every other value a stable callback in this file reads this way.
295
295
  const commandCatalogRef = useRef<TCommandCatalog | null>(null);
296
296
  const [sessionStatus, setSessionStatus] = useState<TSessionStatus | null>(null);
297
- // #3289 §3: learned from the first frame the server sends this connection, so its own messages and
297
+ // Learned from the first frame the server sends this connection, so its own messages and
298
298
  // prompts never carry a "from" label — only ANOTHER driver's do.
299
299
  const [ownDriverId, setOwnDriverId] = useState<TDriverId | null>(null);
300
- // #3282 §4e: `/help` opens the GUI's own Help sheet — it never reaches the session (`send` below
300
+ // `/help` opens the GUI's own Help sheet — it never reaches the session (`send` below
301
301
  // catches it), so there is no round trip and no stale terminal-style text dump to replace.
302
302
  const [helpOpen, setHelpOpen] = useState(false);
303
303
  const openHelp = useCallback((): void => setHelpOpen(true), []);
@@ -307,7 +307,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
307
307
  const streamingTextRef = useRef('');
308
308
  // The tools of the running turn, mirrored so the turn's end can keep them in the conversation.
309
309
  const activeToolsRef = useRef<IActiveTool[]>([]);
310
- // #3288: the running turn's text/tools in the order they happened — see `TTurnSegment` above.
310
+ // The running turn's text/tools in the order they happened — see `TTurnSegment` above.
311
311
  const turnSegmentsRef = useRef<TTurnSegment[]>([]);
312
312
  // A screen this surface's command asked for: it answers the command in place of the command's own
313
313
  // reply — with the "not available" line (`text`), or with nothing when the screen opened (null).
@@ -328,7 +328,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
328
328
  }, []);
329
329
  /**
330
330
  * End the running turn: its text and tool calls stay in the conversation as SEPARATE entries, in
331
- * the order they happened (#3288) — not one tools-block grouped ahead of a single merged reply.
331
+ * the order they happened — not one tools-block grouped ahead of a single merged reply.
332
332
  */
333
333
  const finishTurn = useCallback(
334
334
  (toolStatus: (tool: IActiveTool) => IActiveTool['status']): void => {
@@ -349,7 +349,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
349
349
  appendEntry({ id: segment.id, role: 'assistant', content: segment.text });
350
350
  }
351
351
  }
352
- // #3288: a turn that changed files ends with one compact summary row, after everything else.
352
+ // A turn that changed files ends with one compact summary row, after everything else.
353
353
  const changedFiles = collectChangedFiles(segments);
354
354
  if (changedFiles.length > 0) {
355
355
  appendEntry({ id: nextId(), role: 'changed-files', files: changedFiles });
@@ -359,7 +359,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
359
359
  );
360
360
  // Commands this surface sent whose result has not come back — a screen request pairs with one.
361
361
  const commandsInFlightRef = useRef(0);
362
- // #3282 §2 (part 2): the `requestId`s of commands sent via `sendCommandSilently`, still awaiting
362
+ // The `requestId`s of commands sent via `sendCommandSilently`, still awaiting
363
363
  // their `command_result`. The wire already correlates a command with its reply by `requestId` (see
364
364
  // `TClientMessage`'s `command` / `TServerMessage`'s `command_result` in `@robota-sdk/agent-transport`)
365
365
  // — a plain `send` command and a `sendCommandSilently` command can be outstanding at once, and only
@@ -389,12 +389,12 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
389
389
  setExecutionDetailInput({ taskId: msg.taskId, status: 'pending' });
390
390
  }
391
391
  if (msg.type === 'command') {
392
- // #3282 §4e: `/help` is the GUI's own Help sheet — never sent to the session.
392
+ // `/help` is the GUI's own Help sheet — never sent to the session.
393
393
  if (msg.name === 'help') {
394
394
  setHelpOpen(true);
395
395
  return;
396
396
  }
397
- // #3282 §4e: a command the GUI leaves out on purpose is caught here, before the session ever
397
+ // A command the GUI leaves out on purpose is caught here, before the session ever
398
398
  // sees it — its own plain sentence answers in place of the session's refusal, exactly like a
399
399
  // typed command's own result would (same entry shape, same place in the conversation). The
400
400
  // catalog entry (when known) carries `runner`/`surfaces`, so a client-only command excluded
@@ -429,7 +429,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
429
429
  if (requestId !== undefined) sessionChangeRequestIdsRef.current.delete(requestId);
430
430
  }, []);
431
431
  /**
432
- * #3282 §2 (part 2): run a command the same way `send` does, but flag it as controls-triggered —
432
+ * Run a command the same way `send` does, but flag it as controls-triggered —
433
433
  * the model/mode/effort pop-up menus use this instead of `send` directly, so their change is
434
434
  * confirmed by the control's own label (via the `get-status` refresh `command_result` already
435
435
  * triggers) rather than a conversation card. A typed `/command` still goes through plain `send`
@@ -438,7 +438,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
438
438
  * `TClientMessage` — giving `command` a `requestId` there would let ANY caller of `send` opt a typed
439
439
  * command into silence, which is exactly what this must not allow.
440
440
  *
441
- * #3282 §4e: `send` also intercepts `help` and an excluded command before either reaches the wire —
441
+ * `send` also intercepts `help` and an excluded command before either reaches the wire —
442
442
  * for `help` that opens the sheet regardless of how it was asked for, but an excluded command's
443
443
  * interception adds a VISIBLE info card, defeating the silence this function promises. Every current
444
444
  * caller (model/mode/effort) names neither, so this is dormant, not exercised — a future caller must
@@ -474,7 +474,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
474
474
  const { requestSessions, setSessionSidebarOpen } = sessionDirectoryState;
475
475
  const { handleSettingsMessage, openSettings, refreshSettings, ...settingsState } =
476
476
  useSettingsState(send);
477
- // #3282 §4b: read inside `handleMessage` via a ref, never `settingsState.settingsOpen` directly —
477
+ // Read inside `handleMessage` via a ref, never `settingsState.settingsOpen` directly —
478
478
  // that reactive value would have to sit in `handleMessage`'s own dependency array, which sits in
479
479
  // the connection effect's dependency array below: opening/closing Settings would give
480
480
  // `handleMessage` a new identity each time, tearing down and reopening the WebSocket underneath it.
@@ -496,7 +496,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
496
496
 
497
497
  const handleMessage = useCallback(
498
498
  (msg: TServerMessage): void => {
499
- // ARCH-2164: touching the exhaustive registry here keeps every decoded variant tied to an
499
+ // Touching the exhaustive registry here keeps every decoded variant tied to an
500
500
  // explicit GUI ownership decision, including variants intentionally handled by focused views.
501
501
  void SERVER_MESSAGE_HANDLING[msg.type];
502
502
  if (handleUsageMessage(msg)) return;
@@ -508,7 +508,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
508
508
  if (handleSessionsMessage(msg)) return;
509
509
  if (handleSettingsMessage(msg)) return;
510
510
  if (handleAgentDefinitionsMessage(msg)) return;
511
- // #3282 §4 part b-3: exclusively owns `background_tasks` (the Scheduled group's roster); a
511
+ // Exclusively owns `background_tasks` (the Scheduled group's roster); a
512
512
  // schedule write instead rides the generic `command`/`cancel-background-task` messages, so
513
513
  // for THOSE types this always returns false — it only refreshes on top of whatever else those
514
514
  // types already do below, never in place of it.
@@ -520,7 +520,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
520
520
  break;
521
521
  }
522
522
  case 'messages': {
523
- // #3288 §2: `display` is the server's projection of this same history into tool rows,
523
+ // `display` is the server's projection of this same history into tool rows,
524
524
  // diffs and "Changed files" — a reload/reconnect/resume replay renders THAT. `display` is
525
525
  // absent only for a host that predates it; that host still gets a working (text-only)
526
526
  // transcript, exactly what this branch always did.
@@ -553,7 +553,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
553
553
  const next = streamingTextRef.current + msg.delta;
554
554
  streamingTextRef.current = next;
555
555
  setStreamingText(next);
556
- // #3288: also track WHERE this text sits relative to the turn's tool calls, for finishTurn.
556
+ // Also track WHERE this text sits relative to the turn's tool calls, for finishTurn.
557
557
  turnSegmentsRef.current = appendTextDeltaToSegments(turnSegmentsRef.current, msg.delta);
558
558
  break;
559
559
  }
@@ -570,7 +570,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
570
570
  }
571
571
  case 'tool_end': {
572
572
  const { state } = msg;
573
- // #3288: attribute to the ONE matching running call (executionId-first) — never map over
573
+ // Attribute to the ONE matching running call (executionId-first) — never map over
574
574
  // every running same-named entry, which would close all of them at once.
575
575
  updateActiveTools((prev) => {
576
576
  const idx = findRunningToolIndex(prev, state);
@@ -587,7 +587,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
587
587
  setExecutionWorkspace(msg.snapshot);
588
588
  break;
589
589
  }
590
- // #3288 §1: a Stop that did not take effect (e.g. the task/loop had already finished) is
590
+ // A Stop that did not take effect (e.g. the task/loop had already finished) is
591
591
  // worth telling the operator; a success shows itself through the snapshot update above.
592
592
  case 'background_task_control_result': {
593
593
  if (msg.action === 'send' && executionDetailInputRef.current === msg.taskId) {
@@ -611,7 +611,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
611
611
  }
612
612
  break;
613
613
  }
614
- // #3280 §2: the prompt queued behind a running turn — shown above the composer, editable
614
+ // The prompt queued behind a running turn — shown above the composer, editable
615
615
  // and removable (`cancel-queue`). `pending: null` means nothing waits; the row disappears.
616
616
  case 'pending': {
617
617
  setQueuedPrompt(
@@ -622,12 +622,12 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
622
622
  case 'permission_request':
623
623
  case 'ask_request':
624
624
  case 'prompt_resolved': {
625
- // REMOTE-007/009: the paired owner renders + answers its own prompts (local == remote).
625
+ // The paired owner renders + answers its own prompts (local == remote).
626
626
  setPendingPrompts((prev) => applyPromptEvent(prev, msg));
627
627
  break;
628
628
  }
629
629
  case 'ui_intent': {
630
- // #3189: `/resume` asks for the session picker — the sidebar, when the host lists sessions.
630
+ // `/resume` asks for the session picker — the sidebar, when the host lists sessions.
631
631
  if (guiScreenForUiIntent(msg.event.intent) === 'session-sidebar' && canListSessions()) {
632
632
  setSessionSidebarOpen(true);
633
633
  requestSessions();
@@ -639,7 +639,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
639
639
  }
640
640
  break;
641
641
  }
642
- // #3282 §4a: `/settings` asks for the Settings screen — always available on this surface.
642
+ // `/settings` asks for the Settings screen — always available on this surface.
643
643
  if (guiScreenForUiIntent(msg.event.intent) === 'settings') {
644
644
  openSettings();
645
645
  if (commandsInFlightRef.current > 0) {
@@ -650,7 +650,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
650
650
  }
651
651
  break;
652
652
  }
653
- // #3282 §4 part b-2: `/plugin` asks for the plugin manager — the Settings screen's
653
+ // `/plugin` asks for the plugin manager — the Settings screen's
654
654
  // Plugins section, the way `show-settings` opens the screen itself above.
655
655
  if (guiScreenForUiIntent(msg.event.intent) === 'settings-plugins') {
656
656
  openSettings('plugins');
@@ -662,7 +662,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
662
662
  }
663
663
  break;
664
664
  }
665
- // #3282 §4 part b-3: `/agent` (bare) asks for the switcher — always available on this
665
+ // `/agent` (bare) asks for the switcher — always available on this
666
666
  // surface, unlike the session picker, which needs a host that lists sessions.
667
667
  if (guiScreenForUiIntent(msg.event.intent) === 'agent-switcher') {
668
668
  openAgentSwitcher();
@@ -674,8 +674,8 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
674
674
  }
675
675
  break;
676
676
  }
677
- // CMD-004 Stage D: a command this surface issued requested a screen the GUI does not have.
678
- // The command's result follows; the unavailable line answers it (TC-05, never silent).
677
+ // A command this surface issued requested a screen the GUI does not have.
678
+ // The command's result follows; the unavailable line answers it (never silent).
679
679
  const unavailable = {
680
680
  name: uiIntentCommandName(msg.event.intent),
681
681
  text: describeUiIntentForGui(msg.event.intent),
@@ -694,14 +694,14 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
694
694
  }
695
695
  break;
696
696
  }
697
- // CMD-004 Stage E: broadcast session events — a rename/clear executed by the HOST (from any
697
+ // Broadcast session events — a rename/clear executed by the HOST (from any
698
698
  // surface, co-driving included) is reflected here; never a silent drop.
699
699
  case 'session_renamed': {
700
700
  setSessionName(msg.event.name);
701
701
  requestSessions();
702
702
  break;
703
703
  }
704
- // #3189: the host made another session current — drop what this one showed, re-read it all.
704
+ // The host made another session current — drop what this one showed, re-read it all.
705
705
  case 'session_switched': {
706
706
  rejectPendingAppRequests('The active session changed.');
707
707
  settleSessionChange();
@@ -762,7 +762,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
762
762
  ]);
763
763
  break;
764
764
  }
765
- // #3189: a new or switch this surface asked for was refused; the reason is the toast, and
765
+ // A new or switch this surface asked for was refused; the reason is the toast, and
766
766
  // commands in flight are untouched. Only the client that asked is told.
767
767
  case 'session_change_failed': {
768
768
  if (
@@ -778,7 +778,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
778
778
  ]);
779
779
  break;
780
780
  }
781
- // #3289 §1: a row's rename landed — refresh the listing so its title (or the live one, if it
781
+ // A row's rename landed — refresh the listing so its title (or the live one, if it
782
782
  // was current) is current everywhere it shows.
783
783
  case 'session_renamed_in_list': {
784
784
  requestSessions();
@@ -791,7 +791,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
791
791
  ]);
792
792
  break;
793
793
  }
794
- // #3289 §1: a row was deleted. If it was this surface's own session, `session_switched`
794
+ // A row was deleted. If it was this surface's own session, `session_switched`
795
795
  // already arrived (the host switches away before answering) and reset the conversation; this
796
796
  // only needs the listing refreshed so the row is gone everywhere it shows.
797
797
  case 'session_deleted': {
@@ -880,7 +880,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
880
880
  send({ type: 'get-status' });
881
881
  send({ type: 'get-commands' });
882
882
  commandsInFlightRef.current = Math.max(0, commandsInFlightRef.current - 1);
883
- // #3282 §4b: a successful `/provider` command run through the raw command path (Add, Edit,
883
+ // A successful `/provider` command run through the raw command path (Add, Edit,
884
884
  // Duplicate — Use/Model/Delete already return a fresh snapshot from their own
885
885
  // `update-settings` reply) changes the "Providers & Models" list; refresh it if the screen
886
886
  // is open, the same way `get-status` above refreshes the model/mode chips. Never touches
@@ -888,13 +888,13 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
888
888
  if (msg.name === 'provider' && msg.success && settingsOpenRef.current) {
889
889
  refreshSettings();
890
890
  }
891
- // #3282 §2 (part 2): identify silence by THIS reply's own `requestId`, not by position or
891
+ // Identify silence by THIS reply's own `requestId`, not by position or
892
892
  // count — see `silentCommandRequestIdsRef` above. No `requestId` (an older host, or a typed
893
893
  // command) always falls through to `silent = false`, never the reverse.
894
894
  const silent =
895
895
  msg.requestId !== undefined && silentCommandRequestIdsRef.current.delete(msg.requestId);
896
- // #3282 §4 part b-3: a selection made in the agent switcher sheet — its own plain
897
- // confirmation line answers it, never a conversation card (issue #3282 §4's decided design).
896
+ // A selection made in the agent switcher sheet — its own plain
897
+ // confirmation line answers it, never a conversation card.
898
898
  if (resolveSwitchResult(msg.requestId, msg)) break;
899
899
  const unavailable = pendingIntentRef.current;
900
900
  pendingIntentRef.current = null;
@@ -913,7 +913,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
913
913
  }
914
914
  // A command that starts a turn (a skill) says nothing itself; the turn is its answer.
915
915
  if (msg.message.trim().length === 0) break;
916
- // #3282 §2 (part 2): a change made through a status control confirms itself through the
916
+ // A change made through a status control confirms itself through the
917
917
  // control's own label (the `get-status` refresh above lands it) — no conversation card. A
918
918
  // failure still needs saying, as a plain notice rather than a card, and the label is left
919
919
  // unchanged since nothing actually applied. A typed command (plain `send`) keeps its card
@@ -939,7 +939,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
939
939
  case 'complete':
940
940
  case 'interrupted': {
941
941
  send({ type: 'get-status' });
942
- // #3280 §2: the turn ending can advance the queue (its next entry now runs) or, if this
942
+ // The turn ending can advance the queue (its next entry now runs) or, if this
943
943
  // was an abort, drop it entirely (`abort()` clears the whole queue) — either way the
944
944
  // composer's queued-message row is stale until this reply refreshes it.
945
945
  send({ type: 'get-pending' });
@@ -1025,7 +1025,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
1025
1025
  client.send({ type: 'get-surface-role' });
1026
1026
  client.send({ type: 'get-commands' });
1027
1027
  client.send({ type: 'get-status' });
1028
- // #3280 §2: a reconnect that lands back on the SAME session (the common case) fires no
1028
+ // A reconnect that lands back on the SAME session (the common case) fires no
1029
1029
  // `session_switched` — its own `get-pending` would be missed — so ask here too, or a stale
1030
1030
  // `queuedPrompt` from before the drop keeps showing (with working Edit/Remove) until the
1031
1031
  // turn it was queued behind happens to end.
@@ -1108,7 +1108,7 @@ export function useSessionClient<TStatus extends string = TConnectionStatus>(
1108
1108
  export function useWsSession(
1109
1109
  url: string,
1110
1110
  ): IWsSessionState<TConnectionStatus> & { connectionLost: boolean } {
1111
- // Issue #3280 §5: retries gave up — the runtime is not coming back by itself. A presentation layer
1111
+ // Retries gave up — the runtime is not coming back by itself. A presentation layer
1112
1112
  // (e.g. `SessionSurface`) reads this to show a banner instead of a callback-driven screen swap.
1113
1113
  const [connectionLost, setConnectionLost] = useState(false);
1114
1114
  const makeClient = useCallback<TMakeSessionClient<TConnectionStatus>>(
package/src/index.ts CHANGED
@@ -23,7 +23,7 @@ export type {
23
23
  TSessionListing,
24
24
  TSessionsError,
25
25
  } from './hooks/session-client-types.js';
26
- // Issue #3280 §5: a host that can restart the runtime calls this before reloading the page, so the
26
+ // A host that can restart the runtime calls this before reloading the page, so the
27
27
  // first session listing after reconnecting can switch back to the session the person was in.
28
28
  export { rememberSessionForRestore } from './hooks/use-session-directory.js';
29
29
 
@@ -46,7 +46,7 @@ export type {
46
46
  export { applyPromptEvent, permissionResponse, askResponse } from './hooks/prompt-state.js';
47
47
  export type { TPendingPrompt } from './hooks/prompt-state.js';
48
48
 
49
- // ── UI-intent (command screen-request) state — CMD-004 Stage D ──
49
+ // ── UI-intent (command screen-request) state — ──
50
50
  export { describeUiIntentForGui, guiScreenForUiIntent } from './hooks/ui-intent-state.js';
51
51
 
52
52
  // ── Presentation components ─────────────────────────────────
@@ -60,12 +60,12 @@ export { SessionSurface, CenteredChrome } from './components/SessionSurface.js';
60
60
  export type { IComposerHandle, IPickedFile } from './components/Composer.js';
61
61
  export { SessionSidebar } from './components/SessionSidebar.js';
62
62
  export { SessionMonitor } from './components/SessionMonitor.js';
63
- // #3282 §4a: the shared modal-shell primitive (also consumed by #3282 §2's mode-change confirmation
64
- // and #3289 §1's session-delete confirmation) and the Settings screen it backs.
63
+ // The shared modal-shell primitive (also consumed by the mode-change confirmation
64
+ // and the session-delete confirmation) and the Settings screen it backs.
65
65
  export { Dialog, ConfirmDialog } from './components/Dialog.js';
66
66
  export type { IDialogProps, IConfirmDialogProps } from './components/Dialog.js';
67
67
  export { SettingsScreen } from './components/SettingsScreen.js';
68
- // #3282 §4e: the Help sheet `/help` opens instead of the terminal-style text list.
68
+ // The Help sheet `/help` opens instead of the terminal-style text list.
69
69
  export { HelpSheet } from './components/HelpSheet.js';
70
70
  // Pieces of a surface's chrome, not roots: render them inside a `agent-ui` scope.
71
71
  export { ProductMark, ProductWordmark } from './components/Brand.js';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * #3282 §4 part b-3 — plain-words presentation for the Agents panel's "Scheduled" group. Never an
2
+ * Plain-words presentation for the Agents panel's "Scheduled" group. Never an
3
3
  * ISO string or a cron expression: a concrete `nextFireAt` is shown in local time as "Today 9:00" /
4
4
  * "Tomorrow 9:00" / a weekday / a date; a handful of common cron shapes are phrased ("Every weekday
5
5
  * at 9:00"); anything else falls back to "Repeats".
@@ -216,7 +216,7 @@
216
216
  }
217
217
 
218
218
  /*
219
- * A code block's own horizontal scrollbar (#3289 §2) stays visible rather than only-on-hover like the
219
+ * A code block's own horizontal scrollbar stays visible rather than only-on-hover like the
220
220
  * rest of the page (`theme.css`'s quiet overlay scrollbar) — a long unwrapped line needs a standing
221
221
  * signal that there is more of it, not one that only appears once the person already found the edge.
222
222
  * `.gui-code-scroll::-webkit-scrollbar-thumb` outranks that page-wide bare `::-webkit-scrollbar-thumb`