@robota-sdk/agent-ui-web 3.2.1 → 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 (56) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/index.cjs +161 -118
  3. package/dist/index.d.cts +65 -63
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +65 -63
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +161 -118
  8. package/dist/index.js.map +1 -1
  9. package/docs/SPEC.md +6 -1
  10. package/package.json +6 -6
  11. package/src/client/ws-session-client.ts +1 -1
  12. package/src/clipboard.ts +2 -2
  13. package/src/components/AgentActivityPanel.tsx +46 -39
  14. package/src/components/AgentSwitcherSheet.tsx +2 -2
  15. package/src/components/Composer.tsx +40 -37
  16. package/src/components/ConversationView.tsx +14 -14
  17. package/src/components/Dialog.tsx +6 -6
  18. package/src/components/DiffLines.tsx +2 -2
  19. package/src/components/ExecutionDetailSheet.tsx +1 -1
  20. package/src/components/HelpSheet.tsx +2 -2
  21. package/src/components/PermissionPrompt.tsx +22 -22
  22. package/src/components/PopupMenu.tsx +1 -1
  23. package/src/components/ProjectPanel.tsx +2 -2
  24. package/src/components/SessionMonitor.tsx +1 -1
  25. package/src/components/SessionSidebar.tsx +8 -8
  26. package/src/components/SessionSurface.tsx +83 -56
  27. package/src/components/SessionSurfaceChrome.tsx +8 -8
  28. package/src/components/SettingsGeneralSection.tsx +1 -1
  29. package/src/components/SettingsMcpSection.tsx +1 -1
  30. package/src/components/SettingsPermissionsSection.tsx +1 -1
  31. package/src/components/SettingsPluginsSection.tsx +1 -1
  32. package/src/components/SettingsProvidersSection.tsx +3 -3
  33. package/src/components/SettingsScreen.tsx +3 -3
  34. package/src/components/StatusControls.tsx +12 -12
  35. package/src/components/composer-attachments.ts +5 -5
  36. package/src/driver-labels.ts +3 -3
  37. package/src/execution-entry-status.ts +1 -1
  38. package/src/goal-view.ts +1 -1
  39. package/src/hooks/command-menu.ts +2 -2
  40. package/src/hooks/excluded-commands.ts +2 -2
  41. package/src/hooks/prompt-state.ts +4 -4
  42. package/src/hooks/server-message-handling.ts +13 -11
  43. package/src/hooks/session-client-types.ts +36 -30
  44. package/src/hooks/ui-intent-state.ts +6 -6
  45. package/src/hooks/use-agent-switcher-state.ts +2 -2
  46. package/src/hooks/use-app-view-requests.ts +8 -1
  47. package/src/hooks/use-media-query.ts +1 -1
  48. package/src/hooks/use-model-list.ts +1 -1
  49. package/src/hooks/use-project-panel.ts +1 -1
  50. package/src/hooks/use-schedules-state.ts +4 -4
  51. package/src/hooks/use-session-directory.ts +11 -6
  52. package/src/hooks/use-settings-state.ts +4 -4
  53. package/src/hooks/useSessionClient.ts +81 -50
  54. package/src/index.ts +5 -5
  55. package/src/schedule-view.ts +1 -1
  56. package/src/styles/surface.css +1 -1
@@ -14,7 +14,7 @@ const STATUS: Record<string, { dot: string; label: string }> = {
14
14
  error: { dot: 'bg-destructive', label: 'Connection error' },
15
15
  };
16
16
 
17
- /** #3282 §4c: every view the primary nav can show — 'project' is always offered (desktop and
17
+ /** Every view the primary nav can show — 'project' is always offered (desktop and
18
18
  * browser alike); 'usage' only where the host opted the surface into it. */
19
19
  export type TSessionView = 'chat' | 'usage' | 'project';
20
20
 
@@ -25,7 +25,7 @@ const VIEW_LABEL: Readonly<Record<TSessionView, string>> = {
25
25
  };
26
26
 
27
27
  /**
28
- * #3289 §1 — the folder this session works in, wherever `document.title` is settable: a page (the
28
+ * The folder this session works in, wherever `document.title` is settable: a page (the
29
29
  * browser tab) and the desktop window that loads it (Electron follows the page's title unless told
30
30
  * otherwise, and nothing in `apps/agent-app/electron/main.ts` overrides it — see its own SPEC note).
31
31
  * Absent while the host has not said, so a not-yet-connected surface keeps whatever title it loaded
@@ -58,14 +58,14 @@ export function SessionTitleBar({
58
58
  status: string;
59
59
  surface?: string;
60
60
  title?: string | null;
61
- /** The folder this session works in (#3289 §1); absent when the host has not said. */
61
+ /** The folder this session works in; absent when the host has not said. */
62
62
  workspace?: { readonly name: string; readonly path: string };
63
63
  showBrand: boolean;
64
64
  view: TSessionView;
65
65
  onView: (view: TSessionView) => void;
66
66
  personalUsageEnabled: boolean;
67
67
  /**
68
- * #3282 §2 (part 2): mirrors `ConnectionBanner`'s own "give-up" flag (`useWsSession`'s
68
+ * Mirrors `ConnectionBanner`'s own "give-up" flag (`useWsSession`'s
69
69
  * `connectionLost`) so this corner indicator can tell exactly when that banner is on screen, to
70
70
  * never say the same thing twice.
71
71
  */
@@ -73,7 +73,7 @@ export function SessionTitleBar({
73
73
  }): React.ReactElement {
74
74
  const state = STATUS[status] ?? STATUS.disconnected;
75
75
  useWorkspaceDocumentTitle(workspace);
76
- // #3282 §2 (part 2): the same "has this surface ever connected" rule `ConnectionBanner` uses to
76
+ // The same "has this surface ever connected" rule `ConnectionBanner` uses to
77
77
  // decide whether it has anything to show — kept in step here so the corner text disappears exactly
78
78
  // when, and only when, the banner takes over saying it (never both, never neither).
79
79
  const everConnectedRef = useRef(false);
@@ -81,7 +81,7 @@ export function SessionTitleBar({
81
81
  if (status === 'connected') everConnectedRef.current = true;
82
82
  }, [status]);
83
83
  const bannerShown = status !== 'connected' && (connectionLost || everConnectedRef.current);
84
- // #3282 §4c principle 4 (#3277): everything the agent implements is usable from the GUI through
84
+ // Everything the agent implements is usable from the GUI through
85
85
  // organized screens — the Project panel (git status/diff, project memory) is one, offered next to
86
86
  // Chat on every surface, desktop and browser alike; Usage stays surface-gated.
87
87
  const items: readonly TSessionView[] = personalUsageEnabled
@@ -142,7 +142,7 @@ export function SessionTitleBar({
142
142
  const BANNER_COLUMN = 'mx-auto w-full max-w-[760px] px-6';
143
143
 
144
144
  /**
145
- * A connection that drops after a working session (issue #3280 §5): a banner directly above the
145
+ * A connection that drops after a working session: a banner directly above the
146
146
  * conversation — never a full-screen replacement — so the conversation and its history stay visible
147
147
  * and scrollable throughout. Shown as soon as the drop happens; says nothing on the very first
148
148
  * connect, because nothing has been lost yet.
@@ -234,7 +234,7 @@ function displayProvider(provider: string | undefined): string {
234
234
  }
235
235
 
236
236
  /**
237
- * A provider/session error classified at the wire boundary (#3289 §3) as one plain sentence naming
237
+ * A provider/session error classified at the wire boundary as one plain sentence naming
238
238
  * what happened and, where there is one, the next step — `null` for a notice this classification does
239
239
  * not cover, which keeps showing its own `message` instead.
240
240
  *
@@ -6,7 +6,7 @@ import type { ISettingsSnapshot, TSettingsPatch } from '@robota-sdk/agent-interf
6
6
 
7
7
  /**
8
8
  * Language, output style and preset — reusing the exact choices and descriptions the runtime
9
- * already builds for `/language`, `/output-style` and `/preset` (#3282 §4a). Never internal ids:
9
+ * already builds for `/language`, `/output-style` and `/preset`. Never internal ids:
10
10
  * every pop-up shows the runtime's human labels.
11
11
  */
12
12
  export function SettingsGeneralSection({
@@ -92,7 +92,7 @@ function ServerRow({
92
92
  }
93
93
 
94
94
  /**
95
- * The MCP Servers section (#3282 §4 part b-2): every configured server's name, where it is
95
+ * The MCP Servers section: every configured server's name, where it is
96
96
  * configured, its live connection status and tool count, an enable/disable switch (approve/reject),
97
97
  * an expandable list of its tool names, and a section-level Reload. Adding or editing a server is
98
98
  * left out — the runtime has no command path that writes MCP config (recorded in
@@ -48,7 +48,7 @@ function PermissionRuleRow({
48
48
  }
49
49
 
50
50
  /**
51
- * Default permission mode, the rules `/permissions` shows, and the sandbox switch (#3282 §4a).
51
+ * Default permission mode, the rules `/permissions` shows, and the sandbox switch.
52
52
  * "Skip all checks" as the mode and a preset that would set it both go through
53
53
  * `onRequestModeChange`'s parent-owned confirmation before anything is sent.
54
54
  */
@@ -60,7 +60,7 @@ function PluginRow({
60
60
  }
61
61
 
62
62
  /**
63
- * The Plugins section (#3282 §4 part b-2): every installed plugin's name, description and enabled
63
+ * The Plugins section: every installed plugin's name, description and enabled
64
64
  * state, an enable/disable switch, a section-level Reload, and — local surfaces only — install and
65
65
  * uninstall, since installing runs third-party code. `canInstall` (from the snapshot) mirrors the
66
66
  * refusal `/plugin install|uninstall` itself gives a remote device or an observer.
@@ -14,7 +14,7 @@ const PRIMARY_BUTTON = `${BUTTON} bg-primary text-primary-foreground hover:opaci
14
14
  const SECONDARY_BUTTON = `${BUTTON} bg-raised text-foreground hover:bg-hover`;
15
15
  const DESTRUCTIVE_BUTTON = `${BUTTON} text-destructive hover:bg-destructive/10`;
16
16
 
17
- /** The "Model" action: a `PopupMenu` of this ONE profile's catalog models (#3282 §4b). */
17
+ /** The "Model" action: a `PopupMenu` of this ONE profile's catalog models. */
18
18
  function ProviderModelButton({
19
19
  profile,
20
20
  modelList,
@@ -142,7 +142,7 @@ function ProviderProfileRow({
142
142
  }
143
143
 
144
144
  /**
145
- * The configured provider profiles (#3282 §4b), each row a plain provider name, its model's label,
145
+ * The configured provider profiles, each row a plain provider name, its model's label,
146
146
  * a checkmark on the one in use, and a plain connection state when one is known. Every action goes
147
147
  * through the same path the equivalent command does: Use/Model apply through `state.updateSettings`
148
148
  * (the same `field` patterns Preset/Mode/Sandbox use); Edit/Test/Duplicate/Add dispatch the raw
@@ -169,7 +169,7 @@ export function SettingsProvidersSection({
169
169
  onRequestDelete: (profileName: string) => void;
170
170
  }): React.ReactElement {
171
171
  // The "Model" button needs each profile's catalog as soon as this section is visible, not only
172
- // once a person opens one — `list-models` is a cheap, observer-safe read (#3282 §2).
172
+ // once a person opens one — `list-models` is a cheap, observer-safe read.
173
173
  // Intentionally empty deps: fetch once per mount, not on every render.
174
174
  useEffect(() => {
175
175
  onRequestModelList();
@@ -19,7 +19,7 @@ interface ISettingsSectionDefinition {
19
19
  }
20
20
 
21
21
  /**
22
- * The Settings screen's sections (#3282 §4). Adding a later section (Advisor) is one entry here plus
22
+ * The Settings screen's sections. Adding a later section (Advisor) is one entry here plus
23
23
  * one component in the switch below — nothing else in this file changes.
24
24
  */
25
25
  const SECTIONS: readonly ISettingsSectionDefinition[] = [
@@ -36,7 +36,7 @@ type TPendingPluginAction =
36
36
  | { kind: 'uninstall'; plugin: ISettingsPlugin };
37
37
 
38
38
  /**
39
- * A large modal sheet with a section sidebar on the left and content on the right (#3282 §4a) — the
39
+ * A large modal sheet with a section sidebar on the left and content on the right — the
40
40
  * GUI's Settings screen, opened by the sidebar's gear button, the desktop app's ⌘, menu item, or
41
41
  * `/settings` (its `show-settings` `ui_intent` calls `state.openSettings()`, wired in
42
42
  * `useSessionClient`). Every change applies at once; there is no Save button. Esc and the Close
@@ -56,7 +56,7 @@ export function SettingsScreen({ state }: { state: IWsSessionState }): React.Rea
56
56
  const [pendingPluginAction, setPendingPluginAction] = useState<TPendingPluginAction | null>(null);
57
57
  const [mcpReloading, setMcpReloading] = useState(false);
58
58
  const [pluginsReloading, setPluginsReloading] = useState(false);
59
- // #3282 §4b: the profile a pending "Delete…" confirms, or null when none is pending.
59
+ // The profile a pending "Delete…" confirms, or null when none is pending.
60
60
  const [pendingProviderDelete, setPendingProviderDelete] = useState<string | null>(null);
61
61
 
62
62
  // Each time the screen opens, land on the section the opener asked for (`/plugin` → Plugins,
@@ -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,14 +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
- // #3282 §4a: the Settings screen's own correlated request/response state (`useSettingsState`).
92
+ // The connection's effective role: the session client holds it and stops sending what an observer may not.
93
+ surface_role: 'reducer-state',
94
+ // The Settings screen's own correlated request/response state (`useSettingsState`).
93
95
  settings: 'reducer-state',
94
96
  settings_error: 'reducer-state',
95
97
  } as const satisfies Readonly<Record<TServerMessage['type'], TServerMessageHandling>>;