@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
@@ -10,7 +10,7 @@ import type { IPopupMenuItem, IPopupMenuSection } from './PopupMenu.js';
10
10
  import type { TCommandCatalog, TModelListSnapshot, TSessionStatus } from '../hooks/session-client-types.js';
11
11
 
12
12
  /**
13
- * #3282 §2 (part 2) — the plain label for each effort selection. Kept in step with
13
+ * The plain label for each effort selection. Kept in step with
14
14
  * `EFFORT_LEVEL_LABELS` in `packages/agent-framework/src/effort/effort-resolution.ts` (which
15
15
  * `agent-command`'s `/effort` re-exports and reports as its own outcome text) — this package cannot
16
16
  * import that one directly (it is a Node-side package this browser bundle does not depend on), so the
@@ -28,7 +28,7 @@ const EFFORT_LEVEL_LABELS: Readonly<Record<TSessionStatus['effort'], string>> =
28
28
  max: 'Maximum',
29
29
  };
30
30
 
31
- /** The primary row (#3282 §2 design point 4); the rest sit under "More". */
31
+ /** The primary row; the rest sit under "More". */
32
32
  const EFFORT_PRIMARY: readonly TSessionStatus['effort'][] = ['auto', 'low', 'medium', 'high'];
33
33
  const EFFORT_MORE: readonly TSessionStatus['effort'][] = ['none', 'minimal', 'xhigh', 'max'];
34
34
 
@@ -48,7 +48,7 @@ const CHIP_CLASS =
48
48
  'flex min-w-0 items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-transparent';
49
49
  const CHIP_WARNING_CLASS = 'text-warning hover:text-warning';
50
50
 
51
- /** One status chip that opens a `PopupMenu` HIG pop-up beneath it (#3282 §2). */
51
+ /** One status chip that opens a `PopupMenu` HIG pop-up beneath it. */
52
52
  function StatusChip({
53
53
  chipLabel,
54
54
  value,
@@ -79,7 +79,7 @@ function StatusChip({
79
79
  aria-label={`${chipLabel}: ${value}`}
80
80
  aria-haspopup="menu"
81
81
  aria-expanded={open}
82
- // #3289 §2: below `md` the value text is hidden (icon-only) — the tooltip is where it stays
82
+ // Below `md` the value text is hidden (icon-only) — the tooltip is where it stays
83
83
  // reachable on hover and keyboard focus, so it always carries the value, not just "Change X".
84
84
  title={connected ? `${chipLabel}: ${value}` : 'Reconnecting…'}
85
85
  disabled={!connected}
@@ -90,7 +90,7 @@ function StatusChip({
90
90
  className={`${CHIP_CLASS} ${warning ? CHIP_WARNING_CLASS : ''}`}
91
91
  >
92
92
  {icon}
93
- {/* #3289 §2: below `md` (the same breakpoint the session-list sheet uses) the chip collapses
93
+ {/* Below `md` (the same breakpoint the session-list sheet uses) the chip collapses
94
94
  to its icon alone — never a truncated fragment ("d…", "claude-…") — the value stays
95
95
  reachable via the tooltip above and the accessible name either way. */}
96
96
  <span className="hidden truncate md:inline">{value}</span>
@@ -110,7 +110,7 @@ function StatusChip({
110
110
  const ICON_PROPS = { size: 14, strokeWidth: 1.75, 'aria-hidden': true } as const;
111
111
 
112
112
  /**
113
- * Model · mode · effort, each a pop-up menu (#3282 §2), and the context the conversation fills.
113
+ * Model · mode · effort, each a pop-up menu, and the context the conversation fills.
114
114
  * Every menu applies its choice through the same command wire path the equivalent typed slash
115
115
  * command uses — `onSilentCommand` — so `/model <id>`, `/mode <name>` and `/effort <value>` and a
116
116
  * click in these menus can never drift apart; the control's own chip label (fed by the session's next
@@ -129,14 +129,14 @@ export function StatusRow({
129
129
  catalog: TCommandCatalog | null;
130
130
  modelList: TModelListSnapshot | null;
131
131
  onRequestModelList: () => void;
132
- /** #3282 §4b: "Manage providers…" opens Settings at the Providers & Models section. */
132
+ /** "Manage providers…" opens Settings at the Providers & Models section. */
133
133
  onManageProviders: () => void;
134
134
  /** Applies a model/mode/effort choice without a conversation card (see the module doc). */
135
135
  onSilentCommand: (name: string, args?: string) => void;
136
136
  connected: boolean;
137
137
  }): React.ReactElement {
138
138
  const used = status ? Math.round(status.context.usedPercentage) : null;
139
- // #3282 §2: "Skip all checks" asks for confirmation on the shared `ConfirmDialog` (#3331) before
139
+ // "Skip all checks" asks for confirmation on the shared `ConfirmDialog` before
140
140
  // applying — every other mode applies at once. `true` while that confirmation is open.
141
141
  const [pendingBypassConfirm, setPendingBypassConfirm] = useState(false);
142
142
 
@@ -158,7 +158,7 @@ export function StatusRow({
158
158
  key: 'manage-providers',
159
159
  label: 'Manage providers…',
160
160
  kind: 'action',
161
- // #3282 §4b: opens Settings at the "Providers & Models" section — the same list, Use/Model/
161
+ // Opens Settings at the "Providers & Models" section — the same list, Use/Model/
162
162
  // Edit/Test/Duplicate/Delete actions and "Add provider…" flow now live there instead of in
163
163
  // this menu's own server-driven picker.
164
164
  onSelect: onManageProviders,
@@ -175,7 +175,7 @@ export function StatusRow({
175
175
  label: sub.displayName ?? sub.name,
176
176
  description: sub.description,
177
177
  checked: sub.name === status?.permissionMode,
178
- // #3282 §2: every mode but "Skip all checks" applies at once; that one opens a
178
+ // Every mode but "Skip all checks" applies at once; that one opens a
179
179
  // confirmation first (rendered below) instead of applying here directly.
180
180
  onSelect:
181
181
  sub.name === 'bypassPermissions'
@@ -253,8 +253,8 @@ export function StatusRow({
253
253
  <ContextRing percent={used ?? 0} />
254
254
  {used === null ? '' : `${used}%`}
255
255
  </span>
256
- {/* #3282 §2: matches the wording the Settings screen's own permission-mode picker already
257
- ships for the identical action (#3331's `SettingsScreen.tsx`), so switching to "Skip all
256
+ {/* Matches the wording the Settings screen's own permission-mode picker already
257
+ ships for the identical action (`SettingsScreen.tsx`), so switching to "Skip all
258
258
  checks" reads the same warning regardless of which control was used. */}
259
259
  <ConfirmDialog
260
260
  open={pendingBypassConfirm}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * #3282 §4d — pure logic behind the composer's attach button and drag-and-drop, kept apart from
2
+ * Pure logic behind the composer's attach button and drag-and-drop, kept apart from
3
3
  * `Composer.tsx` so it is testable without React or a DOM.
4
4
  *
5
5
  * The runtime resolves `@relative/path` references already (`prompt-file-reference-resolver.ts` in
@@ -26,7 +26,7 @@ export const MAX_ATTACHMENT_TOTAL_BYTES = 256 * 1024;
26
26
  */
27
27
  export const MAX_ATTACHMENT_COUNT = 8;
28
28
 
29
- /** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3 (#3282 §4). */
29
+ /** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3. */
30
30
  export const ATTACHMENT_NO_PATH_NOTICE = 'Only files inside this project folder can be attached.';
31
31
 
32
32
  /** Shown once the running count of attachments would pass `MAX_ATTACHMENT_COUNT`. */
@@ -50,7 +50,7 @@ export interface IDraftAttachment {
50
50
  }
51
51
 
52
52
  /**
53
- * One file the desktop host's native "Attach files" dialog returned (#3282 §4d). Kept in structural
53
+ * One file the desktop host's native "Attach files" dialog returned. Kept in structural
54
54
  * sync with `IPickedFile` in `apps/agent-gui-web/src/gui-host.ts` and
55
55
  * `apps/agent-app/electron/sidecar.ts` — the three are not imported across the Electron/browser/
56
56
  * presentation package boundaries, same as this codebase already does for the trust question.
@@ -228,11 +228,11 @@ export function evaluateCandidateFile(
228
228
  }
229
229
 
230
230
  /**
231
- * Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2 (#3282 §4).
231
+ * Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2.
232
232
  * Always prefixed with `./`: the parser's `isPathLikeReference` only treats a token as a file
233
233
  * reference when it has a `.` somewhere or an explicit relative-path prefix, so a dotless name like
234
234
  * `Makefile`, `Dockerfile` or `LICENSE` — common, useful, and never refused for lacking one — would
235
- * otherwise be sent as plain text with no file and no diagnostic (follow-up to #3282 §4d). `./` makes
235
+ * otherwise be sent as plain text with no file and no diagnostic. `./` makes
236
236
  * every path path-like regardless of its name, and the resolver collapses it back to the plain
237
237
  * relative path before use.
238
238
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * #3289 §3 — plain, human labels for a REMOTE-014 E5 driver id, and whether one names the SAME KIND
2
+ * Plain, human labels for a driver id, and whether one names the SAME KIND
3
3
  * of surface as this connection's own. Shared by every place the GUI shows who drove a turn (a
4
4
  * message, a permission/ask prompt): the person's own input never carries a label, and a turn from a
5
5
  * different kind of surface does — in words, never a raw id.
@@ -16,7 +16,7 @@ import { AGENT_DRIVER_ID, OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-se
16
16
  import type { TDriverId } from '@robota-sdk/agent-interface-session';
17
17
 
18
18
  /**
19
- * `remote` (a plain WS remote connection's own id, `'remote:ws'`) and `peer` (an unrelated REMOTE-014
19
+ * `remote` (a plain WS remote connection's own id, `'remote:ws'`) and `peer` (an unrelated
20
20
  * mesh peer's turn, `'peer:<sessionId>'`) are kept as DIFFERENT kinds even though both render the same
21
21
  * label text: a plain WS viewer's own id is `'remote:ws'`, and folding a peer's turn into that same
22
22
  * kind would make `isSameSurface` treat a co-driver's turn as this connection's own and hide it — the
@@ -76,7 +76,7 @@ export function humanDriverLabel(driverId: TDriverId): string {
76
76
 
77
77
  /**
78
78
  * The ready-to-render attribution phrase: "from <label>" for a co-driver, or "Automatic — loop"
79
- * for the agent's own wake-ups — "from automatic" reads as a typo, not a source. #3288 §1: every
79
+ * for the agent's own wake-ups — "from automatic" reads as a typo, not a source. Every
80
80
  * `AGENT_DRIVER_ID` turn is, today, a loop's own injected iteration (fixed or self-paced —
81
81
  * `requestWakeup` in `interactive-session.ts` is the one place that stamps this id); if a
82
82
  * differently-sourced agent-wakeup turn is ever added, this label needs to name that source
@@ -1,5 +1,5 @@
1
1
  /**
2
- * #3288 §1: one true-status label for an execution-workspace entry, shared by the Agents panel row
2
+ * One true-status label for an execution-workspace entry, shared by the Agents panel row
3
3
  * and its detail sheet so the two never say something different about the same entry.
4
4
  *
5
5
  * A completed task whose result still reports a refused tool call is not "Done" — it is "Needs
package/src/goal-view.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * #3282 §4 part b-3 — the Agents panel's Goal row: the objective, and its status in plain words.
2
+ * The Agents panel's Goal row: the objective, and its status in plain words.
3
3
  */
4
4
 
5
5
  import type { IGoalState } from '@robota-sdk/agent-interface-session';
@@ -1,8 +1,8 @@
1
1
  /**
2
- * #3186 — what the composer's `/` menu offers for a draft. Pure, so the matching is testable without
2
+ * What the composer's `/` menu offers for a draft. Pure, so the matching is testable without
3
3
  * rendering: the TUI's autocomplete offers commands and skills the same way.
4
4
  *
5
- * #3282 §4e: a command the GUI cannot run at all (`isExcludedCommand`) never appears here — there is
5
+ * A command the GUI cannot run at all (`isExcludedCommand`) never appears here — there is
6
6
  * nothing useful for choosing it to do, and typing it by hand is answered separately (see
7
7
  * `excluded-commands.ts`, `useSessionClient.ts`). Commands are listed before skills, each in its own
8
8
  * group, so the menu can head them "Commands" and "Skills".
@@ -1,9 +1,9 @@
1
1
  /**
2
- * #3282 §4e — built-in commands left out of the GUI on purpose (the design decisions in
2
+ * Built-in commands left out of the GUI on purpose (the design decisions in
3
3
  * `packages/agent-gui-web/docs/SPEC.md` group them by reason). The `/` menu never lists one of these
4
4
  * (`command-menu.ts`), and typing one by hand anyway is caught before it reaches the session
5
5
  * (`useSessionClient.ts`'s `send`) and answered with the plain sentence here — never the session's own
6
- * refusal, and never the generic "not available on this surface" line CMD-004 uses for a screen this
6
+ * refusal, and never the generic "not available on this surface" line used for a screen this
7
7
  * surface simply has not built yet (see `ui-intent-state.ts`).
8
8
  */
9
9
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Pure prompt-state derivation for the web UI (REMOTE-009 Step 4 / REMOTE-007 render+answer).
2
+ * Pure prompt-state derivation for the web UI (render + answer).
3
3
  *
4
4
  * The paired browser is the session OWNER (local == remote), so it must render and ANSWER the owner's own
5
5
  * permission/ask prompts. The server carries `permission_request`/`ask_request` (a prompt appears) and
@@ -32,9 +32,9 @@ export function applyPromptEvent(
32
32
  ): readonly TPendingPrompt[] {
33
33
  switch (msg.type) {
34
34
  case 'permission_request': {
35
- // REMOTE-014 E5: keep requesterDriverId so the surface can show WHICH driver's turn raised the
36
- // prompt. Issue #3288 §1: keep `requester` too, so it can show WHICH background agent did.
37
- // #3288 §2: diffLines/diffFile (Edit/Write) and cwd (Shell, only when it differs) are the
35
+ // Keep requesterDriverId so the surface can show WHICH driver's turn raised the
36
+ // prompt. Keep `requester` too, so it can show WHICH background agent did.
37
+ // diffLines/diffFile (Edit/Write) and cwd (Shell, only when it differs) are the
38
38
  // server's diff/command preview — carried through unchanged so the prompt can render them
39
39
  // instead of raw args.
40
40
  const { id, toolName, toolArgs, requesterDriverId, requester, diffLines, diffFile, cwd } =
@@ -4,7 +4,7 @@ export type TServerMessageHandling =
4
4
  'reducer-state' | 'visible-notice' | 'intentionally-not-rendered' | 'transport-control';
5
5
 
6
6
  /**
7
- * ARCH-2164: exhaustive ownership for every decoded server variant on the shared GUI surface.
7
+ * Exhaustive ownership for every decoded server variant on the shared GUI surface.
8
8
  * Adding a wire variant without deciding its GUI disposition is a compile error instead of a silent
9
9
  * reducer omission. `intentionally-not-rendered` is an explicit disposition for server-side query
10
10
  * snapshots and domain events that this session/usage surface does not currently visualize.
@@ -28,10 +28,10 @@ export const SERVER_MESSAGE_HANDLING = {
28
28
  turn_source: 'intentionally-not-rendered',
29
29
  commands: 'reducer-state',
30
30
  session_status: 'reducer-state',
31
- // #3282 §2 part 2: the reply to `list-models`, fed into `modelList` state by the model control's
31
+ // The reply to `list-models`, fed into `modelList` state by the model control's
32
32
  // pop-up menu (`useModelListState`).
33
33
  model_list: 'reducer-state',
34
- // #3282 §4 part b-3: the agent switcher sheet's own correlated request/response state (`useAgentSwitcherState`).
34
+ // The agent switcher sheet's own correlated request/response state (`useAgentSwitcherState`).
35
35
  agent_definitions: 'reducer-state',
36
36
  sessions: 'reducer-state',
37
37
  sessions_error: 'reducer-state',
@@ -47,10 +47,10 @@ export const SERVER_MESSAGE_HANDLING = {
47
47
  stored_session_usage_report: 'reducer-state',
48
48
  stored_session_usage_report_error: 'visible-notice',
49
49
  executing: 'intentionally-not-rendered',
50
- // #3280 §2: the prompt queued behind a running turn — shown above the composer with Edit/Remove.
50
+ // The prompt queued behind a running turn — shown above the composer with Edit/Remove.
51
51
  pending: 'reducer-state',
52
52
  execution_workspace_event: 'reducer-state',
53
- // #3288 §1: the Agents panel's detail sheet reads a page at a time.
53
+ // The Agents panel's detail sheet reads a page at a time.
54
54
  execution_detail: 'reducer-state',
55
55
  execution_detail_error: 'reducer-state',
56
56
  // Esc's loop stop is a TUI-only shortcut; this surface's loops stop through the Agents panel
@@ -58,7 +58,7 @@ export const SERVER_MESSAGE_HANDLING = {
58
58
  waiting_loop_stop: 'intentionally-not-rendered',
59
59
  // The Agents panel already reflects a task's cancel/close/send through `execution_workspace_event`;
60
60
  // nothing here renders this narrower, task-only event on its own.
61
- // #3282 §4c: the Project panel's reads — reduced into state by `useProjectPanelState`.
61
+ // The Project panel's reads — reduced into state by `useProjectPanelState`.
62
62
  project_status: 'reducer-state',
63
63
  project_diff: 'reducer-state',
64
64
  project_memory: 'reducer-state',
@@ -70,7 +70,7 @@ export const SERVER_MESSAGE_HANDLING = {
70
70
  plan_event: 'intentionally-not-rendered',
71
71
  context_file_refreshed: 'intentionally-not-rendered',
72
72
  branch_event: 'intentionally-not-rendered',
73
- // #3282 §4 part b-3: the Agents panel's "Scheduled" group's own roster (`useSchedulesState`).
73
+ // The Agents panel's "Scheduled" group's own roster (`useSchedulesState`).
74
74
  background_tasks: 'reducer-state',
75
75
  background_task: 'intentionally-not-rendered',
76
76
  background_job_groups: 'intentionally-not-rendered',
@@ -82,16 +82,16 @@ export const SERVER_MESSAGE_HANDLING = {
82
82
  ui_intent: 'reducer-state',
83
83
  session_renamed: 'reducer-state',
84
84
  history_cleared: 'reducer-state',
85
- // #3288 §1: a failed Stop (e.g. a task that finished a moment before it arrived) is worth telling
86
- // the operator; a success shows itself through the workspace snapshot's own update. #3282 §4 part
87
- // b-3: a schedule's Delete uses this same `cancel-background-task` write — `useSchedulesState`
85
+ // A failed Stop (e.g. a task that finished a moment before it arrived) is worth telling
86
+ // the operator; a success shows itself through the workspace snapshot's own update. A schedule's
87
+ // Delete uses this same `cancel-background-task` write — `useSchedulesState`
88
88
  // refreshes the roster on a successful cancel, and a failure gets the same visible notice as Stop.
89
89
  background_task_control_result: 'visible-notice',
90
90
  protocol_error: 'visible-notice',
91
91
  resume_gap: 'transport-control',
92
92
  // The connection's effective role: the session client holds it and stops sending what an observer may not.
93
93
  surface_role: 'reducer-state',
94
- // #3282 §4a: the Settings screen's own correlated request/response state (`useSettingsState`).
94
+ // The Settings screen's own correlated request/response state (`useSettingsState`).
95
95
  settings: 'reducer-state',
96
96
  settings_error: 'reducer-state',
97
97
  } as const satisfies Readonly<Record<TServerMessage['type'], TServerMessageHandling>>;
@@ -38,7 +38,7 @@ export type TCommandCatalog = Omit<Extract<TServerMessage, { type: 'commands' }>
38
38
  export type TSessionStatus = Extract<TServerMessage, { type: 'session_status' }>['status'];
39
39
 
40
40
  /**
41
- * #3282 §2 (part 2): the models a person may switch to, grouped by configured provider profile —
41
+ * The models a person may switch to, grouped by configured provider profile —
42
42
  * what the model control's pop-up menu is built from. `null` until requested (`list-models`).
43
43
  */
44
44
  export type TModelListSnapshot = Omit<
@@ -59,7 +59,7 @@ export type TCurrentSessionUsageReport = Extract<
59
59
  { type: 'usage_report' }
60
60
  >['report'];
61
61
 
62
- /** #3282 §4c — the Project panel's reads. Each result crosses the wire unchanged from the session
62
+ /** The Project panel's reads. Each result crosses the wire unchanged from the session
63
63
  * method's own return value (see `wire-messages.ts`'s doc comment on `project_status`). */
64
64
  export type TProjectStatusRead = Extract<TServerMessage, { type: 'project_status' }>['result'];
65
65
  export type TProjectDiffRead = Extract<TServerMessage, { type: 'project_diff' }>['result'];
@@ -70,7 +70,7 @@ export interface IConversationMessage {
70
70
  role: 'user' | 'assistant';
71
71
  content: string;
72
72
  isStreaming?: boolean;
73
- /** REMOTE-014 E5: the co-driving author of a user turn. */
73
+ /** The co-driving author of a user turn. */
74
74
  author?: string;
75
75
  }
76
76
 
@@ -101,7 +101,7 @@ export interface IChangedFileSummary {
101
101
  diffLines: readonly IDiffLine[];
102
102
  }
103
103
 
104
- /** #3288: a turn that changed files ends with this compact summary row. */
104
+ /** A turn that changed files ends with this compact summary row. */
105
105
  export interface IChangedFilesEntry {
106
106
  id: string;
107
107
  role: 'changed-files';
@@ -117,28 +117,28 @@ export interface IActiveTool {
117
117
  status: 'running' | 'done' | 'error';
118
118
  input?: string;
119
119
  result?: IToolState['result'];
120
- /** #3288: correlates this call across `tool_start`/`tool_end` — how a finished call is matched. */
120
+ /** Correlates this call across `tool_start`/`tool_end` — how a finished call is matched. */
121
121
  executionId?: string;
122
- /** #3288: a unified diff (Edit/Write), built server-side — the same one an approval prompt showed. */
122
+ /** A unified diff (Edit/Write), built server-side — the same one an approval prompt showed. */
123
123
  diffLines?: readonly IDiffLine[];
124
- /** #3288: the file `diffLines` concerns, workspace-relative when the server could make it so. */
124
+ /** The file `diffLines` concerns, workspace-relative when the server could make it so. */
125
125
  diffFile?: string;
126
- /** #3288: the tool's raw result payload (Shell output, Read content, …), already capped server-side. */
126
+ /** The tool's raw result payload (Shell output, Read content, …), already capped server-side. */
127
127
  toolResultData?: string;
128
128
  /** Admitted observations; opaque resource URIs do not authorize a client-side fetch. */
129
129
  toolResultParts?: IToolState['toolResultParts'];
130
- /** #3288: a workspace-relative display form of a path argument, additive to `input`/`firstArg`. */
130
+ /** A workspace-relative display form of a path argument, additive to `input`/`firstArg`. */
131
131
  displayPath?: string;
132
- /** #3288: set when this call is a projected `/command` tool — render "Ran /<commandName>". */
132
+ /** Set when this call is a projected `/command` tool — render "Ran /<commandName>". */
133
133
  commandName?: string;
134
- /** #3288: true for an internal signal tool (e.g. goal-status) — never rendered as a call. */
134
+ /** True for an internal signal tool (e.g. goal-status) — never rendered as a call. */
135
135
  internal?: boolean;
136
136
 
137
137
  appView?: IToolAppViewRef;
138
138
  }
139
139
 
140
140
  /**
141
- * #3280 §2: a prompt the host queued behind the running turn — the server has already taken it, so it
141
+ * A prompt the host queued behind the running turn — the server has already taken it, so it
142
142
  * is not lost, but it will not run until the turn ends. `count` is the TOTAL number waiting (the shown
143
143
  * `text` plus any behind it), matching the wire's `pendingCount`.
144
144
  */
@@ -152,7 +152,7 @@ export interface ISessionNotice {
152
152
  /**
153
153
  * `session-change-refused`: the host refused a new or switch this surface asked for.
154
154
  * `background-task-control-failed`: a Stop in the Agents panel did not take effect.
155
- * `command-failed` (#3282 §2 part 2): a change made through a status control (model/mode/effort)
155
+ * `command-failed`: a change made through a status control (model/mode/effort)
156
156
  * failed — a plain notice rather than a conversation card, since the control's own label is where
157
157
  * a successful change confirms itself and the label stays unchanged when it fails.
158
158
  */
@@ -165,7 +165,7 @@ export interface ISessionNotice {
165
165
  | 'context-compacted';
166
166
  message: string;
167
167
  /**
168
- * A `session-error` classified at the wire boundary (#3289 §3) — present only when the host could
168
+ * A `session-error` classified at the wire boundary — present only when the host could
169
169
  * tell what kind of failure this was. `SessionNotices` maps it to one plain sentence and keeps
170
170
  * `message` behind a "Details" disclosure; absent, it shows `message` as before.
171
171
  */
@@ -195,7 +195,7 @@ export type TMakeSessionClient<TStatus extends string = TConnectionStatus> = (ca
195
195
  export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
196
196
  status: TStatus;
197
197
  /**
198
- * Set by `useWsSession` once its reconnect retries give up (issue #3280 §5): the runtime is not
198
+ * Set by `useWsSession` once its reconnect retries give up: the runtime is not
199
199
  * coming back by itself. Cleared again once `status` reaches `connected`. Undefined for a reducer
200
200
  * with no such concept (e.g. the WebRTC surface, which has its own `failed`/`refused` statuses).
201
201
  */
@@ -215,7 +215,7 @@ export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
215
215
  /** Null until the session has answered; refreshed after every command and turn. */
216
216
  sessionStatus: TSessionStatus | null;
217
217
  /**
218
- * #3282 §2 (part 2): the models the model control's pop-up menu offers. Null until requested — the
218
+ * The models the model control's pop-up menu offers. Null until requested — the
219
219
  * menu asks for it when it opens (or refreshes it) rather than every surface fetching it eagerly.
220
220
  */
221
221
  modelList: TModelListSnapshot | null;
@@ -239,13 +239,13 @@ export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
239
239
  setSessionSidebarOpen: (open: boolean) => void;
240
240
  send: (msg: TClientMessage) => void;
241
241
  /**
242
- * #3282 §2 (part 2): run a command the SAME way `send({type:'command', ...})` does, but mark its
242
+ * Run a command the SAME way `send({type:'command', ...})` does, but mark its
243
243
  * outcome as controls-triggered — `useSessionClient` skips the conversation card for a change made
244
244
  * this way (the control's own label confirms it instead), while a typed command keeps its card.
245
245
  */
246
246
  sendCommandSilently: (name: string, args?: string) => void;
247
247
  pendingPrompts: readonly TPendingPrompt[];
248
- /** #3280 §2: the prompt queued behind a running turn, or null when none is queued. */
248
+ /** The prompt queued behind a running turn, or null when none is queued. */
249
249
  queuedPrompt: IQueuedPrompt | null;
250
250
  answerPermission: (id: string, result: TPermissionResultValue) => void;
251
251
  answerAsk: (id: string, response: TActionResponse) => void;
@@ -263,29 +263,29 @@ export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
263
263
  requestCurrentSessionUsage: () => void;
264
264
  sessionNotices: readonly ISessionNotice[];
265
265
  dismissSessionNotice: (id: string) => void;
266
- /** #3282 §4a: whether the Settings modal is shown. `/settings` and the gear button both open it. */
266
+ /** Whether the Settings modal is shown. `/settings` and the gear button both open it. */
267
267
  settingsOpen: boolean;
268
268
  settingsStatus: 'idle' | 'loading' | 'ready' | 'error';
269
269
  /** Null until the first `get-settings` reply arrives. */
270
270
  settingsSnapshot: ISettingsSnapshot | null;
271
271
  /** The plain message from the most recent failed read or write, if any. */
272
272
  settingsError: string | null;
273
- /** #3282 §4 part b-2: which section the screen should land on for the open in progress. */
273
+ /** Which section the screen should land on for the open in progress. */
274
274
  settingsInitialSectionId: string | null;
275
275
  /**
276
276
  * Opens the screen and (re-)fetches its snapshot — the "reopen and see the new value" path.
277
277
  * `sectionId` jumps straight to that section (e.g. `/plugin` → `'plugins'`, "Manage providers…"
278
- * (#3282 §2/§4b) → `'providers'`); omitted opens on the screen's own default (General).
278
+ * → `'providers'`); omitted opens on the screen's own default (General).
279
279
  */
280
280
  openSettings: (sectionId?: string) => void;
281
281
  closeSettings: () => void;
282
282
  /** A failed write leaves `settingsSnapshot` at its previous value; `settingsError` names why. */
283
283
  updateSettings: (patch: TSettingsPatch) => void;
284
- /** #3282 §4e: whether the Help sheet is shown. `/help` opens it instead of reaching the session. */
284
+ /** Whether the Help sheet is shown. `/help` opens it instead of reaching the session. */
285
285
  helpOpen: boolean;
286
286
  openHelp: () => void;
287
287
  closeHelp: () => void;
288
- /** #3288 §1: the id of the execution-workspace entry open in the detail sheet, or null when closed. */
288
+ /** The id of the execution-workspace entry open in the detail sheet, or null when closed. */
289
289
  openEntryId: string | null;
290
290
  executionDetailStatus: 'idle' | 'loading' | 'ready' | 'error';
291
291
  executionDetailRecords: readonly IExecutionDetailRecord[];
@@ -300,21 +300,21 @@ export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
300
300
  openExecutionDetail: (entryId: string) => void;
301
301
  loadMoreExecutionDetail: () => void;
302
302
  closeExecutionDetail: () => void;
303
- /** #3282 §4c — the Project panel's "Changes" section (git status). */
303
+ /** The Project panel's "Changes" section (git status). */
304
304
  projectStatusState: 'idle' | 'loading' | 'ready' | 'error';
305
305
  projectStatus: TProjectStatusRead | null;
306
306
  requestProjectStatus: () => void;
307
- /** #3282 §4c — the Project panel's "File diff" section, for the file `projectDiffPath` names. */
307
+ /** The Project panel's "File diff" section, for the file `projectDiffPath` names. */
308
308
  projectDiffState: 'idle' | 'loading' | 'ready' | 'error';
309
309
  projectDiffPath: string | null;
310
310
  projectDiff: TProjectDiffRead | null;
311
311
  requestProjectDiff: (path: string) => void;
312
- /** #3282 §4c — the Project panel's "Memory" section. */
312
+ /** The Project panel's "Memory" section. */
313
313
  projectMemoryState: 'idle' | 'loading' | 'ready' | 'error';
314
314
  projectMemory: TProjectMemoryRead | null;
315
315
  requestProjectMemory: () => void;
316
316
  /**
317
- * #3282 §4 part b-3: whether the agent switcher sheet is shown. `/agent` (bare) and this surface's
317
+ * Whether the agent switcher sheet is shown. `/agent` (bare) and this surface's
318
318
  * own control both open it.
319
319
  */
320
320
  agentSwitcherOpen: boolean;
@@ -331,7 +331,7 @@ export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
331
331
  /** Runs the same path as `/agent <name>`. */
332
332
  selectAgent: (agentType: string) => void;
333
333
  /**
334
- * #3282 §4 part b-3: the schedules shown in the Agents panel's "Scheduled" group — every
334
+ * The schedules shown in the Agents panel's "Scheduled" group — every
335
335
  * `IBackgroundTaskState` of `kind: 'scheduled'`, refreshed on connect and after a write.
336
336
  */
337
337
  scheduledTasks: readonly IBackgroundTaskState<'scheduled'>[];
@@ -340,7 +340,7 @@ export interface IWsSessionState<TStatus extends string = TConnectionStatus> {
340
340
  resumeSchedule: (taskId: string) => void;
341
341
  /**
342
342
  * Permanently stops a schedule — the same `cancel-background-task` write Stop already uses. A
343
- * failure surfaces the same way a failed Stop does (#3288 §1's `background-task-control-failed`
343
+ * failure surfaces the same way a failed Stop does (the `background-task-control-failed`
344
344
  * session notice) — no separate error state here.
345
345
  */
346
346
  deleteSchedule: (taskId: string) => void;
@@ -1,10 +1,10 @@
1
1
  /**
2
- * CMD-004 Stage D — how the GUI surface answers a `ui_intent`.
2
+ * How the GUI surface answers a `ui_intent`.
3
3
  *
4
4
  * The server requester-routes `ui_intent`, so everything that arrives here was asked for by THIS
5
5
  * surface and must be answered visibly. An intent with a GUI screen opens it (the session picker is
6
- * the session sidebar, #3189; settings and the plugin manager both open the Settings modal, #3282
7
- * §4a/§4 part b-2; the agent switcher is a sheet, #3282 §4 part b-3). #3282 §4e: an intent whose
6
+ * the session sidebar; settings and the plugin manager both open the Settings modal; the agent
7
+ * switcher is a sheet). An intent whose
8
8
  * command is on the GUI's exclusion list never reaches here at all (`excluded-commands.ts` catches it
9
9
  * before the session is asked) — this module only answers a screen this HOST cannot back even though
10
10
  * the GUI knows how to render it (the session picker, when the host cannot list sessions), or a kind
@@ -40,8 +40,8 @@ export function guiScreenForUiIntent(
40
40
  ): 'session-sidebar' | 'settings' | 'settings-plugins' | 'agent-switcher' | null {
41
41
  if (intent.type === 'show-session-picker') return 'session-sidebar';
42
42
  if (intent.type === 'show-settings') return 'settings';
43
- // #3282 §4 part b-2: `/plugin` opens the Settings screen's Plugins section, the way #3331 mapped
44
- // `show-settings` — replacing the "not available on this surface" line this intent used to get.
43
+ // `/plugin` opens the Settings screen's Plugins section, the way `show-settings`
44
+ // does.
45
45
  if (intent.type === 'show-plugin-manager') return 'settings-plugins';
46
46
  if (intent.type === 'show-agent-switcher') return 'agent-switcher';
47
47
  return null;
@@ -51,7 +51,7 @@ export function guiScreenForUiIntent(
51
51
  * Describe one intent for this surface: a plain, honest sentence for a screen this HOST cannot back
52
52
  * right now (a session picker with no session list), the `/theme` exclusion this intent defensively
53
53
  * reuses, or an unrecognized future kind. Never called for `show-settings`, `show-plugin-manager` or
54
- * `show-agent-switcher` — all three always have a screen now (#3282 §4a, §4 part b-2, §4 part b-3) —
54
+ * `show-agent-switcher` — all three always have a screen now —
55
55
  * but their cases are left out of this switch rather than kept as dead branches that would claim
56
56
  * otherwise; an unknown wire-level kind (a newer host) still yields an explicit notice naming the raw
57
57
  * kind, so the "never a silent no-op" floor holds for future intents too.
@@ -24,10 +24,10 @@ function nextRequestId(prefix: string): string {
24
24
  }
25
25
 
26
26
  /**
27
- * #3282 §4 part b-3: the agent switcher sheet's own correlated request/response state, the same
27
+ * The agent switcher sheet's own correlated request/response state, the same
28
28
  * shape `useSettingsState` already uses for `get-settings`/`update-settings` — independent from the
29
29
  * chat conversation, because choosing an agent is answered by the sheet's own confirmation line
30
- * ("Default agent: Explore"), never a conversation card (issue #3282 §4's decided design).
30
+ * ("Default agent: Explore"), never a conversation card.
31
31
  *
32
32
  * `openAgentSwitcher` re-fetches every time (`/agent` bare, or reopening after a close), so a change
33
33
  * made elsewhere reaches an open-then-reopened sheet, matching `useSettingsState`'s own reasoning.
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
 
3
3
  /**
4
- * Tracks whether `query` currently matches, reactively (#3289 §2 — the narrow-window session sheet
4
+ * Tracks whether `query` currently matches, reactively (the narrow-window session sheet
5
5
  * below `md`). A host without `window.matchMedia` (an older embedding, or a non-browser test) reports
6
6
  * `false` and never subscribes, rather than throwing.
7
7
  */
@@ -13,7 +13,7 @@ function nextRequestId(): string {
13
13
  }
14
14
 
15
15
  /**
16
- * #3282 §2 (part 2) — the model list the model control's pop-up menu is built from, requested on
16
+ * The model list the model control's pop-up menu is built from, requested on
17
17
  * demand (`list-models`) and correlated by `requestId` (mirrors `usePersonalUsageState`'s pattern) so
18
18
  * a reply to an older request — one already superseded by a newer one, e.g. across a reconnect — is
19
19
  * never applied over the list a still-open menu is currently showing.
@@ -25,7 +25,7 @@ function nextRequestId(prefix: string): string {
25
25
  }
26
26
 
27
27
  /**
28
- * #3282 §4c — the Project panel's correlated request/response state (status, one file's diff, project
28
+ * The Project panel's correlated request/response state (status, one file's diff, project
29
29
  * memory), independent from chat state. Mirrors `usePersonalUsageState`'s shape: a status per read, a
30
30
  * `requestId` ref that drops a stale reply (a diff request superseded by clicking another file before
31
31
  * the first answered), and a request function per read.
@@ -10,15 +10,15 @@ type TSchedulesState = Pick<
10
10
  >;
11
11
 
12
12
  /**
13
- * #3282 §4 part b-3: the Agents panel's "Scheduled" group. No `requestId` correlates
13
+ * The Agents panel's "Scheduled" group. No `requestId` correlates
14
14
  * `get-background-tasks`/`background_tasks` (the message carries none — see `wire-messages.ts`), so
15
15
  * this owns that whole message type for the GUI: every reply is a full refresh of the roster,
16
16
  * filtered server-side to `kind: 'scheduled'` (the same tasks `/schedule list` shows).
17
17
  *
18
18
  * Pause/resume send the SAME `command` text `/schedule pause <id>` / `/schedule resume <id>` runs.
19
19
  * Delete sends the existing `cancel-background-task` write — the same one Stop already uses for any
20
- * other background-task kind (#3288/#3332) — since a schedule IS a background task, and cancelling
21
- * one is permanent (distinct from the reversible pause), matching #3282 §4's "Delete…" requirement.
20
+ * other background-task kind — since a schedule IS a background task, and cancelling
21
+ * one is permanent (distinct from the reversible pause).
22
22
  */
23
23
  export function useSchedulesState(send: (msg: TClientMessage) => void): TSchedulesState & {
24
24
  /** Handles `background_tasks` and a `cancel` `background_task_control_result` — both refresh. */
@@ -46,7 +46,7 @@ export function useSchedulesState(send: (msg: TClientMessage) => void): TSchedul
46
46
  if (msg.type === 'background_task_control_result' && msg.action === 'cancel') {
47
47
  // A schedule's Delete — refresh so a successful cancel drops it from the group at once; a
48
48
  // refused one leaves the row exactly as it was (the central reducer's own case already
49
- // turns a failure into a plain-message session notice — #3288 §1 — so this never duplicates it).
49
+ // turns a failure into a plain-message session notice — so this never duplicates it).
50
50
  if (msg.success) requestSchedules();
51
51
  return false; // not exclusively ours — other future consumers may still want this event
52
52
  }