@robota-sdk/agent-ui-web 0.0.0-stage → 3.0.0-beta.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +400 -0
- package/LICENSE +661 -0
- package/README.md +19 -2
- package/dist/browser/index.d.ts +715 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +7923 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/node/index.cjs +7973 -0
- package/dist/node/index.d.cts +715 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +715 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +7923 -0
- package/dist/node/index.js.map +1 -0
- package/docs/README.md +62 -0
- package/docs/SPEC.md +124 -0
- package/package.json +99 -3
- package/src/client/runtime-host.ts +84 -0
- package/src/client/ws-session-client.ts +131 -0
- package/src/clipboard.ts +81 -0
- package/src/components/AgentActivityPanel.tsx +371 -0
- package/src/components/AgentSwitcherSheet.tsx +93 -0
- package/src/components/Brand.tsx +33 -0
- package/src/components/Composer.tsx +685 -0
- package/src/components/ConversationView.tsx +772 -0
- package/src/components/Dialog.tsx +230 -0
- package/src/components/DiffLines.tsx +81 -0
- package/src/components/ExecutionDetailSheet.tsx +142 -0
- package/src/components/HelpSheet.tsx +104 -0
- package/src/components/PermissionPrompt.tsx +661 -0
- package/src/components/PersonalUsageDashboard.tsx +39 -0
- package/src/components/PersonalUsageDashboardDetails.tsx +368 -0
- package/src/components/PersonalUsageDashboardSections.tsx +250 -0
- package/src/components/PopupMenu.tsx +166 -0
- package/src/components/ProjectPanel.tsx +268 -0
- package/src/components/SessionMonitor.tsx +180 -0
- package/src/components/SessionSidebar.tsx +429 -0
- package/src/components/SessionSurface.tsx +455 -0
- package/src/components/SessionSurfaceChrome.tsx +316 -0
- package/src/components/SettingsFields.tsx +159 -0
- package/src/components/SettingsGeneralSection.tsx +49 -0
- package/src/components/SettingsMcpSection.tsx +157 -0
- package/src/components/SettingsPermissionsSection.tsx +137 -0
- package/src/components/SettingsPluginsSection.tsx +165 -0
- package/src/components/SettingsProvidersSection.tsx +211 -0
- package/src/components/SettingsScreen.tsx +340 -0
- package/src/components/StatusControls.tsx +294 -0
- package/src/components/composer-attachments.ts +246 -0
- package/src/components/personal-usage-dashboard-types.ts +26 -0
- package/src/driver-labels.ts +88 -0
- package/src/execution-entry-status.ts +55 -0
- package/src/goal-view.ts +19 -0
- package/src/hooks/command-menu.ts +65 -0
- package/src/hooks/excluded-commands.ts +52 -0
- package/src/hooks/prompt-state.ts +83 -0
- package/src/hooks/server-message-handling.ts +92 -0
- package/src/hooks/session-client-types.ts +329 -0
- package/src/hooks/ui-intent-state.ts +74 -0
- package/src/hooks/use-agent-switcher-state.ts +125 -0
- package/src/hooks/use-execution-detail.ts +118 -0
- package/src/hooks/use-media-query.ts +27 -0
- package/src/hooks/use-model-list.ts +45 -0
- package/src/hooks/use-personal-usage.ts +138 -0
- package/src/hooks/use-project-panel.ts +117 -0
- package/src/hooks/use-schedules-state.ts +92 -0
- package/src/hooks/use-session-directory.ts +202 -0
- package/src/hooks/use-settings-state.ts +115 -0
- package/src/hooks/useSessionClient.ts +991 -0
- package/src/index.ts +66 -0
- package/src/product-identity.tsx +26 -0
- package/src/schedule-view.ts +92 -0
- package/src/styles/surface.css +231 -0
- package/src/styles/surface.css.d.mts +1 -0
- package/src/styles/theme.css +38 -0
- package/src/styles/theme.css.d.mts +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["requestCounter","nextRequestId","requestCounter","nextRequestId","requestCounter","nextRequestId","requestCounter","nextRequestId","requestCounter","nextRequestId","requestCounter","nextRequestId","BUTTON","PRIMARY_BUTTON","SECONDARY_BUTTON","DESTRUCTIVE_BUTTON","STATUS_TAG_TONE","PRIMARY_BUTTON","SECONDARY_BUTTON","DESTRUCTIVE_BUTTON","BUTTON","number","PERCENT","StatusRow","StatusRow"],"sources":["../../src/hooks/excluded-commands.ts","../../src/hooks/prompt-state.ts","../../src/hooks/ui-intent-state.ts","../../src/client/ws-session-client.ts","../../src/hooks/server-message-handling.ts","../../src/hooks/use-agent-switcher-state.ts","../../src/hooks/use-execution-detail.ts","../../src/hooks/use-model-list.ts","../../src/hooks/use-personal-usage.ts","../../src/hooks/use-project-panel.ts","../../src/hooks/use-schedules-state.ts","../../src/product-identity.tsx","../../src/hooks/use-session-directory.ts","../../src/hooks/use-settings-state.ts","../../src/hooks/useSessionClient.ts","../../src/client/runtime-host.ts","../../src/clipboard.ts","../../src/driver-labels.ts","../../src/components/DiffLines.tsx","../../src/components/ConversationView.tsx","../../src/components/Dialog.tsx","../../src/goal-view.ts","../../src/schedule-view.ts","../../src/execution-entry-status.ts","../../src/components/AgentActivityPanel.tsx","../../src/components/AgentSwitcherSheet.tsx","../../src/components/PermissionPrompt.tsx","../../src/components/SessionSidebar.tsx","../../src/components/PersonalUsageDashboardDetails.tsx","../../src/components/PersonalUsageDashboardSections.tsx","../../src/components/PersonalUsageDashboard.tsx","../../src/components/Brand.tsx","../../src/hooks/command-menu.ts","../../src/components/composer-attachments.ts","../../src/components/PopupMenu.tsx","../../src/components/StatusControls.tsx","../../src/components/Composer.tsx","../../src/components/ExecutionDetailSheet.tsx","../../src/components/HelpSheet.tsx","../../src/components/ProjectPanel.tsx","../../src/components/SessionSurfaceChrome.tsx","../../src/components/SettingsFields.tsx","../../src/components/SettingsGeneralSection.tsx","../../src/components/SettingsMcpSection.tsx","../../src/components/SettingsPermissionsSection.tsx","../../src/components/SettingsPluginsSection.tsx","../../src/components/SettingsProvidersSection.tsx","../../src/components/SettingsScreen.tsx","../../src/hooks/use-media-query.ts","../../src/components/SessionSurface.tsx","../../src/components/SessionMonitor.tsx"],"sourcesContent":["/**\n * #3282 §4e — built-in commands left out of the GUI on purpose (the design decisions in\n * `packages/agent-gui-web/docs/SPEC.md` group them by reason). The `/` menu never lists one of these\n * (`command-menu.ts`), and typing one by hand anyway is caught before it reaches the session\n * (`useSessionClient.ts`'s `send`) and answered with the plain sentence here — never the session's own\n * refusal, and never the generic \"not available on this surface\" line CMD-004 uses for a screen this\n * surface simply has not built yet (see `ui-intent-state.ts`).\n */\n\nimport type { TCommandRunner, TCommandSurface } from '@robota-sdk/agent-interface-command';\n\n/** The minimum a catalog entry needs for the exclusion check below. */\nexport interface IExcludableCommandEntry {\n readonly name: string;\n readonly runner?: TCommandRunner;\n readonly surfaces?: readonly TCommandSurface[];\n}\n\n/**\n * One plain sentence per excluded command, naming what to use instead — never the command's own\n * name again, never \"not available on this surface\". `theme`, `keybindings`, `editor` and `shell`\n * also declare `runner: 'client', surfaces: ['terminal']` server-side (a second, independent way to\n * reach the same \"excluded\" answer, checked by {@link isExcludedCommand} below); `statusline`,\n * `exit` and `devices` do not, and are excluded here only because the GUI has its own, better answer.\n */\nconst EXCLUDED_COMMAND_MESSAGES: ReadonlyMap<string, string> = new Map([\n ['theme', 'This app follows your system appearance.'],\n ['keybindings', 'This app uses standard keyboard shortcuts here.'],\n ['editor', 'Type your message directly in the composer.'],\n ['statusline', 'The GUI shows session status in its own status row, not a terminal status line.'],\n ['shell', 'Use the Terminal app for shell commands.'],\n ['exit', 'Close the window or tab to end this session; delete it from the sidebar to remove it for good.'],\n ['devices', 'Device pairing is managed from the terminal for now.'],\n]);\n\n/** A terminal-client command with no curated sentence above still gets a plain, honest one. */\nconst GENERIC_CLIENT_MESSAGE = 'This command runs in the host terminal.';\n\n/**\n * Whether a catalog entry is left out of the GUI: named above, or declared `runner: 'client'` for\n * surfaces that do not include `'gui'` (a client-only command the GUI cannot run itself, whether or\n * not this file has a curated sentence for it yet).\n */\nexport function isExcludedCommand(entry: IExcludableCommandEntry): boolean {\n if (EXCLUDED_COMMAND_MESSAGES.has(entry.name)) return true;\n return entry.runner === 'client' && !(entry.surfaces ?? []).includes('gui');\n}\n\n/** The plain sentence for an excluded command name — a safe generic one when none is curated. */\nexport function excludedCommandMessage(name: string): string {\n return EXCLUDED_COMMAND_MESSAGES.get(name) ?? GENERIC_CLIENT_MESSAGE;\n}\n","/**\n * Pure prompt-state derivation for the web UI (REMOTE-009 Step 4 / REMOTE-007 render+answer).\n *\n * The paired browser is the session OWNER (local == remote), so it must render and ANSWER the owner's own\n * permission/ask prompts. The server carries `permission_request`/`ask_request` (a prompt appears) and\n * `prompt_resolved` (it was answered — by this or a co-driving surface — and must dismiss). The answer travels\n * back as `permission-response`/`ask-response`. This module is the pure list-transition + message-builder logic,\n * consumed by `useWsSession` and shared unchanged by BOTH the WS and RTC session clients (same `TServerMessage`s).\n */\n\nimport type { TActionResponse } from '@robota-sdk/agent-interface-transport';\nimport type {\n IAskRequestEvent,\n IPermissionRequestEvent,\n TPermissionResultValue,\n} from '@robota-sdk/agent-interface-session';\nimport type { TClientMessage, TServerMessage } from '@robota-sdk/agent-transport';\n\n/** A prompt awaiting the owner's answer, rendered by the UI (built from the server event payloads). */\nexport type TPendingPrompt =\n | ({ readonly kind: 'permission' } & IPermissionRequestEvent)\n | ({ readonly kind: 'ask' } & IAskRequestEvent);\n\n/**\n * Fold a server message into the pending-prompt list: append on `permission_request`/`ask_request`, remove on\n * `prompt_resolved`. Any other message returns the list unchanged (referential-equality preserved). Duplicate\n * ids are not re-appended (idempotent against a resend).\n */\nexport function applyPromptEvent(\n prompts: readonly TPendingPrompt[],\n msg: TServerMessage,\n): readonly TPendingPrompt[] {\n switch (msg.type) {\n case 'permission_request': {\n // REMOTE-014 E5: keep requesterDriverId so the surface can show WHICH driver's turn raised the\n // prompt. Issue #3288 §1: keep `requester` too, so it can show WHICH background agent did.\n // #3288 §2: diffLines/diffFile (Edit/Write) and cwd (Shell, only when it differs) are the\n // server's diff/command preview — carried through unchanged so the prompt can render them\n // instead of raw args.\n const { id, toolName, toolArgs, requesterDriverId, requester, diffLines, diffFile, cwd } =\n msg.event;\n if (prompts.some((p) => p.id === id)) return prompts;\n return [\n ...prompts,\n {\n kind: 'permission',\n id,\n toolName,\n toolArgs,\n ...(requesterDriverId ? { requesterDriverId } : {}),\n ...(requester ? { requester } : {}),\n ...(diffLines ? { diffLines, diffFile } : {}),\n ...(cwd ? { cwd } : {}),\n },\n ];\n }\n case 'ask_request': {\n const { id, request, requesterDriverId } = msg.event;\n if (prompts.some((p) => p.id === id)) return prompts;\n return [\n ...prompts,\n { kind: 'ask', id, request, ...(requesterDriverId ? { requesterDriverId } : {}) },\n ];\n }\n case 'prompt_resolved': {\n const { id } = msg.event;\n const next = prompts.filter((p) => p.id !== id);\n return next.length === prompts.length ? prompts : next;\n }\n default:\n return prompts;\n }\n}\n\n/** Build the client message that answers a permission prompt. */\nexport function permissionResponse(id: string, result: TPermissionResultValue): TClientMessage {\n return { type: 'permission-response', id, result };\n}\n\n/** Build the client message that answers an ask prompt. */\nexport function askResponse(id: string, response: TActionResponse): TClientMessage {\n return { type: 'ask-response', id, response };\n}\n","/**\n * CMD-004 Stage D — how the GUI surface answers a `ui_intent`.\n *\n * The server requester-routes `ui_intent`, so everything that arrives here was asked for by THIS\n * surface and must be answered visibly. An intent with a GUI screen opens it (the session picker is\n * the session sidebar, #3189; settings and the plugin manager both open the Settings modal, #3282\n * §4a/§4 part b-2; the agent switcher is a sheet, #3282 §4 part b-3). #3282 §4e: an intent whose\n * command is on the GUI's exclusion list never reaches here at all (`excluded-commands.ts` catches it\n * before the session is asked) — this module only answers a screen this HOST cannot back even though\n * the GUI knows how to render it (the session picker, when the host cannot list sessions), or a kind\n * this build does not know at all — and it never says \"not available on this surface\": each gets its\n * own plain, honest sentence instead.\n */\n\nimport { excludedCommandMessage } from './excluded-commands.js';\n\nimport type { TCommandUiIntent } from '@robota-sdk/agent-interface-command';\n\n/** The command a user runs for the screen an intent asks for — how the info line is labelled. */\nexport function uiIntentCommandName(intent: TCommandUiIntent): string {\n switch (intent.type) {\n case 'show-plugin-manager':\n return 'plugin';\n case 'show-settings':\n return 'settings';\n case 'show-session-picker':\n return 'resume';\n case 'show-agent-switcher':\n return 'agent';\n case 'show-theme-picker':\n return 'theme';\n default:\n return (intent as { type: string }).type;\n }\n}\n\n/** The GUI screen an intent opens, or null when this surface has none for it. */\nexport function guiScreenForUiIntent(\n intent: TCommandUiIntent,\n): 'session-sidebar' | 'settings' | 'settings-plugins' | 'agent-switcher' | null {\n if (intent.type === 'show-session-picker') return 'session-sidebar';\n if (intent.type === 'show-settings') return 'settings';\n // #3282 §4 part b-2: `/plugin` opens the Settings screen's Plugins section, the way #3331 mapped\n // `show-settings` — replacing the \"not available on this surface\" line this intent used to get.\n if (intent.type === 'show-plugin-manager') return 'settings-plugins';\n if (intent.type === 'show-agent-switcher') return 'agent-switcher';\n return null;\n}\n\n/**\n * Describe one intent for this surface: a plain, honest sentence for a screen this HOST cannot back\n * right now (a session picker with no session list), the `/theme` exclusion this intent defensively\n * reuses, or an unrecognized future kind. Never called for `show-settings`, `show-plugin-manager` or\n * `show-agent-switcher` — all three always have a screen now (#3282 §4a, §4 part b-2, §4 part b-3) —\n * but their cases are left out of this switch rather than kept as dead branches that would claim\n * otherwise; an unknown wire-level kind (a newer host) still yields an explicit notice naming the raw\n * kind, so the \"never a silent no-op\" floor holds for future intents too.\n */\nexport function describeUiIntentForGui(intent: TCommandUiIntent): string {\n switch (intent.type) {\n case 'show-session-picker':\n // The GUI knows how to render this screen (`guiScreenForUiIntent` says so); it is only reached\n // here when the HOST itself cannot list sessions, which is this host's limitation, not a\n // missing GUI feature.\n return 'This host cannot list sessions, so there is nothing to resume.';\n case 'show-theme-picker':\n // Defensive only: `/theme` is on the exclusion list and never reaches the session from the GUI.\n return excludedCommandMessage('theme');\n default:\n // Spec-mandated unsupported signal for an intent kind this build does not know (not a\n // fallback: the explicit notice IS the defined behavior for an unrenderable intent).\n return `This client does not have a screen for '${(intent as { type: string }).type}' yet.`;\n }\n}\n","/**\n * Browser WebSocket client for a running agent-cli sidecar session.\n * Wraps the native WebSocket API with typed agent-transport-ws messages.\n */\n\nimport { decodeFrame, decodeServerMessage } from '@robota-sdk/agent-transport/client';\n\nimport type { TServerMessage, TClientMessage } from '@robota-sdk/agent-transport';\n\nexport type { TServerMessage, TClientMessage };\n\nexport type TConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error';\n\nexport interface IWsSessionClientCallbacks {\n onMessage: (msg: TServerMessage) => void;\n onStatusChange: (status: TConnectionStatus) => void;\n /**\n * The connection is gone for good: the retries ran out without reaching the server again. The status\n * stays `disconnected`; this is what tells a host the runtime is not coming back by itself.\n */\n onGiveUp?: () => void;\n}\n\nexport interface IWsSessionClient {\n connect: () => void;\n disconnect: () => void;\n send: (msg: TClientMessage) => void;\n status: () => TConnectionStatus;\n}\n\nconst RECONNECT_DELAY_MS = 2000;\nconst MAX_RECONNECT_ATTEMPTS = 10;\n\nexport function createWsSessionClient(\n url: string,\n callbacks: IWsSessionClientCallbacks,\n): IWsSessionClient {\n let ws: WebSocket | null = null;\n let currentStatus: TConnectionStatus = 'disconnected';\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let reconnectAttempts = 0;\n let intentionalDisconnect = false;\n\n function setStatus(s: TConnectionStatus): void {\n currentStatus = s;\n callbacks.onStatusChange(s);\n }\n\n function scheduleReconnect(): void {\n if (intentionalDisconnect) return;\n if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n callbacks.onGiveUp?.();\n return;\n }\n reconnectTimer = setTimeout(() => {\n reconnectAttempts++;\n doConnect();\n }, RECONNECT_DELAY_MS);\n }\n\n function doConnect(): void {\n setStatus('connecting');\n ws = new WebSocket(url);\n\n ws.onopen = (): void => {\n reconnectAttempts = 0;\n setStatus('connected');\n // Request full message history on connect\n send({ type: 'get-messages' });\n };\n\n ws.onmessage = (event: MessageEvent): void => {\n const data = event.data;\n if (typeof data !== 'string') return;\n // Issue #2045: `raw → owner decoder → typed`. A malformed frame (invalid JSON or an invalid\n // shape) must not throw inside the handler (it would break the socket's message pump and\n // freeze the UI) and must not be cast onward; surface it via the normal path.\n const decoded = decodeFrame(data, decodeServerMessage);\n if (!decoded.ok) {\n callbacks.onMessage({\n type: 'protocol_error',\n message: `Malformed message from server (${decoded.reason})`,\n });\n return;\n }\n callbacks.onMessage(decoded.message);\n };\n\n ws.onclose = (): void => {\n ws = null;\n if (!intentionalDisconnect) {\n setStatus('disconnected');\n scheduleReconnect();\n } else {\n setStatus('disconnected');\n }\n };\n\n ws.onerror = (): void => {\n setStatus('error');\n };\n }\n\n function connect(): void {\n intentionalDisconnect = false;\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n doConnect();\n }\n\n function disconnect(): void {\n intentionalDisconnect = true;\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n ws?.close();\n ws = null;\n setStatus('disconnected');\n }\n\n function send(msg: TClientMessage): void {\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n }\n\n return { connect, disconnect, send, status: () => currentStatus };\n}\n","import type { TServerMessage } from '@robota-sdk/agent-transport';\n\nexport type TServerMessageHandling =\n 'reducer-state' | 'visible-notice' | 'intentionally-not-rendered' | 'transport-control';\n\n/**\n * ARCH-2164: exhaustive ownership for every decoded server variant on the shared GUI surface.\n * Adding a wire variant without deciding its GUI disposition is a compile error instead of a silent\n * reducer omission. `intentionally-not-rendered` is an explicit disposition for server-side query\n * snapshots and domain events that this session/usage surface does not currently visualize.\n */\nexport const SERVER_MESSAGE_HANDLING = {\n text_delta: 'reducer-state',\n user_message: 'reducer-state',\n tool_start: 'reducer-state',\n tool_end: 'reducer-state',\n thinking: 'reducer-state',\n complete: 'reducer-state',\n interrupted: 'reducer-state',\n error: 'visible-notice',\n command_result: 'reducer-state',\n messages: 'reducer-state',\n // #3189: the full history and its change signal serve a client that renders the whole session\n // (the TUI); this surface builds its transcript from `messages` and the streamed turn.\n history: 'intentionally-not-rendered',\n history_changed: 'intentionally-not-rendered',\n context: 'intentionally-not-rendered',\n turn_source: 'intentionally-not-rendered',\n commands: 'reducer-state',\n session_status: 'reducer-state',\n // #3282 §2 part 2: the reply to `list-models`, fed into `modelList` state by the model control's\n // pop-up menu (`useModelListState`).\n model_list: 'reducer-state',\n // #3282 §4 part b-3: the agent switcher sheet's own correlated request/response state (`useAgentSwitcherState`).\n agent_definitions: 'reducer-state',\n sessions: 'reducer-state',\n sessions_error: 'reducer-state',\n session_switched: 'reducer-state',\n session_change_failed: 'visible-notice',\n session_renamed_in_list: 'reducer-state',\n session_rename_failed: 'visible-notice',\n session_deleted: 'reducer-state',\n session_delete_failed: 'visible-notice',\n usage_report: 'reducer-state',\n personal_usage_report: 'reducer-state',\n personal_usage_report_error: 'visible-notice',\n stored_session_usage_report: 'reducer-state',\n stored_session_usage_report_error: 'visible-notice',\n executing: 'intentionally-not-rendered',\n // #3280 §2: the prompt queued behind a running turn — shown above the composer with Edit/Remove.\n pending: 'reducer-state',\n execution_workspace_event: 'reducer-state',\n // #3288 §1: the Agents panel's detail sheet reads a page at a time.\n execution_detail: 'reducer-state',\n execution_detail_error: 'reducer-state',\n // Esc's loop stop is a TUI-only shortcut; this surface's loops stop through the Agents panel\n // instead (`/loop stop <id>`, or `stop-waiting-loop` is simply never sent here).\n waiting_loop_stop: 'intentionally-not-rendered',\n // The Agents panel already reflects a task's cancel/close/send through `execution_workspace_event`;\n // nothing here renders this narrower, task-only event on its own.\n // #3282 §4c: the Project panel's reads — reduced into state by `useProjectPanelState`.\n project_status: 'reducer-state',\n project_diff: 'reducer-state',\n project_memory: 'reducer-state',\n background_task_event: 'intentionally-not-rendered',\n background_job_group_event: 'intentionally-not-rendered',\n plan_event: 'intentionally-not-rendered',\n context_file_refreshed: 'intentionally-not-rendered',\n branch_event: 'intentionally-not-rendered',\n // #3282 §4 part b-3: the Agents panel's \"Scheduled\" group's own roster (`useSchedulesState`).\n background_tasks: 'reducer-state',\n background_task: 'intentionally-not-rendered',\n background_job_groups: 'intentionally-not-rendered',\n background_job_group: 'intentionally-not-rendered',\n background_task_log: 'intentionally-not-rendered',\n permission_request: 'reducer-state',\n ask_request: 'reducer-state',\n prompt_resolved: 'reducer-state',\n ui_intent: 'reducer-state',\n session_renamed: 'reducer-state',\n history_cleared: 'reducer-state',\n // #3288 §1: a failed Stop (e.g. a task that finished a moment before it arrived) is worth telling\n // the operator; a success shows itself through the workspace snapshot's own update. #3282 §4 part\n // b-3: a schedule's Delete uses this same `cancel-background-task` write — `useSchedulesState`\n // refreshes the roster on a successful cancel, and a failure gets the same visible notice as Stop.\n background_task_control_result: 'visible-notice',\n protocol_error: 'visible-notice',\n resume_gap: 'transport-control',\n // #3282 §4a: the Settings screen's own correlated request/response state (`useSettingsState`).\n settings: 'reducer-state',\n settings_error: 'reducer-state',\n} as const satisfies Readonly<Record<TServerMessage['type'], TServerMessageHandling>>;\n","import { useCallback, useRef, useState } from 'react';\n\nimport type { IWsSessionState } from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { IWireAgentDefinitionSummary } from '@robota-sdk/agent-transport';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TAgentSwitcherState = Pick<\n IWsSessionState,\n | 'agentSwitcherOpen'\n | 'agentSwitcherStatus'\n | 'agentDefinitions'\n | 'currentAgentType'\n | 'agentSwitchMessage'\n | 'openAgentSwitcher'\n | 'closeAgentSwitcher'\n | 'selectAgent'\n>;\n\nlet requestCounter = 0;\nfunction nextRequestId(prefix: string): string {\n requestCounter += 1;\n return `${prefix}_${requestCounter}_${Date.now()}`;\n}\n\n/**\n * #3282 §4 part b-3: the agent switcher sheet's own correlated request/response state, the same\n * shape `useSettingsState` already uses for `get-settings`/`update-settings` — independent from the\n * chat conversation, because choosing an agent is answered by the sheet's own confirmation line\n * (\"Default agent: Explore\"), never a conversation card (issue #3282 §4's decided design).\n *\n * `openAgentSwitcher` re-fetches every time (`/agent` bare, or reopening after a close), so a change\n * made elsewhere reaches an open-then-reopened sheet, matching `useSettingsState`'s own reasoning.\n *\n * Choosing an agent runs the SAME path as typing `/agent <name>` — a plain `command` wire message —\n * so the write goes through `executeCommand` exactly like every other command; this hook only\n * correlates its own request so the reply lands in the sheet, not the conversation. The central\n * `command_result` case in `useSessionClient` calls `resolveSwitchResult` to hand it the reply\n * (and, critically, still performs the shared `commandsInFlightRef` bookkeeping itself — this hook\n * never short-circuits `command_result` the way `handleAgentDefinitionsMessage` short-circuits reads).\n */\nexport function useAgentSwitcherState(send: (msg: TClientMessage) => void): TAgentSwitcherState & {\n /** Handles `agent_definitions` only — never `command_result` (see the note above). */\n handleAgentDefinitionsMessage: (msg: TServerMessage) => boolean;\n /**\n * Called from the central `command_result` case. Returns true when this requestId was the\n * sheet's own pending switch, meaning the central handler must not also add a conversation card.\n */\n resolveSwitchResult: (\n requestId: string | undefined,\n result: { success: boolean; message: string },\n ) => boolean;\n} {\n const [agentSwitcherOpen, setAgentSwitcherOpen] = useState(false);\n const [agentSwitcherStatus, setAgentSwitcherStatus] =\n useState<IWsSessionState['agentSwitcherStatus']>('idle');\n const [agentDefinitions, setAgentDefinitions] = useState<\n readonly IWireAgentDefinitionSummary[]\n >([]);\n const [currentAgentType, setCurrentAgentType] = useState<string | null>(null);\n const [agentSwitchMessage, setAgentSwitchMessage] = useState<string | null>(null);\n const listRequestRef = useRef<string | null>(null);\n const switchRequestRef = useRef<string | null>(null);\n\n const handleAgentDefinitionsMessage = useCallback((msg: TServerMessage): boolean => {\n if (msg.type !== 'agent_definitions') return false;\n if (listRequestRef.current !== msg.requestId) return true; // superseded by a later request\n setAgentDefinitions(msg.agents);\n setCurrentAgentType(msg.current);\n setAgentSwitcherStatus('ready');\n return true;\n }, []);\n\n const requestAgentDefinitions = useCallback((): void => {\n const requestId = nextRequestId('get_agent_definitions');\n listRequestRef.current = requestId;\n send({ type: 'get-agent-definitions', requestId });\n }, [send]);\n\n const openAgentSwitcher = useCallback((): void => {\n setAgentSwitcherOpen(true);\n setAgentSwitcherStatus((current) => (current === 'ready' ? 'ready' : 'loading'));\n setAgentSwitchMessage(null);\n requestAgentDefinitions();\n }, [requestAgentDefinitions]);\n\n const closeAgentSwitcher = useCallback((): void => {\n setAgentSwitcherOpen(false);\n }, []);\n\n const selectAgent = useCallback(\n (agentType: string): void => {\n const requestId = nextRequestId('agent_switch');\n switchRequestRef.current = requestId;\n setAgentSwitchMessage(null);\n // The SAME path `/agent <name>` runs: a plain command, not a dedicated write message.\n send({ type: 'command', name: 'agent', args: agentType, requestId });\n },\n [send],\n );\n\n const resolveSwitchResult = useCallback(\n (requestId: string | undefined, result: { success: boolean; message: string }): boolean => {\n if (requestId === undefined || switchRequestRef.current !== requestId) return false;\n switchRequestRef.current = null;\n setAgentSwitchMessage(result.message);\n if (result.success) requestAgentDefinitions();\n return true;\n },\n [requestAgentDefinitions],\n );\n\n return {\n agentSwitcherOpen,\n agentSwitcherStatus,\n agentDefinitions,\n currentAgentType,\n agentSwitchMessage,\n openAgentSwitcher,\n closeAgentSwitcher,\n selectAgent,\n handleAgentDefinitionsMessage,\n resolveSwitchResult,\n };\n}\n","/**\n * #3288 §1: the detail sheet's own correlated request/response state — which entry is open, its\n * accumulated transcript records, and whether more can be loaded. Owned independently from chat\n * state, the same way `use-personal-usage.ts` owns the usage-report protocol: one `requestId` per\n * outstanding read, so a stale reply for an entry the operator already closed (or a page they moved\n * past) is dropped rather than clobbering what is now on screen.\n */\n\nimport { useCallback, useRef, useState } from 'react';\n\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type {\n IExecutionDetailCursor,\n IExecutionDetailRecord,\n} from '@robota-sdk/agent-interface-execution';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\nexport type TExecutionDetailStatus = 'idle' | 'loading' | 'ready' | 'error';\n\nexport interface IExecutionDetailState {\n /** The entry currently open in the detail sheet, or null when it is closed. */\n openEntryId: string | null;\n executionDetailStatus: TExecutionDetailStatus;\n /** Every record read so far for the open entry, oldest first — pages accumulate via loadMoreExecutionDetail. */\n executionDetailRecords: readonly IExecutionDetailRecord[];\n executionDetailError: string | null;\n /** True once a page came back with no `nextCursor` — nothing more to load. */\n executionDetailComplete: boolean;\n openExecutionDetail: (entryId: string) => void;\n loadMoreExecutionDetail: () => void;\n closeExecutionDetail: () => void;\n}\n\nlet requestCounter = 0;\nfunction nextRequestId(): string {\n requestCounter += 1;\n return `execdetail_${requestCounter}_${Date.now()}`;\n}\n\nexport function useExecutionDetailState(\n send: (msg: TClientMessage) => void,\n): IExecutionDetailState & { handleExecutionDetailMessage: (msg: TServerMessage) => boolean } {\n const [openEntryId, setOpenEntryId] = useState<string | null>(null);\n const [status, setStatus] = useState<TExecutionDetailStatus>('idle');\n const [records, setRecords] = useState<readonly IExecutionDetailRecord[]>([]);\n const [error, setError] = useState<string | null>(null);\n const [complete, setComplete] = useState(false);\n const requestIdRef = useRef<string | null>(null);\n const entryIdRef = useRef<string | null>(null);\n const nextCursorRef = useRef<IExecutionDetailCursor | undefined>(undefined);\n\n const readPage = useCallback(\n (entryId: string, cursor?: IExecutionDetailCursor): void => {\n const requestId = nextRequestId();\n requestIdRef.current = requestId;\n entryIdRef.current = entryId;\n setStatus('loading');\n setError(null);\n send({ type: 'read-execution-detail', requestId, entryId, cursor });\n },\n [send],\n );\n\n const openExecutionDetail = useCallback(\n (entryId: string): void => {\n setOpenEntryId(entryId);\n setRecords([]);\n setComplete(false);\n nextCursorRef.current = undefined;\n readPage(entryId);\n },\n [readPage],\n );\n\n const loadMoreExecutionDetail = useCallback((): void => {\n if (!openEntryId || status === 'loading' || complete) return;\n readPage(openEntryId, nextCursorRef.current);\n }, [openEntryId, status, complete, readPage]);\n\n const closeExecutionDetail = useCallback((): void => {\n setOpenEntryId(null);\n requestIdRef.current = null;\n entryIdRef.current = null;\n setRecords([]);\n setStatus('idle');\n setError(null);\n setComplete(false);\n }, []);\n\n const handleExecutionDetailMessage = useCallback((msg: TServerMessage): boolean => {\n if (msg.type !== 'execution_detail' && msg.type !== 'execution_detail_error') return false;\n if (requestIdRef.current !== msg.requestId) return true; // A stale/superseded read — ignored.\n if (msg.type === 'execution_detail_error') {\n setStatus('error');\n setError(msg.message);\n return true;\n }\n setRecords((previous) =>\n msg.page.cursor === undefined ? msg.page.records : [...previous, ...msg.page.records],\n );\n nextCursorRef.current = msg.page.nextCursor;\n setComplete(msg.page.nextCursor === undefined);\n setStatus('ready');\n return true;\n }, []);\n\n return {\n openEntryId,\n executionDetailStatus: status,\n executionDetailRecords: records,\n executionDetailError: error,\n executionDetailComplete: complete,\n openExecutionDetail,\n loadMoreExecutionDetail,\n closeExecutionDetail,\n handleExecutionDetailMessage,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\n\nimport type { IWsSessionState, TModelListSnapshot } from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TModelListState = Pick<IWsSessionState, 'modelList' | 'requestModelList'>;\n\nlet requestCounter = 0;\nfunction nextRequestId(): string {\n requestCounter += 1;\n return `model_list_${requestCounter}_${Date.now()}`;\n}\n\n/**\n * #3282 §2 (part 2) — the model list the model control's pop-up menu is built from, requested on\n * demand (`list-models`) and correlated by `requestId` (mirrors `usePersonalUsageState`'s pattern) so\n * a reply to an older request — one already superseded by a newer one, e.g. across a reconnect — is\n * never applied over the list a still-open menu is currently showing.\n */\nexport function useModelListState(send: (msg: TClientMessage) => void): TModelListState & {\n handleModelListMessage: (msg: TServerMessage) => boolean;\n} {\n const [modelList, setModelList] = useState<TModelListSnapshot | null>(null);\n const requestRef = useRef<string | null>(null);\n\n const handleModelListMessage = useCallback((msg: TServerMessage): boolean => {\n if (msg.type !== 'model_list') return false;\n if (requestRef.current !== msg.requestId) return true;\n setModelList({\n groups: msg.groups,\n currentModel: msg.currentModel,\n ...(msg.currentProfile !== undefined ? { currentProfile: msg.currentProfile } : {}),\n });\n return true;\n }, []);\n\n const requestModelList = useCallback((): void => {\n const requestId = nextRequestId();\n requestRef.current = requestId;\n send({ type: 'list-models', requestId });\n }, [send]);\n\n return { modelList, requestModelList, handleModelListMessage };\n}\n","import { useCallback, useRef, useState } from 'react';\n\nimport type {\n IWsSessionState,\n TCurrentSessionUsageReport,\n TPersonalUsageReport,\n TStoredSessionUsageReport,\n} from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TPersonalUsageState = Pick<\n IWsSessionState,\n | 'personalUsageStatus'\n | 'personalUsageReport'\n | 'personalUsageError'\n | 'requestPersonalUsage'\n | 'storedSessionUsageStatus'\n | 'storedSessionUsageReport'\n | 'storedSessionUsageSessionId'\n | 'storedSessionUsageError'\n | 'requestStoredSessionUsage'\n | 'currentSessionUsageStatus'\n | 'currentSessionUsageReport'\n | 'requestCurrentSessionUsage'\n>;\n\nlet requestCounter = 0;\nfunction nextRequestId(prefix: string): string {\n requestCounter += 1;\n return `${prefix}_${requestCounter}_${Date.now()}`;\n}\n\n/** Own the correlated aggregate/stored/current usage protocol state independently from chat state. */\nexport function usePersonalUsageState(send: (msg: TClientMessage) => void): TPersonalUsageState & {\n handleUsageMessage: (msg: TServerMessage) => boolean;\n} {\n const [personalUsageStatus, setPersonalUsageStatus] =\n useState<IWsSessionState['personalUsageStatus']>('idle');\n const [personalUsageReport, setPersonalUsageReport] = useState<TPersonalUsageReport | null>(null);\n const [personalUsageError, setPersonalUsageError] = useState<string | null>(null);\n const [storedSessionUsageStatus, setStoredSessionUsageStatus] =\n useState<IWsSessionState['storedSessionUsageStatus']>('idle');\n const [storedSessionUsageReport, setStoredSessionUsageReport] =\n useState<TStoredSessionUsageReport | null>(null);\n const [storedSessionUsageSessionId, setStoredSessionUsageSessionId] = useState<string | null>(\n null,\n );\n const [storedSessionUsageError, setStoredSessionUsageError] = useState<string | null>(null);\n const [currentSessionUsageStatus, setCurrentSessionUsageStatus] =\n useState<IWsSessionState['currentSessionUsageStatus']>('idle');\n const [currentSessionUsageReport, setCurrentSessionUsageReport] =\n useState<TCurrentSessionUsageReport | null>(null);\n const personalRequestRef = useRef<string | null>(null);\n const storedRequestRef = useRef<string | null>(null);\n\n const handleUsageMessage = useCallback((msg: TServerMessage): boolean => {\n if (msg.type === 'personal_usage_report' || msg.type === 'personal_usage_report_error') {\n if (personalRequestRef.current !== msg.requestId) return true;\n setPersonalUsageReport(msg.type === 'personal_usage_report' ? msg.report : null);\n setPersonalUsageError(msg.type === 'personal_usage_report_error' ? msg.message : null);\n setPersonalUsageStatus(msg.type === 'personal_usage_report' ? 'ready' : 'error');\n return true;\n }\n if (msg.type === 'usage_report') {\n setCurrentSessionUsageReport(msg.report);\n setCurrentSessionUsageStatus('ready');\n return true;\n }\n if (\n msg.type === 'stored_session_usage_report' ||\n msg.type === 'stored_session_usage_report_error'\n ) {\n if (storedRequestRef.current !== msg.requestId) return true;\n setStoredSessionUsageReport(msg.type === 'stored_session_usage_report' ? msg.report : null);\n setStoredSessionUsageSessionId(msg.sessionId);\n setStoredSessionUsageError(\n msg.type === 'stored_session_usage_report_error' ? msg.message : null,\n );\n setStoredSessionUsageStatus(msg.type === 'stored_session_usage_report' ? 'ready' : 'error');\n return true;\n }\n if (msg.type === 'protocol_error') {\n setCurrentSessionUsageStatus((current) => (current === 'loading' ? 'error' : current));\n }\n return false;\n }, []);\n\n const requestPersonalUsage = useCallback(\n (period: '7d' | '30d'): void => {\n const requestId = nextRequestId('personal_usage');\n personalRequestRef.current = requestId;\n setPersonalUsageStatus('loading');\n setPersonalUsageError(null);\n send({\n type: 'get-personal-usage-report',\n requestId,\n period,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',\n });\n },\n [send],\n );\n\n const requestStoredSessionUsage = useCallback(\n (sessionId: string): void => {\n const requestId = nextRequestId('stored_session_usage');\n storedRequestRef.current = requestId;\n setStoredSessionUsageStatus('loading');\n setStoredSessionUsageReport(null);\n setStoredSessionUsageSessionId(sessionId);\n setStoredSessionUsageError(null);\n send({ type: 'get-stored-session-usage-report', requestId, sessionId });\n },\n [send],\n );\n\n const requestCurrentSessionUsage = useCallback((): void => {\n setCurrentSessionUsageStatus('loading');\n send({ type: 'get-usage-report' });\n }, [send]);\n\n return {\n personalUsageStatus,\n personalUsageReport,\n personalUsageError,\n requestPersonalUsage,\n storedSessionUsageStatus,\n storedSessionUsageReport,\n storedSessionUsageSessionId,\n storedSessionUsageError,\n requestStoredSessionUsage,\n currentSessionUsageStatus,\n currentSessionUsageReport,\n requestCurrentSessionUsage,\n handleUsageMessage,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\n\nimport type { IWsSessionState } from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TProjectPanelState = Pick<\n IWsSessionState,\n | 'projectStatusState'\n | 'projectStatus'\n | 'requestProjectStatus'\n | 'projectDiffState'\n | 'projectDiffPath'\n | 'projectDiff'\n | 'requestProjectDiff'\n | 'projectMemoryState'\n | 'projectMemory'\n | 'requestProjectMemory'\n>;\n\nlet requestCounter = 0;\nfunction nextRequestId(prefix: string): string {\n requestCounter += 1;\n return `${prefix}_${requestCounter}_${Date.now()}`;\n}\n\n/**\n * #3282 §4c — the Project panel's correlated request/response state (status, one file's diff, project\n * memory), independent from chat state. Mirrors `usePersonalUsageState`'s shape: a status per read, a\n * `requestId` ref that drops a stale reply (a diff request superseded by clicking another file before\n * the first answered), and a request function per read.\n */\nexport function useProjectPanelState(\n send: (msg: TClientMessage) => void,\n): TProjectPanelState & { handleProjectMessage: (msg: TServerMessage) => boolean } {\n const [projectStatusState, setProjectStatusState] =\n useState<IWsSessionState['projectStatusState']>('idle');\n const [projectStatus, setProjectStatus] = useState<IWsSessionState['projectStatus']>(null);\n const [projectDiffState, setProjectDiffState] =\n useState<IWsSessionState['projectDiffState']>('idle');\n const [projectDiffPath, setProjectDiffPath] = useState<string | null>(null);\n const [projectDiff, setProjectDiff] = useState<IWsSessionState['projectDiff']>(null);\n const [projectMemoryState, setProjectMemoryState] =\n useState<IWsSessionState['projectMemoryState']>('idle');\n const [projectMemory, setProjectMemory] = useState<IWsSessionState['projectMemory']>(null);\n\n const statusRequestRef = useRef<string | null>(null);\n const diffRequestRef = useRef<string | null>(null);\n const memoryRequestRef = useRef<string | null>(null);\n\n const handleProjectMessage = useCallback((msg: TServerMessage): boolean => {\n if (msg.type === 'project_status') {\n if (statusRequestRef.current !== msg.requestId) return true;\n setProjectStatus(msg.result);\n setProjectStatusState('ready');\n return true;\n }\n if (msg.type === 'project_diff') {\n if (diffRequestRef.current !== msg.requestId) return true;\n setProjectDiff(msg.result);\n setProjectDiffState('ready');\n return true;\n }\n if (msg.type === 'project_memory') {\n if (memoryRequestRef.current !== msg.requestId) return true;\n setProjectMemory(msg.result);\n setProjectMemoryState('ready');\n return true;\n }\n if (msg.type === 'protocol_error' && msg.requestId !== undefined) {\n if (statusRequestRef.current === msg.requestId) setProjectStatusState('error');\n if (diffRequestRef.current === msg.requestId) setProjectDiffState('error');\n if (memoryRequestRef.current === msg.requestId) setProjectMemoryState('error');\n }\n return false;\n }, []);\n\n const requestProjectStatus = useCallback((): void => {\n const requestId = nextRequestId('project_status');\n statusRequestRef.current = requestId;\n setProjectStatusState('loading');\n send({ type: 'project-status', requestId });\n }, [send]);\n\n const requestProjectDiff = useCallback(\n (path: string): void => {\n const requestId = nextRequestId('project_diff');\n diffRequestRef.current = requestId;\n setProjectDiffPath(path);\n setProjectDiffState('loading');\n setProjectDiff(null);\n send({ type: 'project-diff', requestId, path });\n },\n [send],\n );\n\n const requestProjectMemory = useCallback((): void => {\n const requestId = nextRequestId('project_memory');\n memoryRequestRef.current = requestId;\n setProjectMemoryState('loading');\n send({ type: 'project-memory', requestId });\n }, [send]);\n\n return {\n projectStatusState,\n projectStatus,\n requestProjectStatus,\n projectDiffState,\n projectDiffPath,\n projectDiff,\n requestProjectDiff,\n projectMemoryState,\n projectMemory,\n requestProjectMemory,\n handleProjectMessage,\n };\n}\n","import { useCallback, useState } from 'react';\n\nimport type { IWsSessionState } from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TSchedulesState = Pick<\n IWsSessionState,\n 'scheduledTasks' | 'pauseSchedule' | 'resumeSchedule' | 'deleteSchedule'\n>;\n\n/**\n * #3282 §4 part b-3: the Agents panel's \"Scheduled\" group. No `requestId` correlates\n * `get-background-tasks`/`background_tasks` (the message carries none — see `wire-messages.ts`), so\n * this owns that whole message type for the GUI: every reply is a full refresh of the roster,\n * filtered server-side to `kind: 'scheduled'` (the same tasks `/schedule list` shows).\n *\n * Pause/resume send the SAME `command` text `/schedule pause <id>` / `/schedule resume <id>` runs.\n * Delete sends the existing `cancel-background-task` write — the same one Stop already uses for any\n * other background-task kind (#3288/#3332) — since a schedule IS a background task, and cancelling\n * one is permanent (distinct from the reversible pause), matching #3282 §4's \"Delete…\" requirement.\n */\nexport function useSchedulesState(send: (msg: TClientMessage) => void): TSchedulesState & {\n /** Handles `background_tasks` and a `cancel` `background_task_control_result` — both refresh. */\n handleSchedulesMessage: (msg: TServerMessage) => boolean;\n requestSchedules: () => void;\n} {\n const [scheduledTasks, setScheduledTasks] = useState<IWsSessionState['scheduledTasks']>([]);\n\n const requestSchedules = useCallback((): void => {\n send({ type: 'get-background-tasks', filter: { kind: 'scheduled' } });\n }, [send]);\n\n const handleSchedulesMessage = useCallback(\n (msg: TServerMessage): boolean => {\n if (msg.type === 'background_tasks') {\n // Defensive narrowing: the request already asked for `kind: 'scheduled'` only, but the wire\n // type carries the full union — this is the one place that turns \"should be\" into \"is\".\n setScheduledTasks(\n msg.tasks.filter(\n (task): task is IWsSessionState['scheduledTasks'][number] => task.kind === 'scheduled',\n ),\n );\n return true;\n }\n if (msg.type === 'background_task_control_result' && msg.action === 'cancel') {\n // A schedule's Delete — refresh so a successful cancel drops it from the group at once; a\n // refused one leaves the row exactly as it was (the central reducer's own case already\n // turns a failure into a plain-message session notice — #3288 §1 — so this never duplicates it).\n if (msg.success) requestSchedules();\n return false; // not exclusively ours — other future consumers may still want this event\n }\n if (msg.type === 'command_result' && msg.name === 'schedule' && msg.success) {\n // Pause/resume ran through the command path — refresh so the row's status/next-run follows.\n requestSchedules();\n return false; // the normal command-result handling (the conversation card) still applies\n }\n return false;\n },\n [requestSchedules],\n );\n\n const pauseSchedule = useCallback(\n (taskId: string): void => {\n send({ type: 'command', name: 'schedule', args: `pause ${taskId}` });\n },\n [send],\n );\n\n const resumeSchedule = useCallback(\n (taskId: string): void => {\n send({ type: 'command', name: 'schedule', args: `resume ${taskId}` });\n },\n [send],\n );\n\n const deleteSchedule = useCallback(\n (taskId: string): void => {\n send({ type: 'cancel-background-task', taskId });\n },\n [send],\n );\n\n return {\n scheduledTasks,\n pauseSchedule,\n resumeSchedule,\n deleteSchedule,\n handleSchedulesMessage,\n requestSchedules,\n };\n}\n","import { createContext, useContext, type ReactNode } from 'react';\n\n/** Only the public identity needed by a browser surface; hosts resolve its values. */\nexport interface IWebProductIdentity {\n readonly identity: { readonly displayName: string; readonly cliName: string };\n readonly storage: { readonly browserNamespace: string };\n}\n\nconst ProductIdentityContext = createContext<IWebProductIdentity | null>(null);\n\nexport function ProductIdentityProvider({ value, children }: {\n readonly value: IWebProductIdentity;\n readonly children?: ReactNode;\n}): React.ReactElement {\n if (!value.identity.displayName || !value.identity.cliName || !value.storage.browserNamespace) {\n throw new Error('A public product identity is required');\n }\n // Changing product replaces the subtree so drafts, in-flight requests and session refs cannot cross it.\n return <ProductIdentityContext.Provider key={value.storage.browserNamespace} value={value}>{children}</ProductIdentityContext.Provider>;\n}\n\nexport function useProductIdentity(): IWebProductIdentity {\n const identity = useContext(ProductIdentityContext);\n if (identity === null) throw new Error('ProductIdentityProvider is required');\n return identity;\n}\n","import { useProductIdentity } from '../product-identity.js';\nimport { useCallback, useRef, useState } from 'react';\n\nimport type { IWsSessionState, TSessionListing, TSessionsError } from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TSessionDirectoryState = Pick<\n IWsSessionState,\n | 'sessionListing'\n | 'sessionsError'\n | 'requestSessions'\n | 'switchSession'\n | 'newSession'\n | 'renameSessionInList'\n | 'deleteSession'\n | 'sessionSidebarOpen'\n | 'setSessionSidebarOpen'\n>;\n\nlet requestCounter = 0;\nfunction nextRequestId(kind: 'sessions' | 'session_change' | 'session_rename' | 'session_delete'): string {\n requestCounter += 1;\n return `${kind}_${requestCounter}_${Date.now()}`;\n}\n\n/** A host that keeps several sessions live says which rows are; an older host says nothing. */\nfunction keepsSessionsLive(listing: TSessionListing): boolean {\n return listing.sessions.some((row) => typeof row.live === 'boolean');\n}\n\n/**\n * Issue #3280 §5: a desktop Reconnect reloads the page, which loses every bit of JS state — so the\n * session to go back to cannot live in a ref like `lastSwitchedIdRef` below. `sessionStorage` survives\n * the reload (and only that tab), so it carries the id across it. Namespaced so a page hosting other\n * state under the same origin does not collide.\n */\nfunction restoreSessionStorageKey(namespace: string): string { return `${namespace}.restoreSessionId`; }\n\n/**\n * Remember `sessionId` so the reload a desktop Reconnect triggers can switch back to it once the\n * first session listing arrives (see {@link useSessionDirectoryState}). Best-effort: a host whose\n * storage is unavailable simply lands on whatever session the daemon puts a fresh connection on, same\n * as before this existed.\n */\nexport function rememberSessionForRestore(namespace: string, sessionId: string): void {\n try {\n window.sessionStorage.setItem(restoreSessionStorageKey(namespace), sessionId);\n } catch {\n /* storage unavailable — nothing to restore after the reload */\n }\n}\n\nfunction readStoredRestoreId(namespace: string): string | null {\n try {\n return window.sessionStorage.getItem(restoreSessionStorageKey(namespace));\n } catch {\n return null;\n }\n}\n\nfunction clearStoredRestoreId(namespace: string): void {\n try {\n window.sessionStorage.removeItem(restoreSessionStorageKey(namespace));\n } catch {\n /* noop — nothing was readable to begin with */\n }\n}\n\n/** A wide window starts with the sidebar open; a narrow one keeps the conversation in view. */\nfunction initialSidebarOpen(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return true;\n return window.matchMedia('(min-width: 768px)').matches;\n}\n\n/**\n * #3189 — the host's session list beside the conversation: requests correlated by id (the latest\n * wins), the start/switch commands, and whether the sidebar is shown.\n *\n * A host that keeps sessions live binds each connection to its own session and puts a new connection\n * on its primary one. After a reconnect to such a host this surface goes back to the session it was\n * on; an older host has one session for everyone, so there is nothing to go back to.\n */\nexport function useSessionDirectoryState(send: (msg: TClientMessage) => void): TSessionDirectoryState & {\n handleSessionsMessage: (msg: TServerMessage) => boolean;\n /** Whether the host can list sessions, as far as its last answer says. */\n canListSessions: () => boolean;\n /** The host made `sessionId` current; show it as current until the fresh list arrives. */\n markCurrent: (sessionId: string) => void;\n /** The connection is (re)established: the next listing decides whether to go back. */\n armRestore: () => void;\n} {\n const { storage: { browserNamespace } } = useProductIdentity();\n const [sessionListing, setSessionListing] = useState<TSessionListing | null>(null);\n const [sessionsError, setSessionsError] = useState<TSessionsError | null>(null);\n const [sessionSidebarOpen, setSessionSidebarOpen] = useState(initialSidebarOpen);\n const latestRequestRef = useRef<string | null>(null);\n const notAvailableRef = useRef(false);\n\n const requestSessions = useCallback((): void => {\n const requestId = nextRequestId('sessions');\n latestRequestRef.current = requestId;\n send({ type: 'list-sessions', requestId });\n }, [send]);\n // The host answers a switch to the current session with nothing; asking it would leave the\n // surface waiting for a reply that never comes.\n const currentIdRef = useRef<string | null>(null);\n currentIdRef.current = sessionListing?.currentSessionId ?? null;\n const switchSession = useCallback(\n (sessionId: string): void => {\n if (sessionId === currentIdRef.current) return;\n send({ type: 'switch-session', sessionId, requestId: nextRequestId('session_change') });\n },\n [send, browserNamespace],\n );\n const newSession = useCallback(\n (): void => send({ type: 'new-session', requestId: nextRequestId('session_change') }),\n [send, browserNamespace],\n );\n // #3289 §1: rename or delete a row in the list — current or not. Both answer on their own\n // correlated reply (`session_renamed_in_list`/`session_deleted`, or a `_failed` counterpart); the\n // reducer in `useSessionClient` refreshes the listing on success and raises a notice on failure.\n const renameSessionInList = useCallback(\n (sessionId: string, name: string): void =>\n send({ type: 'rename-session', sessionId, name, requestId: nextRequestId('session_rename') }),\n [send, browserNamespace],\n );\n const deleteSession = useCallback(\n (sessionId: string): void =>\n send({ type: 'delete-session', sessionId, requestId: nextRequestId('session_delete') }),\n [send, browserNamespace],\n );\n\n // The session this surface last moved to, and — once per connection — whether to go back to it.\n const lastSwitchedIdRef = useRef<string | null>(null);\n const restoreTargetRef = useRef<string | null>(null);\n const armRestore = useCallback((): void => {\n // A remembered id (issue #3280 §5, a desktop Reconnect's reload) wins over the same-page target:\n // on a fresh page, `lastSwitchedIdRef` is always null anyway, so this only ever adds a source.\n restoreTargetRef.current = readStoredRestoreId(browserNamespace) ?? lastSwitchedIdRef.current;\n }, [browserNamespace]);\n const restoreAfterReconnect = useCallback(\n (listing: TSessionListing): void => {\n const target = restoreTargetRef.current;\n restoreTargetRef.current = null;\n if (target === null) return;\n // Spent on this first listing whether or not the switch below actually happens — a fresh\n // launch with no remembered id never reaches here (target stays null), so nothing switches.\n clearStoredRestoreId(browserNamespace);\n if (target === listing.currentSessionId) return;\n if (!keepsSessionsLive(listing)) return;\n // A session the host no longer lists (an empty one it never saved) is nothing to go back to.\n if (!listing.sessions.some((row) => row.id === target)) return;\n send({ type: 'switch-session', sessionId: target, requestId: nextRequestId('session_change') });\n },\n [send, browserNamespace],\n );\n\n const handleSessionsMessage = useCallback(\n (msg: TServerMessage): boolean => {\n if (msg.type !== 'sessions' && msg.type !== 'sessions_error') return false;\n if (msg.requestId !== latestRequestRef.current) return true;\n if (msg.type === 'sessions') {\n notAvailableRef.current = false;\n setSessionListing(msg.listing);\n setSessionsError(null);\n restoreAfterReconnect(msg.listing);\n return true;\n }\n restoreTargetRef.current = null;\n notAvailableRef.current = msg.code === 'not_available';\n if (msg.code === 'not_available') setSessionListing(null);\n setSessionsError({ code: msg.code, message: msg.message });\n return true;\n },\n [restoreAfterReconnect],\n );\n\n const canListSessions = useCallback((): boolean => !notAvailableRef.current, []);\n const markCurrent = useCallback((sessionId: string): void => {\n lastSwitchedIdRef.current = sessionId;\n setSessionListing((previous) =>\n previous === null ? previous : { ...previous, currentSessionId: sessionId },\n );\n }, [browserNamespace]);\n\n return {\n sessionListing,\n sessionsError,\n requestSessions,\n switchSession,\n newSession,\n renameSessionInList,\n deleteSession,\n sessionSidebarOpen,\n setSessionSidebarOpen,\n handleSessionsMessage,\n canListSessions,\n markCurrent,\n armRestore,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\n\nimport type { IWsSessionState } from './session-client-types.js';\nimport type { TClientMessage } from '../client/ws-session-client.js';\nimport type { ISettingsSnapshot, TSettingsPatch } from '@robota-sdk/agent-interface-session';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\ntype TSettingsState = Pick<\n IWsSessionState,\n | 'settingsOpen'\n | 'settingsStatus'\n | 'settingsSnapshot'\n | 'settingsError'\n | 'settingsInitialSectionId'\n | 'openSettings'\n | 'closeSettings'\n | 'updateSettings'\n>;\n\nlet requestCounter = 0;\nfunction nextRequestId(prefix: string): string {\n requestCounter += 1;\n return `${prefix}_${requestCounter}_${Date.now()}`;\n}\n\n/**\n * #3282 §4a: the Settings screen's own correlated request/response state, independent from chat\n * state — the same shape `usePersonalUsageState` already uses for `get-personal-usage-report`.\n *\n * `openSettings` re-fetches every time (opening from the gear, `/settings`, or reopening after a\n * close): the simplest correct way for a change made elsewhere — another typed command, another\n * client — to reach an open-then-reopened screen, per #3282 §4's \"pick the simplest correct option.\"\n *\n * A failed UPDATE never touches `settingsSnapshot`, so a control bound to it shows the value it had\n * before the write — \"a failed write leaves the control at its previous value\" — while\n * `settingsError` carries the plain message to show beside it. A failed INITIAL load (no snapshot to\n * fall back to) is the one case that flips `settingsStatus` to `'error'`.\n */\nexport function useSettingsState(send: (msg: TClientMessage) => void): TSettingsState & {\n handleSettingsMessage: (msg: TServerMessage) => boolean;\n /**\n * #3282 §4b: re-fetch without touching `settingsOpen`/`settingsInitialSectionId` — for a change\n * made elsewhere while the screen is already open (e.g. \"Add provider…\"'s ask flow finishing).\n * Unlike `openSettings`, this never resets which section is showing.\n */\n refreshSettings: () => void;\n} {\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [settingsStatus, setSettingsStatus] = useState<IWsSessionState['settingsStatus']>('idle');\n const [settingsSnapshot, setSettingsSnapshot] = useState<ISettingsSnapshot | null>(null);\n const [settingsError, setSettingsError] = useState<string | null>(null);\n // #3282 §4 part b-2 / §4b: which section to land on THIS open — `/plugin` opens straight to\n // Plugins, \"Manage providers…\" straight to Providers & Models; everything else (the gear,\n // `/settings`) opens on the screen's own default (General).\n const [settingsInitialSectionId, setSettingsInitialSectionId] = useState<string | null>(null);\n const requestRef = useRef<string | null>(null);\n\n const handleSettingsMessage = useCallback((msg: TServerMessage): boolean => {\n if (msg.type !== 'settings' && msg.type !== 'settings_error') return false;\n if (requestRef.current !== msg.requestId) return true; // superseded by a later request\n if (msg.type === 'settings') {\n setSettingsSnapshot(msg.settings);\n setSettingsStatus('ready');\n setSettingsError(null);\n return true;\n }\n setSettingsError(msg.message);\n setSettingsStatus((current) => (current === 'ready' ? 'ready' : 'error'));\n return true;\n }, []);\n\n const fetchSettings = useCallback((): void => {\n setSettingsStatus((current) => (current === 'ready' ? 'ready' : 'loading'));\n setSettingsError(null);\n const requestId = nextRequestId('get_settings');\n requestRef.current = requestId;\n send({ type: 'get-settings', requestId });\n }, [send]);\n\n const openSettings = useCallback(\n (sectionId?: string): void => {\n setSettingsOpen(true);\n setSettingsInitialSectionId(sectionId ?? null);\n fetchSettings();\n },\n [fetchSettings],\n );\n\n const closeSettings = useCallback((): void => {\n setSettingsOpen(false);\n }, []);\n\n const updateSettings = useCallback(\n (patch: TSettingsPatch): void => {\n const requestId = nextRequestId('update_settings');\n requestRef.current = requestId;\n setSettingsError(null);\n send({ type: 'update-settings', requestId, patch });\n },\n [send],\n );\n\n return {\n settingsOpen,\n settingsStatus,\n settingsSnapshot,\n settingsError,\n settingsInitialSectionId,\n openSettings,\n closeSettings,\n updateSettings,\n handleSettingsMessage,\n refreshSettings: fetchSettings,\n };\n}\n","/**\n * GUI-005 — the transport-neutral session reducer for the GUI presentation layer. Reconstructs conversation\n * state from the wire `TServerMessage` stream, independent of HOW the bytes arrive (a `TMakeSessionClient`\n * factory over `{onMessage, onStatusChange}`). The core is **generic over its status type** (default\n * `TConnectionStatus`) so a surface with extra connection states (e.g. the browser WebRTC surface) can widen it\n * WITHOUT this core depending on that surface's types — keeping the dependency direction acyclic.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\n\nimport { excludedCommandMessage, isExcludedCommand } from './excluded-commands.js';\nimport {\n applyPromptEvent,\n askResponse,\n permissionResponse,\n type TPendingPrompt,\n} from './prompt-state.js';\nimport {\n describeUiIntentForGui,\n guiScreenForUiIntent,\n uiIntentCommandName,\n} from './ui-intent-state.js';\nimport { createWsSessionClient } from '../client/ws-session-client.js';\nimport { SERVER_MESSAGE_HANDLING } from './server-message-handling.js';\nimport { useAgentSwitcherState } from './use-agent-switcher-state.js';\nimport { useExecutionDetailState } from './use-execution-detail.js';\nimport { useModelListState } from './use-model-list.js';\nimport { usePersonalUsageState } from './use-personal-usage.js';\nimport { useProjectPanelState } from './use-project-panel.js';\nimport { useSchedulesState } from './use-schedules-state.js';\nimport { useSessionDirectoryState } from './use-session-directory.js';\nimport { useSettingsState } from './use-settings-state.js';\n\nimport type {\n IActiveTool,\n IChangedFileSummary,\n IQueuedPrompt,\n ISessionClientHandle,\n ISessionNotice,\n IWsSessionState,\n TCommandCatalog,\n TConversationEntry,\n TMakeSessionClient,\n TSessionStatus,\n} from './session-client-types.js';\nimport type { TConnectionStatus, TClientMessage } from '../client/ws-session-client.js';\nimport type { TActionResponse } from '@robota-sdk/agent-interface-transport';\nimport type {\n IHistoryDisplaySegment,\n IToolState,\n TDriverId,\n TPermissionResultValue,\n} from '@robota-sdk/agent-interface-session';\nimport type { IExecutionWorkspaceSnapshot } from '@robota-sdk/agent-interface-execution';\nimport type { TServerMessage } from '@robota-sdk/agent-transport';\n\nexport type {\n IActiveTool,\n IChangedFileSummary,\n IChangedFilesEntry,\n ICommandOutputEntry,\n IConversationMessage,\n IToolGroupEntry,\n IQueuedPrompt,\n ISessionClientHandle,\n ISessionNotice,\n IWsSessionState,\n TConversationEntry,\n TMakeSessionClient,\n} from './session-client-types.js';\n\nlet msgCounter = 0;\nfunction nextId(): string {\n return `msg_${++msgCounter}_${Date.now()}`;\n}\n\nlet silentCommandRequestCounter = 0;\nfunction nextSilentCommandRequestId(): string {\n silentCommandRequestCounter += 1;\n return `silent_command_${silentCommandRequestCounter}_${Date.now()}`;\n}\n\n/**\n * #3288: a finished turn's text and tool calls, in the order they actually happened — a running\n * turn accumulates these alongside (not instead of) the legacy `streamingText`/`activeTools` live\n * view, and `finishTurn` flushes them as SEPARATE conversation entries in order, instead of one\n * tools-block followed by one merged text block. Consecutive tool calls with no text between them\n * collapse into one `tools` segment (one expandable group), matching the TUI's own transcript order.\n */\ninterface ITurnTextSegment {\n readonly type: 'text';\n readonly id: string;\n readonly text: string;\n}\ninterface ITurnToolsSegment {\n readonly type: 'tools';\n readonly id: string;\n readonly tools: readonly IActiveTool[];\n}\ntype TTurnSegment = ITurnTextSegment | ITurnToolsSegment;\n\nfunction appendTextDeltaToSegments(segments: TTurnSegment[], delta: string): TTurnSegment[] {\n const last = segments[segments.length - 1];\n if (last && last.type === 'text') {\n return [...segments.slice(0, -1), { ...last, text: last.text + delta }];\n }\n return [...segments, { type: 'text', id: nextId(), text: delta }];\n}\n\nfunction pushToolStartSegment(segments: TTurnSegment[], tool: IActiveTool): TTurnSegment[] {\n const last = segments[segments.length - 1];\n if (last && last.type === 'tools') {\n return [...segments.slice(0, -1), { ...last, tools: [...last.tools, tool] }];\n }\n return [...segments, { type: 'tools', id: nextId(), tools: [tool] }];\n}\n\n/**\n * #3288: attribute a `tool_end` to its call by executionId first — two same-named parallel calls\n * can finish out of start order, and matching by name+running alone would close whichever running\n * call of that name is found first, not the one that actually finished. Falls back to name+running\n * only when the event carries no executionId (legacy fixtures / hosts that predate it).\n */\nfunction findRunningToolIndex(\n tools: readonly IActiveTool[],\n state: { toolName: string; executionId?: string },\n): number {\n return state.executionId !== undefined\n ? tools.findIndex((t) => t.executionId === state.executionId && t.status === 'running')\n : tools.findIndex((t) => t.name === state.toolName && t.status === 'running');\n}\n\n/**\n * The fields an `IToolState` contributes to a FRESH `IActiveTool` — shared by live `tool_start` (which\n * adds `status: 'running'` itself) and history replay (which merges `toolEndFields` on top, since a\n * replayed call is always already finished). Factored out so the two paths cannot drift on what a\n * tool row's identity fields are.\n */\nfunction baseActiveToolFrom(state: IToolState): Omit<IActiveTool, 'status'> {\n return {\n id: nextId(),\n name: state.toolName,\n input: state.firstArg,\n ...(state.executionId ? { executionId: state.executionId } : {}),\n ...(state.displayPath ? { displayPath: state.displayPath } : {}),\n ...(state.commandName ? { commandName: state.commandName } : {}),\n ...(state.internal ? { internal: true } : {}),\n };\n}\n\n/** The fields a `tool_end` state contributes to the matched `IActiveTool`. */\nfunction toolEndFields(\n state: IToolState,\n): Pick<\n IActiveTool,\n 'status' | 'result' | 'diffLines' | 'diffFile' | 'toolResultData' | 'toolResultParts'\n> {\n return {\n // A tool that finished with an error result renders failed (ToolCard/ToolGroup key off\n // `status === 'error'`) — `isRunning` alone cannot tell success from failure.\n status: state.isRunning\n ? 'running'\n : state.result === 'error' || state.result === 'denied'\n ? 'error'\n : 'done',\n result: state.result,\n ...(state.diffLines ? { diffLines: state.diffLines } : {}),\n ...(state.diffFile ? { diffFile: state.diffFile } : {}),\n ...(state.toolResultData !== undefined ? { toolResultData: state.toolResultData } : {}),\n ...(state.toolResultParts ? { toolResultParts: state.toolResultParts } : {}),\n };\n}\n\n/** Apply a `tool_end` to the ONE matching running tool across a turn's segments (never all of them). */\nfunction applyToolEndToSegments(segments: TTurnSegment[], state: IToolState): TTurnSegment[] {\n let matched = false;\n return segments.map((segment) => {\n if (matched || segment.type !== 'tools') return segment;\n const idx = findRunningToolIndex(segment.tools, state);\n if (idx === -1) return segment;\n matched = true;\n const tools = [...segment.tools];\n tools[idx] = { ...tools[idx]!, ...toolEndFields(state) };\n return { ...segment, tools };\n });\n}\n\n/**\n * #3288: the files an Edit/Write call touched during the turn, aggregated by path — the \"Changed\n * files\" row's data. `diffLines` keeps the LATEST edit's diff (what a click on the file opens); the\n * +/- counts sum every edit to that path within the turn.\n */\nfunction collectChangedFiles(segments: readonly TTurnSegment[]): IChangedFileSummary[] {\n const byPath = new Map<string, IChangedFileSummary>();\n for (const segment of segments) {\n if (segment.type !== 'tools') continue;\n for (const tool of segment.tools) {\n if (!tool.diffFile || !tool.diffLines || tool.diffLines.length === 0) continue;\n const added = tool.diffLines.filter((line) => line.type === 'add').length;\n const removed = tool.diffLines.filter((line) => line.type === 'remove').length;\n const existing = byPath.get(tool.diffFile);\n byPath.set(tool.diffFile, {\n path: tool.diffFile,\n added: (existing?.added ?? 0) + added,\n removed: (existing?.removed ?? 0) + removed,\n diffLines: tool.diffLines,\n });\n }\n }\n return [...byPath.values()];\n}\n\n/** A replayed call's `IActiveTool`: `baseActiveToolFrom`'s identity fields plus `tool_end`'s result\n * fields — a replayed call is always already finished, so `toolEndFields` always yields `status`\n * `'done'`/`'error'`, never `'running'`. */\nfunction activeToolFromHistoricalState(state: IToolState): IActiveTool {\n return { ...baseActiveToolFrom(state), ...toolEndFields(state) };\n}\n\n/**\n * #3288 §2: turns the server's chronological `IHistoryDisplaySegment[]` (a reload/reconnect/resume\n * replay) into the SAME `TConversationEntry[]` shape a live turn's `finishTurn` produces — reusing its\n * exact segment-accumulation and \"Changed files\" logic, so the two paths cannot draw the turn/grouping\n * boundary differently. A `'text', role: 'user'` segment is both its own entry AND the turn boundary:\n * everything accumulated since the previous one (the assistant's reply, its tool calls in between, and\n * one \"Changed files\" row when any of those touched a file) flushes immediately before it.\n */\nfunction buildConversationEntriesFromDisplaySegments(\n displaySegments: readonly IHistoryDisplaySegment[],\n): TConversationEntry[] {\n const entries: TConversationEntry[] = [];\n let turnSegments: TTurnSegment[] = [];\n\n const flushTurn = (): void => {\n for (const segment of turnSegments) {\n entries.push(\n segment.type === 'tools'\n ? { id: segment.id, role: 'tools', tools: segment.tools }\n : { id: segment.id, role: 'assistant', content: segment.text },\n );\n }\n const changedFiles = collectChangedFiles(turnSegments);\n if (changedFiles.length > 0) {\n entries.push({ id: nextId(), role: 'changed-files', files: changedFiles });\n }\n turnSegments = [];\n };\n\n for (const segment of displaySegments) {\n if (segment.type === 'text' && segment.role === 'user') {\n flushTurn();\n entries.push({ id: nextId(), role: 'user', content: segment.content });\n } else if (segment.type === 'text') {\n turnSegments = appendTextDeltaToSegments(turnSegments, segment.content);\n } else {\n turnSegments = pushToolStartSegment(\n turnSegments,\n activeToolFromHistoricalState(segment.tool),\n );\n }\n }\n flushTurn();\n\n return entries;\n}\n\nexport function useSessionClient<TStatus extends string = TConnectionStatus>(\n makeClient: TMakeSessionClient<TStatus>,\n): IWsSessionState<TStatus> {\n const [status, setStatus] = useState<TStatus>('disconnected' as TStatus);\n const [messages, setMessages] = useState<TConversationEntry[]>([]);\n const [activeTools, setActiveTools] = useState<IActiveTool[]>([]);\n const [streamingText, setStreamingText] = useState('');\n const [isThinking, setIsThinking] = useState(false);\n const [executionWorkspace, setExecutionWorkspace] = useState<IExecutionWorkspaceSnapshot | null>(\n null,\n );\n const [pendingPrompts, setPendingPrompts] = useState<readonly TPendingPrompt[]>([]);\n const [queuedPrompt, setQueuedPrompt] = useState<IQueuedPrompt | null>(null);\n const [sessionName, setSessionName] = useState<string | null>(null);\n const [sessionNotices, setSessionNotices] = useState<readonly ISessionNotice[]>([]);\n const [commandCatalog, setCommandCatalog] = useState<TCommandCatalog | null>(null);\n // #3282 §4e: `send` (below) reads this on every `command` message to catch an excluded command —\n // a ref (not the state above) so `send`'s identity never changes as the catalog arrives or is\n // refreshed, matching every other value a stable callback in this file reads this way.\n const commandCatalogRef = useRef<TCommandCatalog | null>(null);\n const [sessionStatus, setSessionStatus] = useState<TSessionStatus | null>(null);\n // #3289 §3: learned from the first frame the server sends this connection, so its own messages and\n // prompts never carry a \"from\" label — only ANOTHER driver's do.\n const [ownDriverId, setOwnDriverId] = useState<TDriverId | null>(null);\n // #3282 §4e: `/help` opens the GUI's own Help sheet — it never reaches the session (`send` below\n // catches it), so there is no round trip and no stale terminal-style text dump to replace.\n const [helpOpen, setHelpOpen] = useState(false);\n const openHelp = useCallback((): void => setHelpOpen(true), []);\n const closeHelp = useCallback((): void => setHelpOpen(false), []);\n\n const clientRef = useRef<ISessionClientHandle | null>(null);\n const streamingTextRef = useRef('');\n // The tools of the running turn, mirrored so the turn's end can keep them in the conversation.\n const activeToolsRef = useRef<IActiveTool[]>([]);\n // #3288: the running turn's text/tools in the order they happened — see `TTurnSegment` above.\n const turnSegmentsRef = useRef<TTurnSegment[]>([]);\n // A screen this surface's command asked for: it answers the command in place of the command's own\n // reply — with the \"not available\" line (`text`), or with nothing when the screen opened (null).\n const pendingIntentRef = useRef<{ name: string; text: string | null } | null>(null);\n // The latest `session_status`, read (not `sessionStatus` state) when a `model_unavailable` notice\n // is created, so the model it names is a snapshot from that moment — never the live status, which\n // `case 'error'` itself just asked the host to refresh and which the person may since have changed.\n const sessionStatusRef = useRef<TSessionStatus | null>(null);\n const updateActiveTools = useCallback(\n (next: (previous: IActiveTool[]) => IActiveTool[]): void => {\n activeToolsRef.current = next(activeToolsRef.current);\n setActiveTools(activeToolsRef.current);\n },\n [],\n );\n const appendEntry = useCallback((entry: TConversationEntry): void => {\n setMessages((previous) => [...previous, entry]);\n }, []);\n /**\n * End the running turn: its text and tool calls stay in the conversation as SEPARATE entries, in\n * the order they happened (#3288) — not one tools-block grouped ahead of a single merged reply.\n */\n const finishTurn = useCallback(\n (toolStatus: (tool: IActiveTool) => IActiveTool['status']): void => {\n const segments = turnSegmentsRef.current;\n turnSegmentsRef.current = [];\n streamingTextRef.current = '';\n setStreamingText('');\n setIsThinking(false);\n updateActiveTools(() => []);\n for (const segment of segments) {\n if (segment.type === 'tools') {\n appendEntry({\n id: segment.id,\n role: 'tools',\n tools: segment.tools.map((tool) => ({ ...tool, status: toolStatus(tool) })),\n });\n } else {\n appendEntry({ id: segment.id, role: 'assistant', content: segment.text });\n }\n }\n // #3288: a turn that changed files ends with one compact summary row, after everything else.\n const changedFiles = collectChangedFiles(segments);\n if (changedFiles.length > 0) {\n appendEntry({ id: nextId(), role: 'changed-files', files: changedFiles });\n }\n },\n [appendEntry, updateActiveTools],\n );\n // Commands this surface sent whose result has not come back — a screen request pairs with one.\n const commandsInFlightRef = useRef(0);\n // #3282 §2 (part 2): the `requestId`s of commands sent via `sendCommandSilently`, still awaiting\n // their `command_result`. The wire already correlates a command with its reply by `requestId` (see\n // `TClientMessage`'s `command` / `TServerMessage`'s `command_result` in `@robota-sdk/agent-transport`)\n // — a plain `send` command and a `sendCommandSilently` command can be outstanding at once, and only\n // this correlation (not send order, which a slow host or a race can violate) reliably tells which\n // in-flight command a given reply answers. A `command_result` with no `requestId` (an older host, or\n // a typed command, which never sets one) always shows its card — silence is opt-in per request, never\n // the default, so a reply this surface cannot identify is never hidden.\n const silentCommandRequestIdsRef = useRef(new Set<string>());\n // Session changes this surface asked for; a refusal answers one of them, not a command.\n const sessionChangesInFlightRef = useRef(0);\n // Their request ids, for this connection: a refusal names the request it answers. A switch\n // answers with no id, so an id that succeeded stays here until the connection is replaced.\n const sessionChangeRequestIdsRef = useRef(new Set<string>());\n const send = useCallback(\n (msg: TClientMessage): void => {\n if (msg.type === 'command') {\n // #3282 §4e: `/help` is the GUI's own Help sheet — never sent to the session.\n if (msg.name === 'help') {\n setHelpOpen(true);\n return;\n }\n // #3282 §4e: a command the GUI leaves out on purpose is caught here, before the session ever\n // sees it — its own plain sentence answers in place of the session's refusal, exactly like a\n // typed command's own result would (same entry shape, same place in the conversation). The\n // catalog entry (when known) carries `runner`/`surfaces`, so a client-only command excluded\n // only by declaration — not yet given its own curated name below — is still caught here, not\n // just filtered from the `/` menu (`command-menu.ts` checks the same way, catalog in hand).\n const catalogEntry = commandCatalogRef.current?.commands.find((c) => c.name === msg.name);\n if (isExcludedCommand(catalogEntry ?? { name: msg.name })) {\n appendEntry({\n id: nextId(),\n role: 'command',\n name: msg.name,\n content: excludedCommandMessage(msg.name),\n tone: 'info',\n });\n return;\n }\n commandsInFlightRef.current += 1;\n }\n if (msg.type === 'switch-session' || msg.type === 'new-session') {\n sessionChangesInFlightRef.current += 1;\n if (msg.requestId !== undefined) sessionChangeRequestIdsRef.current.add(msg.requestId);\n }\n clientRef.current?.send(msg);\n },\n [appendEntry],\n );\n /** One session change this surface asked for has been answered. */\n const settleSessionChange = useCallback((requestId?: string): void => {\n sessionChangesInFlightRef.current = Math.max(0, sessionChangesInFlightRef.current - 1);\n if (requestId !== undefined) sessionChangeRequestIdsRef.current.delete(requestId);\n }, []);\n /**\n * #3282 §2 (part 2): run a command the same way `send` does, but flag it as controls-triggered —\n * the model/mode/effort pop-up menus use this instead of `send` directly, so their change is\n * confirmed by the control's own label (via the `get-status` refresh `command_result` already\n * triggers) rather than a conversation card. A typed `/command` still goes through plain `send`\n * (and so never carries a `requestId`, keeping it outside this mechanism entirely). The `requestId`\n * is generated here, not read back off `msg`, because `send`'s parameter type is the public\n * `TClientMessage` — giving `command` a `requestId` there would let ANY caller of `send` opt a typed\n * command into silence, which is exactly what this must not allow.\n *\n * #3282 §4e: `send` also intercepts `help` and an excluded command before either reaches the wire —\n * for `help` that opens the sheet regardless of how it was asked for, but an excluded command's\n * interception adds a VISIBLE info card, defeating the silence this function promises. Every current\n * caller (model/mode/effort) names neither, so this is dormant, not exercised — a future caller must\n * not pass one of those two through here.\n */\n const sendCommandSilently = useCallback(\n (name: string, args?: string): void => {\n const requestId = nextSilentCommandRequestId();\n silentCommandRequestIdsRef.current.add(requestId);\n send({ type: 'command', name, requestId, ...(args ? { args } : {}) });\n },\n [send],\n );\n const { handleUsageMessage, ...personalUsageState } = usePersonalUsageState(send);\n const { handleExecutionDetailMessage, closeExecutionDetail, ...executionDetailState } =\n useExecutionDetailState(send);\n const { handleModelListMessage, ...modelListState } = useModelListState(send);\n const { handleProjectMessage, ...projectPanelState } = useProjectPanelState(send);\n const {\n handleSessionsMessage,\n canListSessions,\n markCurrent,\n armRestore,\n ...sessionDirectoryState\n } = useSessionDirectoryState(send);\n const { requestSessions, setSessionSidebarOpen } = sessionDirectoryState;\n const { handleSettingsMessage, openSettings, refreshSettings, ...settingsState } =\n useSettingsState(send);\n // #3282 §4b: read inside `handleMessage` via a ref, never `settingsState.settingsOpen` directly —\n // that reactive value would have to sit in `handleMessage`'s own dependency array, which sits in\n // the connection effect's dependency array below: opening/closing Settings would give\n // `handleMessage` a new identity each time, tearing down and reopening the WebSocket underneath it.\n const settingsOpenRef = useRef(false);\n settingsOpenRef.current = settingsState.settingsOpen;\n const {\n handleAgentDefinitionsMessage,\n resolveSwitchResult,\n openAgentSwitcher,\n ...agentSwitcherState\n } = useAgentSwitcherState(send);\n const { handleSchedulesMessage, requestSchedules, ...schedulesState } = useSchedulesState(send);\n\n const handleMessage = useCallback(\n (msg: TServerMessage): void => {\n // ARCH-2164: touching the exhaustive registry here keeps every decoded variant tied to an\n // explicit GUI ownership decision, including variants intentionally handled by focused views.\n void SERVER_MESSAGE_HANDLING[msg.type];\n if (handleUsageMessage(msg)) return;\n if (handleExecutionDetailMessage(msg)) return;\n if (handleModelListMessage(msg)) return;\n if (handleProjectMessage(msg)) return;\n if (handleSessionsMessage(msg)) return;\n if (handleSettingsMessage(msg)) return;\n if (handleAgentDefinitionsMessage(msg)) return;\n // #3282 §4 part b-3: exclusively owns `background_tasks` (the Scheduled group's roster); a\n // schedule write instead rides the generic `command`/`cancel-background-task` messages, so\n // for THOSE types this always returns false — it only refreshes on top of whatever else those\n // types already do below, never in place of it.\n if (handleSchedulesMessage(msg)) return;\n switch (msg.type) {\n case 'messages': {\n // #3288 §2: `display` is the server's projection of this same history into tool rows,\n // diffs and \"Changed files\" — a reload/reconnect/resume replay renders THAT. `display` is\n // absent only for a host that predates it; that host still gets a working (text-only)\n // transcript, exactly what this branch always did.\n const reconstructed: TConversationEntry[] = msg.display\n ? buildConversationEntriesFromDisplaySegments(msg.display)\n : msg.messages.flatMap((m) => {\n if (m.role !== 'user' && m.role !== 'assistant') return [];\n const content = m.content ?? '';\n return [{ id: nextId(), role: m.role as 'user' | 'assistant', content }];\n });\n setMessages(reconstructed);\n if (msg.driverId) setOwnDriverId(msg.driverId);\n break;\n }\n case 'user_message': {\n setMessages((prev) => [\n ...prev,\n {\n id: nextId(),\n role: 'user',\n content: msg.content ?? '',\n ...(msg.driverId ? { author: msg.driverId } : {}),\n },\n ]);\n break;\n }\n case 'text_delta': {\n // The ref is the reply's source of truth, updated as each piece arrives: a `complete` in\n // the same batch reads it before React has run a state updater, and would lose the reply.\n const next = streamingTextRef.current + msg.delta;\n streamingTextRef.current = next;\n setStreamingText(next);\n // #3288: also track WHERE this text sits relative to the turn's tool calls, for finishTurn.\n turnSegmentsRef.current = appendTextDeltaToSegments(turnSegmentsRef.current, msg.delta);\n break;\n }\n case 'thinking': {\n setIsThinking(msg.isThinking);\n break;\n }\n case 'tool_start': {\n const { state } = msg;\n const tool: IActiveTool = { ...baseActiveToolFrom(state), status: 'running' };\n updateActiveTools((prev) => [...prev, tool]);\n turnSegmentsRef.current = pushToolStartSegment(turnSegmentsRef.current, tool);\n break;\n }\n case 'tool_end': {\n const { state } = msg;\n // #3288: attribute to the ONE matching running call (executionId-first) — never map over\n // every running same-named entry, which would close all of them at once.\n updateActiveTools((prev) => {\n const idx = findRunningToolIndex(prev, state);\n if (idx === -1) return prev;\n const next = [...prev];\n next[idx] = { ...next[idx]!, ...toolEndFields(state) };\n return next;\n });\n turnSegmentsRef.current = applyToolEndToSegments(turnSegmentsRef.current, state);\n break;\n }\n case 'execution_workspace_event': {\n setExecutionWorkspace(msg.snapshot);\n break;\n }\n // #3288 §1: a Stop that did not take effect (e.g. the task/loop had already finished) is\n // worth telling the operator; a success shows itself through the snapshot update above.\n case 'background_task_control_result': {\n if (!msg.success) {\n setSessionNotices((previous) => [\n ...previous,\n {\n id: nextId(),\n kind: 'background-task-control-failed',\n message: msg.message ?? `Could not stop ${msg.taskId}.`,\n },\n ]);\n }\n break;\n }\n // #3280 §2: the prompt queued behind a running turn — shown above the composer, editable\n // and removable (`cancel-queue`). `pending: null` means nothing waits; the row disappears.\n case 'pending': {\n setQueuedPrompt(\n msg.pending === null ? null : { text: msg.pending, count: msg.pendingCount ?? 1 },\n );\n break;\n }\n case 'permission_request':\n case 'ask_request':\n case 'prompt_resolved': {\n // REMOTE-007/009: the paired owner renders + answers its own prompts (local == remote).\n setPendingPrompts((prev) => applyPromptEvent(prev, msg));\n break;\n }\n case 'ui_intent': {\n // #3189: `/resume` asks for the session picker — the sidebar, when the host lists sessions.\n if (guiScreenForUiIntent(msg.event.intent) === 'session-sidebar' && canListSessions()) {\n setSessionSidebarOpen(true);\n requestSessions();\n if (commandsInFlightRef.current > 0) {\n pendingIntentRef.current = {\n name: uiIntentCommandName(msg.event.intent),\n text: null,\n };\n }\n break;\n }\n // #3282 §4a: `/settings` asks for the Settings screen — always available on this surface.\n if (guiScreenForUiIntent(msg.event.intent) === 'settings') {\n openSettings();\n if (commandsInFlightRef.current > 0) {\n pendingIntentRef.current = {\n name: uiIntentCommandName(msg.event.intent),\n text: null,\n };\n }\n break;\n }\n // #3282 §4 part b-2: `/plugin` asks for the plugin manager — the Settings screen's\n // Plugins section, the way `show-settings` opens the screen itself above.\n if (guiScreenForUiIntent(msg.event.intent) === 'settings-plugins') {\n openSettings('plugins');\n if (commandsInFlightRef.current > 0) {\n pendingIntentRef.current = {\n name: uiIntentCommandName(msg.event.intent),\n text: null,\n };\n }\n break;\n }\n // #3282 §4 part b-3: `/agent` (bare) asks for the switcher — always available on this\n // surface, unlike the session picker, which needs a host that lists sessions.\n if (guiScreenForUiIntent(msg.event.intent) === 'agent-switcher') {\n openAgentSwitcher();\n if (commandsInFlightRef.current > 0) {\n pendingIntentRef.current = {\n name: uiIntentCommandName(msg.event.intent),\n text: null,\n };\n }\n break;\n }\n // CMD-004 Stage D: a command this surface issued requested a screen the GUI does not have.\n // The command's result follows; the unavailable line answers it (TC-05, never silent).\n const unavailable = {\n name: uiIntentCommandName(msg.event.intent),\n text: describeUiIntentForGui(msg.event.intent),\n };\n if (commandsInFlightRef.current > 0) {\n pendingIntentRef.current = unavailable;\n } else {\n // No command of ours awaits a reply (a model-run command, a broadcast): say it now.\n appendEntry({\n id: nextId(),\n role: 'command',\n name: unavailable.name,\n content: unavailable.text,\n tone: 'info',\n });\n }\n break;\n }\n // CMD-004 Stage E: broadcast session events — a rename/clear executed by the HOST (from any\n // surface, co-driving included) is reflected here; never a silent drop.\n case 'session_renamed': {\n setSessionName(msg.event.name);\n requestSessions();\n break;\n }\n // #3189: the host made another session current — drop what this one showed, re-read it all.\n case 'session_switched': {\n settleSessionChange();\n // Nothing of the old session stays on screen until the new one's answers arrive.\n sessionStatusRef.current = null;\n setSessionStatus(null);\n streamingTextRef.current = '';\n turnSegmentsRef.current = [];\n setStreamingText('');\n setIsThinking(false);\n updateActiveTools(() => []);\n setMessages([]);\n setPendingPrompts([]);\n setQueuedPrompt(null);\n setSessionName(null);\n setExecutionWorkspace(null);\n closeExecutionDetail();\n markCurrent(msg.event.sessionId);\n send({ type: 'get-messages' });\n send({ type: 'get-status' });\n send({ type: 'get-commands' });\n send({ type: 'get-execution-workspace' });\n send({ type: 'get-pending' });\n requestSessions();\n requestSchedules();\n break;\n }\n case 'history_cleared': {\n streamingTextRef.current = '';\n turnSegmentsRef.current = [];\n setStreamingText('');\n setMessages([]);\n break;\n }\n case 'error': {\n // The `model_unavailable` notice's model is captured NOW, from the wire frame when it\n // named one or else the status this ref already holds — never read live later from\n // render, which would race the `get-status` below (and any model switch after it).\n const model = msg.model ?? sessionStatusRef.current?.model;\n send({ type: 'get-status' });\n finishTurn((tool) => (tool.status === 'running' ? 'error' : tool.status));\n setSessionNotices((previous) => [\n ...previous,\n {\n id: nextId(),\n kind: 'session-error',\n message: msg.message,\n ...(msg.code !== undefined && { code: msg.code }),\n ...(msg.provider !== undefined && { provider: msg.provider }),\n ...(model !== undefined && { model }),\n ...(msg.retryAfterSeconds !== undefined && {\n retryAfterSeconds: msg.retryAfterSeconds,\n }),\n },\n ]);\n break;\n }\n // #3189: a new or switch this surface asked for was refused; the reason is the toast, and\n // commands in flight are untouched. Only the client that asked is told.\n case 'session_change_failed': {\n if (\n msg.requestId !== undefined &&\n !sessionChangeRequestIdsRef.current.has(msg.requestId)\n ) {\n break;\n }\n settleSessionChange(msg.requestId);\n setSessionNotices((previous) => [\n ...previous,\n { id: nextId(), kind: 'session-change-refused', message: msg.message },\n ]);\n break;\n }\n // #3289 §1: a row's rename landed — refresh the listing so its title (or the live one, if it\n // was current) is current everywhere it shows.\n case 'session_renamed_in_list': {\n requestSessions();\n break;\n }\n case 'session_rename_failed': {\n setSessionNotices((previous) => [\n ...previous,\n { id: nextId(), kind: 'session-change-refused', message: msg.message },\n ]);\n break;\n }\n // #3289 §1: a row was deleted. If it was this surface's own session, `session_switched`\n // already arrived (the host switches away before answering) and reset the conversation; this\n // only needs the listing refreshed so the row is gone everywhere it shows.\n case 'session_deleted': {\n requestSessions();\n break;\n }\n case 'session_delete_failed': {\n setSessionNotices((previous) => [\n ...previous,\n { id: nextId(), kind: 'session-change-refused', message: msg.message },\n ]);\n break;\n }\n case 'protocol_error': {\n if (sessionChangesInFlightRef.current > 0) {\n // A host older than `session_change_failed` refuses a session change with this.\n settleSessionChange();\n setSessionNotices((previous) => [\n ...previous,\n { id: nextId(), kind: 'protocol-error', message: msg.message },\n ]);\n break;\n }\n // A command can end in a protocol error instead of a result; a screen it asked for still shows.\n commandsInFlightRef.current = Math.max(0, commandsInFlightRef.current - 1);\n const unavailable = pendingIntentRef.current;\n pendingIntentRef.current = null;\n if (unavailable !== null && unavailable.text !== null) {\n appendEntry({\n id: nextId(),\n role: 'command',\n name: unavailable.name,\n content: unavailable.text,\n tone: 'info',\n });\n }\n setSessionNotices((previous) => [\n ...previous,\n { id: nextId(), kind: 'protocol-error', message: msg.message },\n ]);\n break;\n }\n case 'commands': {\n const catalog = { commands: msg.commands, skills: msg.skills };\n commandCatalogRef.current = catalog;\n setCommandCatalog(catalog);\n break;\n }\n case 'session_status': {\n sessionStatusRef.current = msg.status;\n setSessionStatus(msg.status);\n break;\n }\n case 'command_result': {\n // A command may change the status (mode, effort, model) or the catalog (plugins, skills).\n send({ type: 'get-status' });\n send({ type: 'get-commands' });\n commandsInFlightRef.current = Math.max(0, commandsInFlightRef.current - 1);\n // #3282 §4b: a successful `/provider` command run through the raw command path (Add, Edit,\n // Duplicate — Use/Model/Delete already return a fresh snapshot from their own\n // `update-settings` reply) changes the \"Providers & Models\" list; refresh it if the screen\n // is open, the same way `get-status` above refreshes the model/mode chips. Never touches\n // which section is showing (`refreshSettings`, unlike `openSettings`).\n if (msg.name === 'provider' && msg.success && settingsOpenRef.current) {\n refreshSettings();\n }\n // #3282 §2 (part 2): identify silence by THIS reply's own `requestId`, not by position or\n // count — see `silentCommandRequestIdsRef` above. No `requestId` (an older host, or a typed\n // command) always falls through to `silent = false`, never the reverse.\n const silent =\n msg.requestId !== undefined && silentCommandRequestIdsRef.current.delete(msg.requestId);\n // #3282 §4 part b-3: a selection made in the agent switcher sheet — its own plain\n // confirmation line answers it, never a conversation card (issue #3282 §4's decided design).\n if (resolveSwitchResult(msg.requestId, msg)) break;\n const unavailable = pendingIntentRef.current;\n pendingIntentRef.current = null;\n if (unavailable !== null && msg.success) {\n // The screen that opened is the answer; otherwise the line saying it cannot open is.\n if (unavailable.text !== null) {\n appendEntry({\n id: nextId(),\n role: 'command',\n name: msg.name,\n content: unavailable.text,\n tone: 'info',\n });\n }\n break;\n }\n // A command that starts a turn (a skill) says nothing itself; the turn is its answer.\n if (msg.message.trim().length === 0) break;\n // #3282 §2 (part 2): a change made through a status control confirms itself through the\n // control's own label (the `get-status` refresh above lands it) — no conversation card. A\n // failure still needs saying, as a plain notice rather than a card, and the label is left\n // unchanged since nothing actually applied. A typed command (plain `send`) keeps its card\n // either way — this ONLY silences a change sent via `sendCommandSilently`.\n if (silent) {\n if (!msg.success) {\n setSessionNotices((previous) => [\n ...previous,\n { id: nextId(), kind: 'command-failed', message: msg.message },\n ]);\n }\n break;\n }\n appendEntry({\n id: nextId(),\n role: 'command',\n name: msg.name,\n content: msg.message,\n tone: msg.success ? 'success' : 'error',\n });\n break;\n }\n case 'complete':\n case 'interrupted': {\n send({ type: 'get-status' });\n // #3280 §2: the turn ending can advance the queue (its next entry now runs) or, if this\n // was an abort, drop it entirely (`abort()` clears the whole queue) — either way the\n // composer's queued-message row is stale until this reply refreshes it.\n send({ type: 'get-pending' });\n // The turn changed this session's preview, message count and time in the list.\n requestSessions();\n finishTurn((tool) => (tool.status === 'running' ? 'done' : tool.status));\n break;\n }\n }\n },\n [\n appendEntry,\n canListSessions,\n closeExecutionDetail,\n finishTurn,\n handleAgentDefinitionsMessage,\n handleExecutionDetailMessage,\n handleModelListMessage,\n handleProjectMessage,\n handleSchedulesMessage,\n handleSessionsMessage,\n handleSettingsMessage,\n handleUsageMessage,\n markCurrent,\n openAgentSwitcher,\n openSettings,\n refreshSettings,\n requestSchedules,\n requestSessions,\n resolveSwitchResult,\n send,\n setSessionSidebarOpen,\n settleSessionChange,\n updateActiveTools,\n ],\n );\n\n const answerPermission = useCallback((id: string, result: TPermissionResultValue): void => {\n clientRef.current?.send(permissionResponse(id, result));\n setPendingPrompts((prev) => prev.filter((p) => p.id !== id)); // optimistic dismiss (prompt_resolved confirms)\n }, []);\n\n const answerAsk = useCallback((id: string, response: TActionResponse): void => {\n clientRef.current?.send(askResponse(id, response));\n setPendingPrompts((prev) => prev.filter((p) => p.id !== id));\n }, []);\n\n const dismissSessionNotice = useCallback((id: string): void => {\n setSessionNotices((previous) => previous.filter((notice) => notice.id !== id));\n }, []);\n\n useEffect(() => {\n const onStatusChange = (next: TStatus): void => {\n setStatus(next);\n // Every client learns what it can offer and what is in effect as soon as it is attached.\n if (next === 'connected') {\n // A reply lost with the old connection never arrives; stop waiting for it.\n commandsInFlightRef.current = 0;\n silentCommandRequestIdsRef.current.clear();\n sessionChangesInFlightRef.current = 0;\n sessionChangeRequestIdsRef.current.clear();\n pendingIntentRef.current = null;\n client.send({ type: 'get-commands' });\n client.send({ type: 'get-status' });\n // #3280 §2: a reconnect that lands back on the SAME session (the common case) fires no\n // `session_switched` — its own `get-pending` would be missed — so ask here too, or a stale\n // `queuedPrompt` from before the drop keeps showing (with working Edit/Remove) until the\n // turn it was queued behind happens to end.\n client.send({ type: 'get-pending' });\n // A host that keeps sessions live puts a new connection on its primary session.\n armRestore();\n requestSessions();\n requestSchedules();\n }\n };\n const client = makeClient({ onMessage: handleMessage, onStatusChange });\n clientRef.current = client;\n client.connect();\n return () => {\n client.disconnect();\n clientRef.current = null;\n };\n }, [makeClient, handleMessage, requestSessions, requestSchedules, armRestore]);\n\n return {\n status,\n messages,\n activeTools,\n streamingText,\n isThinking,\n executionWorkspace,\n sessionName,\n commandCatalog,\n sessionStatus,\n ownDriverId,\n send,\n sendCommandSilently,\n pendingPrompts,\n queuedPrompt,\n answerPermission,\n answerAsk,\n ...personalUsageState,\n ...executionDetailState,\n closeExecutionDetail,\n ...modelListState,\n ...projectPanelState,\n ...sessionDirectoryState,\n sessionNotices,\n dismissSessionNotice,\n openSettings,\n ...settingsState,\n openAgentSwitcher,\n ...agentSwitcherState,\n ...schedulesState,\n helpOpen,\n openHelp,\n closeHelp,\n };\n}\n\n/** Connect to a `agent` sidecar over WebSocket (loopback / localhost path). */\nexport function useWsSession(\n url: string,\n): IWsSessionState<TConnectionStatus> & { connectionLost: boolean } {\n // Issue #3280 §5: retries gave up — the runtime is not coming back by itself. A presentation layer\n // (e.g. `SessionSurface`) reads this to show a banner instead of a callback-driven screen swap.\n const [connectionLost, setConnectionLost] = useState(false);\n const makeClient = useCallback<TMakeSessionClient<TConnectionStatus>>(\n (cb) => createWsSessionClient(url, { ...cb, onGiveUp: () => setConnectionLost(true) }),\n [url],\n );\n const state = useSessionClient(makeClient);\n useEffect(() => {\n if (state.status === 'connected') setConnectionLost(false);\n }, [state.status]);\n return { ...state, connectionLost };\n}\n","import type { IPickedFile } from '../components/Composer.js';\n\n/** Runtime events delivered by a native host. Browser pages have no process to supervise. */\nexport type TClientRuntimeState = 'starting' | 'ready' | 'fatal';\n\n/** A folder decision must be shown before requesting the runtime endpoint. */\nexport interface IClientTrustQuestion {\n readonly folder: string;\n readonly loads: readonly string[];\n}\n\nexport type TClientTrustChoice = 'trust' | 'restricted' | 'quit';\n\n/**\n * Browser-safe shape for a narrow native preload bridge. The native host implements validation,\n * trust, process lifecycle, and endpoint ownership; the renderer receives only these operations.\n */\nexport interface IClientRuntimeBridge {\n readonly runtimeMode?: 'local' | 'remote';\n getEndpoint(): Promise<string | null>;\n signalReady(): void;\n onState(listener: (state: TClientRuntimeState, detail?: string) => void): () => void;\n restartRuntime(): Promise<void>;\n trustQuestion(): Promise<IClientTrustQuestion | null>;\n answerTrust(choice: TClientTrustChoice): Promise<{ error?: string }>;\n pickFiles?(): Promise<readonly IPickedFile[]>;\n getPathForFile?(file: File): string;\n onOpenSettings?(listener: () => void): () => void;\n openPath?(path: string): Promise<{ error?: string }>;\n}\n\n/** A React renderer can use this contract with either a desktop bridge or a browser endpoint. */\nexport interface IClientRuntimeHost {\n readonly kind: 'desktop' | 'browser';\n getEndpoint(): Promise<string | null>;\n signalReady(): void;\n onState(listener: (state: TClientRuntimeState, detail?: string) => void): () => void;\n readonly restartRuntime?: () => Promise<void>;\n readonly trustQuestion?: () => Promise<IClientTrustQuestion | null>;\n readonly answerTrust?: (choice: TClientTrustChoice) => Promise<{ error?: string }>;\n readonly pickFiles?: () => Promise<readonly IPickedFile[]>;\n readonly getPathForFile?: (file: File) => string;\n onOpenSettings(listener: () => void): () => void;\n readonly openMemoryInEditor?: (path: string) => void;\n}\n\nexport interface IClientRuntimeEnvironment {\n readonly bridge?: IClientRuntimeBridge;\n readonly document: Pick<Document, 'querySelector'>;\n readonly location: Pick<Location, 'search' | 'host' | 'protocol'>;\n}\n\n/** Resolve host capabilities without importing Electron or a Node CLI into the browser graph. */\nexport function resolveClientRuntimeHost(environment: IClientRuntimeEnvironment): IClientRuntimeHost {\n const { bridge } = environment;\n if (bridge) {\n const local = bridge.runtimeMode !== 'remote';\n return {\n kind: 'desktop',\n getEndpoint: () => bridge.getEndpoint(),\n signalReady: () => bridge.signalReady(),\n onState: (listener) => bridge.onState(listener),\n restartRuntime: () => bridge.restartRuntime(),\n trustQuestion: local ? () => bridge.trustQuestion() : undefined,\n answerTrust: local ? (choice) => bridge.answerTrust(choice) : undefined,\n pickFiles: local && bridge.pickFiles ? () => bridge.pickFiles!() : undefined,\n getPathForFile: local && bridge.getPathForFile ? (file) => bridge.getPathForFile!(file) : undefined,\n onOpenSettings: (listener) => bridge.onOpenSettings?.(listener) ?? (() => {}),\n openMemoryInEditor: local && bridge.openPath ? (path) => { void bridge.openPath!(path); } : undefined,\n };\n }\n\n const injected = environment.document.querySelector('meta[name=\"ws-url\"]')?.getAttribute('content')?.trim();\n const query = new URLSearchParams(environment.location.search).get('ws')?.trim();\n const protocol = environment.location.protocol === 'https:' ? 'wss:' : 'ws:';\n const endpoint = injected || query || `${protocol}//${environment.location.host}`;\n return {\n kind: 'browser',\n getEndpoint: async () => endpoint,\n signalReady: () => {},\n onState: () => () => {},\n onOpenSettings: () => () => {},\n };\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * Copies `text` to the clipboard for the \"Copy code\" / \"Copy message\" buttons (#3289 §2): the async\n * Clipboard API first, and — for a page that denies it (no permission granted yet, a browser that\n * never implements it, or a context that disallows it outright) — a same-tick `execCommand('copy')`\n * fallback through a throwaway, invisible textarea. Rejects only once both have failed, so a caller\n * can show its own \"Couldn't copy\" message rather than a browser one.\n */\nexport async function copyToClipboard(text: string): Promise<void> {\n if (navigator.clipboard?.writeText) {\n try {\n await navigator.clipboard.writeText(text);\n return;\n } catch {\n // Falls through to the legacy path — some browsers reject outside a user gesture, or before a\n // permission prompt has ever been answered.\n }\n }\n if (!legacyCopy(text)) throw new Error('copy failed');\n}\n\n/**\n * `execCommand('copy')` needs a selection in the page, so the text is staged in an offscreen field —\n * which needs focus to be selectable, so whatever had focus (typically the \"Copy code\" / \"Copy\n * message\" button a keyboard user just activated) is restored afterward rather than left on `<body>`.\n */\nfunction legacyCopy(text: string): boolean {\n if (typeof document === 'undefined' || typeof document.execCommand !== 'function') return false;\n const previouslyFocused =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const field = document.createElement('textarea');\n field.value = text;\n field.setAttribute('readonly', '');\n field.style.position = 'fixed';\n field.style.top = '0';\n field.style.left = '0';\n field.style.opacity = '0';\n field.style.pointerEvents = 'none';\n document.body.appendChild(field);\n field.focus();\n field.select();\n let ok = false;\n try {\n ok = document.execCommand('copy');\n } catch {\n ok = false;\n }\n document.body.removeChild(field);\n previouslyFocused?.focus();\n return ok;\n}\n\nexport type TCopyStatus = 'idle' | 'copied' | 'error';\n\n/** How long the \"Copied\" / \"Couldn't copy\" feedback holds before the button reads its normal label again. */\nconst FEEDBACK_MS = 2000;\n\n/**\n * The \"Copy code\" / \"Copy message\" buttons' shared feedback state: `copied` or `error` for a couple of\n * seconds, then back to `idle` — drives both the button's own label swap and an `aria-live` region a\n * screen reader announces without moving focus (#3289 §2).\n */\nexport function useCopyFeedback(): { status: TCopyStatus; copy: (text: string) => void } {\n const [status, setStatus] = useState<TCopyStatus>('idle');\n\n useEffect(() => {\n if (status === 'idle') return undefined;\n const timer = setTimeout(() => setStatus('idle'), FEEDBACK_MS);\n return () => clearTimeout(timer);\n }, [status]);\n\n const copy = (text: string): void => {\n copyToClipboard(text).then(\n () => setStatus('copied'),\n () => setStatus('error'),\n );\n };\n\n return { status, copy };\n}\n","/**\n * #3289 §3 — plain, human labels for a REMOTE-014 E5 driver id, and whether one names the SAME KIND\n * of surface as this connection's own. Shared by every place the GUI shows who drove a turn (a\n * message, a permission/ask prompt): the person's own input never carries a label, and a turn from a\n * different kind of surface does — in words, never a raw id.\n *\n * The server hands every WebSocket connection of one `--serve` process the SAME driver id (there is\n * no per-connection id), so two browser tabs of one local page cannot be told apart — and are the\n * same person anyway. A label therefore names the KIND of surface a turn came from (terminal, desktop\n * app, browser, remote device), not a specific window, and is shown only when that kind differs from\n * this window's own.\n */\n\nimport { AGENT_DRIVER_ID, OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\n\nimport type { TDriverId } from '@robota-sdk/agent-interface-session';\n\n/**\n * `remote` (a plain WS remote connection's own id, `'remote:ws'`) and `peer` (an unrelated REMOTE-014\n * mesh peer's turn, `'peer:<sessionId>'`) are kept as DIFFERENT kinds even though both render the same\n * label text: a plain WS viewer's own id is `'remote:ws'`, and folding a peer's turn into that same\n * kind would make `isSameSurface` treat a co-driver's turn as this connection's own and hide it — the\n * exact class of bug this module exists to prevent.\n */\ntype TSurfaceKind = 'terminal' | 'desktop-app' | 'browser' | 'remote' | 'peer';\n\n/**\n * The kind of surface a driver id names. `null` for an id this function does not recognize — it still\n * gets a label (see `humanDriverLabel`), just not one claimed to match anything else's kind.\n */\nfunction surfaceKindOf(driverId: TDriverId): TSurfaceKind | null {\n if (driverId.startsWith('attach:')) return 'terminal';\n if (driverId === 'app') return 'desktop-app';\n if (driverId === 'browser') return 'browser';\n if (driverId === 'remote:ws') return 'remote';\n if (driverId.startsWith('peer:')) return 'peer';\n return null;\n}\n\n/**\n * True when `driverId` names the reserved local-operator id, or a surface of the SAME KIND as this\n * connection's own (once learned) — so this window never labels its own kind of turn as a co-driver's.\n * The agent's own wake-ups (`AGENT_DRIVER_ID`) are never \"the same surface\": automatic is always\n * labelled, even though no human window could ever learn that as its own id.\n */\nexport function isSameSurface(driverId: TDriverId, ownDriverId: TDriverId | null): boolean {\n if (driverId === OWNER_DRIVER_ID) return true;\n if (driverId === AGENT_DRIVER_ID) return false;\n if (ownDriverId === null) return false;\n if (driverId === ownDriverId) return true;\n const kind = surfaceKindOf(driverId);\n return kind !== null && kind === surfaceKindOf(ownDriverId);\n}\n\n/**\n * A plain noun phrase for the kind of surface that drove a turn which was not this connection's own.\n * Never returns a raw id: an id this function does not recognize still gets the least presumptuous\n * label (\"another surface\") rather than being echoed back.\n */\nexport function humanDriverLabel(driverId: TDriverId): string {\n const kind = surfaceKindOf(driverId);\n switch (kind) {\n case 'terminal':\n return 'the terminal';\n case 'desktop-app':\n return 'the desktop app';\n case 'browser':\n return 'the browser';\n case 'remote':\n case 'peer':\n return 'a remote device';\n default:\n return 'another surface';\n }\n}\n\n/**\n * The ready-to-render attribution phrase: \"from <label>\" for a co-driver, or \"Automatic — loop\"\n * for the agent's own wake-ups — \"from automatic\" reads as a typo, not a source. #3288 §1: every\n * `AGENT_DRIVER_ID` turn is, today, a loop's own injected iteration (fixed or self-paced —\n * `requestWakeup` in `interactive-session.ts` is the one place that stamps this id); if a\n * differently-sourced agent-wakeup turn is ever added, this label needs to name that source\n * instead of assuming \"loop\".\n */\nexport function driverAttributionText(driverId: TDriverId): string {\n if (driverId === AGENT_DRIVER_ID) return 'Automatic — loop';\n return `from ${humanDriverLabel(driverId)}`;\n}\n","'use client';\n\nimport React, { useState } from 'react';\n\nimport type { IDiffLine } from '@robota-sdk/agent-interface-session';\n\n/**\n * #3288: renders a unified diff already built server-side (the ONE diff builder, in\n * `interactive-session-streaming.ts`) — a tool row's expanded result and the Edit/Write permission\n * prompt both show the SAME diff through this, so approving a change and reviewing it afterwards\n * look identical. This component never computes a diff itself, only displays one.\n */\n\nconst DIFF_LINE_STYLE: Record<IDiffLine['type'], string> = {\n add: 'bg-accent/10 text-foreground',\n remove: 'bg-destructive/10 text-foreground',\n context: 'text-muted-foreground',\n hunk: 'text-subtle',\n};\nconst DIFF_LINE_PREFIX: Record<IDiffLine['type'], string> = {\n add: '+ ',\n remove: '- ',\n context: ' ',\n hunk: '',\n};\n\n// #3288 review SHOULD 3: the server already caps each diff side at 500 lines — this is a second,\n// independent fold so a diff that size never renders into the DOM at once either.\nconst DIFF_FOLD_LINES = 40;\nconst DIFF_EXPANDED_CAP_LINES = 500;\n\nfunction DiffLine({ line }: { line: IDiffLine }): React.ReactElement {\n return (\n <div className={`whitespace-pre ${DIFF_LINE_STYLE[line.type]}`}>\n {`${DIFF_LINE_PREFIX[line.type]}${line.text}`}\n </div>\n );\n}\n\nexport function DiffLines({ diffLines }: { diffLines: readonly IDiffLine[] }): React.ReactElement {\n const [open, setOpen] = useState(false);\n if (diffLines.length <= DIFF_FOLD_LINES) {\n return (\n <pre className=\"overflow-x-auto rounded-lg bg-sidebar px-3 py-2 font-mono text-[12.5px] leading-relaxed\">\n {diffLines.map((line, index) => (\n // A diff's line order IS its identity — index is a stable, correct key here.\n <DiffLine key={index} line={line} />\n ))}\n </pre>\n );\n }\n\n const capped = diffLines.slice(0, DIFF_EXPANDED_CAP_LINES);\n const shown = open ? capped : diffLines.slice(0, DIFF_FOLD_LINES);\n // Expanding reveals up to DIFF_EXPANDED_CAP_LINES, not the whole diff — the label must promise\n // only what \"Show more\" actually shows, or a capped Edit (each side capped at 500 on the server)\n // can claim more lines than clicking through ever produces.\n const revealedByExpanding = Math.min(diffLines.length, DIFF_EXPANDED_CAP_LINES) - DIFF_FOLD_LINES;\n return (\n <div className=\"flex flex-col gap-1\">\n <pre className=\"overflow-x-auto rounded-lg bg-sidebar px-3 py-2 font-mono text-[12.5px] leading-relaxed\">\n {shown.map((line, index) => (\n // A diff's line order IS its identity — index is a stable, correct key here.\n <DiffLine key={index} line={line} />\n ))}\n </pre>\n {open && diffLines.length > DIFF_EXPANDED_CAP_LINES && (\n <p className=\"text-[12px] text-subtle\">\n {diffLines.length - DIFF_EXPANDED_CAP_LINES} more lines not shown\n </p>\n )}\n <button\n type=\"button\"\n onClick={() => setOpen((value) => !value)}\n className=\"self-start text-[12.5px] text-accent hover:underline\"\n >\n {open ? 'Show less' : `Show more (${revealedByExpanding} more lines)`}\n </button>\n </div>\n );\n}\n","'use client';\n\nimport React, { useRef, useLayoutEffect, useState } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport {\n ArrowDown,\n Bot,\n Check,\n ChevronDown,\n ChevronRight,\n Copy,\n FileText,\n Globe,\n LoaderCircle,\n Pencil,\n Search,\n SquareTerminal,\n Wrench,\n} from 'lucide-react';\n\nimport { useCopyFeedback } from '../clipboard.js';\nimport { driverAttributionText, isSameSurface } from '../driver-labels.js';\nimport { DiffLines } from './DiffLines.js';\n\nimport type {\n IActiveTool,\n IChangedFileSummary,\n ICommandOutputEntry,\n TConversationEntry,\n} from '../hooks/useSessionClient.js';\nimport type { TDriverId } from '@robota-sdk/agent-interface-session';\n\n/** The plain text of a react-markdown children tree — used to copy a code block's real source. */\nfunction nodeText(node: React.ReactNode): string {\n if (node === null || node === undefined || typeof node === 'boolean') return '';\n if (typeof node === 'string' || typeof node === 'number') return String(node);\n if (Array.isArray(node)) return node.map(nodeText).join('');\n if (React.isValidElement(node)) {\n return nodeText((node.props as { children?: React.ReactNode }).children);\n }\n return '';\n}\n\n/**\n * \"Copy code\" / \"Copy message\" (#3289 §2): shows on hover and keyboard focus, always on touch (no\n * `hover` capability to gate on there). The transient \"Copied\" / \"Couldn't copy\" feedback lives in a\n * visually hidden `aria-live` region that is a SIBLING of the button, not a descendant of it — ARIA\n * name computation only ever looks at an element's own subtree, so a live region placed anywhere\n * inside the button would still contribute its (mutating) text to the button's accessible name even\n * while merely `sr-only`-clipped rather than `aria-hidden`, silently turning a constant name like\n * \"Copy code\" into a moving target such as \"Copy code Copied\". Keeping the two siblings instead gives\n * the button a name computed purely from its own constant visible label, and lets the live region\n * announce without touching that name or moving focus off the button. (An explicit `aria-label` on the\n * button would also pin the name, but collides here: Playwright's `getByLabel` matches on the\n * `aria-label` attribute by substring, and \"Copy message\" would then match any lookup for the\n * composer's own `aria-label=\"message\"`.) A live region doubling as the button's own sole\n * name-contributing content is the most fragile shape of all: it held the label text itself here at\n * first, and Chromium's accessibility tree then failed to expose that name at all after the button\n * remounted (streaming text settling into a message) under load — caught by the browser e2e, not the\n * jsdom unit tests, which never touch the real accessibility tree.\n */\nfunction CopyButton({\n label,\n getText,\n className = '',\n}: {\n label: string;\n getText: () => string;\n className?: string;\n}): React.ReactElement {\n const { status, copy } = useCopyFeedback();\n const feedback = status === 'copied' ? 'Copied' : status === 'error' ? \"Couldn't copy\" : '';\n return (\n <span className={`inline-flex items-center ${className}`}>\n <button\n type=\"button\"\n onClick={() => copy(getText())}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border/60 bg-card px-2 py-1 text-[12px] font-medium text-muted-foreground shadow-sm shadow-black/5 transition-colors hover:bg-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {status === 'copied' ? (\n <Check size={13} strokeWidth={2} />\n ) : (\n <Copy size={13} strokeWidth={1.9} />\n )}\n <span>{label}</span>\n </button>\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {feedback}\n </span>\n </span>\n );\n}\n\n/**\n * A fenced code block: unwrapped and horizontally scrollable (a visible scrollbar via\n * `.gui-code-scroll`, `tabIndex=0` and a name so a keyboard user without a mouse wheel can still reach\n * the rest of a long line), with its own \"Copy code\" button revealed on hover/focus (#3289 §2).\n */\nfunction CodeBlockRenderer({ children }: { children?: React.ReactNode }): React.ReactElement {\n const text = nodeText(children).replace(/\\n$/, '');\n return (\n <div className=\"group/code relative my-1\">\n <pre\n tabIndex={0}\n aria-label=\"Code block, horizontally scrollable\"\n className=\"gui-code-scroll overflow-x-auto rounded-xl bg-sidebar px-4 py-3.5 font-mono text-[13px] leading-[1.65] focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {children}\n </pre>\n <CopyButton\n label=\"Copy code\"\n getText={() => text}\n className=\"absolute right-2 top-2 opacity-0 transition-opacity group-hover/code:opacity-100 group-focus-within/code:opacity-100 [@media(hover:none)]:opacity-100\"\n />\n </div>\n );\n}\n\ninterface IConversationViewProps {\n messages: readonly TConversationEntry[];\n activeTools: IActiveTool[];\n streamingText: string;\n isThinking: boolean;\n /** This connection's own driver id (§3289 §3), threaded to every message it renders. */\n ownDriverId: TDriverId | null;\n}\n\n/** Agent markdown, set as reading prose: headings step down gently, code sits on its own quiet surface. */\nfunction AgentMarkdown({ children }: { children: string }): React.ReactElement {\n return (\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n components={{\n h1: ({ children: c }) => (\n <h1 className=\"mt-6 text-[20px] font-semibold leading-snug tracking-[-0.015em] first:mt-0\">\n {c}\n </h1>\n ),\n h2: ({ children: c }) => (\n <h2 className=\"mt-6 text-[17px] font-semibold leading-snug tracking-[-0.01em] first:mt-0\">\n {c}\n </h2>\n ),\n h3: ({ children: c }) => (\n <h3 className=\"mt-5 text-[15px] font-semibold leading-snug first:mt-0\">{c}</h3>\n ),\n p: ({ children: c }) => <p>{c}</p>,\n pre: CodeBlockRenderer,\n code: ({ className, children: c }) => {\n const isBlock = Boolean(className);\n return isBlock ? (\n <code className={`font-mono ${className ?? ''}`}>{c}</code>\n ) : (\n <code className=\"rounded-md bg-raised px-1.5 py-0.5 font-mono text-[0.85em]\">{c}</code>\n );\n },\n ul: ({ children: c }) => (\n <ul className=\"ml-5 list-outside list-disc space-y-1.5 marker:text-subtle\">{c}</ul>\n ),\n ol: ({ children: c }) => (\n <ol className=\"ml-5 list-outside list-decimal space-y-1.5 marker:text-subtle\">{c}</ol>\n ),\n li: ({ children: c }) => <li className=\"pl-1\">{c}</li>,\n strong: ({ children: c }) => <strong className=\"font-semibold\">{c}</strong>,\n em: ({ children: c }) => <em className=\"italic\">{c}</em>,\n a: ({ href, children: c }) => (\n <a\n href={href}\n className=\"text-accent underline decoration-accent/40 underline-offset-[3px] hover:decoration-accent\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {c}\n </a>\n ),\n table: ({ children: c }) => (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full border-collapse text-[14px]\">{c}</table>\n </div>\n ),\n thead: ({ children: c }) => <thead>{c}</thead>,\n th: ({ children: c }) => (\n <th className=\"border-b border-border px-3 py-2 text-left text-[13px] font-medium text-muted-foreground\">\n {c}\n </th>\n ),\n td: ({ children: c }) => <td className=\"px-3 py-2 align-top\">{c}</td>,\n blockquote: ({ children: c }) => (\n <blockquote className=\"border-l-2 border-subtle/50 pl-4 text-muted-foreground\">\n {c}\n </blockquote>\n ),\n hr: () => <hr className=\"mx-auto my-7 w-12 border-subtle/40\" />,\n }}\n >\n {children}\n </ReactMarkdown>\n );\n}\n\nfunction UserBlock({\n content,\n author,\n ownDriverId,\n}: {\n content: string;\n author?: string;\n /** This connection's own driver id (§3289 §3): its own turns never carry a \"from\" label. */\n ownDriverId: TDriverId | null;\n}): React.ReactElement {\n // REMOTE-014 E5 (display-only, OWNER PRINCIPLE): show WHO drove this turn when it wasn't this\n // connection's own — in plain words, never the raw server-assigned id. #3288 §1: AGENT_DRIVER_ID\n // (never \"the same surface\" — see isSameSurface) reads \"Automatic — loop\": today it is only ever\n // a loop's own injected turn (requestWakeup in interactive-session.ts).\n const coDriver =\n author && !isSameSurface(author, ownDriverId) ? driverAttributionText(author) : undefined;\n return (\n <div className=\"flex flex-col items-end gap-1.5 pl-12\">\n {coDriver && <span className=\"px-1 text-[12.5px] text-subtle\">{coDriver}</span>}\n <div className=\"max-w-full whitespace-pre-wrap break-words rounded-[20px] bg-raised px-4 py-2.5 text-[15px] leading-relaxed text-foreground\">\n {content}\n </div>\n </div>\n );\n}\n\nfunction AgentBlock({\n content,\n isStreaming = false,\n}: {\n content: string;\n isStreaming?: boolean;\n}): React.ReactElement {\n return (\n <div className=\"group/message gui-prose\">\n <AgentMarkdown>{content}</AgentMarkdown>\n {isStreaming && (\n <span className=\"ml-1 inline-block h-2.5 w-2.5 animate-pulse rounded-full bg-foreground/70 align-middle\" />\n )}\n {/* #3289 §2: every assistant message can copy its own Markdown source; a still-streaming one\n has nothing settled yet to copy, so the button waits for it to finish. */}\n {!isStreaming && (\n <div className=\"flex opacity-0 transition-opacity focus-within:opacity-100 group-hover/message:opacity-100 group-focus-within/message:opacity-100 [@media(hover:none)]:opacity-100\">\n <CopyButton label=\"Copy message\" getText={() => content} />\n </div>\n )}\n </div>\n );\n}\n\n/** A recognisable glyph for the common tools; anything else is a wrench. */\nfunction ToolIcon({ name, className }: { name: string; className?: string }): React.ReactElement {\n const key = name.toLowerCase();\n const props = { size: 15, strokeWidth: 1.75, className };\n if (/(read|view|cat|open)/u.test(key)) return <FileText {...props} />;\n if (/(grep|glob|search|find|list|ls)/u.test(key)) return <Search {...props} />;\n if (/(edit|write|patch|replace|create)/u.test(key)) return <Pencil {...props} />;\n if (/(bash|shell|exec|run|command|terminal)/u.test(key)) return <SquareTerminal {...props} />;\n if (/(web|fetch|http|url|browse)/u.test(key)) return <Globe {...props} />;\n if (/(agent|task|delegate)/u.test(key)) return <Bot {...props} />;\n return <Wrench {...props} />;\n}\n\n/**\n * #3288: a long path's directory is shortened FROM THE LEFT so the filename — the part someone\n * actually needs to recognise — is never the part that gets cut.\n */\nconst PATH_DISPLAY_MAX_CHARS = 52;\nfunction shortenDirectoryFromLeft(path: string, maxChars: number = PATH_DISPLAY_MAX_CHARS): string {\n if (path.length <= maxChars) return path;\n const lastSlash = path.lastIndexOf('/');\n if (lastSlash === -1) return path; // no directory part to shorten — the filename is never cut\n const fileName = path.slice(lastSlash + 1);\n const dir = path.slice(0, lastSlash);\n const budget = maxChars - fileName.length - 2; // room for the leading \"…/\"\n if (budget <= 0) return `…/${fileName}`;\n return `…${dir.slice(dir.length - budget)}/${fileName}`;\n}\n\n/** Best-effort read of a tool's raw JSON result payload (`IToolInvocationResult`-shaped). */\nfunction parseToolResult(\n raw: string | undefined,\n): { output?: string; error?: string; exitCode?: number; structured?: string } | undefined {\n if (!raw) return undefined;\n try {\n const value: unknown = JSON.parse(raw);\n if (typeof value !== 'object' || value === null) return { output: raw };\n const parsed = value as Record<string, unknown>;\n return {\n structured: raw,\n ...(typeof parsed.output === 'string' && parsed.output ? { output: parsed.output } : {}),\n ...(typeof parsed.error === 'string' && parsed.error ? { error: parsed.error } : {}),\n ...(typeof parsed.exitCode === 'number' ? { exitCode: parsed.exitCode } : {}),\n };\n } catch {\n // allow-fallback: not the expected JSON shape — show it as plain text rather than nothing.\n return { output: raw };\n }\n}\n\n/** Long output folds after ~20 lines; \"Show more\" expands up to a hard cap with a trailing note. */\nconst OUTPUT_FOLD_LINES = 20;\nconst OUTPUT_EXPANDED_CAP_LINES = 500;\nfunction FoldedOutput({ text }: { text: string }): React.ReactElement {\n const [open, setOpen] = useState(false);\n const lines = text.split('\\n');\n if (lines.length <= OUTPUT_FOLD_LINES) {\n return (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-[12.5px] leading-relaxed text-muted-foreground\">\n {text}\n </pre>\n );\n }\n const capped = lines.slice(0, OUTPUT_EXPANDED_CAP_LINES);\n const shown = open ? capped : lines.slice(0, OUTPUT_FOLD_LINES);\n return (\n <div className=\"flex flex-col gap-1\">\n <pre className=\"whitespace-pre-wrap break-words font-mono text-[12.5px] leading-relaxed text-muted-foreground\">\n {shown.join('\\n')}\n </pre>\n {open && lines.length > OUTPUT_EXPANDED_CAP_LINES && (\n <p className=\"text-[12px] text-subtle\">\n {lines.length - OUTPUT_EXPANDED_CAP_LINES} more lines\n </p>\n )}\n <button\n type=\"button\"\n onClick={() => setOpen((value) => !value)}\n className=\"self-start text-[12.5px] text-accent hover:underline\"\n >\n {open ? 'Show less' : `Show more (${lines.length - OUTPUT_FOLD_LINES} more lines)`}\n </button>\n </div>\n );\n}\n\nfunction ToolObservations({ tool }: { tool: IActiveTool }): React.ReactElement {\n return (\n <div className=\"flex flex-col gap-2\">\n {tool.executionId && <code className=\"text-[12px] text-subtle\">{tool.executionId}</code>}\n {tool.toolResultParts?.map((part, index) => {\n switch (part.type) {\n case 'text':\n return <FoldedOutput key={index} text={part.text} />;\n case 'image_inline':\n if (/^image\\/(png|jpeg|gif|webp)$/.test(part.mimeType)) {\n return (\n <img\n key={index}\n src={`data:${part.mimeType};base64,${part.data}`}\n alt={`Observation from ${tool.name}`}\n className=\"max-h-96 max-w-full object-contain\"\n />\n );\n }\n return <p key={index}>Image preview unavailable ({part.mimeType}).</p>;\n case 'image_uri':\n return (\n <div key={index}>\n <p>Image reference; an admitted read is required.</p>\n <code>{part.uri}</code>\n </div>\n );\n case 'resource_link':\n return (\n <div key={index}>\n <p>{part.title ?? part.name}</p>\n <code>{part.uri}</code>\n <p>An admitted read is required.</p>\n </div>\n );\n case 'resource_embedded':\n return (\n <div key={index}>\n <code>{part.uri}</code>\n {part.text !== undefined ? (\n <FoldedOutput text={part.text} />\n ) : (\n <p>\n Embedded binary resource ({part.mimeType ?? 'unknown type'}); preview\n unavailable.\n </p>\n )}\n </div>\n );\n case 'audio_inline':\n return <p key={index}>Audio preview unavailable ({part.mimeType}).</p>;\n }\n })}\n </div>\n );\n}\n\nfunction ToolCard({ tool }: { tool: IActiveTool }): React.ReactElement {\n const running = tool.status === 'running';\n const failed = tool.status === 'error';\n const [open, setOpen] = useState(false);\n const parsedResult = parseToolResult(tool.toolResultData);\n const hasDiff = tool.diffLines !== undefined && tool.diffLines.length > 0;\n const hasOutput =\n Boolean(parsedResult?.output) ||\n Boolean(parsedResult?.error) ||\n Boolean(parsedResult?.structured);\n const hasObservations = Boolean(tool.toolResultParts?.length);\n const expandable = hasDiff || hasOutput || hasObservations;\n // #3288: a projected `/command` tool shows its command, never the internal provider tool name.\n const label = tool.commandName ? `Ran /${tool.commandName}` : tool.name;\n\n return (\n <div className=\"flex flex-col text-[14px]\">\n <button\n type=\"button\"\n disabled={!expandable}\n aria-expanded={expandable ? open : undefined}\n onClick={() => expandable && setOpen((value) => !value)}\n className={`group -mx-2 flex min-w-0 items-center gap-2.5 rounded-lg px-2 py-1 text-left ${\n expandable ? 'hover:bg-hover' : 'cursor-default'\n }`}\n >\n <ToolIcon\n name={tool.name}\n className={`flex-shrink-0 ${failed ? 'text-destructive' : 'text-subtle'}`}\n />\n <span\n className={`flex-shrink-0 font-medium ${\n running ? 'gui-shimmer' : failed ? 'text-destructive' : 'text-muted-foreground'\n }`}\n >\n {label}\n </span>\n {tool.displayPath ? (\n // #3288: server-computed, workspace-relative, and never middle/end-truncated — only the\n // directory portion is shortened, from the left, so the filename always reads in full.\n <span\n className=\"min-w-0 flex-1 break-all font-mono text-[12.5px] text-subtle\"\n title={tool.displayPath}\n >\n {shortenDirectoryFromLeft(tool.displayPath)}\n </span>\n ) : (\n typeof tool.input === 'string' &&\n tool.input && (\n <span className=\"min-w-0 truncate font-mono text-[12.5px] text-subtle\">\n {tool.input}\n </span>\n )\n )}\n <span className=\"ml-auto flex flex-shrink-0 items-center gap-1.5 text-[12.5px] text-subtle\">\n {running ? (\n <LoaderCircle size={14} className=\"animate-spin\" aria-label=\"running\" />\n ) : failed ? (\n <span className=\"text-destructive\">failed</span>\n ) : (\n <Check size={14} aria-label=\"done\" />\n )}\n {expandable && (\n <ChevronRight size={13} className={`transition-transform ${open ? 'rotate-90' : ''}`} />\n )}\n </span>\n </button>\n {open && expandable && (\n <div className=\"flex flex-col gap-1 pb-1 pl-[25px] pt-0.5\">\n {hasDiff && <DiffLines diffLines={tool.diffLines!} />}\n {!hasDiff && parsedResult?.output && <FoldedOutput text={parsedResult.output} />}\n {parsedResult?.error && (\n <p className=\"font-mono text-[12.5px] text-destructive\">{parsedResult.error}</p>\n )}\n {typeof parsedResult?.exitCode === 'number' && (\n <p className=\"text-[12px] text-subtle\">exit {parsedResult.exitCode}</p>\n )}\n {parsedResult?.structured && <FoldedOutput text={parsedResult.structured} />}\n {hasObservations && <ToolObservations tool={tool} />}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * Command output taller than this starts folded, so one reply never buries the conversation. Lines\n * count where layout cannot be measured; a rendered card also folds on its measured height, because\n * a few long wrapped lines are as tall as many short ones.\n */\nconst COMMAND_FOLD_LINES = 12;\nconst COMMAND_FOLD_HEIGHT_PX = 280;\n\nconst COMMAND_TONE: Record<ICommandOutputEntry['tone'], { dot: string; text: string }> = {\n success: { dot: 'bg-accent', text: 'text-foreground/90' },\n error: { dot: 'bg-destructive', text: 'text-destructive' },\n info: { dot: 'bg-subtle', text: 'text-muted-foreground' },\n};\n\n/**\n * A slash command's outcome, where it was typed: line breaks kept, long output folded. #3289 §2 — the\n * card itself still marks the command (the `/name` header stays monospace, a fixed-width label rather\n * than prose), but its BODY reads as normal text — proportional, normal size — unless it is actually a\n * long listing (the same measure that decides whether to fold it: many raw lines, or tall once\n * rendered), which stays monospace so columns of a real dump still line up.\n */\nfunction CommandCard({ entry }: { entry: ICommandOutputEntry }): React.ReactElement {\n const lines = entry.content.split('\\n');\n const bodyRef = useRef<HTMLPreElement>(null);\n const [tall, setTall] = useState(false);\n const [open, setOpen] = useState(false);\n useLayoutEffect(() => {\n const body = bodyRef.current;\n if (body) setTall(body.scrollHeight > COMMAND_FOLD_HEIGHT_PX);\n }, [entry.content]);\n const byLines = lines.length > COMMAND_FOLD_LINES;\n const foldable = byLines || tall;\n const folded = foldable && !open;\n const shown = folded && byLines ? lines.slice(0, COMMAND_FOLD_LINES).join('\\n') : entry.content;\n const tone = COMMAND_TONE[entry.tone];\n return (\n <div\n data-testid=\"command-output\"\n data-tone={entry.tone}\n className=\"overflow-hidden rounded-xl bg-card\"\n >\n <div className=\"flex items-center gap-2 px-4 pt-3 text-[13px]\">\n <span className={`h-1.5 w-1.5 rounded-full ${tone.dot}`} />\n <span className=\"font-mono font-medium text-foreground\">/{entry.name}</span>\n </div>\n <div className=\"relative\">\n <pre\n ref={bodyRef}\n style={folded ? { maxHeight: COMMAND_FOLD_HEIGHT_PX } : undefined}\n className={`overflow-hidden whitespace-pre-wrap break-words px-4 pb-3 pt-1.5 leading-[1.65] ${\n foldable ? 'font-mono text-[13px]' : 'font-sans text-[14.5px]'\n } ${tone.text}`}\n >\n {shown}\n </pre>\n {folded && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-card to-transparent\" />\n )}\n </div>\n {foldable && (\n <button\n type=\"button\"\n onClick={() => setOpen((value) => !value)}\n className=\"flex w-full items-center gap-1.5 px-4 pb-2.5 pt-1 text-left text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <ChevronDown size={14} className={open ? 'rotate-180' : ''} />\n {open ? 'Show less' : `Show all ${lines.length} lines`}\n </button>\n )}\n </div>\n );\n}\n\n/** One line per finished turn's tool calls; the calls themselves open on demand. */\nfunction ToolGroup({ tools }: { tools: readonly IActiveTool[] }): React.ReactElement | null {\n const [open, setOpen] = useState(false);\n // #3288: an internal signal tool (e.g. the goal-status tool) is never shown as a call — the goal\n // bar shows progress instead. A group made up ENTIRELY of internal tools renders nothing at all.\n const visible = tools.filter((tool) => !tool.internal);\n const failed = visible.filter((tool) => tool.status === 'error').length;\n const names = [\n ...new Set(visible.map((tool) => (tool.commandName ? `/${tool.commandName}` : tool.name))),\n ].join(', ');\n if (visible.length === 0) return null;\n return (\n <div className=\"text-[14px]\">\n <button\n type=\"button\"\n aria-expanded={open}\n onClick={() => setOpen((value) => !value)}\n className=\"group -mx-2 flex max-w-full items-center gap-2.5 rounded-lg px-2 py-1 text-left text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Wrench size={15} strokeWidth={1.75} className=\"flex-shrink-0 text-subtle\" />\n <span className=\"flex-shrink-0\">\n {visible.length} tool {visible.length === 1 ? 'call' : 'calls'}\n </span>\n <span className=\"min-w-0 truncate text-subtle\">{names}</span>\n {failed > 0 && (\n <span className=\"flex-shrink-0 rounded-md bg-destructive/12 px-1.5 text-[12.5px] text-destructive\">\n {failed} failed\n </span>\n )}\n <ChevronRight\n size={14}\n className={`flex-shrink-0 text-subtle transition-transform ${open ? 'rotate-90' : ''}`}\n />\n </button>\n {open && (\n <div className=\"mt-0.5 flex flex-col pl-[25px]\">\n {visible.map((tool) => (\n <ToolCard key={tool.id} tool={tool} />\n ))}\n </div>\n )}\n </div>\n );\n}\n\n/** #3288: a turn that changed files ends with this compact row — a click opens that file's diff. */\nfunction ChangedFilesRow({ files }: { files: readonly IChangedFileSummary[] }): React.ReactElement {\n const [openPath, setOpenPath] = useState<string | null>(null);\n return (\n <div className=\"flex flex-col gap-0.5 rounded-lg bg-card px-2 py-1.5 text-[13px]\">\n <p className=\"px-1 text-[12px] font-medium text-muted-foreground\">Changed files</p>\n {files.map((file) => (\n <div key={file.path} className=\"flex flex-col\">\n <button\n type=\"button\"\n aria-expanded={openPath === file.path}\n onClick={() => setOpenPath((current) => (current === file.path ? null : file.path))}\n className=\"flex min-w-0 items-center gap-2 rounded-md px-1 py-1 text-left hover:bg-hover\"\n >\n <span\n className=\"min-w-0 flex-1 break-all font-mono text-[12.5px] text-foreground\"\n title={file.path}\n >\n {shortenDirectoryFromLeft(file.path)}\n </span>\n <span className=\"flex-shrink-0 font-mono text-[12px] text-accent\">+{file.added}</span>\n <span className=\"flex-shrink-0 font-mono text-[12px] text-destructive\">\n -{file.removed}\n </span>\n </button>\n {openPath === file.path && (\n <div className=\"pl-2\">\n <DiffLines diffLines={file.diffLines} />\n </div>\n )}\n </div>\n ))}\n </div>\n );\n}\n\nfunction ThinkingIndicator(): React.ReactElement {\n return <p className=\"gui-shimmer w-fit text-[15px] font-medium\">Thinking…</p>;\n}\n\n/** \"At the bottom\" for auto-scroll purposes — within this many px of the true bottom. */\nconst NEAR_BOTTOM_PX = 80;\n\nexport function ConversationView({\n messages,\n activeTools,\n streamingText,\n isThinking,\n ownDriverId,\n}: IConversationViewProps): React.ReactElement {\n const containerRef = useRef<HTMLElement>(null);\n // The source of truth `handleScroll` and the follow effect both read synchronously (a ref, so\n // neither sees a stale value from its own last render); mirrored into state to re-render the \"Jump\n // to latest\" button.\n const isAtBottomRef = useRef(true);\n const [isAtBottom, setIsAtBottomState] = useState(true);\n // New content arrived while the person was scrolled away from the bottom — cleared the moment they\n // return to it, by hand or via \"Jump to latest\".\n const [hasNewBelow, setHasNewBelow] = useState(false);\n\n const setIsAtBottom = (value: boolean): void => {\n isAtBottomRef.current = value;\n setIsAtBottomState(value);\n };\n\n /**\n * #3289 §2 — follows new content only while the person is already at the bottom: streaming text, a\n * finished message, a tool starting or finishing. Expanding something already on screen (a tool row,\n * a folded command card) touches none of these, so it never causes a jump — the effect simply does\n * not run for it. An instant jump rather than a smooth one, so it can never itself be mistaken by\n * `handleScroll` below for the person scrolling away mid-animation.\n */\n useLayoutEffect(() => {\n if (isAtBottomRef.current) {\n const el = containerRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n setIsAtBottom(true);\n setHasNewBelow(false);\n } else {\n setHasNewBelow(true);\n }\n }, [messages.length, streamingText, isThinking, activeTools.length]);\n\n const handleScroll = (): void => {\n const el = containerRef.current;\n if (!el) return;\n const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= NEAR_BOTTOM_PX;\n setIsAtBottom(atBottom);\n // Returning to the bottom by hand (not just via \"Jump to latest\") also clears the \"new content\n // below\" flag — otherwise it stays stale and re-shows the button on a later scroll-up even though\n // nothing new has arrived since.\n if (atBottom) setHasNewBelow(false);\n };\n\n const jumpToLatest = (): void => {\n const el = containerRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n setIsAtBottom(true);\n setHasNewBelow(false);\n };\n\n const isEmpty =\n messages.length === 0 && !isThinking && activeTools.length === 0 && !streamingText;\n const isReplyStreaming =\n isThinking || streamingText.length > 0 || activeTools.some((tool) => tool.status === 'running');\n const showJumpToLatest = !isAtBottom && (hasNewBelow || isReplyStreaming);\n\n return (\n <div className=\"agent-ui relative flex h-full min-h-0 flex-col\">\n <main\n ref={containerRef}\n onScroll={handleScroll}\n className=\"min-h-0 flex-1 overflow-y-auto\"\n aria-label=\"Conversation\"\n >\n <div className=\"mx-auto flex w-full max-w-[760px] flex-col gap-6 px-6 pb-6 pt-8\">\n {isEmpty && (\n <div className=\"flex h-full items-center justify-center\">\n <p className=\"text-[14px] text-subtle\">No messages yet</p>\n </div>\n )}\n\n {messages.map((entry) => {\n switch (entry.role) {\n case 'user':\n return (\n <UserBlock\n key={entry.id}\n content={entry.content}\n author={entry.author}\n ownDriverId={ownDriverId}\n />\n );\n case 'assistant':\n return <AgentBlock key={entry.id} content={entry.content} />;\n case 'command':\n return <CommandCard key={entry.id} entry={entry} />;\n case 'tools':\n return <ToolGroup key={entry.id} tools={entry.tools} />;\n case 'changed-files':\n return <ChangedFilesRow key={entry.id} files={entry.files} />;\n }\n })}\n\n {isThinking && !streamingText && <ThinkingIndicator />}\n\n {activeTools.some((tool) => !tool.internal) && (\n <div className=\"flex flex-col\">\n {activeTools\n .filter((tool) => !tool.internal)\n .map((tool) => (\n <ToolCard key={tool.id} tool={tool} />\n ))}\n </div>\n )}\n\n {streamingText && <AgentBlock content={streamingText} isStreaming />}\n </div>\n </main>\n\n {showJumpToLatest && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-4 flex justify-center\">\n <button\n type=\"button\"\n onClick={jumpToLatest}\n className=\"pointer-events-auto flex items-center gap-1.5 rounded-full bg-primary px-3.5 py-2 text-[13px] font-medium text-primary-foreground shadow-lg shadow-black/25 hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n >\n <ArrowDown size={14} strokeWidth={2.25} />\n Jump to latest\n </button>\n </div>\n )}\n </div>\n );\n}\n","'use client';\n\nimport React, { useEffect, useId, useRef } from 'react';\n\n/**\n * The shared modal-shell primitive (#3282 §4a, coordinated with #3282 §2's model/mode controls,\n * which also need a confirmation dialog). Whichever of the two lands first defines this file; the\n * other rebases onto it rather than shipping a second copy.\n *\n * `role=\"dialog\"` (or `\"alertdialog\"`, see `role` below), `aria-modal`, a labelled title, focus moved\n * inside on open, a focus trap while open, Esc to close, and focus restored to whatever had it before\n * the dialog opened. A backdrop click closes the dialog UNLESS `destructive` is set — a destructive\n * dialog (e.g. \"Remove this rule?\") is dismissed only by an explicit choice, never an accidental\n * outside click.\n */\nexport interface IDialogProps {\n open: boolean;\n /** Called on Esc, a backdrop click (non-destructive only), or an explicit close control. */\n onClose: () => void;\n title: string;\n children: React.ReactNode;\n /** Disables backdrop-click-to-close for a dialog whose only correct exits are explicit buttons. */\n destructive?: boolean;\n /** Overrides the panel's own classes; the backdrop/overlay classes are never overridden. */\n panelClassName?: string;\n /** Id of an element describing the dialog, wired to `aria-describedby`. */\n describedById?: string;\n /**\n * Overrides what focus returns to when the dialog closes (#3289 §1). Default behavior captures\n * `document.activeElement` when the dialog opens — which fails for a caller whose opener element\n * is removed from the DOM in the SAME update that opens this dialog (e.g. a menu item whose menu\n * closes as this dialog opens): by the time this dialog's open-effect runs, the browser has already\n * moved focus to `document.body`. Such a caller passes the stable element focus should return to.\n */\n restoreFocusTo?: HTMLElement | null;\n /**\n * #3282 §4e: `'alertdialog'` for a dialog that interrupts to demand a decision before anything else\n * can happen (a destructive confirmation) — WAI-ARIA's distinction from a dialog the user can set\n * aside. Default `'dialog'`.\n */\n role?: 'dialog' | 'alertdialog';\n}\n\nconst FOCUSABLE_SELECTOR =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Every currently-open `Dialog`, oldest first. All instances share one `document` keydown listener\n * target, so listeners fire in REGISTRATION order regardless of nesting — the outer dialog's\n * listener (registered first, when it opened) always runs before a nested `ConfirmDialog`'s\n * (registered later, when the user triggered it). `stopImmediatePropagation` on the outer listener\n * therefore cannot defer to the inner one; it fires first and would win. Instead, each instance\n * checks this stack and only acts when it is the LAST (topmost) entry — an outer dialog that isn't\n * topmost no-ops on the keydown and lets the still-registered inner listener act.\n */\nlet openDialogStack: symbol[] = [];\n\nexport function Dialog({\n open,\n onClose,\n title,\n children,\n destructive = false,\n panelClassName,\n describedById,\n restoreFocusTo,\n role = 'dialog',\n}: IDialogProps): React.ReactElement | null {\n const panelRef = useRef<HTMLDivElement>(null);\n const previouslyFocused = useRef<HTMLElement | null>(null);\n const titleId = useId();\n const dialogToken = useRef<symbol>(Symbol('dialog'));\n\n // Move focus in on open, and restore it to the opener once the dialog goes away. `restoreFocusTo`,\n // given, overrides the ambient `document.activeElement` this would otherwise capture.\n useEffect(() => {\n if (!open) return undefined;\n previouslyFocused.current = restoreFocusTo ?? (document.activeElement as HTMLElement | null);\n const panel = panelRef.current;\n const firstFocusable = panel?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR);\n (firstFocusable ?? panel)?.focus();\n return () => {\n previouslyFocused.current?.focus?.();\n };\n }, [open]);\n\n // Tracks this dialog's membership on the shared open-dialog stack. Kept separate from the\n // keydown-listener effect below (which also depends on `onClose`) so that a caller passing a\n // fresh `onClose` identity on every render can never re-push this dialog and wrongly promote it\n // to topmost — membership changes only when `open` itself flips.\n useEffect(() => {\n if (!open) return undefined;\n const token = dialogToken.current;\n openDialogStack.push(token);\n return () => {\n openDialogStack = openDialogStack.filter((entry) => entry !== token);\n };\n }, [open]);\n\n // Esc closes; Tab traps focus inside the panel while it is open. Both no-op unless this dialog\n // is the topmost open one (see `openDialogStack` above).\n useEffect(() => {\n if (!open) return undefined;\n const token = dialogToken.current;\n const isTopmost = (): boolean => openDialogStack[openDialogStack.length - 1] === token;\n const onKeyDown = (event: KeyboardEvent): void => {\n if (!isTopmost()) return;\n if (event.key === 'Escape') {\n event.stopImmediatePropagation();\n onClose();\n return;\n }\n if (event.key !== 'Tab') return;\n const panel = panelRef.current;\n if (!panel) return;\n const focusable = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last?.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first?.focus();\n }\n };\n document.addEventListener('keydown', onKeyDown, true);\n return () => document.removeEventListener('keydown', onKeyDown, true);\n }, [open, onClose]);\n\n if (!open) return null;\n\n return (\n <div\n className=\"agent-ui fixed inset-0 z-50 flex items-center justify-center bg-black/55 p-4 backdrop-blur-[2px]\"\n onMouseDown={(event) => {\n if (destructive) return;\n if (event.target === event.currentTarget) onClose();\n }}\n >\n <div\n ref={panelRef}\n role={role}\n aria-modal=\"true\"\n aria-labelledby={titleId}\n {...(describedById ? { 'aria-describedby': describedById } : {})}\n tabIndex={-1}\n className={\n panelClassName ??\n 'flex max-h-[85vh] w-full max-w-lg flex-col rounded-2xl bg-card text-card-foreground shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] focus:outline-none'\n }\n >\n <h2 id={titleId} className=\"sr-only\">\n {title}\n </h2>\n {children}\n </div>\n </div>\n );\n}\n\nconst BUTTON =\n 'inline-flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[14px] font-medium transition-colors';\nconst PRIMARY_BUTTON = `${BUTTON} bg-primary text-primary-foreground hover:opacity-90`;\nconst SECONDARY_BUTTON = `${BUTTON} bg-raised text-foreground hover:bg-hover`;\nconst DESTRUCTIVE_BUTTON = `${BUTTON} bg-destructive text-destructive-foreground hover:opacity-90`;\n\nexport interface IConfirmDialogProps {\n open: boolean;\n title: string;\n body: React.ReactNode;\n confirmLabel: string;\n cancelLabel?: string;\n /** Styles the confirm button as destructive; the dialog itself also stops closing on a backdrop click. */\n destructive?: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n /** Forwarded to `Dialog` — see its doc comment. */\n restoreFocusTo?: HTMLElement | null;\n}\n\n/**\n * A title, a body, Cancel focused by default, and a confirm button that can be styled destructive.\n * #3282 §4e: `destructive` also picks the dialog's ARIA role — `\"alertdialog\"` when it is (an action\n * that demands a decision before anything else, e.g. deleting a session), `\"dialog\"` otherwise.\n */\nexport function ConfirmDialog({\n open,\n title,\n body,\n confirmLabel,\n cancelLabel = 'Cancel',\n destructive = false,\n onConfirm,\n onCancel,\n restoreFocusTo,\n}: IConfirmDialogProps): React.ReactElement | null {\n const bodyId = useId();\n return (\n <Dialog\n open={open}\n onClose={onCancel}\n title={title}\n destructive={destructive}\n role={destructive ? 'alertdialog' : 'dialog'}\n describedById={bodyId}\n restoreFocusTo={restoreFocusTo}\n panelClassName=\"w-full max-w-sm rounded-2xl bg-card p-5 text-card-foreground shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] focus:outline-none\"\n >\n <p className=\"text-[15px] font-medium text-foreground\">{title}</p>\n <p id={bodyId} className=\"mt-2 text-[13.5px] text-muted-foreground\">\n {body}\n </p>\n <div className=\"mt-5 flex justify-end gap-2\">\n {/* Cancel is the FIRST focusable element in the panel, so Dialog's open-focus lands here. */}\n <button type=\"button\" className={SECONDARY_BUTTON} onClick={onCancel}>\n {cancelLabel}\n </button>\n <button\n type=\"button\"\n className={destructive ? DESTRUCTIVE_BUTTON : PRIMARY_BUTTON}\n onClick={onConfirm}\n >\n {confirmLabel}\n </button>\n </div>\n </Dialog>\n );\n}\n","/**\n * #3282 §4 part b-3 — the Agents panel's Goal row: the objective, and its status in plain words.\n */\n\nimport type { IGoalState } from '@robota-sdk/agent-interface-session';\n\nconst STOP_REASON_LABELS: Record<NonNullable<IGoalState['stopReason']>, string> = {\n satisfied: 'Satisfied',\n 'max-iterations': 'Stopped — reached the iteration limit',\n cancelled: 'Cancelled',\n 'no-progress': 'Stopped — no progress',\n};\n\n/** The goal's status in plain words — never the raw `status`/`stopReason` identifiers. */\nexport function describeGoalStatus(goal: IGoalState): string {\n if (goal.status === 'active') return 'Active';\n if (goal.status === 'satisfied') return 'Satisfied';\n return goal.stopReason ? STOP_REASON_LABELS[goal.stopReason] : 'Stopped';\n}\n","/**\n * #3282 §4 part b-3 — plain-words presentation for the Agents panel's \"Scheduled\" group. Never an\n * ISO string or a cron expression: a concrete `nextFireAt` is shown in local time as \"Today 9:00\" /\n * \"Tomorrow 9:00\" / a weekday / a date; a handful of common cron shapes are phrased (\"Every weekday\n * at 9:00\"); anything else falls back to \"Repeats\".\n */\n\nimport type {\n IBackgroundTaskState,\n TBackgroundTaskStatus,\n} from '@robota-sdk/agent-interface-execution';\n\ntype TScheduleTask = IBackgroundTaskState<'scheduled'>;\n\nconst WEEKDAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n\nfunction formatClock(date: Date): string {\n return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });\n}\n\nfunction isSameLocalDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** A concrete fire time, in plain local words — never the raw ISO string. */\nexport function formatPlainLocalTime(iso: string, now: Date = new Date()): string {\n const when = new Date(iso);\n if (Number.isNaN(when.getTime())) return 'Unknown';\n const clock = formatClock(when);\n if (isSameLocalDay(when, now)) return `Today ${clock}`;\n const tomorrow = new Date(now);\n tomorrow.setDate(now.getDate() + 1);\n if (isSameLocalDay(when, tomorrow)) return `Tomorrow ${clock}`;\n const sixDaysOut = new Date(now);\n sixDaysOut.setDate(now.getDate() + 6);\n if (when.getTime() > now.getTime() && when.getTime() <= sixDaysOut.getTime()) {\n return `${WEEKDAY_NAMES[when.getDay()]} ${clock}`;\n }\n const date = when.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n return `${date} ${clock}`;\n}\n\n/** A handful of common 5-field cron shapes phrased plainly; anything else is `undefined`. */\nfunction describeCronPlainly(cronExpression: string): string | undefined {\n const fields = cronExpression.trim().split(/\\s+/);\n if (fields.length !== 5) return undefined;\n const [minute, hour, dayOfMonth, month, dayOfWeek] = fields;\n if (dayOfMonth !== '*' || month !== '*') return undefined;\n if (!/^\\d{1,2}$/.test(minute!) || !/^\\d{1,2}$/.test(hour!)) return undefined;\n const clock = `${hour!.padStart(2, '0')}:${minute!.padStart(2, '0')}`;\n if (dayOfWeek === '*') return `Every day at ${clock}`;\n if (dayOfWeek === '1-5') return `Every weekday at ${clock}`;\n if (dayOfWeek === '0,6' || dayOfWeek === '6,0') return `Every weekend at ${clock}`;\n if (/^\\d$/.test(dayOfWeek!)) {\n const name = WEEKDAY_NAMES[Number(dayOfWeek)];\n return name ? `Every ${name} at ${clock}` : undefined;\n }\n return undefined;\n}\n\n/** \"When it runs next\" — the Scheduled row's own line, in plain local time (never raw wire data). */\nexport function describeScheduleTiming(task: TScheduleTask, now: Date = new Date()): string {\n if (task.nextFireAt) return formatPlainLocalTime(task.nextFireAt, now);\n const cronExpression = task.schedule?.cronExpression;\n if (!cronExpression) return '—';\n return describeCronPlainly(cronExpression) ?? 'Repeats';\n}\n\n/** What a schedule runs — its one-line instruction, never the raw label/id. */\nexport function describeScheduleInstruction(task: TScheduleTask): string {\n return task.schedule?.agentInstruction ?? task.label;\n}\n\nconst STATUS_LABELS: Partial<Record<TBackgroundTaskStatus, string>> = {\n sleeping: 'Active',\n paused: 'Paused',\n running: 'Running',\n queued: 'Queued',\n cancelled: 'Deleted',\n failed: 'Failed',\n completed: 'Done',\n waiting_permission: 'Needs you',\n};\n\n/** The schedule's status in plain words. */\nexport function describeScheduleStatus(task: TScheduleTask): string {\n return STATUS_LABELS[task.status] ?? task.status;\n}\n","/**\n * #3288 §1: one true-status label for an execution-workspace entry, shared by the Agents panel row\n * and its detail sheet so the two never say something different about the same entry.\n *\n * A completed task whose result still reports a refused tool call is not \"Done\" — it is \"Needs\n * permission\" (retrospective: the run is over, but something in it was refused). That is distinct\n * from `attention === 'permission'`, which means a turn is PARKED waiting for an answer right now\n * (\"Needs you\").\n */\nimport type {\n IExecutionWorkspaceEntry,\n TExecutionAttention,\n TExecutionWorkspaceStatus,\n} from '@robota-sdk/agent-interface-execution';\n\nexport type TExecutionStatusTone = 'warning' | 'destructive' | 'success' | 'muted';\n\nexport interface IExecutionStatusLabel {\n readonly label: string;\n readonly tone: TExecutionStatusTone;\n}\n\nexport function hadDeniedToolCalls(\n status: TExecutionWorkspaceStatus,\n deniedToolCalls: number | undefined,\n): boolean {\n return status === 'completed' && (deniedToolCalls ?? 0) > 0;\n}\n\nexport function describeExecutionEntryStatus(\n entry: Pick<IExecutionWorkspaceEntry, 'status' | 'attention' | 'deniedToolCalls'>,\n): IExecutionStatusLabel | null {\n return describeExecutionStatus(entry.status, entry.attention, entry.deniedToolCalls);\n}\n\nexport function describeExecutionStatus(\n status: TExecutionWorkspaceStatus,\n attention: TExecutionAttention,\n deniedToolCalls: number | undefined,\n): IExecutionStatusLabel | null {\n if (attention === 'permission' || status === 'waiting_permission') {\n return { label: 'Needs you', tone: 'warning' };\n }\n if (attention === 'failed' || status === 'failed') {\n return { label: 'Failed', tone: 'destructive' };\n }\n if (hadDeniedToolCalls(status, deniedToolCalls)) {\n return { label: 'Needs permission', tone: 'warning' };\n }\n if (status === 'completed') return { label: 'Done', tone: 'muted' };\n if (status === 'cancelled') return { label: 'Stopped', tone: 'muted' };\n if (status === 'queued') return { label: 'Queued', tone: 'muted' };\n if (status === 'running') return { label: 'Running', tone: 'muted' };\n return null;\n}\n","import {\n CircleCheck,\n CircleDashed,\n CircleX,\n LoaderCircle,\n Pause,\n Play,\n ShieldAlert,\n Square,\n Target,\n} from 'lucide-react';\nimport React, { useState } from 'react';\n\nimport { ConfirmDialog } from './Dialog.js';\nimport { describeGoalStatus } from '../goal-view.js';\nimport {\n describeScheduleInstruction,\n describeScheduleStatus,\n describeScheduleTiming,\n} from '../schedule-view.js';\n\nimport { describeExecutionStatus, hadDeniedToolCalls } from '../execution-entry-status.js';\n\nimport type {\n IBackgroundTaskState,\n IExecutionWorkspaceEntry,\n TExecutionAttention,\n TExecutionWorkspaceStatus,\n} from '@robota-sdk/agent-interface-execution';\nimport type { IGoalState } from '@robota-sdk/agent-interface-session';\n\ninterface IAgentActivityPanelProps {\n tasks: readonly IExecutionWorkspaceEntry[];\n /** #3282 §4 part b-3: the \"Scheduled\" group — every `kind: 'scheduled'` background task. */\n schedules?: readonly IBackgroundTaskState<'scheduled'>[];\n /** #3282 §4 part b-3: the current goal (`/goal`), or null/absent when none is set. */\n goal?: IGoalState | null;\n onPauseSchedule?: (taskId: string) => void;\n onResumeSchedule?: (taskId: string) => void;\n onDeleteSchedule?: (taskId: string) => void;\n onCancelGoal?: () => void;\n className?: string;\n /** #3288 §1: open a non-main-thread entry's detail sheet. */\n onSelect?: (entry: IExecutionWorkspaceEntry) => void;\n /** #3288 §1: the main thread entry was clicked — the conversation beside this panel is it. */\n onReturnToConversation?: () => void;\n /** #3288 §1: Stop was clicked on a running/queued entry. */\n onStop?: (entry: IExecutionWorkspaceEntry) => void;\n /** #3288 §1: the entry whose detail sheet is currently open, for a highlighted row. */\n selectedEntryId?: string;\n}\n\nexport function AgentActivityPanel({\n tasks,\n schedules = [],\n goal = null,\n onPauseSchedule,\n onResumeSchedule,\n onDeleteSchedule,\n onCancelGoal,\n className,\n onSelect,\n onReturnToConversation,\n onStop,\n selectedEntryId,\n}: IAgentActivityPanelProps): React.ReactElement {\n const runningCount = tasks.filter((t) => t.status === 'running').length;\n // A deleted schedule is `cancelled` forever (the manager never removes the record) — the group\n // shows only what is still relevant, exactly like Delete disappearing the row it was pressed on.\n const visibleSchedules = schedules.filter((task) => task.status !== 'cancelled');\n const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);\n const pendingDelete = visibleSchedules.find((task) => task.id === pendingDeleteId) ?? null;\n\n return (\n <div className={`agent-ui flex flex-col overflow-hidden ${className ?? ''}`}>\n <div className=\"flex h-12 flex-shrink-0 items-center gap-2 px-4\">\n <span className=\"text-[14px] font-medium text-foreground\">Agents</span>\n {runningCount > 0 && (\n <span className=\"ml-auto text-[13px] tabular-nums text-subtle\">\n {runningCount} running\n </span>\n )}\n </div>\n\n <div className=\"flex-1 space-y-4 overflow-y-auto px-3 pb-3\">\n {tasks.length > 0 && (\n <div className=\"space-y-2\">\n {tasks.map((entry) => (\n <AgentCard\n key={entry.id}\n entry={entry}\n selected={entry.id === selectedEntryId}\n onOpen={\n entry.kind === 'main_thread'\n ? onReturnToConversation\n : onSelect && (() => onSelect(entry))\n }\n onStop={\n onStop && entry.controls?.includes('cancel') ? () => onStop(entry) : undefined\n }\n />\n ))}\n </div>\n )}\n\n {visibleSchedules.length > 0 && (\n <div>\n <p className=\"px-0.5 pb-1.5 text-[12px] font-medium uppercase tracking-wide text-subtle\">\n Scheduled\n </p>\n <div className=\"space-y-2\">\n {visibleSchedules.map((task) => (\n <ScheduleCard\n key={task.id}\n task={task}\n onPause={onPauseSchedule}\n onResume={onResumeSchedule}\n onRequestDelete={() => setPendingDeleteId(task.id)}\n />\n ))}\n </div>\n </div>\n )}\n\n {goal && (\n <div>\n <p className=\"px-0.5 pb-1.5 text-[12px] font-medium uppercase tracking-wide text-subtle\">\n Goal\n </p>\n <GoalCard goal={goal} onCancel={onCancelGoal} />\n </div>\n )}\n </div>\n\n <ConfirmDialog\n open={pendingDelete !== null}\n title=\"Delete this schedule?\"\n body={\n pendingDelete\n ? `\"${describeScheduleInstruction(pendingDelete)}\" will stop running. This cannot be undone.`\n : ''\n }\n confirmLabel=\"Delete\"\n destructive\n onConfirm={() => {\n if (pendingDelete) onDeleteSchedule?.(pendingDelete.id);\n setPendingDeleteId(null);\n }}\n onCancel={() => setPendingDeleteId(null)}\n />\n </div>\n );\n}\n\nfunction AgentCard({\n entry,\n selected,\n onOpen,\n onStop,\n}: {\n entry: IExecutionWorkspaceEntry;\n selected: boolean;\n onOpen?: () => void;\n onStop?: () => void;\n}): React.ReactElement {\n const needsYou = entry.attention === 'permission' || entry.status === 'waiting_permission';\n const failed = entry.attention === 'failed' || entry.status === 'failed';\n return (\n <div\n role={onOpen ? 'button' : undefined}\n tabIndex={onOpen ? 0 : undefined}\n onClick={onOpen}\n onKeyDown={\n onOpen\n ? (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n onOpen();\n }\n }\n : undefined\n }\n aria-label={onOpen ? entry.title : undefined}\n className={`rounded-xl px-3.5 py-3 transition-opacity duration-500 ${\n needsYou ? 'bg-warning/10' : failed ? 'bg-destructive/10' : 'bg-card'\n } ${entry.status === 'completed' ? 'opacity-60' : ''} ${\n selected ? 'ring-1 ring-inset ring-accent' : ''\n } ${onOpen ? 'cursor-pointer' : ''}`}\n >\n <div className=\"flex items-center gap-2.5\">\n <StatusIcon status={entry.status} attention={entry.attention} deniedToolCalls={entry.deniedToolCalls} />\n <span className=\"min-w-0 flex-1 truncate text-[14px] font-medium text-foreground\">\n {entry.title}\n </span>\n <AttentionTag status={entry.status} attention={entry.attention} deniedToolCalls={entry.deniedToolCalls} />\n {onStop && (\n <button\n type=\"button\"\n aria-label={`Stop ${entry.title}`}\n onClick={(event) => {\n event.stopPropagation();\n onStop();\n }}\n className=\"flex-shrink-0 rounded-md px-1.5 py-px text-[12px] text-subtle hover:bg-destructive/10 hover:text-destructive\"\n >\n Stop\n </button>\n )}\n </div>\n {entry.currentAction && (\n <p className=\"mt-1.5 truncate pl-[26px] text-[13px] leading-snug text-muted-foreground\">\n {entry.currentAction}\n </p>\n )}\n {entry.preview && (\n <p className=\"mt-1 truncate pl-[26px] font-mono text-[12px] leading-snug text-subtle\">\n {entry.preview}\n </p>\n )}\n </div>\n );\n}\n\n/** #3282 §4 part b-3: one row of the \"Scheduled\" group — what it runs, when next, status, controls. */\nfunction ScheduleCard({\n task,\n onPause,\n onResume,\n onRequestDelete,\n}: {\n task: IBackgroundTaskState<'scheduled'>;\n onPause?: (taskId: string) => void;\n onResume?: (taskId: string) => void;\n onRequestDelete: () => void;\n}): React.ReactElement {\n const isPaused = task.status === 'paused';\n return (\n <div className=\"rounded-xl bg-card px-3.5 py-3\">\n <p className=\"truncate text-[14px] font-medium text-foreground\">\n {describeScheduleInstruction(task)}\n </p>\n <p className=\"mt-1 text-[13px] text-muted-foreground\">\n {describeScheduleTiming(task)} · {describeScheduleStatus(task)}\n </p>\n <div className=\"mt-2 flex items-center gap-3\">\n {isPaused ? (\n <button\n type=\"button\"\n onClick={() => onResume?.(task.id)}\n className=\"flex items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Play size={11} fill=\"currentColor\" aria-hidden=\"true\" />\n Resume\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => onPause?.(task.id)}\n className=\"flex items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Pause size={11} fill=\"currentColor\" aria-hidden=\"true\" />\n Pause\n </button>\n )}\n <button\n type=\"button\"\n onClick={onRequestDelete}\n className=\"flex items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-destructive hover:bg-destructive/10\"\n >\n Delete…\n </button>\n </div>\n </div>\n );\n}\n\n/** #3282 §4 part b-3: the current goal — objective, plain-word status, and Cancel. */\nfunction GoalCard({\n goal,\n onCancel,\n}: {\n goal: IGoalState;\n onCancel?: () => void;\n}): React.ReactElement {\n return (\n <div className=\"rounded-xl bg-card px-3.5 py-3\">\n <div className=\"flex items-center gap-2.5\">\n <Target size={16} strokeWidth={1.9} className=\"flex-shrink-0 text-accent\" />\n <span className=\"min-w-0 flex-1 truncate text-[14px] font-medium text-foreground\">\n {goal.objective}\n </span>\n </div>\n <p className=\"mt-1.5 pl-[26px] text-[13px] text-muted-foreground\">\n {describeGoalStatus(goal)}\n </p>\n {goal.status === 'active' && (\n <div className=\"mt-2 pl-[26px]\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"flex items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Square size={11} fill=\"currentColor\" aria-hidden=\"true\" />\n Cancel goal\n </button>\n </div>\n )}\n </div>\n );\n}\n\nfunction StatusIcon({\n status,\n attention,\n deniedToolCalls,\n}: {\n status: TExecutionWorkspaceStatus;\n attention: TExecutionAttention;\n deniedToolCalls?: number;\n}): React.ReactElement {\n const props = { size: 16, strokeWidth: 1.9, className: 'flex-shrink-0' } as const;\n if (attention === 'permission' || status === 'waiting_permission') {\n return <ShieldAlert {...props} className={`${props.className} text-warning`} />;\n }\n if (attention === 'failed' || status === 'failed') {\n return <CircleX {...props} className={`${props.className} text-destructive`} />;\n }\n if (hadDeniedToolCalls(status, deniedToolCalls)) {\n return <ShieldAlert {...props} className={`${props.className} text-warning`} />;\n }\n if (status === 'running') {\n return (\n <LoaderCircle\n {...props}\n className={`${props.className} animate-spin text-muted-foreground`}\n />\n );\n }\n if (status === 'completed') {\n return <CircleCheck {...props} className={`${props.className} text-success`} />;\n }\n if (status === 'cancelled') {\n return <CircleX {...props} className={`${props.className} text-subtle`} />;\n }\n return <CircleDashed {...props} className={`${props.className} text-subtle`} />;\n}\n\nconst STATUS_TAG_TONE: Record<string, string> = {\n warning: 'bg-warning/15 text-warning',\n destructive: 'bg-destructive/12 text-destructive',\n muted: 'text-subtle',\n};\n\nfunction AttentionTag({\n attention,\n status,\n deniedToolCalls,\n}: {\n attention: TExecutionAttention;\n status: TExecutionWorkspaceStatus;\n deniedToolCalls?: number;\n}): React.ReactElement | null {\n const described = describeExecutionStatus(status, attention, deniedToolCalls);\n // A running row already has its spinner; no text tag repeats that.\n if (!described || described.label === 'Running') return null;\n return (\n <span className={`flex-shrink-0 rounded-md px-1.5 py-px text-[12px] ${STATUS_TAG_TONE[described.tone]}`}>\n {described.label}\n </span>\n );\n}\n","'use client';\n\nimport { Check, X } from 'lucide-react';\nimport React from 'react';\n\nimport { Dialog } from './Dialog.js';\n\nimport type { IWsSessionState } from '../hooks/useSessionClient.js';\n\n/**\n * #3282 §4 part b-3 — the agent switcher: `show-agent-switcher` (`/agent` with no args) opens this\n * sheet instead of the surface's old \"not available\" line. It lists every agent this session can\n * run — name, one-line description, and where each is defined in plain words — with the current one\n * checked. Choosing a row runs the SAME path as typing `/agent <name>` (a plain `command`, wired in\n * `useAgentSwitcherState`); the reply is this sheet's own confirmation line, never a conversation\n * card (issue #3282 §4's decided design).\n */\nexport function AgentSwitcherSheet({ state }: { state: IWsSessionState }): React.ReactElement | null {\n if (!state.agentSwitcherOpen) return null;\n\n const loading = state.agentSwitcherStatus === 'loading' && state.agentDefinitions.length === 0;\n\n return (\n <Dialog\n open\n onClose={() => state.closeAgentSwitcher()}\n title=\"Switch agent\"\n panelClassName=\"flex max-h-[min(560px,85vh)] w-full max-w-md flex-col overflow-hidden rounded-2xl bg-card text-card-foreground shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] focus:outline-none\"\n >\n <div className=\"flex h-12 flex-shrink-0 items-center gap-1 border-b border-border px-3\">\n <h2 className=\"px-1.5 text-[15px] font-semibold text-foreground\">Switch agent</h2>\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={() => state.closeAgentSwitcher()}\n className=\"ml-auto rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <X size={17} strokeWidth={1.75} />\n </button>\n </div>\n\n {state.agentSwitchMessage ? (\n <p\n role=\"status\"\n className=\"mx-4 mt-3 rounded-lg bg-raised px-3 py-2 text-[13px] text-foreground\"\n >\n {state.agentSwitchMessage}\n </p>\n ) : null}\n\n <ul className=\"min-h-0 flex-1 overflow-y-auto p-2\">\n {loading ? (\n <li className=\"px-3 py-4 text-[13.5px] text-muted-foreground\">Loading agents…</li>\n ) : state.agentDefinitions.length === 0 ? (\n <li className=\"px-3 py-4 text-[13.5px] text-muted-foreground\">\n No agents are configured on this host.\n </li>\n ) : (\n state.agentDefinitions.map((agent) => {\n const isCurrent = agent.name === state.currentAgentType;\n return (\n <li key={agent.name}>\n <button\n type=\"button\"\n onClick={() => state.selectAgent(agent.name)}\n aria-current={isCurrent ? 'true' : undefined}\n className=\"flex w-full items-start gap-3 rounded-xl px-3 py-2.5 text-left hover:bg-hover\"\n >\n <span className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center\">\n {isCurrent ? (\n <Check size={16} strokeWidth={2} className=\"text-accent\" />\n ) : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-[14px] font-medium text-foreground\">\n {agent.name}\n </span>\n <span className=\"block truncate text-[13px] text-muted-foreground\">\n {agent.description}\n </span>\n <span className=\"block truncate text-[12px] text-subtle\">\n {agent.definedIn}\n </span>\n </span>\n </button>\n </li>\n );\n })\n )}\n </ul>\n </Dialog>\n );\n}\n","'use client';\n\nimport { MessageCircleQuestion, ShieldAlert } from 'lucide-react';\nimport React, {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { driverAttributionText, isSameSurface } from '../driver-labels.js';\nimport { ConfirmDialog } from './Dialog.js';\nimport { DiffLines } from './DiffLines.js';\n\nimport type { TPendingPrompt } from '../hooks/prompt-state.js';\nimport type { TActionResponse } from '@robota-sdk/agent-interface-transport';\nimport type { TDriverId } from '@robota-sdk/agent-interface-session';\n\n/**\n * Renders the owner's pending permission/ask prompts (REMOTE-007/009). Under local == remote the paired\n * browser owner answers its OWN prompts; the first pending prompt is shown. A gated tool call blocks\n * until answered, so this is on the critical path — not decorative.\n *\n * `modal` covers the page; `dock` sits above the composer, as desktop agent apps place a pending\n * question next to where the answer is typed. Once armed and focused, its keys answer it: 1–9 picks\n * an option, Esc cancels a question or denies a permission — but it never takes that focus away from\n * a field the person is typing in (#3280 §1). A click on a button always answers at once. An ask whose\n * request sets `allowFreeText` also shows a text field (masked when `masked` is set); Enter there\n * submits it, and its own keys never reach the prompt's shortcuts (#3280 §3). The field's typed value\n * lives only in `FreeTextField`, keyed by `prompt.id`: a cancelled or answered prompt's text can\n * never reach the next prompt's render, masked or not, because the field is a fresh component\n * instance rather than a value carried over in this component's own state (#3280 §3 follow-up).\n */\ninterface IPermissionPromptProps {\n prompts: readonly TPendingPrompt[];\n onAnswerPermission: (id: string, result: boolean) => void;\n onAnswerAsk: (id: string, response: TActionResponse) => void;\n /** This connection's own driver id (§3289 §3): a prompt raised by its own turn shows no requester. */\n ownDriverId?: TDriverId | null;\n layout?: 'modal' | 'dock';\n /** How long a new prompt waits before its keys answer it; tests pass their own. */\n armDelayMs?: number;\n /**\n * Called once an answer (click or key) leaves no prompt behind, if focus was inside this prompt at\n * the time — so the caller can send it back to the field the person was typing in (the composer,\n * for the `dock` layout). Not called when another prompt immediately follows: focus stays on the\n * prompt for that one instead.\n */\n onFocusReturn?: () => void;\n}\n\n/**\n * A prompt can appear while the user is typing in a field. For this long after it appears the prompt\n * leaves focus where it is, so the keystrokes land there instead of answering (`1` allows). A mouse\n * click on a button still answers at once, and Esc still denies or cancels, because both are\n * deliberate. A click made with the keyboard (Enter or Space on a focused button) waits for the\n * prompt to arm like any other key.\n */\nexport const PROMPT_ARM_DELAY_MS = 450;\n\n/** An element whose keystrokes the person relies on — a prompt must never take focus from one of these. */\nfunction isEditableElement(element: Element | null): boolean {\n return element !== null && element.matches('input, textarea, select, [contenteditable]');\n}\n\n/**\n * #3282 §2 (part 2) — the one destructive option in the provider profile action menu (the ask built\n * by `askProviderProfileAction` in `agent-command`'s `provider-command-profile.ts`, `request.id`\n * `'provider-profile-action'`): \"Delete\" answers to no digit and renders apart from Switch/Edit/Test/\n * Duplicate, styled destructive — the exact audited bug (\"Delete looks the same as Switch, answers to\n * the key 5\"). Every other ask (this `request.id` check is the only thing that scopes the change) is\n * unaffected.\n */\nfunction isDestructiveAskOption(requestId: string | undefined, value: string): boolean {\n return requestId === 'provider-profile-action' && value === 'delete';\n}\n\nexport function PermissionPrompt({\n prompts,\n onAnswerPermission,\n onAnswerAsk,\n ownDriverId = null,\n layout = 'modal',\n armDelayMs = PROMPT_ARM_DELAY_MS,\n onFocusReturn,\n}: IPermissionPromptProps): React.ReactElement | null {\n const prompt = prompts[0];\n const promptId = prompt?.id;\n const containerRef = useRef<HTMLDivElement>(null);\n const fieldRef = useRef<HTMLInputElement>(null);\n // A question with a text field (#3280 §3): the field is the prompt's primary control, so focus goes\n // there instead of the container wherever this component would otherwise focus the container itself.\n const hasFreeText = prompt?.kind === 'ask' && prompt.request.allowFreeText === true;\n const [armedId, setArmedId] = useState<string | undefined>(undefined);\n const [hasFocus, setHasFocus] = useState(false);\n // Whether the field itself (not just some part of the prompt) currently holds focus — digits typed\n // there type into the field rather than choosing a numbered option, so the hint must say so. Lifted\n // from `FreeTextField` via `onFocusChange`, since the hint text is rendered up here.\n const [fieldFocused, setFieldFocused] = useState(false);\n // Imperative escape hatch onto the field's own state (see `FreeTextField`): every path that ends\n // this prompt clears it explicitly, rather than relying on the field ever being asked to render a\n // value that belongs to a prompt other than the one it was mounted for.\n const fieldHandleRef = useRef<IFreeTextFieldHandle>(null);\n // Set at the moment of an answer if focus was inside the prompt then; consumed once the prompt list\n // empties (see below) or cleared once a next prompt shows it was not needed after all.\n const pendingFocusReturnRef = useRef(false);\n useEffect(() => {\n if (promptId === undefined || armDelayMs <= 0) return undefined;\n const timer = setTimeout(() => setArmedId(promptId), armDelayMs);\n return () => clearTimeout(timer);\n }, [promptId, armDelayMs]);\n const armed = promptId !== undefined && (armDelayMs <= 0 || armedId === promptId);\n // Answering one prompt renders the next into the same buttons, so a button focused to answer the\n // last one would take Enter, Space or a held key as an answer to this one. Focus goes back to the\n // prompt itself, whose keys wait for the prompt to arm. A reused button is still there to find this\n // way — but the free-text field is its own component, remounted (not updated) for the new prompt.id\n // (#3280 §3 follow-up), and removing a focused DOM node drops focus to <body> as an intrinsic side\n // effect, before this effect ever runs. `pendingFocusReturnRef` (set at answer time, and not yet\n // cleared — the effect that clears it is a passive one, ordered after this layout effect) still\n // remembers that focus was in this prompt, so that case is reclaimed too.\n useLayoutEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const active = document.activeElement;\n const focusStillInside = !!(active && active !== container && container.contains(active));\n const focusJustLeftOnRemount = pendingFocusReturnRef.current && active === document.body;\n if (focusStillInside || focusJustLeftOnRemount) {\n container.focus();\n }\n }, [promptId]);\n // Once armed the prompt takes focus, so its keys answer it rather than type wherever the person\n // already is — UNLESS that is an editable field, whose keystrokes must never be stolen (#3280 §1).\n useEffect(() => {\n if (!armed) return;\n if (isEditableElement(document.activeElement)) return;\n (hasFreeText ? fieldRef.current : containerRef.current)?.focus();\n }, [armed, promptId, hasFreeText]);\n // An answer given while focus was inside this prompt sends it back to the composer once no prompt is\n // left to show it — unless another one immediately follows (handled above by keeping focus in place).\n useEffect(() => {\n if (promptId !== undefined) {\n pendingFocusReturnRef.current = false;\n return;\n }\n setHasFocus(false);\n if (pendingFocusReturnRef.current) {\n pendingFocusReturnRef.current = false;\n onFocusReturn?.();\n }\n }, [promptId, onFocusReturn]);\n if (!prompt) return null;\n\n // #3282 §2 (part 2): the provider-delete confirmation (`buildProviderDelete` in agent-command's\n // `provider-command-profile-lifecycle.ts`) is now built on the shared `ConfirmDialog` (#3331)\n // instead of the generic ask grid — a destructive-styled \"Delete\", Cancel focused by default, and\n // no accidental backdrop-click dismissal. `confirmAction`'s own `CONFIRM_YES`/`CONFIRM_NO` values\n // ('yes'/'no', from `@robota-sdk/agent-core`) are answered directly; this bypasses the rest of this\n // component's dock/modal chrome and digit-shortcut machinery entirely, since `Dialog` already owns\n // its own focus trap, Esc handling and focus restore.\n if (prompt.kind === 'ask' && prompt.request.id === 'provider-delete') {\n return (\n <ConfirmDialog\n open\n title={prompt.request.title}\n body=\"This removes it from your provider profiles. This can't be undone.\"\n confirmLabel=\"Delete\"\n destructive\n onCancel={() => onAnswerAsk(prompt.id, { type: 'cancelled' })}\n onConfirm={() => onAnswerAsk(prompt.id, { type: 'answer', values: ['yes'] })}\n />\n );\n }\n\n // REMOTE-014 E5 (display-only): the prompt belongs to the driver whose turn raised it. Shown so the owner\n // can tell a co-driver's tool-gate from their own — it NEVER changes who is authorized to answer (owner).\n const requesterLabel =\n prompt.requesterDriverId && !isSameSurface(prompt.requesterDriverId, ownDriverId)\n ? driverAttributionText(prompt.requesterDriverId)\n : undefined;\n\n const dock = layout === 'dock';\n /** Records whether the answer being given leaves the prompt able to hand focus back afterwards. */\n const rememberFocus = (): void => {\n const container = containerRef.current;\n const active = document.activeElement;\n pendingFocusReturnRef.current = !!(container && active && container.contains(active));\n };\n const answerPermission = (id: string, result: boolean): void => {\n rememberFocus();\n onAnswerPermission(id, result);\n };\n // Every ask answer — submit, Cancel, Esc (in the field or at the dock), or picking an option while\n // text sits untyped in the field — clears whatever the field holds first. `FreeTextField` also\n // clears itself on its own submit/Esc paths; this is the single choke point that covers the rest\n // (the Cancel button, the dock's Esc, and answering by option) without every call site repeating it.\n const answerAsk = (id: string, response: TActionResponse): void => {\n rememberFocus();\n fieldHandleRef.current?.clear();\n onAnswerAsk(id, response);\n };\n /** A click anywhere in the prompt that isn't on a button or the field itself focuses the field. */\n const onContainerClick = (event: React.MouseEvent<HTMLDivElement>): void => {\n if (!hasFreeText) return;\n if ((event.target as HTMLElement).closest('button, input')) return;\n fieldRef.current?.focus();\n };\n /** A mouse click answers at once; a keyboard click (`detail` 0) waits for the prompt to arm. */\n const onButton =\n (answer: () => void) =>\n (event: React.MouseEvent): void => {\n if (!armed && event.detail === 0) return;\n answer();\n };\n const onDockKey = (event: React.KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n if (prompt.kind === 'permission') answerPermission(prompt.id, false);\n else answerAsk(prompt.id, { type: 'cancelled' });\n return;\n }\n // A key that reaches a prompt still arming (it kept focus from the one before) is dropped.\n if (!armed) return;\n const index = Number(event.key) - 1;\n if (!Number.isInteger(index) || index < 0) return;\n if (prompt.kind === 'permission') {\n if (index < 2) answerPermission(prompt.id, index === 0);\n return;\n }\n // #3282 §2: a destructive option (Delete) is excluded from the digit-answerable list entirely —\n // no stray digit, including the one it happened to render at before this menu was reordered, can\n // ever answer it.\n const digitOptions = (prompt.request.options ?? []).filter(\n (opt) => !isDestructiveAskOption(prompt.request.id, opt.value),\n );\n const option = digitOptions[index];\n if (option) answerAsk(prompt.id, { type: 'answer', values: [option.value] });\n };\n // #3288: an Edit/Write request carries the same server-built diff `tool_end` would show.\n const hasDiff = prompt.kind === 'permission' && (prompt.diffLines?.length ?? 0) > 0;\n const shellCommand =\n prompt.kind === 'permission' && typeof prompt.toolArgs['command'] === 'string'\n ? prompt.toolArgs['command']\n : undefined;\n const isShellCommand = prompt.kind === 'permission' && prompt.toolName === 'Bash' && shellCommand !== undefined;\n const askRequestId = prompt.kind === 'ask' ? prompt.request.id : undefined;\n const askOptions = prompt.kind === 'ask' ? (prompt.request.options ?? []) : [];\n // #3282 §2: Delete (in the provider profile action menu) renders apart from the rest, destructive-\n // styled and with no digit shortcut — every other ask's options are unaffected (`destructiveAskOption`\n // stays undefined, so `primaryAskOptions` is just `askOptions`, same order, same shortcuts).\n const destructiveAskOption = askOptions.find((opt) => isDestructiveAskOption(askRequestId, opt.value));\n const primaryAskOptions = destructiveAskOption\n ? askOptions.filter((opt) => opt !== destructiveAskOption)\n : askOptions;\n // Digits choose an option only while focus is on the prompt itself, not the field — a hint promising\n // \"1–9 choose\" while the field holds focus would be wrong, since digits type there instead.\n const askArmedHint =\n hasFreeText && fieldFocused\n ? 'Enter to submit · Esc to cancel'\n : askOptions.length > 0\n ? '1–9 choose · Esc cancel'\n : 'Esc cancels';\n return (\n <div\n className={\n dock\n ? 'agent-ui gui-rise flex-shrink-0'\n : 'agent-ui fixed inset-0 z-50 flex items-center justify-center bg-black/55 p-4 backdrop-blur-[2px]'\n }\n >\n <div\n ref={containerRef}\n tabIndex={dock ? 0 : -1}\n onKeyDown={dock ? onDockKey : undefined}\n onClick={onContainerClick}\n onFocus={() => setHasFocus(true)}\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {\n setHasFocus(false);\n }\n }}\n role=\"dialog\"\n aria-label=\"pending question\"\n data-armed={dock ? String(armed) : undefined}\n className={`w-full rounded-2xl bg-card text-[14px] text-card-foreground shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] focus:outline-none focus-visible:ring-2 focus-visible:ring-ring ${\n dock ? 'p-4' : 'max-w-lg p-5'\n }`}\n >\n {prompt.kind === 'permission' ? (\n <>\n <div className=\"flex items-start gap-3\">\n <span className=\"mt-0.5 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning\">\n <ShieldAlert size={17} strokeWidth={1.9} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-[13px] text-muted-foreground\">\n Permission request\n {requesterLabel && (\n <>\n {' '}\n · <span className=\"text-foreground\">{requesterLabel}</span>\n </>\n )}\n </p>\n {/* #3288 §2: an Edit/Write request with a server-built diff preview shows \"Edit\n <path>\" (or \"Background agent <label> wants to edit <path>\") and the diff, in\n place of \"Allow <tool> to run?\" and raw arguments.\n Issue #3288 §1: a background agent's own request ALWAYS names it — whether or\n not there's a diff to show — so this never reads as an unattributed ask for\n exactly the riskiest calls (a diff-bearing Edit/Write). */}\n {prompt.requester?.kind === 'background-agent' ? (\n <p className=\"mt-0.5 text-[15px] font-medium text-foreground\">\n Background agent <span className=\"font-semibold\">{prompt.requester.label}</span>{' '}\n wants to{' '}\n {hasDiff ? (\n <>\n {prompt.toolName.toLowerCase()}{' '}\n <span className=\"font-mono font-semibold\">{prompt.diffFile}</span>\n </>\n ) : (\n <>\n run <span className=\"font-semibold\">{prompt.toolName}</span>\n </>\n )}\n </p>\n ) : hasDiff ? (\n <p className=\"mt-0.5 text-[15px] font-medium text-foreground\">\n {prompt.toolName}{' '}\n <span className=\"font-mono font-semibold\">{prompt.diffFile}</span>\n </p>\n ) : (\n <p className=\"mt-0.5 text-[15px] font-medium text-foreground\">\n Allow <span className=\"font-semibold\">{prompt.toolName}</span> to run?\n </p>\n )}\n {hasDiff ? (\n <div className=\"mt-2.5\">\n <DiffLines diffLines={prompt.diffLines!} />\n </div>\n ) : isShellCommand ? (\n <ShellCommandPreview command={shellCommand} cwd={prompt.cwd} />\n ) : (\n <ToolArgs args={prompt.toolArgs} />\n )}\n </div>\n </div>\n <div className=\"mt-4 flex flex-wrap items-center gap-2 pl-11\">\n <button\n type=\"button\"\n aria-keyshortcuts={dock ? '1' : undefined}\n className={PRIMARY_BUTTON}\n onClick={onButton(() => answerPermission(prompt.id, true))}\n >\n <span>Allow</span>\n {dock && <Kbd>1</Kbd>}\n </button>\n <button\n type=\"button\"\n aria-keyshortcuts={dock ? '2' : undefined}\n className={SECONDARY_BUTTON}\n onClick={onButton(() => answerPermission(prompt.id, false))}\n >\n <span>Deny</span>\n {dock && <Kbd>2</Kbd>}\n </button>\n {dock && (\n <KeyHint hasFocus={hasFocus} armed={armed} armedText=\"1 allow · 2 deny · Esc deny\" />\n )}\n </div>\n </>\n ) : (\n <>\n <div className=\"flex items-start gap-3\">\n <span className=\"mt-0.5 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-accent/15 text-accent\">\n <MessageCircleQuestion size={17} strokeWidth={1.9} />\n </span>\n <div className=\"min-w-0 flex-1\">\n {requesterLabel && (\n <p className=\"text-[13px] text-muted-foreground\">\n <span className=\"text-foreground\">{requesterLabel}</span>\n </p>\n )}\n <p className=\"text-[15px] font-medium text-foreground\">{prompt.request.title}</p>\n {prompt.request.description ? (\n <p className=\"mt-1 leading-relaxed text-muted-foreground\">\n {prompt.request.description}\n </p>\n ) : null}\n </div>\n </div>\n <div className=\"mt-4 flex flex-col gap-3 pl-11\">\n {primaryAskOptions.length > 0 && (\n <div className=\"flex flex-wrap items-start gap-3\">\n {primaryAskOptions.map((opt, index) => (\n <div key={opt.value} className=\"flex flex-col items-start gap-0.5\">\n <button\n type=\"button\"\n aria-keyshortcuts={dock && index < 9 ? String(index + 1) : undefined}\n className={index === 0 ? PRIMARY_BUTTON : SECONDARY_BUTTON}\n onClick={onButton(() =>\n answerAsk(prompt.id, { type: 'answer', values: [opt.value] }),\n )}\n >\n <span>{opt.label}</span>\n {dock && index < 9 && <Kbd>{index + 1}</Kbd>}\n </button>\n {opt.description && (\n <p className=\"pl-0.5 text-[11.5px] leading-snug text-subtle\">\n {opt.description}\n </p>\n )}\n </div>\n ))}\n </div>\n )}\n {/* #3282 §2: Delete sits apart at the bottom, styled destructive, with no digit\n shortcut — the audited bug was that it looked and behaved just like Switch. */}\n {destructiveAskOption && (\n <div className=\"flex flex-wrap items-start gap-3\">\n <button\n type=\"button\"\n className={DESTRUCTIVE_BUTTON}\n onClick={onButton(() =>\n answerAsk(prompt.id, { type: 'answer', values: [destructiveAskOption.value] }),\n )}\n >\n <span>{destructiveAskOption.label}</span>\n </button>\n </div>\n )}\n {hasFreeText && (\n <div className=\"flex flex-col gap-1.5\">\n {askOptions.length > 0 && (\n <p className=\"text-[12.5px] text-subtle\">or type an answer</p>\n )}\n <FreeTextField\n // Remounts on every new prompt — no value typed for one question can ever appear\n // in the DOM for another, masked or not, since this is a fresh component instance\n // with its own fresh `useState('')`, not a value this component reset in an effect.\n key={prompt.id}\n ref={fieldHandleRef}\n inputRef={fieldRef}\n masked={prompt.request.masked === true}\n placeholder={prompt.request.placeholder}\n allowEmpty={prompt.request.allowEmpty === true}\n wrapClick={onButton}\n onSubmit={(value) =>\n answerAsk(prompt.id, { type: 'answer', values: [], text: value })\n }\n onCancel={() => answerAsk(prompt.id, { type: 'cancelled' })}\n onFocusChange={setFieldFocused}\n />\n </div>\n )}\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n className={GHOST_BUTTON}\n onClick={onButton(() => answerAsk(prompt.id, { type: 'cancelled' }))}\n >\n Cancel\n </button>\n {dock && (\n <KeyHint hasFocus={hasFocus} armed={armed} armedText={askArmedHint} />\n )}\n </div>\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\nconst BUTTON =\n 'inline-flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[14px] font-medium transition-colors';\nconst PRIMARY_BUTTON = `${BUTTON} bg-primary text-primary-foreground hover:opacity-90`;\nconst SECONDARY_BUTTON = `${BUTTON} bg-raised text-foreground hover:bg-hover`;\nconst GHOST_BUTTON = `${BUTTON} text-muted-foreground hover:bg-hover hover:text-foreground`;\n/** #3282 §2: a destructive ask option (Delete) — visually distinct from Switch/Edit/Test/Duplicate. */\nconst DESTRUCTIVE_BUTTON = `${BUTTON} bg-destructive/12 text-destructive hover:bg-destructive/20`;\n\n/** Imperative escape hatch a parent can use to wipe the field's value without owning it. */\ninterface IFreeTextFieldHandle {\n clear: () => void;\n}\n\ninterface IFreeTextFieldProps {\n masked: boolean;\n placeholder?: string;\n /** An empty answer is otherwise blocked, mirroring the runtime's own text-prompt renderers. */\n allowEmpty: boolean;\n /** The field's own DOM node, so the parent can move focus onto it once the prompt arms. */\n inputRef: React.RefObject<HTMLInputElement | null>;\n /** The parent's mouse-vs-keyboard-click gate (`armed`), applied to this field's own button. */\n wrapClick: (answer: () => void) => (event: React.MouseEvent) => void;\n onSubmit: (value: string) => void;\n onCancel: () => void;\n onFocusChange: (focused: boolean) => void;\n}\n\n/**\n * The free-text answer to one ask prompt. Its value lives ONLY here, in a component the parent\n * mounts with `key={prompt.id}` — so a cancelled or answered prompt's typed (possibly masked, e.g.\n * an API key) text cannot survive into another prompt's render: there is no shared state to carry it,\n * only a fresh `useState('')` on the next prompt's fresh instance. `clear` is exposed via `ref` as a\n * second line of defense, so the parent can wipe the value at the moment ANY answer is given (Cancel,\n * an option pick, the dock's Esc) and not only from the paths already local to this component (submit,\n * Esc-in-field) — belt and suspenders, not a substitute for the remount.\n */\nconst FreeTextField = forwardRef<IFreeTextFieldHandle, IFreeTextFieldProps>(function FreeTextField(\n { masked, placeholder, allowEmpty, inputRef, wrapClick, onSubmit, onCancel, onFocusChange },\n ref,\n) {\n const [value, setValue] = useState('');\n useImperativeHandle(ref, () => ({ clear: () => setValue('') }), []);\n /** The typed value, trimmed as the runtime's own text-prompt renderers do; blocked while empty. */\n const submit = (): void => {\n const trimmed = value.trim();\n if (!trimmed && !allowEmpty) return;\n setValue('');\n onSubmit(trimmed);\n };\n const cancel = (): void => {\n setValue('');\n onCancel();\n };\n /**\n * A key that reaches the field types into it — the prompt's own shortcuts never see it (#3280 §3).\n * The Enter that only finishes an IME composition (Korean/Japanese/Chinese) must not submit the\n * still-uncommitted text — `isComposing` (or `keyCode` 229, on browsers that predate it) marks that\n * Enter; the keystroke is left alone so the browser can commit the composition normally.\n */\n const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n event.stopPropagation();\n if (event.key === 'Enter') {\n if (event.nativeEvent.isComposing || event.keyCode === 229) return;\n event.preventDefault();\n submit();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n cancel();\n }\n };\n return (\n <div className=\"flex items-center gap-2\">\n <input\n ref={inputRef}\n type={masked ? 'password' : 'text'}\n autoComplete=\"off\"\n aria-label=\"answer\"\n placeholder={placeholder}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n onKeyDown={onKeyDown}\n onFocus={() => onFocusChange(true)}\n onBlur={() => onFocusChange(false)}\n className=\"min-w-0 flex-1 rounded-lg bg-sidebar px-3 py-1.5 text-[14px] text-foreground placeholder:text-subtle focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n <button\n type=\"button\"\n className={PRIMARY_BUTTON}\n disabled={!value.trim() && !allowEmpty}\n onClick={wrapClick(submit)}\n >\n Continue\n </button>\n </div>\n );\n});\n\nfunction Kbd({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n // Hidden from the accessible name: the button is \"Allow\", and the hint line says which key answers.\n <kbd\n aria-hidden=\"true\"\n className=\"rounded bg-foreground/10 px-1 font-mono text-[11px] leading-[1.45] opacity-70\"\n >\n {children}\n </kbd>\n );\n}\n\nfunction KeyHint({\n hasFocus,\n armed,\n armedText,\n}: {\n hasFocus: boolean;\n armed: boolean;\n /** What the prompt's keys do once armed — differs by kind and by whether it offers free text. */\n armedText: string;\n}): React.ReactElement {\n const text = !hasFocus\n ? 'Click to answer, or press Shift+Tab to use the keys'\n : armed\n ? armedText\n : 'keys answer in a moment · click to answer now';\n return <p className=\"ml-auto text-[12.5px] text-subtle\">{text}</p>;\n}\n\n/**\n * Everything the tool was asked to do, since that is what the owner approves: a command line first, as it\n * is, then every other argument (where it runs, what it is fed) as `key: value`. Nothing is cut; a long\n * block scrolls.\n */\nfunction ToolArgs({ args }: { args: unknown }): React.ReactElement | null {\n if (args === null || typeof args !== 'object') return null;\n const entries = Object.entries(args as Record<string, unknown>);\n if (entries.length === 0) return null;\n const commandKey = entries.find(\n ([key, value]) => (key === 'command' || key === 'cmd') && typeof value === 'string',\n )?.[0];\n const rest = entries.filter(([key]) => key !== commandKey);\n const format = (value: unknown): string =>\n typeof value === 'string' ? value : JSON.stringify(value, null, 2);\n return (\n <div className=\"mt-2.5 max-h-48 overflow-auto rounded-lg bg-sidebar px-3 py-2 font-mono text-[12.5px] leading-relaxed\">\n {commandKey ? (\n <pre className=\"whitespace-pre-wrap break-all text-foreground\">\n {(args as Record<string, string>)[commandKey]}\n </pre>\n ) : null}\n {rest.length > 0 ? (\n <dl className={`text-muted-foreground ${commandKey ? 'mt-1.5' : ''}`}>\n {rest.map(([key, value]) => (\n <div key={key} className=\"flex gap-2\">\n <dt className=\"flex-shrink-0 text-subtle\">{key}:</dt>\n <dd className=\"min-w-0 whitespace-pre-wrap break-all\">{format(value)}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </div>\n );\n}\n\n/**\n * #3288: a Shell request shows just its command — and the working directory only when the server\n * says it differs from the workspace — instead of every raw argument.\n */\nfunction ShellCommandPreview({\n command,\n cwd,\n}: {\n command: string;\n cwd?: string;\n}): React.ReactElement {\n return (\n <div className=\"mt-2.5 max-h-48 overflow-auto rounded-lg bg-sidebar px-3 py-2 font-mono text-[12.5px] leading-relaxed\">\n <pre className=\"whitespace-pre-wrap break-all text-foreground\">{command}</pre>\n {cwd && (\n <p className=\"mt-1.5 text-muted-foreground\">\n <span className=\"text-subtle\">in </span>\n {cwd}\n </p>\n )}\n </div>\n );\n}\n","import {\n MoreHorizontal,\n PanelLeftClose,\n PanelLeftOpen,\n Pencil,\n Settings,\n SquarePen,\n Trash2,\n} from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { ConfirmDialog } from './Dialog.js';\n\nimport type { IWsSessionState } from '../hooks/useSessionClient.js';\n\ntype TListedSession = NonNullable<IWsSessionState['sessionListing']>['sessions'][number];\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\n/** \"just now\", \"5m ago\", \"3h ago\", \"2d ago\", then the date. */\nexport function formatUpdatedAt(updatedAt: string, now: number = Date.now()): string {\n const at = Date.parse(updatedAt);\n if (Number.isNaN(at)) return '';\n const elapsed = Math.max(0, now - at);\n if (elapsed < MINUTE) return 'just now';\n if (elapsed < HOUR) return `${Math.floor(elapsed / MINUTE)}m ago`;\n if (elapsed < DAY) return `${Math.floor(elapsed / HOUR)}h ago`;\n if (elapsed < 7 * DAY) return `${Math.floor(elapsed / DAY)}d ago`;\n return new Date(at).toISOString().slice(0, 10);\n}\n\n/**\n * A session's title (#3289 §1): its name, else its stable first-message title, else \"New session\".\n * Never `preview` — that field is the raw latest reply, which is what changes every turn and is what\n * this title exists to stop showing.\n */\nexport function sessionTitle(session: TListedSession): string {\n const name = session.name?.trim();\n if (name) return name;\n const title = session.title?.trim();\n if (title) return title;\n return 'New session';\n}\n\n/**\n * The clients on a row besides this surface: its own row counts this surface among them. Null when\n * the host does not count clients, or no one else is there.\n */\nexport function otherClients(session: TListedSession, isCurrent: boolean): number | null {\n if (session.clients === undefined) return null;\n const others = session.clients - (isCurrent ? 1 : 0);\n return others >= 1 ? others : null;\n}\n\n/** Where a row's context menu opens: the point the mouse or the \"More\" button gave it. */\ninterface IMenuAnchor {\n readonly sessionId: string;\n readonly x: number;\n readonly y: number;\n}\n\n/**\n * #3189 — this workspace's sessions on the left of the conversation, as in Claude Code Desktop:\n * start a new one, or click another to make it current. Rows the host could not read are listed,\n * disabled, so a damaged session never looks deleted. A refused switch comes back as a notice.\n * A host that keeps several sessions live marks the rows running now and counts who else is on them.\n *\n * #3289 §1 — each row also carries a \"More\" menu (or right-click) with Rename (inline) and Delete\n * (confirmed). Rename on the current row reuses the `/rename` command, which also updates the live\n * session's own name and title bar; rename on any other row writes the stored record directly.\n */\nexport function SessionSidebar({\n state,\n brand,\n className,\n}: {\n state: IWsSessionState;\n /** The app's name, shown at the top of the sidebar while it is open. */\n brand?: React.ReactNode;\n className?: string;\n}): React.ReactElement {\n const listing = state.sessionListing ?? null;\n const current = listing?.currentSessionId ?? null;\n const unreadable = listing?.unreadableSessionIds ?? [];\n const failed = state.sessionsError?.code === 'list_failed' ? state.sessionsError.message : null;\n // #3282 §2 (part 2): session switching is disabled while disconnected, like the status chips and\n // the `/` command menu — a switch could not reach the host either.\n const connected = state.status === 'connected';\n\n const [menu, setMenu] = useState<IMenuAnchor | null>(null);\n const [renamingId, setRenamingId] = useState<string | null>(null);\n const [renameValue, setRenameValue] = useState('');\n const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);\n const menuRef = useRef<HTMLDivElement | null>(null);\n // Each row's own \"More\" button, kept live across re-renders so the delete dialog can send focus\n // back to the exact control that opened it — even though the menu item that was actually clicked\n // is gone by the time the dialog mounts (see `Dialog`'s `restoreFocusTo`).\n const moreButtonRefs = useRef(new Map<string, HTMLButtonElement>());\n\n // A menu closes on Escape or a click outside it — the usual context-menu contract.\n useEffect(() => {\n if (menu === null) return undefined;\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') setMenu(null);\n };\n const onPointerDown = (event: MouseEvent): void => {\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) setMenu(null);\n };\n document.addEventListener('keydown', onKeyDown);\n document.addEventListener('mousedown', onPointerDown);\n return () => {\n document.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('mousedown', onPointerDown);\n };\n }, [menu]);\n\n // A keyboard user who opened the menu lands on its first item, not nowhere.\n useEffect(() => {\n if (menu === null) return;\n menuRef.current?.querySelector<HTMLElement>('[role=\"menuitem\"]')?.focus();\n }, [menu]);\n\n const openMenuAt = (sessionId: string, x: number, y: number): void => setMenu({ sessionId, x, y });\n\n const startRename = (session: TListedSession): void => {\n setMenu(null);\n setRenamingId(session.id);\n setRenameValue(sessionTitle(session));\n };\n\n const saveRename = (session: TListedSession): void => {\n const name = renameValue.trim();\n setRenamingId(null);\n if (name.length === 0 || name === sessionTitle(session)) return;\n if (session.id === current) {\n // The current session's own rename path: it also updates the live name and title bar.\n state.send({ type: 'command', name: 'rename', args: name });\n } else {\n state.renameSessionInList?.(session.id, name);\n }\n };\n\n const rows = listing?.sessions ?? [];\n const confirmingSession = rows.find((row) => row.id === confirmDeleteId) ?? null;\n\n return (\n <aside\n aria-label=\"Sessions\"\n className={`agent-ui flex w-[272px] flex-shrink-0 flex-col overflow-hidden bg-sidebar ${className ?? ''}`}\n >\n <div className=\"flex h-12 flex-shrink-0 items-center gap-2 px-4\">\n {brand}\n <button\n type=\"button\"\n aria-label=\"Hide sessions\"\n title=\"Hide sessions\"\n onClick={() => state.setSessionSidebarOpen?.(false)}\n className=\"ml-auto rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <PanelLeftClose size={17} strokeWidth={1.75} />\n </button>\n </div>\n\n <div className=\"flex-shrink-0 px-2.5 pt-1\">\n <button\n type=\"button\"\n disabled={!connected}\n title={connected ? undefined : 'Reconnecting…'}\n onClick={() => state.newSession?.()}\n className=\"flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-[14px] font-medium text-foreground hover:bg-hover disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-transparent\"\n >\n <SquarePen size={16} strokeWidth={1.75} className=\"text-muted-foreground\" />\n New session\n </button>\n </div>\n\n <div className=\"flex-1 overflow-y-auto px-2.5 pb-3 pt-4\">\n <h2 className=\"px-2.5 pb-1.5 text-[12.5px] font-medium text-subtle\">Sessions</h2>\n {failed !== null ? (\n <p className=\"px-2.5 py-1 text-[13px] leading-snug text-destructive\">{failed}</p>\n ) : null}\n {listing === null && failed === null ? (\n <p className=\"px-2.5 py-1 text-[13px] text-subtle\">Loading sessions…</p>\n ) : null}\n <ul className=\"space-y-px\">\n {rows.map((session) => {\n const isCurrent = session.id === current;\n const others = otherClients(session, isCurrent);\n const isRenaming = renamingId === session.id;\n // #3289 §3 review: the explicit `aria-label` below replaces name-from-content outright, so\n // the live dot's and \"N other(s)\" span's own text no longer reaches the accessible name —\n // folded back in here as a description instead, alongside the relative time.\n const statusParts = [\n session.live === true ? 'Live' : null,\n others !== null ? `${others} ${others === 1 ? 'other' : 'others'}` : null,\n ].filter((part): part is string => part !== null);\n const statusId = `session-status-${session.id}`;\n const updatedId = `session-updated-${session.id}`;\n const describedBy =\n statusParts.length > 0 ? `${updatedId} ${statusId}` : updatedId;\n return (\n <li key={session.id} className=\"group relative\">\n {isRenaming ? (\n <input\n autoFocus\n aria-label={`Rename ${sessionTitle(session)}`}\n value={renameValue}\n onChange={(event) => setRenameValue(event.target.value)}\n onBlur={() => saveRename(session)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n saveRename(session);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n setRenamingId(null);\n }\n }}\n className=\"w-full rounded-lg border border-accent bg-raised px-2.5 py-2 text-[14px] text-foreground outline-none\"\n />\n ) : (\n <button\n type=\"button\"\n aria-current={isCurrent ? 'true' : undefined}\n // #3289 §3: an explicit name + description — the row's own visible text has no\n // whitespace between its parts, so an unlabelled button reads as one run-together\n // string (\"Title validation fixjust now\") to a screen reader.\n aria-label={sessionTitle(session)}\n aria-describedby={describedBy}\n // #3282 §2 (part 2): while disconnected, the tooltip says why instead of previewing.\n title={connected ? session.preview || session.id : 'Reconnecting…'}\n disabled={!connected}\n onClick={() => {\n if (!isCurrent) state.switchSession?.(session.id);\n }}\n onContextMenu={(event) => {\n event.preventDefault();\n openMenuAt(session.id, event.clientX, event.clientY);\n }}\n className={`w-full rounded-lg py-2 pl-2.5 pr-8 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${\n isCurrent ? 'bg-raised' : 'hover:bg-hover'\n }`}\n >\n <span\n className={`block truncate text-[14px] leading-snug ${\n isCurrent ? 'font-medium text-foreground' : 'text-foreground/85'\n }`}\n >\n {sessionTitle(session)}\n </span>\n <span className=\"mt-0.5 flex items-center gap-1.5 text-[12.5px] tabular-nums text-subtle\">\n {session.live === true ? (\n <span\n title=\"Live in the host\"\n className=\"h-1.5 w-1.5 flex-shrink-0 rounded-full bg-accent\"\n >\n <span className=\"sr-only\">live</span>\n </span>\n ) : null}\n <span id={updatedId}>{formatUpdatedAt(session.updatedAt)}</span>\n {others !== null ? (\n <>\n <span aria-hidden=\"true\">·</span>\n <span className=\"text-muted-foreground\">\n {others} {others === 1 ? 'other' : 'others'}\n </span>\n </>\n ) : null}\n {statusParts.length > 0 ? (\n <span id={statusId} className=\"sr-only\">\n {statusParts.join(', ')}\n </span>\n ) : null}\n </span>\n </button>\n )}\n {!isRenaming ? (\n <button\n type=\"button\"\n ref={(el) => {\n if (el) moreButtonRefs.current.set(session.id, el);\n else moreButtonRefs.current.delete(session.id);\n }}\n aria-label={`More for ${sessionTitle(session)}`}\n aria-haspopup=\"menu\"\n aria-expanded={menu?.sessionId === session.id}\n onClick={(event) => {\n const rect = event.currentTarget.getBoundingClientRect();\n openMenuAt(session.id, rect.left, rect.bottom);\n }}\n className=\"absolute right-1 top-1/2 -translate-y-1/2 rounded-md p-1 text-muted-foreground opacity-0 hover:bg-hover hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100\"\n >\n <MoreHorizontal size={15} strokeWidth={1.75} />\n </button>\n ) : null}\n </li>\n );\n })}\n {unreadable.length > 0 && (\n // Listed, never dropped, so an unreadable session does not look deleted — but as one\n // folded line: a store can hold many old records, and each is nothing to click.\n <li>\n <details className=\"px-2.5 py-2 text-[12.5px] text-subtle\">\n <summary className=\"cursor-pointer text-muted-foreground hover:text-foreground\">\n {unreadable.length} older {unreadable.length === 1 ? 'session' : 'sessions'} in\n this folder can't be opened\n </summary>\n <ul className=\"mt-1.5 space-y-0.5 font-mono text-[11.5px]\">\n {unreadable.map((id) => (\n <li key={id} className=\"truncate\">\n {id}\n </li>\n ))}\n </ul>\n </details>\n </li>\n )}\n </ul>\n </div>\n\n <div className=\"flex-shrink-0 border-t border-border/60 px-2.5 py-2\">\n <button\n type=\"button\"\n aria-label=\"Settings\"\n title=\"Settings\"\n onClick={() => state.openSettings()}\n className=\"flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-[13.5px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Settings size={16} strokeWidth={1.75} />\n Settings\n </button>\n </div>\n\n {menu !== null\n ? (() => {\n const session = rows.find((row) => row.id === menu.sessionId);\n if (!session) return null;\n return (\n <div\n ref={menuRef}\n role=\"menu\"\n aria-label={`Actions for ${sessionTitle(session)}`}\n style={{ position: 'fixed', left: menu.x, top: menu.y }}\n className=\"gui-rise z-40 min-w-[140px] overflow-hidden rounded-lg bg-popover py-1 text-popover-foreground shadow-xl shadow-black/30\"\n >\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={() => startRename(session)}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-[13.5px] hover:bg-hover\"\n >\n <Pencil size={14} strokeWidth={1.75} />\n Rename\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={() => {\n setMenu(null);\n setConfirmDeleteId(session.id);\n }}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-[13.5px] text-destructive hover:bg-hover\"\n >\n <Trash2 size={14} strokeWidth={1.75} />\n Delete…\n </button>\n </div>\n );\n })()\n : null}\n\n {confirmingSession !== null ? (\n <ConfirmDialog\n open\n title={`Delete \"${sessionTitle(confirmingSession)}\"?`}\n body=\"This removes its conversation from this computer.\"\n confirmLabel=\"Delete\"\n destructive\n restoreFocusTo={moreButtonRefs.current.get(confirmingSession.id) ?? null}\n onCancel={() => setConfirmDeleteId(null)}\n onConfirm={() => {\n state.deleteSession?.(confirmingSession.id);\n setConfirmDeleteId(null);\n }}\n />\n ) : null}\n </aside>\n );\n}\n\n/** The collapsed sidebar: a narrow rail that opens it again. */\nexport function SessionSidebarRail({ state }: { state: IWsSessionState }): React.ReactElement {\n // #3282 §2 (part 2): starting a session is disabled while disconnected, matching the open sidebar.\n const connected = state.status === 'connected';\n return (\n <div className=\"flex w-12 flex-shrink-0 flex-col items-center gap-1 bg-sidebar py-2.5\">\n <button\n type=\"button\"\n aria-label=\"Show sessions\"\n title=\"Show sessions\"\n onClick={() => state.setSessionSidebarOpen?.(true)}\n className=\"rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <PanelLeftOpen size={17} strokeWidth={1.75} />\n </button>\n <button\n type=\"button\"\n aria-label=\"New session\"\n title={connected ? 'New session' : 'Reconnecting…'}\n disabled={!connected}\n onClick={() => state.newSession?.()}\n className=\"rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-transparent\"\n >\n <SquarePen size={16} strokeWidth={1.75} />\n </button>\n <button\n type=\"button\"\n aria-label=\"Settings\"\n title=\"Settings\"\n onClick={() => state.openSettings()}\n className=\"mt-auto rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Settings size={16} strokeWidth={1.75} />\n </button>\n </div>\n );\n}\n","import { sessionTitle } from './SessionSidebar.js';\n\nimport type { Dispatch, SetStateAction } from 'react';\nimport type {\n TPersonalUsageDimension,\n TPersonalUsageDashboardState,\n TPersonalUsageReport,\n TUsageBreakdown,\n} from './personal-usage-dashboard-types.js';\n\nconst number = new Intl.NumberFormat('en-US');\nconst PERCENT = 100;\n\n/** Breakdown tabs backed by a dimension list (never \"activity\", which has no \"unknown\" bucket). */\nconst DIMENSION_BREAKDOWNS = ['model', 'provider', 'surface', 'source'] as const;\n\nfunction breakdownLabel(value: TUsageBreakdown): string {\n return value === 'activity' ? 'Activity' : `By ${value}`;\n}\n\n/** Plain words for a surface key; anything not called out is title-cased from its raw key. */\nfunction surfaceLabel(key: string): string {\n if (key === 'cli') return 'Terminal';\n if (key === 'remote') return 'App or browser';\n return key\n .split(/[-_]/)\n .filter((word) => word.length > 0)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/** A tab is hidden when every bit of its usage landed in one \"we don't know\" bucket. */\nfunction isSingleUnknownBucket(dimensions: readonly TPersonalUsageDimension[]): boolean {\n return dimensions.length === 1 && dimensions[0]?.key === 'unknown';\n}\n\nfunction visibleBreakdowns(report: TPersonalUsageReport): readonly TUsageBreakdown[] {\n const shown = DIMENSION_BREAKDOWNS.filter(\n (value) => !isSingleUnknownBucket(dimensionsFor(report, value)),\n );\n return [...shown, 'activity'];\n}\n\nconst sessionTimestampFormat = new Intl.DateTimeFormat(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n});\n\n/**\n * Never the raw id: a session this workspace's own listing knows is named from that local data (the\n * same `sessionTitle()` the sidebar uses — its `name`, else its own preview, both this person's own\n * local data already shown in this GUI). A session outside that listing (e.g. from another workspace)\n * falls back to the report's content-free first-seen timestamp, or else a plain placeholder — never to\n * the id itself.\n */\nfunction sessionDisplayTitle(\n state: TPersonalUsageDashboardState,\n report: TPersonalUsageReport,\n sessionId: string,\n): string {\n const listed = state.sessionListing?.sessions.find((session) => session.id === sessionId);\n if (listed) return sessionTitle(listed);\n const firstSeen = report.sessionFirstSeen?.[sessionId];\n const at = firstSeen ? new Date(firstSeen) : undefined;\n if (at && !Number.isNaN(at.getTime())) {\n return `Session from ${sessionTimestampFormat.format(at)}`;\n }\n return 'Another session';\n}\n\nexport function Stat({\n label,\n value,\n note,\n}: {\n label: string;\n value: string;\n /** A qualifier shown beside the label, e.g. that a cost is estimated. */\n note?: string;\n}): React.ReactElement {\n return (\n <div className=\"rounded-2xl bg-card p-5\">\n <div className=\"flex items-baseline gap-1.5 text-[13px] text-muted-foreground\">\n {label}\n {note ? <span className=\"text-subtle\">· {note}</span> : null}\n </div>\n <div className=\"mt-1.5 text-[28px] font-semibold tabular-nums tracking-[-0.02em] text-foreground\">\n {value}\n </div>\n </div>\n );\n}\n\nfunction dimensionsFor(\n report: TPersonalUsageReport,\n breakdown: TUsageBreakdown,\n): readonly TPersonalUsageDimension[] {\n if (breakdown === 'model') return report.byModel;\n if (breakdown === 'provider') return report.byProvider;\n if (breakdown === 'surface') return report.bySurface;\n if (breakdown === 'source') return report.bySource;\n return [];\n}\n\nexport function BreakdownPanel({\n state,\n report,\n breakdown,\n setBreakdown,\n}: {\n state: TPersonalUsageDashboardState;\n report: TPersonalUsageReport;\n breakdown: TUsageBreakdown;\n setBreakdown: Dispatch<SetStateAction<TUsageBreakdown>>;\n}): React.ReactElement {\n const visible = visibleBreakdowns(report);\n // The tab the person picked may have just become hidden (its usage is now a single unknown bucket,\n // or was all along for a freshly loaded report) — fall back to showing the first visible tab rather\n // than an empty panel, without forcing that choice back into the person's own selection state.\n const active = visible.includes(breakdown) ? breakdown : (visible[0] ?? breakdown);\n return (\n <section className=\"rounded-2xl bg-card p-5\">\n <BreakdownHeader breakdown={active} setBreakdown={setBreakdown} visible={visible} />\n <div className=\"mt-4 space-y-3\">\n {active === 'activity' ? (\n <ActivityRows report={report} />\n ) : (\n <DimensionRows\n state={state}\n report={report}\n breakdown={active}\n dimensions={dimensionsFor(report, active)}\n />\n )}\n </div>\n </section>\n );\n}\n\nfunction BreakdownHeader({\n breakdown,\n setBreakdown,\n visible,\n}: {\n breakdown: TUsageBreakdown;\n setBreakdown: Dispatch<SetStateAction<TUsageBreakdown>>;\n visible: readonly TUsageBreakdown[];\n}): React.ReactElement {\n return (\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h2 className=\"text-[15px] font-semibold\">Breakdown</h2>\n <div className=\"flex flex-wrap gap-1\">\n {visible.map((value) => (\n <button\n key={value}\n type=\"button\"\n aria-pressed={breakdown === value}\n onClick={() => setBreakdown(value)}\n className={`rounded-lg px-2.5 py-1 text-[13px] font-medium ${\n breakdown === value\n ? 'bg-raised text-foreground'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {breakdownLabel(value)}\n </button>\n ))}\n </div>\n </div>\n );\n}\n\nfunction ActivityRows({ report }: { report: TPersonalUsageReport }): React.ReactElement {\n return (\n <>\n {report.byActivity.map((activity) => (\n <div\n key={activity.key}\n className=\"flex items-center justify-between gap-3 rounded-xl bg-raised/60 px-3.5 py-2.5\"\n >\n <div className=\"min-w-0\">\n <span className=\"truncate text-[14px] text-foreground\">{activity.label}</span>\n {/* A literal space (not just a CSS margin) so the name and its kind never read as one\n run-together word to a screen reader or a plain-text copy. */}\n {' '}\n <span className=\"ml-1 rounded-full bg-raised px-2 py-0.5 text-[11px] font-medium text-subtle\">\n {activity.kind}\n </span>\n </div>\n <span className=\"text-[13px] tabular-nums text-muted-foreground\">\n {number.format(activity.count)} calls\n </span>\n </div>\n ))}\n </>\n );\n}\n\nfunction DimensionRows({\n state,\n report,\n breakdown,\n dimensions,\n}: {\n state: TPersonalUsageDashboardState;\n report: TPersonalUsageReport;\n breakdown: TUsageBreakdown;\n dimensions: readonly TPersonalUsageDimension[];\n}): React.ReactElement {\n return (\n <>\n {dimensions.map((dimension) => (\n <div key={dimension.key} className=\"grid grid-cols-[minmax(0,1fr)_auto] gap-3\">\n <DimensionSummary report={report} breakdown={breakdown} dimension={dimension} />\n <span className=\"self-start text-[13px] tabular-nums text-subtle\">\n {number.format(dimension.turns)} turns\n </span>\n <SessionButtons state={state} report={report} dimension={dimension} />\n </div>\n ))}\n </>\n );\n}\n\nfunction DimensionSummary({\n report,\n breakdown,\n dimension,\n}: {\n report: TPersonalUsageReport;\n breakdown: TUsageBreakdown;\n dimension: TPersonalUsageDimension;\n}): React.ReactElement {\n const width =\n report.totals.totalTokens === 0\n ? 0\n : (dimension.totalTokens / report.totals.totalTokens) * PERCENT;\n const label = breakdown === 'surface' ? surfaceLabel(dimension.key) : dimension.label;\n return (\n <div className=\"min-w-0\">\n <div className=\"flex justify-between gap-3 text-[14px]\">\n <span className=\"truncate text-foreground\">{label}</span>\n <span className=\"tabular-nums text-muted-foreground\">\n {number.format(dimension.totalTokens)} tokens\n </span>\n </div>\n <div className=\"mt-2 h-1.5 overflow-hidden rounded-full bg-raised\">\n <div className=\"h-full rounded-full bg-accent/80\" style={{ width: `${width}%` }} />\n </div>\n </div>\n );\n}\n\nfunction SessionButtons({\n state,\n report,\n dimension,\n}: {\n state: TPersonalUsageDashboardState;\n report: TPersonalUsageReport;\n dimension: TPersonalUsageDimension;\n}): React.ReactElement {\n return (\n <div className=\"col-span-2 flex flex-wrap gap-1\">\n {dimension.sessionIds.map((sessionId) => {\n const title = sessionDisplayTitle(state, report, sessionId);\n return (\n <button\n key={sessionId}\n type=\"button\"\n data-session-id={sessionId}\n onClick={() => state.requestStoredSessionUsage(sessionId)}\n aria-label={`Open session ${title}`}\n className=\"rounded-md bg-raised px-2 py-1 text-[12px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <span className=\"max-w-40 truncate align-middle\">{title}</span>\n </button>\n );\n })}\n </div>\n );\n}\n\nexport function StoredSessionPanel({\n state,\n}: {\n state: TPersonalUsageDashboardState;\n}): React.ReactElement | null {\n if (state.storedSessionUsageStatus === 'idle') return null;\n const sessionId = state.storedSessionUsageSessionId ?? '';\n const title = state.personalUsageReport\n ? sessionDisplayTitle(state, state.personalUsageReport, sessionId)\n : 'Another session';\n return (\n <section className=\"rounded-2xl bg-card p-5\" aria-label=\"Session usage detail\" data-session-id={sessionId}>\n <h2 className=\"text-[15px] font-semibold\">{title}</h2>\n <StoredSessionContent state={state} />\n </section>\n );\n}\n\nfunction StoredSessionContent({\n state,\n}: {\n state: TPersonalUsageDashboardState;\n}): React.ReactElement | null {\n if (state.storedSessionUsageStatus === 'loading') {\n return <p className=\"mt-3 text-[14px] text-muted-foreground\">Loading trace…</p>;\n }\n if (state.storedSessionUsageStatus === 'error') {\n return (\n <p className=\"mt-3 text-[14px] text-destructive\">\n {state.storedSessionUsageError ?? 'Session usage failed.'}\n </p>\n );\n }\n const report = state.storedSessionUsageReport;\n if (!report) return null;\n return (\n <div className=\"mt-3 grid gap-3 sm:grid-cols-3\">\n <Stat label=\"Session tokens\" value={number.format(report.totalTokens)} />\n <Stat label=\"Sources\" value={number.format(report.bySource.length)} />\n <Stat label=\"Trace turns\" value={number.format(report.timeline.length)} />\n </div>\n );\n}\n\n/** Plain, one-per-condition sentences — shown only for what this view actually left out. */\nfunction coverageSentences(report: TPersonalUsageReport): string[] {\n const { coverage } = report;\n const sentences: string[] = [];\n if (coverage.unsupportedSessions > 0) {\n const noun = coverage.unsupportedSessions === 1 ? 'session' : 'sessions';\n const verb = coverage.unsupportedSessions === 1 ? 'uses' : 'use';\n sentences.push(\n `${number.format(coverage.unsupportedSessions)} older ${noun} ${verb} a format this view can't read and ${coverage.unsupportedSessions === 1 ? \"isn't\" : \"aren't\"} counted.`,\n );\n }\n if (coverage.corruptSessions > 0) {\n const noun = coverage.corruptSessions === 1 ? 'session' : 'sessions';\n sentences.push(\n `${number.format(coverage.corruptSessions)} ${noun} couldn't be read and ${coverage.corruptSessions === 1 ? \"isn't\" : \"aren't\"} counted.`,\n );\n }\n if (coverage.legacyObservations > 0) {\n const noun = coverage.legacyObservations === 1 ? 'record' : 'records';\n sentences.push(\n `${number.format(coverage.legacyObservations)} older usage ${noun} ${coverage.legacyObservations === 1 ? \"doesn't\" : \"don't\"} have enough detail to break down, so ${coverage.legacyObservations === 1 ? 'it counts' : 'they count'} only toward the totals.`,\n );\n }\n return sentences;\n}\n\nexport function CoverageNote({\n report,\n}: {\n report: TPersonalUsageReport;\n}): React.ReactElement | null {\n const sentences = coverageSentences(report);\n if (sentences.length === 0) return null;\n return (\n <div className=\"space-y-1.5 rounded-xl bg-warning/10 px-4 py-3 text-[13px] leading-relaxed text-warning\">\n {sentences.map((sentence) => (\n <p key={sentence}>{sentence}</p>\n ))}\n </div>\n );\n}\n","import {\n BreakdownPanel,\n CoverageNote,\n Stat,\n StoredSessionPanel,\n} from './PersonalUsageDashboardDetails.js';\n\nimport type { Dispatch, SetStateAction } from 'react';\nimport type {\n TPersonalUsageDashboardState,\n TPersonalUsageReport,\n TUsageBreakdown,\n} from './personal-usage-dashboard-types.js';\n\nconst number = new Intl.NumberFormat('en-US');\nconst PERCENT = 100;\nconst MINIMUM_BAR_PERCENT = 2;\nconst MONTH_DAY_OFFSET = 'YYYY-'.length;\nconst COST_DECIMAL_PLACES = 2;\n\nfunction StatusPanel({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <div className=\"flex min-h-64 items-center justify-center rounded-2xl bg-card px-8 text-center text-[15px] text-muted-foreground\">\n {children}\n </div>\n );\n}\n\nexport function UsageHeader({\n period,\n setPeriod,\n requestCurrentSessionUsage,\n}: {\n period: '7d' | '30d';\n setPeriod: Dispatch<SetStateAction<'7d' | '30d'>>;\n requestCurrentSessionUsage: () => void;\n}): React.ReactElement {\n return (\n <div className=\"flex flex-wrap items-end justify-between gap-4\">\n <div>\n <div className=\"text-[13px] font-medium text-accent\">Local analytics</div>\n <h1 className=\"mt-1 text-[28px] font-semibold tracking-[-0.02em]\">Personal usage</h1>\n <p className=\"mt-1 text-[15px] text-muted-foreground\">\n Cross-session activity grouped by local calendar day.\n </p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={requestCurrentSessionUsage}\n className=\"rounded-lg bg-raised px-3 py-2 text-[13px] font-medium text-foreground hover:bg-hover\"\n >\n Current session trace\n </button>\n <PeriodPicker period={period} setPeriod={setPeriod} />\n </div>\n </div>\n );\n}\n\nfunction PeriodPicker({\n period,\n setPeriod,\n}: {\n period: '7d' | '30d';\n setPeriod: Dispatch<SetStateAction<'7d' | '30d'>>;\n}): React.ReactElement {\n return (\n <div className=\"flex rounded-lg bg-raised p-0.5\">\n {(['7d', '30d'] as const).map((value) => (\n <button\n key={value}\n type=\"button\"\n aria-pressed={period === value}\n onClick={() => setPeriod(value)}\n className={`rounded-md px-3 py-1.5 text-[13px] font-medium transition-colors ${\n period === value\n ? 'bg-card text-foreground shadow-sm shadow-black/25'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {value === '7d' ? '7 days' : '30 days'}\n </button>\n ))}\n </div>\n );\n}\n\nexport function CurrentSessionPanel({\n state,\n}: {\n state: TPersonalUsageDashboardState;\n}): React.ReactElement | null {\n if (state.currentSessionUsageStatus === 'idle') return null;\n let content: React.ReactNode = null;\n if (state.currentSessionUsageStatus === 'loading') {\n content = <p className=\"mt-3 text-[14px] text-muted-foreground\">Loading trace…</p>;\n } else if (state.currentSessionUsageStatus === 'error') {\n content = <p className=\"mt-3 text-[14px] text-destructive\">Current trace unavailable.</p>;\n } else if (state.currentSessionUsageReport) {\n const turns = state.currentSessionUsageReport.timeline.length;\n content = (\n <p className=\"mt-3 text-[14px] tabular-nums text-muted-foreground\">\n This session: {number.format(state.currentSessionUsageReport.totalTokens)} tokens\n {turns > 0 ? ` · ${number.format(turns)} turns` : ''}\n </p>\n );\n }\n return (\n <section className=\"rounded-2xl bg-card p-5\" aria-label=\"Current session usage\">\n <h2 className=\"text-[15px] font-semibold\">Current session trace</h2>\n {content}\n </section>\n );\n}\n\n/** Only meaningful beside a shown dollar figure — \"Unknown\" already says nothing was priced. */\nfunction unpricedTurnsNote(report: TPersonalUsageReport): string | null {\n if (report.totals.costStatus === 'unknown') return null;\n const count = report.totals.unpricedTurns ?? 0;\n if (count === 0) return null;\n const turnWord = count === 1 ? 'turn' : 'turns';\n const verb = count === 1 ? 'is' : 'are';\n return `${number.format(count)} ${turnWord} without a price ${verb} not included.`;\n}\n\nfunction UsageStats({ report }: { report: TPersonalUsageReport }): React.ReactElement {\n const cost =\n report.totals.costStatus === 'unknown'\n ? 'Unknown'\n : `$${report.totals.costUsd.toFixed(COST_DECIMAL_PLACES)}`;\n const note = unpricedTurnsNote(report);\n return (\n <section aria-label=\"Usage totals\">\n <div className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <Stat label=\"Turns\" value={number.format(report.totals.turns)} />\n <Stat label=\"Sessions\" value={number.format(report.totals.sessions)} />\n <Stat label=\"Tokens\" value={number.format(report.totals.totalTokens)} />\n <Stat\n label=\"Cost\"\n value={cost}\n {...(report.totals.costStatus === 'estimated' ? { note: 'estimated' } : {})}\n />\n </div>\n {note ? <p className=\"mt-2 text-[13px] text-subtle\">{note}</p> : null}\n </section>\n );\n}\n\nfunction DailyUsageChart({ report }: { report: TPersonalUsageReport }): React.ReactElement {\n const maxTokens = report.daily.reduce(\n (maximum, day) => Math.max(maximum, day.totals.totalTokens),\n 0,\n );\n return (\n <section className=\"rounded-2xl bg-card p-5\">\n <DailyChartHeader report={report} />\n <div\n className=\"mt-5 flex h-44 items-end gap-1.5\"\n role=\"img\"\n aria-label=\"Daily token usage chart\"\n >\n {report.daily.map((day) => {\n const height =\n maxTokens === 0\n ? MINIMUM_BAR_PERCENT\n : Math.max(MINIMUM_BAR_PERCENT, (day.totals.totalTokens / maxTokens) * PERCENT);\n return (\n <div\n key={day.date}\n className=\"group flex min-w-0 flex-1 flex-col items-center justify-end gap-2\"\n >\n <div className=\"relative flex h-32 w-full items-end\">\n <div\n className=\"mx-auto w-full max-w-10 rounded-md bg-accent/75 transition-colors group-hover:bg-accent\"\n style={{ height: `${height}%` }}\n title={`${day.date}: ${number.format(day.totals.totalTokens)} tokens`}\n />\n </div>\n <span className=\"truncate text-[12px] tabular-nums text-subtle\">\n {day.date.slice(MONTH_DAY_OFFSET)}\n </span>\n </div>\n );\n })}\n </div>\n </section>\n );\n}\n\nfunction DailyChartHeader({ report }: { report: TPersonalUsageReport }): React.ReactElement {\n return (\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-[15px] font-semibold\">Daily usage</h2>\n <p className=\"mt-0.5 text-[13px] text-muted-foreground\">\n {report.interval.startDate} → {report.interval.endDate} · {report.timezone}\n </p>\n </div>\n {report.daily.at(-1)?.partial ? (\n <span className=\"rounded-full bg-warning/15 px-2.5 py-1 text-[12px] font-medium text-warning\">\n Partial day\n </span>\n ) : null}\n </div>\n );\n}\n\nexport function PersonalUsageContent({\n state,\n breakdown,\n setBreakdown,\n}: {\n state: TPersonalUsageDashboardState;\n breakdown: TUsageBreakdown;\n setBreakdown: Dispatch<SetStateAction<TUsageBreakdown>>;\n}): React.ReactElement {\n const report = state.personalUsageReport;\n if (state.personalUsageStatus === 'loading' && !report) {\n return <StatusPanel>Loading local usage…</StatusPanel>;\n }\n if (state.personalUsageStatus === 'error') {\n return <StatusPanel>{state.personalUsageError ?? 'Usage report failed.'}</StatusPanel>;\n }\n if (!report || isEmptyReport(report)) {\n return <StatusPanel>No recorded usage in this period.</StatusPanel>;\n }\n return (\n <>\n <UsageStats report={report} />\n <DailyUsageChart report={report} />\n <BreakdownPanel\n state={state}\n report={report}\n breakdown={breakdown}\n setBreakdown={setBreakdown}\n />\n <StoredSessionPanel state={state} />\n <CoverageNote report={report} />\n </>\n );\n}\n\nfunction isEmptyReport(report: TPersonalUsageReport): boolean {\n return (\n report.totals.sessions === 0 &&\n report.totals.totalTokens === 0 &&\n report.byActivity.length === 0\n );\n}\n","import { useEffect, useState } from 'react';\n\nimport {\n CurrentSessionPanel,\n PersonalUsageContent,\n UsageHeader,\n} from './PersonalUsageDashboardSections.js';\n\nimport type {\n TPersonalUsageDashboardState,\n TUsageBreakdown,\n} from './personal-usage-dashboard-types.js';\n\nexport function PersonalUsageDashboard({\n state,\n}: {\n state: TPersonalUsageDashboardState;\n}): React.ReactElement {\n const [period, setPeriod] = useState<'7d' | '30d'>('7d');\n const [breakdown, setBreakdown] = useState<TUsageBreakdown>('model');\n\n useEffect(() => {\n state.requestPersonalUsage(period);\n }, [period, state.requestPersonalUsage]);\n\n return (\n <main className=\"agent-ui gui-rise h-full overflow-y-auto p-5 md:p-7\" aria-label=\"Personal usage\">\n <div className=\"mx-auto flex max-w-6xl flex-col gap-5\">\n <UsageHeader\n period={period}\n setPeriod={setPeriod}\n requestCurrentSessionUsage={state.requestCurrentSessionUsage}\n />\n <CurrentSessionPanel state={state} />\n <PersonalUsageContent state={state} breakdown={breakdown} setBreakdown={setBreakdown} />\n </div>\n </main>\n );\n}\n","import { useProductIdentity } from '../product-identity.js';\n/** Shared mark: a rounded face with two eyes, in the accent colour. */\nexport function ProductMark({ size = 20 }: { size?: number }): React.ReactElement {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n className=\"flex-shrink-0\"\n >\n <rect x=\"1\" y=\"2.5\" width=\"18\" height=\"15\" rx=\"5.5\" className=\"fill-accent\" />\n <circle cx=\"7.2\" cy=\"10\" r=\"1.7\" className=\"fill-accent-foreground\" />\n <circle cx=\"12.8\" cy=\"10\" r=\"1.7\" className=\"fill-accent-foreground\" />\n </svg>\n );\n}\n\n/** The mark and the wordmark, as the app's name in its chrome. */\nexport function ProductWordmark({ surface }: { surface?: string }): React.ReactElement {\n const { identity } = useProductIdentity();\n return (\n <span className=\"flex items-center gap-2\">\n <ProductMark />\n <span className=\"text-[15px] font-semibold tracking-[-0.01em] text-foreground\">{identity.displayName}</span>\n {surface ? (\n <span className=\"rounded-md bg-raised px-1.5 py-px text-[11px] font-medium text-muted-foreground\">\n {surface}\n </span>\n ) : null}\n </span>\n );\n}\n","/**\n * #3186 — what the composer's `/` menu offers for a draft. Pure, so the matching is testable without\n * rendering: the TUI's autocomplete offers commands and skills the same way.\n *\n * #3282 §4e: a command the GUI cannot run at all (`isExcludedCommand`) never appears here — there is\n * nothing useful for choosing it to do, and typing it by hand is answered separately (see\n * `excluded-commands.ts`, `useSessionClient.ts`). Commands are listed before skills, each in its own\n * group, so the menu can head them \"Commands\" and \"Skills\".\n */\n\nimport { isExcludedCommand } from './excluded-commands.js';\n\nimport type { TCommandCatalog } from './session-client-types.js';\n\nexport interface ICommandMenuItem {\n readonly name: string;\n readonly description: string;\n readonly kind: 'command' | 'skill';\n}\n\n/** How many rows the menu shows before it scrolls — a CSS sizing constant, not a data limit. */\nexport const COMMAND_MENU_VISIBLE_ROWS = 8;\n\n/** A generous ceiling on total matches, so a huge personal skill folder can never make the menu unusable. */\nconst COMMAND_MENU_MAX_MATCHES = 200;\n\n/**\n * The menu for a draft, or null when no menu applies: the draft is not a bare `/name` being typed\n * (it has arguments, or does not start with `/`), or nothing matches. Within each group (commands,\n * then skills), names that start with what was typed come first, then names that merely contain it.\n */\nexport function commandMenuFor(\n catalog: TCommandCatalog | null,\n draft: string,\n): readonly ICommandMenuItem[] | null {\n if (!catalog || !draft.startsWith('/') || /\\s/u.test(draft)) return null;\n const query = draft.slice(1).toLowerCase();\n const commandItems: ICommandMenuItem[] = catalog.commands\n .filter((c) => !isExcludedCommand(c))\n .map((c) => ({ name: c.name, description: c.description, kind: 'command' as const }));\n const skillItems: ICommandMenuItem[] = catalog.skills\n .filter((s) => s.userInvocable)\n .map((s) => ({ name: s.name, description: s.description, kind: 'skill' as const }));\n const matched = [...matchGroup(commandItems, query), ...matchGroup(skillItems, query)].slice(\n 0,\n COMMAND_MENU_MAX_MATCHES,\n );\n return matched.length > 0 ? matched : null;\n}\n\n/** One group's matches: names starting with the query, then names merely containing it, each alphabetical. */\nfunction matchGroup(\n items: readonly ICommandMenuItem[],\n query: string,\n): readonly ICommandMenuItem[] {\n const starts = items.filter((i) => i.name.toLowerCase().startsWith(query));\n const contains = items.filter(\n (i) => !i.name.toLowerCase().startsWith(query) && i.name.toLowerCase().includes(query),\n );\n return [...starts.sort(byName), ...contains.sort(byName)];\n}\n\nfunction byName(a: ICommandMenuItem, b: ICommandMenuItem): number {\n return a.name.localeCompare(b.name);\n}\n","/**\n * #3282 §4d — pure logic behind the composer's attach button and drag-and-drop, kept apart from\n * `Composer.tsx` so it is testable without React or a DOM.\n *\n * The runtime resolves `@relative/path` references already (`prompt-file-reference-resolver.ts` in\n * agent-framework): inside the workspace only, 64 KiB per file, 256 KiB total per prompt, 8 references\n * per prompt, and (as of this change) refuses a binary file outright rather than mangling it. This\n * module mirrors those same limits client-side so a file the runtime would refuse is never even\n * attached: the running size and count totals, and a MIME/extension heuristic that refuses a\n * likely-binary file (images included) up front, before it ever becomes a chip. It does not (and\n * cannot) reuse the resolver's code directly: that package is Node-only, and this one ships to a plain\n * browser tab too (agent-ui-web SPEC: no dependency on agent-framework).\n */\n\n/** Mirrors `DEFAULT_MAX_FILE_BYTES` in `packages/agent-framework/src/context/prompt-file-reference-resolver.ts`. */\nexport const MAX_ATTACHMENT_FILE_BYTES = 64 * 1024;\n/** Mirrors `DEFAULT_MAX_TOTAL_BYTES` in the same resolver. */\nexport const MAX_ATTACHMENT_TOTAL_BYTES = 256 * 1024;\n/**\n * Mirrors `DEFAULT_MAX_REFERENCES` in the same resolver — not imported directly (agent-ui-web has no\n * dependency on agent-framework; see the module comment above), so this is a named constant kept in\n * sync by hand. The runtime counts every `@`-reference in the final prompt, including one a person\n * typed by hand, so a batch that fits under this count can still be refused server-side if the typed\n * text already carries references of its own — this client-side count only catches the common case\n * (the chips alone) before the round trip.\n */\nexport const MAX_ATTACHMENT_COUNT = 8;\n\n/** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3 (#3282 §4). */\nexport const ATTACHMENT_NO_PATH_NOTICE = 'Only files inside this project folder can be attached.';\n\n/** Shown once the running count of attachments would pass `MAX_ATTACHMENT_COUNT`. */\nexport const ATTACHMENT_COUNT_LIMIT_NOTICE = `You can attach up to ${MAX_ATTACHMENT_COUNT} files to one message.`;\n\n/**\n * Shown whenever a file looks binary (images included) — refused outright, never attached. Images are\n * not sent as native content in this version (the wire and the interactive-session runtime do not\n * carry `parts` from a submitted prompt yet), and the `@`-reference resolver has no way to carry\n * binary bytes as text either, so there is no path that would work today.\n */\nexport const ATTACHMENT_NON_TEXT_NOTICE = \"Images and other non-text files can't be attached yet.\";\n\n/** One file the composer has turned into an `@`-reference chip, kept in the draft until sent or removed. */\nexport interface IDraftAttachment {\n readonly id: string;\n readonly name: string;\n /** Workspace-relative, forward-slash separated — what `@` names once inserted into the prompt. */\n readonly relativePath: string;\n readonly size: number;\n}\n\n/**\n * One file the desktop host's native \"Attach files\" dialog returned (#3282 §4d). Kept in structural\n * sync with `IPickedFile` in `apps/agent-gui-web/src/gui-host.ts` and\n * `apps/agent-app/electron/sidecar.ts` — the three are not imported across the Electron/browser/\n * presentation package boundaries, same as this codebase already does for the trust question.\n */\nexport interface IPickedFile {\n readonly path: string;\n readonly name: string;\n readonly size: number;\n}\n\n/** A file offered to the composer, from a drop, the plain HTML picker, or the desktop dialog. */\nexport interface ICandidateFile {\n readonly name: string;\n readonly size: number;\n readonly mimeType?: string;\n /** The real filesystem path, when the host could resolve one (desktop only). */\n readonly absolutePath?: string;\n}\n\nexport type TAttachOutcome =\n | { readonly kind: 'attached'; readonly attachment: IDraftAttachment }\n | { readonly kind: 'rejected'; readonly message: string };\n\nfunction normalizeSeparators(path: string): string {\n return path.replaceAll('\\\\', '/');\n}\n\n/**\n * Collapses `.` and `..` segments the way a filesystem path would, without touching disk — this\n * module ships to a plain browser tab too, so it cannot use `node:path`. Applied to both sides before\n * `relativeWorkspacePath` compares them, so a non-canonical path (one carrying `./`/`../` noise, e.g.\n * from a symlink or an odd OS report) is judged by what it actually resolves to, not by a raw string\n * prefix match that a `..` segment could otherwise defeat (a target that merely *starts with* the\n * root string, `../` and all, must not be treated as inside it just because the raw text lines up).\n */\nfunction normalizePathSegments(path: string): string {\n const isRooted = path.startsWith('/');\n const out: string[] = [];\n for (const segment of path.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (out.length > 0 && out[out.length - 1] !== '..') out.pop();\n else if (!isRooted) out.push('..');\n // A rooted path cannot go above its own root — drop silently, matching path.normalize.\n continue;\n }\n out.push(segment);\n }\n return (isRooted ? '/' : '') + out.join('/');\n}\n\n/**\n * The file's path relative to the workspace root, or `null` when it is not inside it (an absolute\n * root is required; a relative/empty `workspacePath` never counts as \"inside\" anything). Both sides\n * are canonicalized first (see `normalizePathSegments`), then compared exactly (case-sensitive): on a\n * case-insensitive filesystem a differently-cased root is treated as outside rather than guessed at —\n * the composer would rather show the plain notice than attach the wrong file.\n */\nexport function relativeWorkspacePath(\n workspacePath: string | undefined,\n absolutePath: string,\n): string | null {\n if (!workspacePath) return null;\n const root = normalizePathSegments(normalizeSeparators(workspacePath));\n if (!root || root === '/') return null;\n const target = normalizePathSegments(normalizeSeparators(absolutePath));\n if (target === root) return null; // the root itself is a directory, not an attachable file\n const prefix = `${root}/`;\n if (!target.startsWith(prefix)) return null;\n const relative = target.slice(prefix.length);\n return relative ? relative : null;\n}\n\n/**\n * A MIME type the browser reports for genuinely text-ish content — never refused as binary: the type\n * starts with `text/`, or ends with `+json`, `+xml`, `/json`, or `/xml` (e.g. `application/ld+json`,\n * `image/svg+xml`). Kept as two separately anchored tests rather than one alternation — `|` binds\n * looser than `^`/`$`, so a single pattern like `/^text\\/|(?:\\+|\\/)(?:json|xml)$/` reads ambiguously\n * (flagged by CodeQL as misleading operator precedence) even though each anchor was already scoped to\n * the alternative it belongs to.\n */\nfunction isTextyMimeType(mimeType: string): boolean {\n return /^text\\//.test(mimeType) || /[+/](?:json|xml)$/.test(mimeType);\n}\nconst BINARY_EXTENSIONS = new Set([\n 'png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'tiff', 'heic',\n 'pdf', 'zip', 'tar', 'gz', 'tgz', '7z', 'rar',\n 'exe', 'dll', 'so', 'dylib', 'bin', 'class', 'wasm',\n 'woff', 'woff2', 'ttf', 'otf', 'eot',\n 'mp3', 'mp4', 'mov', 'avi', 'wav', 'ogg', 'flac', 'm4a',\n]);\n\n/**\n * Whether the file looks binary (never sniffs bytes — MIME/extension only). When the browser reports\n * a MIME type (a drop, or the plain HTML picker), ANY non-text type refuses it — images, PDFs,\n * archives, and anything else a browser did not call `text/…`/JSON/XML. The desktop dialog gives no\n * MIME type at all, so a picked file falls back to the extension denylist.\n */\nexport function looksBinary(name: string, mimeType: string | undefined): boolean {\n if (mimeType) return !isTextyMimeType(mimeType);\n const dot = name.lastIndexOf('.');\n if (dot < 0) return false;\n return BINARY_EXTENSIONS.has(name.slice(dot + 1).toLowerCase());\n}\n\n/** The same terminator set `prompt-file-reference-parser.ts` stops an `@`-reference token at. */\nconst UNSAFE_REFERENCE_CHARS = /[\\s)\\]}>,;\"'`]/u;\n/**\n * `prompt-file-reference-parser.ts`'s `stripTrailingPunctuation` also trims a trailing `.,:;!?` off\n * every captured `@token` — `,`/`;` are already covered above (they can appear anywhere in that\n * set), but a path merely *ending* in `.`/`:`/`!`/`?` (fine mid-string — most files have a `.`) would\n * otherwise be silently truncated by the runtime, so the chip would name a file the reference can\n * never actually resolve.\n */\nconst UNSAFE_TRAILING_CHAR = /[.,:;!?]$/u;\n\nfunction formatBytes(bytes: number): string {\n return bytes >= 1024 ? `${Math.round(bytes / 1024)} KB` : `${bytes} B`;\n}\n\nfunction randomId(): string {\n const cryptoObj = typeof crypto !== 'undefined' ? crypto : undefined;\n if (cryptoObj?.randomUUID) return cryptoObj.randomUUID();\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Decide what a candidate file becomes: a chip, or a plain rejection message. `existingTotalBytes` is\n * the sum of what is already attached and `existingCount` is how many chips there already are, so the\n * running 256 KiB total and the 8-file count are both enforced before the file is added rather than\n * after. The count is checked first, matching the runtime's own `checkReferenceBudget` (which also\n * checks its reference-count limit before it ever looks at the path) — once the cap is reached, every\n * further file gets the same one clear reason rather than a mix of different messages depending on\n * which check happens to run first for each one.\n */\nexport function evaluateCandidateFile(\n file: ICandidateFile,\n workspacePath: string | undefined,\n existingTotalBytes: number,\n existingCount: number,\n): TAttachOutcome {\n if (existingCount >= MAX_ATTACHMENT_COUNT) {\n return { kind: 'rejected', message: ATTACHMENT_COUNT_LIMIT_NOTICE };\n }\n if (!file.absolutePath) return { kind: 'rejected', message: ATTACHMENT_NO_PATH_NOTICE };\n const relativePath = relativeWorkspacePath(workspacePath, file.absolutePath);\n if (relativePath === null) return { kind: 'rejected', message: ATTACHMENT_NO_PATH_NOTICE };\n if (UNSAFE_REFERENCE_CHARS.test(relativePath) || UNSAFE_TRAILING_CHAR.test(relativePath)) {\n return {\n kind: 'rejected',\n message: `\"${file.name}\" has a character (such as a space, or a period/punctuation mark at the end) the @file syntax can't carry — rename it to attach it.`,\n };\n }\n // Refused outright, never mangled: the runtime's own @-reference resolver now refuses a binary\n // file the same way (a NUL byte in the first 8 KiB) — this just says so before the round trip.\n if (looksBinary(file.name, file.mimeType)) {\n return { kind: 'rejected', message: ATTACHMENT_NON_TEXT_NOTICE };\n }\n if (file.size > MAX_ATTACHMENT_FILE_BYTES) {\n return {\n kind: 'rejected',\n message: `${file.name} is larger than ${formatBytes(MAX_ATTACHMENT_FILE_BYTES)} and cannot be attached.`,\n };\n }\n if (existingTotalBytes + file.size > MAX_ATTACHMENT_TOTAL_BYTES) {\n return {\n kind: 'rejected',\n message: `Attachments cannot total more than ${formatBytes(MAX_ATTACHMENT_TOTAL_BYTES)}.`,\n };\n }\n return {\n kind: 'attached',\n attachment: { id: randomId(), name: file.name, relativePath, size: file.size },\n };\n}\n\n/**\n * Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2 (#3282 §4).\n * Always prefixed with `./`: the parser's `isPathLikeReference` only treats a token as a file\n * reference when it has a `.` somewhere or an explicit relative-path prefix, so a dotless name like\n * `Makefile`, `Dockerfile` or `LICENSE` — common, useful, and never refused for lacking one — would\n * otherwise be sent as plain text with no file and no diagnostic (follow-up to #3282 §4d). `./` makes\n * every path path-like regardless of its name, and the resolver collapses it back to the plain\n * relative path before use.\n */\nexport function buildPromptWithAttachments(\n prompt: string,\n attachments: readonly IDraftAttachment[],\n): string {\n if (attachments.length === 0) return prompt;\n const refs = attachments.map((attachment) => `@./${attachment.relativePath}`).join(' ');\n return prompt ? `${prompt}\\n\\n${refs}` : refs;\n}\n","'use client';\n\nimport { Check } from 'lucide-react';\nimport { useEffect, useId, useRef, useState } from 'react';\n\n/** One choice in a `PopupMenu`. `kind: 'radio'` (the default) is a value the person picks between,\n * shown with a checkmark when it is the current one; `kind: 'action'` runs something instead (e.g.\n * \"Manage providers…\") and never carries a checked state. */\nexport interface IPopupMenuItem {\n readonly key: string;\n readonly label: string;\n readonly description?: string;\n readonly kind?: 'radio' | 'action';\n readonly checked?: boolean;\n readonly destructive?: boolean;\n readonly onSelect: () => void;\n}\n\nexport interface IPopupMenuSection {\n readonly heading?: string;\n readonly items: readonly IPopupMenuItem[];\n}\n\n/**\n * A HIG pop-up menu (#3282 §2): `role=\"menu\"`, its choices as `menuitemradio` with `aria-checked`\n * (or `menuitem` for a plain action, e.g. \"Manage providers…\"), grouped into optional labelled\n * sections. Arrow keys move a roving focus, Enter/Space activates the focused item, and Esc — or a\n * click outside — closes the menu and returns focus to the trigger that opened it, so the person\n * never loses their place. Initial focus lands on the current (checked) item, matching a native\n * pop-up menu; nothing else is pre-highlighted beyond that same focus ring.\n */\nexport function PopupMenu({\n label,\n sections,\n onClose,\n triggerRef,\n}: {\n label: string;\n sections: readonly IPopupMenuSection[];\n onClose: () => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}): React.ReactElement {\n const items = sections.flatMap((section) => section.items);\n const idPrefix = useId();\n const containerRef = useRef<HTMLDivElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [focusedIndex, setFocusedIndex] = useState(() => {\n const current = items.findIndex((item) => item.checked);\n return current >= 0 ? current : 0;\n });\n\n const close = (returnFocus: boolean): void => {\n onClose();\n if (returnFocus) triggerRef.current?.focus();\n };\n\n useEffect(() => {\n itemRefs.current[focusedIndex]?.focus();\n }, [focusedIndex]);\n\n useEffect(() => {\n const onPointerDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (containerRef.current?.contains(target)) return;\n if (triggerRef.current?.contains(target)) return;\n close(false);\n };\n document.addEventListener('mousedown', onPointerDown);\n return () => document.removeEventListener('mousedown', onPointerDown);\n // Registered once (empty deps): `onClose`/`triggerRef` are stable for the life of an open menu —\n // it is remounted fresh (`{open ? <PopupMenu .../> : null}`) rather than kept open across a prop\n // change that would replace them.\n }, []);\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n close(true);\n return;\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n if (items.length === 0) return;\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setFocusedIndex((index) => (index + step + items.length) % items.length);\n return;\n }\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n const item = items[focusedIndex];\n if (item) {\n item.onSelect();\n close(true);\n }\n }\n };\n\n let cursor = -1;\n return (\n <div\n ref={containerRef}\n role=\"menu\"\n aria-label={label}\n onKeyDown={onKeyDown}\n className=\"gui-rise absolute bottom-full z-30 mb-2 max-h-[360px] w-64 overflow-y-auto rounded-2xl bg-popover p-1.5 shadow-2xl shadow-black/35 focus:outline-none\"\n >\n {sections.map((section, sectionIndex) => (\n <div key={section.heading ?? sectionIndex} role=\"group\" aria-label={section.heading}>\n {section.heading ? (\n <div\n role=\"presentation\"\n className=\"px-3 pb-1 pt-2 text-[11px] font-medium uppercase tracking-wide text-subtle\"\n >\n {section.heading}\n </div>\n ) : null}\n {section.items.map((item) => {\n cursor += 1;\n const index = cursor;\n const isAction = item.kind === 'action';\n // The accessible NAME is the label alone (`aria-label`) — otherwise a screen reader would\n // read the description as part of it (button content is used as the name by default). The\n // description is still announced, as a `aria-describedby` description instead.\n const descId = item.description ? `${idPrefix}-desc-${item.key}` : undefined;\n return (\n <button\n key={item.key}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role={isAction ? 'menuitem' : 'menuitemradio'}\n aria-checked={isAction ? undefined : item.checked === true}\n aria-label={item.label}\n aria-describedby={descId}\n tabIndex={index === focusedIndex ? 0 : -1}\n onFocus={() => setFocusedIndex(index)}\n onClick={() => {\n item.onSelect();\n close(true);\n }}\n className={`flex w-full items-start gap-2 rounded-xl px-3 py-2 text-left text-[13.5px] ${\n item.destructive ? 'text-destructive hover:bg-destructive/10' : 'text-foreground hover:bg-hover'\n } ${index === focusedIndex ? 'bg-hover' : ''}`}\n >\n {isAction ? null : (\n <span className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center\">\n {item.checked ? <Check size={14} strokeWidth={2.25} aria-hidden=\"true\" /> : null}\n </span>\n )}\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate\">{item.label}</span>\n {item.description ? (\n <span id={descId} className=\"block truncate text-[12px] text-muted-foreground\">\n {item.description}\n </span>\n ) : null}\n </span>\n </button>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n","'use client';\n\nimport { Gauge, Shield, Sparkles } from 'lucide-react';\nimport { useRef, useState } from 'react';\n\nimport { ConfirmDialog } from './Dialog.js';\nimport { PopupMenu } from './PopupMenu.js';\n\nimport type { IPopupMenuItem, IPopupMenuSection } from './PopupMenu.js';\nimport type { TCommandCatalog, TModelListSnapshot, TSessionStatus } from '../hooks/session-client-types.js';\n\n/**\n * #3282 §2 (part 2) — the plain label for each effort selection. Kept in step with\n * `EFFORT_LEVEL_LABELS` in `packages/agent-framework/src/effort/effort-resolution.ts` (which\n * `agent-command`'s `/effort` re-exports and reports as its own outcome text) — this package cannot\n * import that one directly (it is a Node-side package this browser bundle does not depend on), so the\n * values are restated here. `Record<TSessionStatus['effort'], …>` still keeps this exhaustive against\n * the real wire type: an effort value added there without a label here is a compile error.\n */\nconst EFFORT_LEVEL_LABELS: Readonly<Record<TSessionStatus['effort'], string>> = {\n auto: 'Auto',\n none: 'None',\n minimal: 'Minimal',\n low: 'Low',\n medium: 'Medium',\n high: 'High',\n xhigh: 'Extra high',\n max: 'Maximum',\n};\n\n/** The primary row (#3282 §2 design point 4); the rest sit under \"More\". */\nconst EFFORT_PRIMARY: readonly TSessionStatus['effort'][] = ['auto', 'low', 'medium', 'high'];\nconst EFFORT_MORE: readonly TSessionStatus['effort'][] = ['none', 'minimal', 'xhigh', 'max'];\n\n/** The `mode` command's subcommand entries (plain label + description per mode), from the catalog. */\nfunction modeSubcommands(\n catalog: TCommandCatalog | null,\n): readonly { name: string; displayName?: string; description: string }[] {\n return catalog?.commands.find((command) => command.name === 'mode')?.subcommands ?? [];\n}\n\n/** The current mode's plain label, falling back to the raw id before the catalog has loaded. */\nfunction modeLabel(catalog: TCommandCatalog | null, mode: string): string {\n return modeSubcommands(catalog).find((sub) => sub.name === mode)?.displayName ?? mode;\n}\n\nconst CHIP_CLASS =\n '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';\nconst CHIP_WARNING_CLASS = 'text-warning hover:text-warning';\n\n/** One status chip that opens a `PopupMenu` HIG pop-up beneath it (#3282 §2). */\nfunction StatusChip({\n chipLabel,\n value,\n icon,\n connected,\n warning = false,\n menuLabel,\n sections,\n onOpen,\n}: {\n chipLabel: string;\n value: string;\n icon: React.ReactElement;\n connected: boolean;\n warning?: boolean;\n menuLabel: string;\n sections: readonly IPopupMenuSection[];\n /** Called just before the menu opens — e.g. to (re)fetch the model list. */\n onOpen?: () => void;\n}): React.ReactElement {\n const [open, setOpen] = useState(false);\n const triggerRef = useRef<HTMLButtonElement>(null);\n return (\n <span className=\"relative\">\n <button\n ref={triggerRef}\n type=\"button\"\n aria-label={`${chipLabel}: ${value}`}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n // #3289 §2: below `md` the value text is hidden (icon-only) — the tooltip is where it stays\n // reachable on hover and keyboard focus, so it always carries the value, not just \"Change X\".\n title={connected ? `${chipLabel}: ${value}` : 'Reconnecting…'}\n disabled={!connected}\n onClick={() => {\n onOpen?.();\n setOpen(true);\n }}\n className={`${CHIP_CLASS} ${warning ? CHIP_WARNING_CLASS : ''}`}\n >\n {icon}\n {/* #3289 §2: below `md` (the same breakpoint the session-list sheet uses) the chip collapses\n to its icon alone — never a truncated fragment (\"d…\", \"claude-…\") — the value stays\n reachable via the tooltip above and the accessible name either way. */}\n <span className=\"hidden truncate md:inline\">{value}</span>\n </button>\n {open ? (\n <PopupMenu\n label={menuLabel}\n sections={sections}\n triggerRef={triggerRef}\n onClose={() => setOpen(false)}\n />\n ) : null}\n </span>\n );\n}\n\nconst ICON_PROPS = { size: 14, strokeWidth: 1.75, 'aria-hidden': true } as const;\n\n/**\n * Model · mode · effort, each a pop-up menu (#3282 §2), and the context the conversation fills.\n * Every menu applies its choice through the same command wire path the equivalent typed slash\n * command uses — `onSilentCommand` — so `/model <id>`, `/mode <name>` and `/effort <value>` and a\n * click in these menus can never drift apart; the control's own chip label (fed by the session's next\n * `session_status`) confirms the change instead of a conversation card.\n */\nexport function StatusRow({\n status,\n catalog,\n modelList,\n onRequestModelList,\n onManageProviders,\n onSilentCommand,\n connected,\n}: {\n status: TSessionStatus | null;\n catalog: TCommandCatalog | null;\n modelList: TModelListSnapshot | null;\n onRequestModelList: () => void;\n /** #3282 §4b: \"Manage providers…\" opens Settings at the Providers & Models section. */\n onManageProviders: () => void;\n /** Applies a model/mode/effort choice without a conversation card (see the module doc). */\n onSilentCommand: (name: string, args?: string) => void;\n connected: boolean;\n}): React.ReactElement {\n const used = status ? Math.round(status.context.usedPercentage) : null;\n // #3282 §2: \"Skip all checks\" asks for confirmation on the shared `ConfirmDialog` (#3331) before\n // applying — every other mode applies at once. `true` while that confirmation is open.\n const [pendingBypassConfirm, setPendingBypassConfirm] = useState(false);\n\n const modelSections: IPopupMenuSection[] = [\n ...(modelList?.groups ?? []).map((group) => ({\n heading: group.providerLabel,\n items: group.models.map(\n (model): IPopupMenuItem => ({\n key: model.id,\n label: model.label,\n checked: model.id === modelList?.currentModel && group.profileName === modelList?.currentProfile,\n onSelect: () => onSilentCommand('model', model.id),\n }),\n ),\n })),\n {\n items: [\n {\n key: 'manage-providers',\n label: 'Manage providers…',\n kind: 'action',\n // #3282 §4b: opens Settings at the \"Providers & Models\" section — the same list, Use/Model/\n // Edit/Test/Duplicate/Delete actions and \"Add provider…\" flow now live there instead of in\n // this menu's own server-driven picker.\n onSelect: onManageProviders,\n },\n ],\n },\n ];\n\n const modeSections: IPopupMenuSection[] = [\n {\n items: modeSubcommands(catalog).map(\n (sub): IPopupMenuItem => ({\n key: sub.name,\n label: sub.displayName ?? sub.name,\n description: sub.description,\n checked: sub.name === status?.permissionMode,\n // #3282 §2: every mode but \"Skip all checks\" applies at once; that one opens a\n // confirmation first (rendered below) instead of applying here directly.\n onSelect:\n sub.name === 'bypassPermissions'\n ? () => setPendingBypassConfirm(true)\n : () => onSilentCommand('mode', sub.name),\n }),\n ),\n },\n ];\n\n const effortSections: IPopupMenuSection[] = [\n {\n items: EFFORT_PRIMARY.map(\n (value): IPopupMenuItem => ({\n key: value,\n label: EFFORT_LEVEL_LABELS[value],\n checked: value === status?.effort,\n onSelect: () => onSilentCommand('effort', value),\n }),\n ),\n },\n {\n heading: 'More',\n items: EFFORT_MORE.map(\n (value): IPopupMenuItem => ({\n key: value,\n label: EFFORT_LEVEL_LABELS[value],\n checked: value === status?.effort,\n onSelect: () => onSilentCommand('effort', value),\n }),\n ),\n },\n ];\n\n return (\n <div className=\"flex min-w-0 flex-1 items-center gap-0.5\">\n {status ? (\n <>\n <StatusChip\n chipLabel=\"mode\"\n value={modeLabel(catalog, status.permissionMode)}\n icon={<Shield {...ICON_PROPS} />}\n connected={connected}\n warning={status.permissionMode === 'bypassPermissions'}\n menuLabel=\"Mode\"\n sections={modeSections}\n />\n <span className=\"ml-auto\" />\n <StatusChip\n chipLabel=\"model\"\n value={status.model}\n icon={<Sparkles {...ICON_PROPS} />}\n connected={connected}\n menuLabel=\"Model\"\n sections={modelSections}\n onOpen={onRequestModelList}\n />\n <StatusChip\n chipLabel=\"effort\"\n value={EFFORT_LEVEL_LABELS[status.effort]}\n icon={<Gauge {...ICON_PROPS} />}\n connected={connected}\n menuLabel=\"Effort\"\n sections={effortSections}\n />\n </>\n ) : (\n <span className=\"ml-auto px-2 text-[13px] text-subtle\">…</span>\n )}\n <span\n className=\"flex items-center gap-1.5 px-1.5 text-[12.5px] tabular-nums text-subtle\"\n title=\"Context used\"\n aria-label={`context ${used ?? 0}% used`}\n >\n <ContextRing percent={used ?? 0} />\n {used === null ? '' : `${used}%`}\n </span>\n {/* #3282 §2: matches the wording the Settings screen's own permission-mode picker already\n ships for the identical action (#3331's `SettingsScreen.tsx`), so switching to \"Skip all\n checks\" reads the same warning regardless of which control was used. */}\n <ConfirmDialog\n open={pendingBypassConfirm}\n title=\"Skip all permission checks?\"\n body=\"Every action runs without asking first — file edits, shell commands and network access included. You can turn this back on at any time.\"\n confirmLabel=\"Skip all checks\"\n destructive\n onCancel={() => setPendingBypassConfirm(false)}\n onConfirm={() => {\n setPendingBypassConfirm(false);\n onSilentCommand('mode', 'bypassPermissions');\n }}\n />\n </div>\n );\n}\n\nfunction ContextRing({ percent }: { percent: number }): React.ReactElement {\n const r = 5;\n const circumference = 2 * Math.PI * r;\n const filled = Math.min(100, Math.max(0, percent)) / 100;\n const tone = percent >= 80 ? 'stroke-warning' : 'stroke-muted-foreground';\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r={r} className=\"fill-none stroke-raised\" strokeWidth=\"2\" />\n <circle\n cx=\"7\"\n cy=\"7\"\n r={r}\n className={`fill-none ${tone}`}\n strokeWidth=\"2\"\n strokeDasharray={`${circumference * filled} ${circumference}`}\n transform=\"rotate(-90 7 7)\"\n />\n </svg>\n );\n}\n","import { useProductIdentity } from '../product-identity.js';\nimport { ArrowUp, Paperclip, Square, Target, X } from 'lucide-react';\nimport { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\n\nimport { commandMenuFor } from '../hooks/command-menu.js';\nimport type { ICommandMenuItem } from '../hooks/command-menu.js';\nimport {\n buildPromptWithAttachments,\n evaluateCandidateFile,\n type ICandidateFile,\n type IDraftAttachment,\n type IPickedFile,\n} from './composer-attachments.js';\nimport { StatusRow } from './StatusControls.js';\n\nimport type {\n IQueuedPrompt,\n TCommandCatalog,\n TModelListSnapshot,\n TSessionStatus,\n} from '../hooks/session-client-types.js';\n\nexport type { IPickedFile } from './composer-attachments.js';\n\n/** What a caller can do to the composer from outside it — currently just reclaiming focus. */\nexport interface IComposerHandle {\n /** Focuses the message field — used to send focus back there once a docked prompt is answered. */\n focus: () => void;\n}\n\n/** The draft as persisted: the typed text plus any attachment chips (#3282 §4d). */\ninterface IStoredDraft {\n readonly text: string;\n readonly attachments: readonly IDraftAttachment[];\n}\n\nconst EMPTY_DRAFT: IStoredDraft = { text: '', attachments: [] };\n\n/**\n * #3282 §4e: a command whose whole job is opening a GUI screen — choosing it from the `/` menu runs\n * it at once (its `ui_intent` opens the screen the normal way) instead of filling the draft and\n * waiting for Enter, since there is nothing useful to type after it. `help` never reaches the\n * session at all (`useSessionClient.ts`'s `send` opens the Help sheet locally). Every other command\n * still \"runs as today\": chosen or Tab-completed, it fills the draft for its arguments.\n */\nconst IMMEDIATE_SCREEN_COMMANDS: ReadonlySet<string> = new Set(['settings', 'resume', 'help']);\n\nfunction isImmediateScreenCommand(item: ICommandMenuItem): boolean {\n return item.kind === 'command' && IMMEDIATE_SCREEN_COMMANDS.has(item.name);\n}\n\n/** The group a menu row falls under — shown as a header above the first row of each. */\nfunction menuGroupLabel(item: ICommandMenuItem): 'Commands' | 'Skills' {\n return item.kind === 'skill' ? 'Skills' : 'Commands';\n}\n\n/**\n * #3280 §4: the unsent draft survives a Chat → Usage → Chat switch (the composer unmounts), a page\n * reload, and a desktop relaunch — `localStorage`, not `sessionStorage`, since only `localStorage`\n * survives a closed window/tab being reopened. Namespaced (the configured draft prefix, matching\n * the configured restore key in `use-session-directory.ts`) so a page hosting other state under the\n * same origin does not collide. Per session id when one is known; a single fallback key before the\n * first status arrives (the gap is brief and is reconciled once it does — see the effect below).\n *\n * #3282 §4d: the stored value is now JSON (`IStoredDraft`), not the bare text string it used to be —\n * `parseStoredDraft` treats anything that does not parse as that shape (including a draft saved\n * before this change shipped) as plain text with no attachments, so an old stored draft still loads.\n */\n\nfunction draftStorageKey(namespace: string, sessionId: string | undefined): string {\n return sessionId ? `${namespace}.draft.${sessionId}` : `${namespace}.draft`;\n}\n\n/**\n * A stored attachment's shape is never trusted blindly: it is `localStorage`, not this component's\n * own state, so it can be edited by hand, left over from a future version with a different shape, or\n * just corrupted. A missing/non-string `name` or `relativePath` would otherwise show a blank chip and\n * send a broken (or empty) `@`-reference on submit — dropped instead of risking either.\n */\nfunction isStoredAttachment(value: unknown): value is IDraftAttachment {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.id === 'string' &&\n typeof candidate.name === 'string' &&\n typeof candidate.relativePath === 'string' &&\n typeof candidate.size === 'number'\n );\n}\n\nfunction parseStoredDraft(raw: string | null): IStoredDraft {\n if (!raw) return EMPTY_DRAFT;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && typeof (parsed as { text?: unknown }).text === 'string') {\n const attachments = (parsed as { attachments?: unknown }).attachments;\n return {\n text: (parsed as { text: string }).text,\n attachments: Array.isArray(attachments) ? attachments.filter(isStoredAttachment) : [],\n };\n }\n } catch {\n // Not JSON — a draft saved before attachments shipped. Fall through to plain text below.\n }\n return { text: raw, attachments: [] };\n}\n\n/** Best-effort: a private window, cleared site data, or a full quota still leaves typing working. */\nfunction readDraft(namespace: string, sessionId: string | undefined): IStoredDraft {\n try {\n return parseStoredDraft(window.localStorage.getItem(draftStorageKey(namespace, sessionId)));\n } catch {\n // allow-fallback: storage unavailable — the draft still lives in component state this session.\n return EMPTY_DRAFT;\n }\n}\n\nfunction writeDraft(namespace: string, sessionId: string | undefined, value: IStoredDraft): void {\n try {\n const key = draftStorageKey(namespace, sessionId);\n if (!value.text && value.attachments.length === 0) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // allow-fallback: same as readDraft — typing (and sending) still work without persistence.\n }\n}\n\n/**\n * The composer — the control centre, as desktop agent apps place it: the message box, a `/` menu of\n * the commands and skills the session offers, and a status row (model, permission mode, effort,\n * context). The row's controls run the session's own commands (`/provider`, `/mode`, `/effort`), so a\n * setting changes through the one path every client shares.\n */\nexport const Composer = forwardRef<\n IComposerHandle,\n {\n onSubmit: (prompt: string) => void;\n onCommand: (name: string, args?: string) => void;\n catalog: TCommandCatalog | null;\n status: TSessionStatus | null;\n /**\n * False while the transport is not `connected` (issue #3280 §5): Enter and Send refuse to submit,\n * and nothing typed is cleared or lost — the composer never sends into a socket that is not there.\n * #3282 §2 (part 2): the status chips and the `/` command menu are disabled the same way.\n */\n connected?: boolean;\n /** #3280 §2: a turn (or a blocking command) is running — Send becomes Stop and Esc stops it too. */\n running: boolean;\n onStop: () => void;\n /** #3280 §2: the prompt queued behind the running turn, or null when none is queued. */\n queued: IQueuedPrompt | null;\n onCancelQueue: () => void;\n /**\n * Opens the host's native multi-file dialog with real filesystem paths — present only on the\n * desktop app (#3282 §4d). Its absence means the attach button falls back to a plain HTML file\n * picker, whose picks a browser can never resolve to a path (rule 3: shown plainly, nothing\n * attached).\n */\n pickFiles?: () => Promise<readonly IPickedFile[]>;\n /**\n * Resolves a dropped or picked `File` to its real filesystem path — present only on the desktop\n * app, via Electron's `webUtils.getPathForFile` (#3282 §4d). Its absence means a drop can never\n * become an `@`-reference either.\n */\n getPathForFile?: (file: File) => string;\n /** #3282 §2 (part 2): the model control's pop-up menu — null until requested. */\n modelList?: TModelListSnapshot | null;\n onRequestModelList?: () => void;\n /** Applies a model/mode/effort choice without a conversation card (the control's label confirms it). */\n onSilentCommand?: (name: string, args?: string) => void;\n /**\n * #3282 §4b: \"Manage providers…\" opens Settings at the Providers & Models section. Defaults to\n * dispatching the bare `/provider` command (the pre-#3282-§4b behavior) for a caller that has not\n * wired Settings — every shipped caller passes its own `state.openSettings('providers')`.\n */\n onManageProviders?: () => void;\n }\n>(function Composer(\n {\n onSubmit,\n onCommand,\n catalog,\n status,\n connected = true,\n running,\n onStop,\n queued,\n onCancelQueue,\n pickFiles,\n getPathForFile,\n modelList = null,\n onRequestModelList = () => {},\n onSilentCommand = onCommand,\n onManageProviders = () => onCommand('provider'),\n },\n ref,\n): React.ReactElement {\n const { identity, storage: { browserNamespace } } = useProductIdentity();\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n useImperativeHandle(ref, () => ({ focus: () => textareaRef.current?.focus() }), []);\n const sessionId = status?.sessionId;\n const sessionIdRef = useRef(sessionId);\n // Whether a REAL session id has ever been seen. A switch goes A -> null -> B — `session_switched`\n // clears `sessionStatus` before `get-status` answers (`useSessionClient.ts`) — so `sessionId` turns\n // transiently `undefined` on an ordinary switch too, not only before the very first status. This\n // ref is the one thing that distinguishes \"no session has ever been known yet\" (the fallback-key\n // migration case) from \"between two known sessions right now\" — never overload `undefined` for it.\n const hasKnownSessionRef = useRef(sessionId !== undefined);\n // The draft (text + attachments) as last set, read inside effects/handlers without depending on\n // component state and risking a stale closure — kept in lockstep with the `draft`/`attachments`\n // state below by `setDraft`/`setAttachments`, the only two places that mutate it.\n const stateRef = useRef<IStoredDraft>(EMPTY_DRAFT);\n const [draft, setDraftState] = useState(() => {\n const initial = readDraft(browserNamespace, sessionId);\n stateRef.current = initial;\n return initial.text;\n });\n const [attachments, setAttachmentsState] = useState<readonly IDraftAttachment[]>(\n () => stateRef.current.attachments,\n );\n const [attachmentNotice, setAttachmentNotice] = useState<string | null>(null);\n const [selected, setSelected] = useState(0);\n const [dismissed, setDismissed] = useState(false);\n // #3282 §2 (part 2): the slash menu is disabled while disconnected — none of its commands could run.\n const menu = dismissed || !connected ? null : commandMenuFor(catalog, draft);\n useEffect(() => {\n setSelected(0);\n setDismissed(false);\n }, [draft]);\n // #3282 §4e: the menu can hold more rows than fit in its scroll area — keyboard navigation (below)\n // must keep the highlighted row in view, not just move the highlight off-screen.\n const menuOptionRefs = useRef<(HTMLButtonElement | null)[]>([]);\n useEffect(() => {\n menuOptionRefs.current[selected]?.scrollIntoView?.({ block: 'nearest' });\n }, [selected, menu]);\n /** #3280 §4: every draft change is persisted at once, so a reload or relaunch loses nothing. */\n const setDraft = (value: string): void => {\n stateRef.current = { ...stateRef.current, text: value };\n setDraftState(value);\n writeDraft(browserNamespace, sessionIdRef.current, stateRef.current);\n };\n /** #3282 §4d: attachment chips persist alongside the text, under the same per-session draft key. */\n const setAttachments = (\n updater: readonly IDraftAttachment[] | ((current: readonly IDraftAttachment[]) => readonly IDraftAttachment[]),\n ): void => {\n const next = typeof updater === 'function' ? updater(stateRef.current.attachments) : updater;\n stateRef.current = { ...stateRef.current, attachments: next };\n setAttachmentsState(next);\n writeDraft(browserNamespace, sessionIdRef.current, stateRef.current);\n };\n // #3280 §4: a session switch shows THAT session's own saved draft, never what was typed for\n // another one. The session id becoming known for the very FIRST time (the fallback key was in use\n // until now) instead carries over what is already typed, rather than discarding it. A transient\n // `undefined` mid-switch, once a real id has already been seen, is not a change at all: stay bound\n // to the last known session — keep showing and writing to ITS draft — until a new CONCRETE id\n // arrives, so keystrokes typed during the round trip never land under the wrong session (or the\n // fallback key).\n useEffect(() => {\n if (sessionId === undefined && hasKnownSessionRef.current) return;\n if (sessionIdRef.current === sessionId) return;\n const firstArrival = !hasKnownSessionRef.current;\n const previous = sessionIdRef.current;\n sessionIdRef.current = sessionId;\n if (sessionId !== undefined) hasKnownSessionRef.current = true;\n const stored = readDraft(browserNamespace, sessionId);\n const hasStored = stored.text !== '' || stored.attachments.length > 0;\n const hasCarryOver = stateRef.current.text !== '' || stateRef.current.attachments.length > 0;\n if (firstArrival && !hasStored && hasCarryOver) {\n writeDraft(browserNamespace, sessionId, stateRef.current);\n writeDraft(browserNamespace, previous, EMPTY_DRAFT);\n return;\n }\n stateRef.current = stored;\n setDraftState(stored.text);\n setAttachmentsState(stored.attachments);\n setAttachmentNotice(null); // a notice belongs to the attempt just made in the session left behind\n }, [browserNamespace, sessionId]);\n\n const submit = (): void => {\n if (!connected) return;\n const text = draft.trim();\n if (!text && attachments.length === 0) return;\n onSubmit(buildPromptWithAttachments(text, attachments));\n setDraft('');\n setAttachments([]);\n setAttachmentNotice(null);\n };\n const workspacePath = status?.workspace?.path;\n const totalAttachedBytes = attachments.reduce((sum, a) => sum + a.size, 0);\n /** Evaluate every dropped/picked file in order, so a mixed batch attaches what it can. */\n const addCandidates = (candidates: readonly ICandidateFile[]): void => {\n if (candidates.length === 0) return;\n let total = totalAttachedBytes;\n let count = attachments.length;\n const added: IDraftAttachment[] = [];\n let notice: string | null = null;\n for (const candidate of candidates) {\n const outcome = evaluateCandidateFile(candidate, workspacePath, total, count);\n if (outcome.kind === 'attached') {\n added.push(outcome.attachment);\n total += outcome.attachment.size;\n count += 1;\n } else {\n notice = outcome.message;\n }\n }\n if (added.length > 0) setAttachments((current) => [...current, ...added]);\n setAttachmentNotice(notice);\n };\n const removeAttachment = (id: string): void => {\n setAttachments((current) => current.filter((a) => a.id !== id));\n };\n const toCandidateFromFile = (file: File): ICandidateFile => ({\n name: file.name,\n size: file.size,\n mimeType: file.type || undefined,\n absolutePath: getPathForFile ? getPathForFile(file) || undefined : undefined,\n });\n const fileInputRef = useRef<HTMLInputElement>(null);\n const handleAttachClick = (): void => {\n if (!connected) return;\n if (pickFiles) {\n pickFiles()\n .then((picked) =>\n addCandidates(picked.map((f) => ({ name: f.name, size: f.size, absolutePath: f.path }))),\n )\n // The host's dialog IPC can reject (e.g. the window closed mid-pick) — say so rather than\n // leaving an unhandled rejection and a button that silently did nothing (rule 5: never fail\n // silently).\n .catch(() => setAttachmentNotice('Could not open the file picker. Try again.'));\n return;\n }\n fileInputRef.current?.click();\n };\n const handleFileInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {\n const files = Array.from(event.target.files ?? []);\n event.target.value = ''; // allow picking the same (rejected) file again after fixing it\n addCandidates(files.map(toCandidateFromFile));\n };\n const dragDepthRef = useRef(0);\n const [isDraggingFiles, setIsDraggingFiles] = useState(false);\n const hasFilesDrag = (event: React.DragEvent): boolean =>\n Array.from(event.dataTransfer?.types ?? []).includes('Files');\n const onDragEnter = (event: React.DragEvent): void => {\n if (!connected || !hasFilesDrag(event)) return;\n event.preventDefault();\n dragDepthRef.current += 1;\n setIsDraggingFiles(true);\n };\n const onDragOver = (event: React.DragEvent): void => {\n if (!connected || !hasFilesDrag(event)) return;\n event.preventDefault(); // required for onDrop to fire\n };\n const onDragLeave = (): void => {\n dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);\n if (dragDepthRef.current === 0) setIsDraggingFiles(false);\n };\n const onDrop = (event: React.DragEvent): void => {\n if (!connected || !hasFilesDrag(event)) return;\n event.preventDefault();\n dragDepthRef.current = 0;\n setIsDraggingFiles(false);\n addCandidates(Array.from(event.dataTransfer?.files ?? []).map(toCandidateFromFile));\n };\n /** Choosing a command from the menu, by mouse or by keyboard (`acceptMenu` below). */\n const chooseMenuItem = (item: ICommandMenuItem): void => {\n if (isImmediateScreenCommand(item)) {\n setDraft('');\n onCommand(item.name);\n return;\n }\n setDraft(`/${item.name} `);\n };\n /** Complete the highlighted name; a draft that already names it is sent instead. A command whose\n * whole job is opening a screen runs at once either way — see `IMMEDIATE_SCREEN_COMMANDS`. */\n const acceptMenu = (): boolean => {\n const item = menu?.[selected];\n if (!item) return false;\n if (!isImmediateScreenCommand(item) && draft === `/${item.name}`) return false;\n chooseMenuItem(item);\n return true;\n };\n /** #3280 §2: Edit puts the queued text back in the draft and cancels the queue behind it (the wire\n * has no per-message cancel — only a whole-queue clear). Offered only when exactly one prompt is\n * queued: with more than one, `cancel-queue` would still drop every one of them, but only the\n * shown prompt's text is known here, so \"Edit\" would silently lose the rest — the row offers only\n * \"Remove all\" instead once `queued.count > 1`. */\n const editQueued = (): void => {\n if (!queued) return;\n setDraft(queued.text);\n onCancelQueue();\n };\n\n return (\n <div\n className=\"relative flex-shrink-0\"\n onDragEnter={onDragEnter}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n onDrop={onDrop}\n >\n {/* #3282 §4d rule 7: the drop target is announced even to someone who cannot drag a file —\n the attach button beside the textarea is how they reach the same result. */}\n <p id=\"composer-attach-hint\" className=\"sr-only\">\n Drag files here, or use Attach files, to add them to your message.\n </p>\n {isDraggingFiles && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"pointer-events-none absolute inset-0 z-20 flex items-center justify-center rounded-[22px] border-2 border-dashed border-accent bg-accent/10 text-[14px] font-medium text-accent\"\n >\n Drop to attach\n </div>\n )}\n {queued && (\n <div\n role=\"status\"\n aria-label=\"queued prompt\"\n className=\"gui-rise mb-2 flex items-center gap-3 rounded-2xl bg-card px-4 py-2 text-[13px]\"\n >\n <span className=\"min-w-0 flex-1 truncate text-muted-foreground\">\n Queued: {queued.text}\n {queued.count > 1 && (\n <span className=\"text-subtle\"> and {queued.count - 1} more</span>\n )}\n </span>\n {queued.count === 1 ? (\n <>\n <button\n type=\"button\"\n onClick={editQueued}\n className=\"flex-shrink-0 rounded-lg px-2 py-1 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={onCancelQueue}\n className=\"flex-shrink-0 rounded-lg px-2 py-1 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n Remove\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={onCancelQueue}\n className=\"flex-shrink-0 rounded-lg px-2 py-1 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n Remove all\n </button>\n )}\n </div>\n )}\n {menu && (\n <div\n role=\"listbox\"\n aria-label=\"commands\"\n className=\"gui-rise absolute bottom-full left-0 right-0 mb-2 max-h-[320px] overflow-y-auto rounded-2xl bg-popover p-1.5 shadow-2xl shadow-black/35\"\n >\n {menu.map((item, index) => {\n // #3282 §4e: \"Commands\" then \"Skills\" — a header appears once, above the first row of\n // its group (the menu always lists every command before every skill, so a group's rows\n // are contiguous).\n const showGroupHeader = index === 0 || menuGroupLabel(menu[index - 1]!) !== menuGroupLabel(item);\n return (\n <div key={`${item.kind}:${item.name}`}>\n {showGroupHeader && (\n <div\n role=\"presentation\"\n className=\"px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wide text-subtle first:pt-1\"\n >\n {menuGroupLabel(item)}\n </div>\n )}\n <button\n ref={(el) => {\n menuOptionRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selected}\n onMouseEnter={() => setSelected(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n chooseMenuItem(item);\n }}\n className={`flex w-full items-baseline gap-3 rounded-xl px-3 py-2 text-left text-[14px] ${\n index === selected ? 'bg-hover' : ''\n }`}\n >\n <span className=\"flex-shrink-0 font-mono text-[13.5px] text-foreground\">\n /{item.name}\n </span>\n <span className=\"min-w-0 flex-1 truncate text-muted-foreground\">\n {item.description}\n </span>\n </button>\n </div>\n );\n })}\n </div>\n )}\n <form\n className=\"rounded-[22px] bg-card px-2.5 pb-2 pt-2.5 shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] transition-shadow focus-within:ring-2 focus-within:ring-ring\"\n onSubmit={(e) => {\n e.preventDefault();\n submit();\n }}\n >\n {attachments.length > 0 && (\n <ul\n aria-label=\"attachments\"\n className=\"mb-1.5 flex flex-wrap gap-1.5 px-1 pt-0.5\"\n >\n {attachments.map((attachment) => (\n <li\n key={attachment.id}\n title={attachment.relativePath}\n className=\"flex max-w-full items-center gap-1.5 rounded-lg bg-raised px-2 py-1 text-[12.5px] text-muted-foreground\"\n >\n <Paperclip size={12} strokeWidth={1.75} aria-hidden=\"true\" className=\"flex-shrink-0\" />\n <span className=\"max-w-[180px] truncate\">{attachment.name}</span>\n <button\n type=\"button\"\n aria-label={`Remove ${attachment.name}`}\n onClick={() => removeAttachment(attachment.id)}\n className=\"flex-shrink-0 rounded-full p-0.5 hover:bg-hover hover:text-foreground\"\n >\n <X size={12} strokeWidth={2} aria-hidden=\"true\" />\n </button>\n </li>\n ))}\n </ul>\n )}\n {attachmentNotice && (\n <p role=\"status\" aria-live=\"polite\" className=\"mb-1.5 px-1.5 text-[12.5px] text-muted-foreground\">\n {attachmentNotice}\n </p>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n onChange={handleFileInputChange}\n className=\"sr-only\"\n tabIndex={-1}\n aria-hidden=\"true\"\n />\n <textarea\n ref={textareaRef}\n aria-label=\"message\"\n aria-describedby=\"composer-attach-hint\"\n rows={1}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n // #3280 §4: an Enter that only finishes an IME (Korean/Japanese/Chinese) composition must\n // not submit or accept the menu — `isComposing` (or `keyCode` 229, on browsers that\n // predate it) marks it; the keystroke is left alone so the browser commits the composition\n // normally, matching the guard the free-text prompt field uses (#3280 §3).\n if (e.key === 'Enter' && (e.nativeEvent.isComposing || e.keyCode === 229)) return;\n if (menu) {\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n e.preventDefault();\n const step = e.key === 'ArrowDown' ? 1 : -1;\n setSelected((index) => (index + step + menu.length) % menu.length);\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n setDismissed(true);\n return;\n }\n if ((e.key === 'Tab' || (e.key === 'Enter' && !e.shiftKey)) && acceptMenu()) {\n e.preventDefault();\n return;\n }\n }\n // #3280 §2: Esc stops a running turn — but only once the menu (handled above) is out of\n // the way, so dismissing the `/` menu never doubles as an abort. Not while disconnected:\n // an abort could not reach the host either (issue #3280 §5).\n if (e.key === 'Escape' && running && connected) {\n e.preventDefault();\n onStop();\n return;\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n }}\n placeholder={`Ask ${identity.displayName} anything — type / for commands`}\n className=\"block max-h-[220px] min-h-[48px] w-full resize-none bg-transparent px-2 py-1 text-[15px] leading-relaxed text-foreground [field-sizing:content] focus:outline-none\"\n />\n <div className=\"mt-1 flex items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Attach files\"\n // #3282 §2 (part 2): the attach button follows the same disconnected rule as the status\n // chips and the `/` menu — a short \"Reconnecting…\" tooltip in place of its usual one.\n title={connected ? 'Attach files' : 'Reconnecting…'}\n disabled={!connected}\n onClick={handleAttachClick}\n className=\"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full text-muted-foreground hover:bg-hover hover:text-foreground disabled:text-subtle disabled:hover:bg-transparent\"\n >\n <Paperclip size={16} strokeWidth={1.75} aria-hidden=\"true\" />\n </button>\n <StatusRow\n status={status}\n catalog={catalog}\n modelList={modelList}\n onRequestModelList={onRequestModelList}\n onManageProviders={onManageProviders}\n onSilentCommand={onSilentCommand}\n connected={connected}\n />\n <button\n type={running ? 'button' : 'submit'}\n onClick={running ? onStop : undefined}\n // Stop is unavailable while disconnected too: an abort could not reach the host either.\n disabled={!connected || (!running && !draft.trim() && attachments.length === 0)}\n aria-description={connected ? undefined : 'Not connected'}\n className=\"ml-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground transition-all hover:opacity-85 disabled:bg-raised disabled:text-subtle\"\n >\n {running ? (\n <Square size={13} strokeWidth={2} fill=\"currentColor\" aria-hidden=\"true\" />\n ) : (\n <ArrowUp size={17} strokeWidth={2.25} aria-hidden=\"true\" />\n )}\n <span className=\"sr-only\">{running ? 'Stop' : 'Send'}</span>\n </button>\n </div>\n </form>\n </div>\n );\n});\n\n/**\n * The goal being pursued (`/goal`), above the composer while it is active — the objective, how far\n * the turn budget has gone, and a way to stop it (`/goal cancel`).\n */\nexport function GoalBar({\n status,\n onStop,\n}: {\n status: TSessionStatus | null;\n onStop: () => void;\n}): React.ReactElement | null {\n const goal = status?.goal;\n if (!goal || goal.status !== 'active') return null;\n const progress = goal.maxIterations > 0 ? Math.min(1, goal.iterations / goal.maxIterations) : 0;\n return (\n <div\n role=\"status\"\n aria-label=\"goal\"\n className=\"gui-rise relative flex flex-shrink-0 items-center gap-3 overflow-hidden rounded-2xl bg-card px-4 py-2.5 text-[14px]\"\n >\n <Target size={16} strokeWidth={1.75} className=\"flex-shrink-0 text-accent\" />\n <span className=\"flex-shrink-0 font-medium text-foreground\">Goal</span>\n <span className=\"min-w-0 flex-1 truncate text-muted-foreground\">{goal.objective}</span>\n <span className=\"flex-shrink-0 text-[13px] tabular-nums text-subtle\">\n {goal.iterations}/{goal.maxIterations}\n </span>\n <button\n type=\"button\"\n aria-label=\"Stop goal\"\n onClick={onStop}\n className=\"flex flex-shrink-0 items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <Square size={11} fill=\"currentColor\" aria-hidden=\"true\" />\n Stop\n </button>\n <span\n aria-hidden=\"true\"\n className=\"absolute bottom-0 left-0 h-[2px] bg-accent/70 transition-[width]\"\n style={{ width: `${progress * 100}%` }}\n />\n </div>\n );\n}\n\n// `StatusRow` (model/mode/effort pop-up menus) and its `ContextRing` moved to `StatusControls.tsx`\n// (#3282 §2 part 2) — imported above.\n","import { X } from 'lucide-react';\nimport React from 'react';\n\nimport { describeExecutionEntryStatus } from '../execution-entry-status.js';\n\nimport type { TExecutionDetailStatus } from '../hooks/use-execution-detail.js';\nimport type {\n IExecutionDetailRecord,\n IExecutionWorkspaceEntry,\n} from '@robota-sdk/agent-interface-execution';\n\nconst STATUS_TAG_TONE: Record<string, string> = {\n warning: 'bg-warning/15 text-warning',\n destructive: 'bg-destructive/12 text-destructive',\n muted: 'bg-subtle/15 text-subtle',\n};\n\nconst RECORD_KIND_LABEL: Record<IExecutionDetailRecord['kind'], string> = {\n message: '',\n tool_activity: 'Tool',\n process_output: 'Output',\n progress: 'Progress',\n result: 'Result',\n error: 'Error',\n group_summary: 'Summary',\n};\n\n/**\n * #3288 §1: opening an Agents panel entry — status, what it was asked (one line, from its\n * headline), its transcript so far (paginated via `onLoadMore`) and, once it has one, its result.\n * `entry === null` renders nothing; the caller owns showing/hiding this over the panel.\n */\nexport function ExecutionDetailSheet({\n entry,\n status,\n records,\n error,\n complete,\n onClose,\n onLoadMore,\n onStop,\n}: {\n entry: IExecutionWorkspaceEntry | null;\n status: TExecutionDetailStatus;\n records: readonly IExecutionDetailRecord[];\n error: string | null;\n complete: boolean;\n onClose: () => void;\n onLoadMore: () => void;\n onStop?: () => void;\n}): React.ReactElement | null {\n if (!entry) return null;\n const described = describeExecutionEntryStatus(entry);\n const canStop = onStop !== undefined && entry.controls.includes('cancel');\n const askedLine = entry.headline?.text ?? entry.preview;\n\n return (\n <div\n role=\"dialog\"\n aria-label={entry.title}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n onClose();\n }\n }}\n className=\"agent-ui absolute inset-y-0 right-0 z-30 flex w-96 flex-col overflow-hidden border-l border-subtle/20 bg-background shadow-2xl shadow-black/40\"\n >\n <div className=\"flex flex-shrink-0 items-center gap-2 border-b border-subtle/15 px-4 py-3\">\n <span className=\"min-w-0 flex-1 truncate text-[14px] font-medium text-foreground\">\n {entry.title}\n </span>\n {described && (\n <span\n className={`flex-shrink-0 rounded-md px-1.5 py-px text-[12px] ${STATUS_TAG_TONE[described.tone]}`}\n >\n {described.label}\n </span>\n )}\n {canStop && (\n <button\n type=\"button\"\n aria-label={`Stop ${entry.title}`}\n onClick={onStop}\n className=\"flex-shrink-0 rounded-md px-1.5 py-px text-[12px] text-subtle hover:bg-destructive/10 hover:text-destructive\"\n >\n Stop\n </button>\n )}\n <button\n type=\"button\"\n aria-label=\"Close\"\n aria-keyshortcuts=\"Escape\"\n onClick={onClose}\n className=\"flex-shrink-0 rounded-md p-1 text-subtle hover:bg-card hover:text-foreground\"\n >\n <X size={16} strokeWidth={1.9} aria-hidden=\"true\" />\n </button>\n </div>\n\n {askedLine && (\n <p className=\"flex-shrink-0 border-b border-subtle/15 px-4 py-2.5 text-[13px] leading-snug text-muted-foreground\">\n {askedLine}\n </p>\n )}\n {entry.deniedToolCalls !== undefined && entry.deniedToolCalls > 0 && (\n <p className=\"flex-shrink-0 px-4 pt-2 text-[12px] text-warning\">\n {entry.deniedToolCalls} tool {entry.deniedToolCalls === 1 ? 'call was' : 'calls were'} refused.\n </p>\n )}\n\n <div className=\"flex-1 space-y-2 overflow-y-auto px-4 py-3\">\n {records.map((record) => (\n <div key={record.id} className=\"text-[13px] leading-relaxed text-foreground\">\n {RECORD_KIND_LABEL[record.kind] && (\n <span className=\"mr-1.5 text-[11px] font-medium uppercase tracking-wide text-subtle\">\n {RECORD_KIND_LABEL[record.kind]}\n </span>\n )}\n <span className=\"whitespace-pre-wrap break-words\">{record.text}</span>\n </div>\n ))}\n {status === 'loading' && records.length === 0 && (\n <p className=\"text-[13px] text-subtle\">Loading…</p>\n )}\n {error && <p className=\"text-[13px] text-destructive\">{error}</p>}\n {!complete && status !== 'loading' && (\n <button\n type=\"button\"\n onClick={onLoadMore}\n className=\"text-[12px] text-subtle underline hover:text-foreground\"\n >\n Load more\n </button>\n )}\n {!complete && status === 'loading' && records.length > 0 && (\n <p className=\"text-[12px] text-subtle\">Loading more…</p>\n )}\n </div>\n </div>\n );\n}\n","'use client';\n\nimport { X } from 'lucide-react';\n\nimport { commandMenuFor } from '../hooks/command-menu.js';\nimport { Dialog } from './Dialog.js';\n\nimport type { TCommandCatalog } from '../hooks/session-client-types.js';\n\n/** One keyboard shortcut this surface answers — #3282 §4e. */\ninterface IShortcut {\n readonly keys: string;\n readonly description: string;\n}\n\nconst SHORTCUTS: readonly IShortcut[] = [\n { keys: '⌘,', description: 'Open Settings' },\n { keys: 'Esc', description: 'Close a dialog or menu, or stop the current run' },\n { keys: 'Shift+Tab', description: 'Answer a pending question with the keyboard' },\n { keys: 'Enter', description: 'Send the message' },\n { keys: 'Shift+Enter', description: 'Start a new line' },\n];\n\n/**\n * The GUI's Help sheet (#3282 §4e), a `Dialog` opened by `/help` — client-only: `useSessionClient.ts`'s\n * `send` catches `/help` before it ever reaches the session and opens this instead, so there is no\n * round trip and nothing terminal-style to replace it with. It lists the same commands and skills the\n * `/` menu offers (`commandMenuFor`, unfiltered by any query — an excluded command has no row here\n * either, exactly like the menu), grouped the same way, plus the keyboard shortcuts this surface\n * answers. The TUI keeps its own text `/help` output; this sheet is the GUI's alone.\n */\nexport function HelpSheet({\n open,\n onClose,\n catalog,\n}: {\n open: boolean;\n onClose: () => void;\n catalog: TCommandCatalog | null;\n}): React.ReactElement | null {\n const items = commandMenuFor(catalog, '/') ?? [];\n const commands = items.filter((item) => item.kind === 'command');\n const skills = items.filter((item) => item.kind === 'skill');\n\n return (\n <Dialog\n open={open}\n onClose={onClose}\n title=\"Help\"\n panelClassName=\"flex h-[min(600px,85vh)] w-full max-w-lg flex-col overflow-hidden rounded-2xl bg-card text-card-foreground shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] focus:outline-none\"\n >\n <div className=\"flex h-12 flex-shrink-0 items-center gap-1 border-b border-border px-3\">\n <h2 className=\"px-1.5 text-[15px] font-semibold text-foreground\">Help</h2>\n <button\n type=\"button\"\n aria-label=\"Close Help\"\n onClick={onClose}\n className=\"ml-auto rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <X size={17} strokeWidth={1.75} />\n </button>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4\">\n <section aria-label=\"Shortcuts\">\n <h3 className=\"text-[11px] font-semibold uppercase tracking-wide text-subtle\">Shortcuts</h3>\n <dl className=\"mt-2 mb-5 space-y-1.5\">\n {SHORTCUTS.map((shortcut) => (\n <div key={shortcut.keys} className=\"flex items-baseline gap-3 text-[13.5px]\">\n <dt className=\"w-24 flex-shrink-0 font-mono text-foreground\">{shortcut.keys}</dt>\n <dd className=\"min-w-0 flex-1 text-muted-foreground\">{shortcut.description}</dd>\n </div>\n ))}\n </dl>\n </section>\n\n <CommandGroup label=\"Commands\" items={commands} />\n {skills.length > 0 && <CommandGroup label=\"Skills\" items={skills} />}\n </div>\n </Dialog>\n );\n}\n\nfunction CommandGroup({\n label,\n items,\n}: {\n label: 'Commands' | 'Skills';\n items: readonly { name: string; description: string }[];\n}): React.ReactElement {\n return (\n <section aria-label={label} className=\"mb-5 last:mb-0\">\n <h3 className=\"text-[11px] font-semibold uppercase tracking-wide text-subtle\">{label}</h3>\n <ul className=\"mt-2 space-y-1.5\">\n {items.map((item) => (\n <li key={item.name} className=\"flex items-baseline gap-3 text-[13.5px]\">\n <span className=\"w-24 flex-shrink-0 truncate font-mono text-foreground\">/{item.name}</span>\n <span className=\"min-w-0 flex-1 text-muted-foreground\">{item.description}</span>\n </li>\n ))}\n </ul>\n </section>\n );\n}\n","'use client';\n\nimport { ChevronRight, ExternalLink, GitBranch, LoaderCircle, RefreshCw } from 'lucide-react';\nimport React, { useEffect, useState } from 'react';\n\nimport { DiffLines } from './DiffLines.js';\n\nimport type {\n IWsSessionState,\n TProjectDiffRead,\n TProjectMemoryRead,\n TProjectStatusRead,\n} from '../hooks/session-client-types.js';\n\n/**\n * #3282 §4c — the Project panel: git status (Changes), one file's diff on click, and project memory as\n * readable text. The owner's design principle 4 (#3277): everything the host implements is usable from\n * the GUI through organized screens — this is git status/diff and project memory's screen. Checkpoints\n * (rewind) are deliberately absent (see `agent-gui-web/docs/SPEC.md`'s design decisions): edit\n * checkpoints only work on a host that can prove a write stays inside the project\n * (`supportsWorkspaceProjectMutation`, Linux-only by design, not a switched-off setting), so there is\n * nothing safe to show here on macOS or Windows.\n */\n\ntype TProjectStatusFile = Extract<TProjectStatusRead, { kind: 'status' }>['files'][number];\n\nconst STATUS_TONE: Readonly<Record<TProjectStatusFile['status'], string>> = {\n Added: 'text-accent',\n Modified: 'text-warning',\n Deleted: 'text-destructive',\n Renamed: 'text-muted-foreground',\n Copied: 'text-muted-foreground',\n Untracked: 'text-subtle',\n Conflicted: 'text-destructive',\n};\n\nfunction StatusRow({\n file,\n open,\n onOpen,\n}: {\n file: TProjectStatusFile;\n open: boolean;\n onOpen: () => void;\n}): React.ReactElement {\n return (\n <button\n type=\"button\"\n aria-expanded={open}\n onClick={onOpen}\n className=\"flex min-w-0 items-center gap-2 rounded-md px-1.5 py-1.5 text-left hover:bg-hover\"\n >\n <ChevronRight\n size={13}\n className={`flex-shrink-0 text-subtle transition-transform ${open ? 'rotate-90' : ''}`}\n />\n <span\n className={`w-[76px] flex-shrink-0 text-[12px] font-medium ${STATUS_TONE[file.status]}`}\n >\n {file.status}\n </span>\n <span className=\"min-w-0 flex-1 truncate break-all font-mono text-[12.5px] text-foreground\" title={file.path}>\n {file.path}\n </span>\n {typeof file.added === 'number' && (\n <span className=\"flex-shrink-0 font-mono text-[12px] text-accent\">+{file.added}</span>\n )}\n {typeof file.removed === 'number' && (\n <span className=\"flex-shrink-0 font-mono text-[12px] text-destructive\">-{file.removed}</span>\n )}\n </button>\n );\n}\n\nfunction ChangesSection({\n status,\n statusState,\n onRefresh,\n openPath,\n onSelectFile,\n diff,\n diffState,\n diffPath,\n}: {\n status: TProjectStatusRead | null;\n statusState: IWsSessionState['projectStatusState'];\n onRefresh: () => void;\n openPath: string | null;\n onSelectFile: (path: string) => void;\n diff: TProjectDiffRead | null;\n diffState: IWsSessionState['projectDiffState'];\n diffPath: string | null;\n}): React.ReactElement {\n return (\n <div className=\"flex flex-col gap-1.5 rounded-xl bg-card px-2 py-2\">\n <div className=\"flex items-center gap-2 px-1\">\n <p className=\"flex-1 text-[12px] font-medium text-muted-foreground\">\n {status?.kind === 'status' && status.branch ? `Changes — ${status.branch}` : 'Changes'}\n </p>\n <button\n type=\"button\"\n onClick={onRefresh}\n disabled={statusState === 'loading'}\n aria-label=\"Refresh\"\n className=\"flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[12px] text-subtle hover:bg-hover hover:text-foreground disabled:opacity-60\"\n >\n <RefreshCw size={12} className={statusState === 'loading' ? 'animate-spin' : ''} />\n Refresh\n </button>\n </div>\n\n {statusState === 'loading' && status === null && (\n <p className=\"px-1 py-2 text-[13px] text-muted-foreground\">Reading git status…</p>\n )}\n {statusState === 'error' && (\n <p className=\"px-1 py-2 text-[13px] text-destructive\">Could not read git status.</p>\n )}\n {status?.kind === 'not-a-repository' && (\n <p className=\"px-1 py-2 text-[13px] text-muted-foreground\">This folder is not a git repository.</p>\n )}\n {status?.kind === 'failed' && <p className=\"px-1 py-2 text-[13px] text-destructive\">{status.message}</p>}\n {status?.kind === 'status' && status.files.length === 0 && (\n <p className=\"px-1 py-2 text-[13px] text-muted-foreground\">No changes.</p>\n )}\n {status?.kind === 'status' &&\n status.files.map((file) => (\n <div key={file.path} className=\"flex flex-col\">\n <StatusRow file={file} open={openPath === file.path} onOpen={() => onSelectFile(file.path)} />\n {openPath === file.path && (\n <div className=\"pl-6\">\n {diffState === 'loading' && diffPath === file.path && (\n <p className=\"py-1.5 text-[12.5px] text-muted-foreground\">Reading diff…</p>\n )}\n {diff && diffPath === file.path && diff.kind === 'diff' && diff.diffLines.length === 0 && (\n <p className=\"py-1.5 text-[12.5px] text-muted-foreground\">No differences.</p>\n )}\n {diff && diffPath === file.path && diff.kind === 'diff' && diff.diffLines.length > 0 && (\n <>\n <DiffLines diffLines={diff.diffLines} />\n {diff.truncated && (\n <p className=\"py-1 text-[12px] text-subtle\">The diff was too long and was cut short.</p>\n )}\n </>\n )}\n {diff && diffPath === file.path && diff.kind === 'failed' && (\n <p className=\"py-1.5 text-[12.5px] text-destructive\">{diff.message}</p>\n )}\n {diff && diffPath === file.path && diff.kind === 'outside-workspace' && (\n <p className=\"py-1.5 text-[12.5px] text-destructive\">\n That path is outside the workspace.\n </p>\n )}\n {diff && diffPath === file.path && diff.kind === 'not-a-repository' && (\n <p className=\"py-1.5 text-[12.5px] text-muted-foreground\">\n This folder is not a git repository.\n </p>\n )}\n </div>\n )}\n </div>\n ))}\n {status?.kind === 'status' && status.truncated && (\n <p className=\"px-1 pt-1 text-[12px] text-subtle\">Showing the first files only — there are more.</p>\n )}\n </div>\n );\n}\n\nfunction MemorySection({\n memory,\n memoryState,\n onOpenInEditor,\n}: {\n memory: TProjectMemoryRead | null;\n memoryState: IWsSessionState['projectMemoryState'];\n onOpenInEditor?: (path: string) => void;\n}): React.ReactElement {\n return (\n <div className=\"flex flex-col gap-1.5 rounded-xl bg-card px-2 py-2\">\n <div className=\"flex items-center gap-2 px-1\">\n <p className=\"flex-1 text-[12px] font-medium text-muted-foreground\">Memory</p>\n {memory?.kind === 'memory' && onOpenInEditor && (\n <button\n type=\"button\"\n onClick={() => onOpenInEditor(memory.path)}\n className=\"flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[12px] text-subtle hover:bg-hover hover:text-foreground\"\n >\n <ExternalLink size={12} />\n Open in editor\n </button>\n )}\n </div>\n {memoryState === 'loading' && memory === null && (\n <p className=\"px-1 py-1 text-[13px] text-muted-foreground\">Reading project memory…</p>\n )}\n {memory?.kind === 'unavailable' && (\n <p className=\"px-1 py-1 text-[13px] text-muted-foreground\">{memory.message}</p>\n )}\n {memory?.kind === 'memory' && (\n <>\n <pre className=\"max-h-64 overflow-y-auto whitespace-pre-wrap break-words rounded-lg bg-sidebar px-3 py-2 font-mono text-[12.5px] leading-relaxed text-foreground\">\n {memory.content.length > 0 ? memory.content : '(empty)'}\n </pre>\n {memory.truncated && (\n <p className=\"px-1 pt-1 text-[12px] text-subtle\">The memory was too long and was cut short.</p>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport function ProjectPanel({\n state,\n className,\n onOpenMemoryInEditor,\n}: {\n state: IWsSessionState;\n className?: string;\n /** Present only when the host can open a file in an external editor (the desktop app). */\n onOpenMemoryInEditor?: (path: string) => void;\n}): React.ReactElement {\n useEffect(() => {\n state.requestProjectStatus();\n state.requestProjectMemory();\n }, [state.requestProjectStatus, state.requestProjectMemory]);\n\n // Which row is expanded — local, and independent from `projectDiffPath` (what the LAST diff\n // request/response concerns): closing a row never needs a request, and reopening a different one\n // must not show the previous file's diff while its own is still loading.\n const [openPath, setOpenPath] = useState<string | null>(null);\n\n return (\n <div className={`agent-ui flex flex-col overflow-hidden ${className ?? ''}`}>\n <div className=\"flex h-12 flex-shrink-0 items-center gap-2 px-4\">\n <GitBranch size={15} className=\"text-subtle\" />\n <span className=\"text-[14px] font-medium text-foreground\">Project</span>\n {state.projectStatusState === 'loading' && (\n <LoaderCircle size={13} className=\"ml-auto animate-spin text-subtle\" aria-label=\"loading\" />\n )}\n </div>\n <div className=\"flex-1 space-y-2 overflow-y-auto px-3 pb-3\">\n <ChangesSection\n status={state.projectStatus}\n statusState={state.projectStatusState}\n onRefresh={() => state.requestProjectStatus()}\n openPath={openPath}\n onSelectFile={(path) => {\n if (openPath === path) {\n setOpenPath(null);\n return;\n }\n setOpenPath(path);\n state.requestProjectDiff(path);\n }}\n diff={state.projectDiffPath === openPath ? state.projectDiff : null}\n diffState={state.projectDiffPath === openPath ? state.projectDiffState : 'loading'}\n diffPath={state.projectDiffPath}\n />\n <MemorySection\n memory={state.projectMemory}\n memoryState={state.projectMemoryState}\n onOpenInEditor={onOpenMemoryInEditor}\n />\n </div>\n </div>\n );\n}\n","import { useProductIdentity } from '../product-identity.js';\nimport { X } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { ProductWordmark } from './Brand.js';\n\nimport type { ISessionNotice } from '../hooks/session-client-types.js';\nimport type { IWsSessionState } from '../hooks/useSessionClient.js';\n\nconst STATUS: Record<string, { dot: string; label: string }> = {\n connected: { dot: 'bg-accent status-glow', label: 'Connected' },\n connecting: { dot: 'bg-warning animate-pulse', label: 'Connecting…' },\n disconnected: { dot: 'bg-subtle', label: 'Disconnected' },\n error: { dot: 'bg-destructive', label: 'Connection error' },\n};\n\n/** #3282 §4c: every view the primary nav can show — 'project' is always offered (desktop and\n * browser alike); 'usage' only where the host opted the surface into it. */\nexport type TSessionView = 'chat' | 'usage' | 'project';\n\nconst VIEW_LABEL: Readonly<Record<TSessionView, string>> = {\n chat: 'Chat',\n usage: 'Usage',\n project: 'Project',\n};\n\n/**\n * #3289 §1 — the folder this session works in, wherever `document.title` is settable: a page (the\n * browser tab) and the desktop window that loads it (Electron follows the page's title unless told\n * otherwise, and nothing in `apps/agent-app/electron/main.ts` overrides it — see its own SPEC note).\n * Absent while the host has not said, so a not-yet-connected surface keeps whatever title it loaded\n * with rather than announcing \"undefined — the agent\".\n */\nfunction useWorkspaceDocumentTitle(workspace: { readonly name: string } | undefined): void {\n const { identity } = useProductIdentity();\n useEffect(() => {\n if (typeof document === 'undefined' || workspace === undefined) return;\n document.title = `${workspace.name} — ${identity.displayName}`;\n }, [workspace, identity.displayName]);\n}\n\n/**\n * The bar over the conversation: the app's name when no sidebar carries it, the workspace folder and\n * the current session's title, the Chat / Usage / Project switch, and the connection state. The\n * state is a dot while all is well and says itself in words once it is not.\n */\nexport function SessionTitleBar({\n status,\n surface,\n title,\n workspace,\n showBrand,\n view,\n onView,\n personalUsageEnabled,\n connectionLost = false,\n}: {\n status: string;\n surface?: string;\n title?: string | null;\n /** The folder this session works in (#3289 §1); absent when the host has not said. */\n workspace?: { readonly name: string; readonly path: string };\n showBrand: boolean;\n view: TSessionView;\n onView: (view: TSessionView) => void;\n personalUsageEnabled: boolean;\n /**\n * #3282 §2 (part 2): mirrors `ConnectionBanner`'s own \"give-up\" flag (`useWsSession`'s\n * `connectionLost`) so this corner indicator can tell exactly when that banner is on screen, to\n * never say the same thing twice.\n */\n connectionLost?: boolean;\n}): React.ReactElement {\n const state = STATUS[status] ?? STATUS.disconnected;\n useWorkspaceDocumentTitle(workspace);\n // #3282 §2 (part 2): the same \"has this surface ever connected\" rule `ConnectionBanner` uses to\n // decide whether it has anything to show — kept in step here so the corner text disappears exactly\n // when, and only when, the banner takes over saying it (never both, never neither).\n const everConnectedRef = useRef(false);\n useEffect(() => {\n if (status === 'connected') everConnectedRef.current = true;\n }, [status]);\n const bannerShown = status !== 'connected' && (connectionLost || everConnectedRef.current);\n // #3282 §4c principle 4 (#3277): everything the agent implements is usable from the GUI through\n // organized screens — the Project panel (git status/diff, project memory) is one, offered next to\n // Chat on every surface, desktop and browser alike; Usage stays surface-gated.\n const items: readonly TSessionView[] = personalUsageEnabled\n ? ['chat', 'usage', 'project']\n : ['chat', 'project'];\n return (\n <header\n className=\"agent-gui-status flex h-12 flex-shrink-0 items-center gap-3 px-5\"\n data-status={status}\n >\n {showBrand ? <ProductWordmark surface={surface} /> : null}\n {/* The usage/project views title themselves; the chat view is titled by its session and workspace. */}\n {view === 'chat' && title ? (\n <h1 className=\"min-w-0 truncate text-[14px] font-medium text-foreground/90\">{title}</h1>\n ) : null}\n {view === 'chat' && workspace ? (\n <span\n title={workspace.path}\n className=\"min-w-0 flex-shrink truncate text-[13px] text-muted-foreground\"\n >\n {workspace.name}\n </span>\n ) : null}\n <div className=\"ml-auto flex items-center gap-3\">\n <nav className=\"flex items-center rounded-lg bg-raised p-0.5\" aria-label=\"Primary\">\n {items.map((item) => (\n <button\n key={item}\n type=\"button\"\n aria-pressed={view === item}\n onClick={() => onView(item)}\n className={`rounded-md px-3 py-1 text-[13px] font-medium transition-colors ${\n view === item\n ? 'bg-card text-foreground shadow-sm shadow-black/25'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {VIEW_LABEL[item]}\n </button>\n ))}\n </nav>\n {bannerShown ? null : (\n <span className=\"flex items-center gap-2\" title={state.label}>\n <span className={`h-2 w-2 rounded-full ${state.dot}`} />\n {status === 'connected' ? (\n <span className=\"sr-only\">{status}</span>\n ) : (\n <span className=\"text-[13px] text-muted-foreground\">{state.label}</span>\n )}\n </span>\n )}\n </div>\n </header>\n );\n}\n\n/** The column every part of the conversation shares (matches `SessionSurface`'s `COLUMN`). */\nconst BANNER_COLUMN = 'mx-auto w-full max-w-[760px] px-6';\n\n/**\n * A connection that drops after a working session (issue #3280 §5): a banner directly above the\n * conversation — never a full-screen replacement — so the conversation and its history stay visible\n * and scrollable throughout. Shown as soon as the drop happens; says nothing on the very first\n * connect, because nothing has been lost yet.\n */\nexport function ConnectionBanner({\n status,\n connectionLost,\n onReconnect,\n}: {\n status: string;\n /** Retries ran out: the runtime is not coming back by itself (`useWsSession`'s `connectionLost`). */\n connectionLost: boolean;\n /** Present only when the host can restart the runtime (desktop); absent in the browser. */\n onReconnect?: () => Promise<void>;\n}): React.ReactElement | null {\n const everConnectedRef = useRef(false);\n useEffect(() => {\n if (status === 'connected') everConnectedRef.current = true;\n }, [status]);\n const { identity } = useProductIdentity();\n const [reconnecting, setReconnecting] = useState(false);\n const [failed, setFailed] = useState<string | null>(null);\n\n if (status === 'connected') return null;\n if (!connectionLost && !everConnectedRef.current) return null;\n\n if (!connectionLost) {\n return (\n <div role=\"status\" className={`${BANNER_COLUMN} flex-shrink-0 pt-3`}>\n <div className=\"flex items-center gap-2.5 rounded-xl bg-warning/15 px-4 py-2.5 text-[13.5px] text-foreground\">\n <span className=\"h-2 w-2 flex-shrink-0 animate-pulse rounded-full bg-warning\" aria-hidden=\"true\" />\n Connection lost. Reconnecting…\n </div>\n </div>\n );\n }\n\n const reconnect = (): void => {\n if (!onReconnect) return;\n setReconnecting(true);\n setFailed(null);\n onReconnect().catch((error: unknown) => {\n setReconnecting(false);\n setFailed(error instanceof Error ? error.message : String(error));\n });\n };\n\n return (\n <div role=\"alert\" className={`${BANNER_COLUMN} flex-shrink-0 pt-3`}>\n <div className=\"flex flex-col gap-2 rounded-xl bg-destructive/15 px-4 py-3 text-[13.5px] text-foreground\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"h-2 w-2 flex-shrink-0 rounded-full bg-destructive\" aria-hidden=\"true\" />\n <span className=\"min-w-0 flex-1\">\n {identity.displayName} stopped.\n {!onReconnect && (\n <>\n {' '}\n Run <code className=\"font-mono\">{identity.cliName} --serve --open</code> again to reopen it.\n </>\n )}\n </span>\n {onReconnect ? (\n <button\n type=\"button\"\n onClick={reconnect}\n disabled={reconnecting}\n className=\"flex-shrink-0 rounded-lg bg-primary px-3 py-1.5 text-[13px] font-medium text-primary-foreground hover:opacity-90 disabled:opacity-60\"\n >\n {reconnecting ? 'Reconnecting…' : 'Reconnect'}\n </button>\n ) : null}\n </div>\n {failed ? <p className=\"text-destructive\">{failed}</p> : null}\n </div>\n </div>\n );\n}\n\n/** The built-in providers whose id does not just-capitalize into their real name (`openai` → `OpenAI`). */\nconst PROVIDER_DISPLAY_NAMES: Readonly<Record<string, string>> = {\n openai: 'OpenAI',\n deepseek: 'DeepSeek',\n};\n\n/** `anthropic` → `Anthropic`; a name with its own casing (`openai` → `OpenAI`) uses that instead. */\nfunction displayProvider(provider: string | undefined): string {\n if (provider === undefined || provider.length === 0) return 'The provider';\n return PROVIDER_DISPLAY_NAMES[provider] ?? provider[0].toUpperCase() + provider.slice(1);\n}\n\n/**\n * A provider/session error classified at the wire boundary (#3289 §3) as one plain sentence naming\n * what happened and, where there is one, the next step — `null` for a notice this classification does\n * not cover, which keeps showing its own `message` instead.\n *\n * `model_unavailable` names `notice.model` — captured on the notice when it was created — and never\n * the session's current model: a still-open notice must keep blaming the model that actually failed\n * even after the person switches to a different one (the very fix the notice suggests).\n */\nfunction noticeSentence(notice: ISessionNotice): string | null {\n const provider = displayProvider(notice.provider);\n switch (notice.code) {\n case 'auth':\n return `${provider} rejected the API key. Check the key for this provider.`;\n case 'rate_limit': {\n const wait =\n notice.retryAfterSeconds !== undefined\n ? `${notice.retryAfterSeconds} second${notice.retryAfterSeconds === 1 ? '' : 's'}`\n : 'a moment';\n return `${provider} is limiting requests. Try again in ${wait}.`;\n }\n case 'model_unavailable':\n return notice.model\n ? `The model \"${notice.model}\" isn't available with this key.`\n : \"The model isn't available with this key.\";\n case 'network':\n return `Can't reach ${provider}. Check your connection.`;\n case 'provider':\n return `${provider} returned an error.`;\n default:\n return null;\n }\n}\n\n/**\n * Session and protocol failures, as dismissible toasts over the top-right corner: they stay until\n * dismissed, and never push the conversation or the composer out of view. Command output is not\n * here — it belongs to the conversation. A provider error classified at the wire boundary shows one\n * plain sentence with the next step; its raw text sits behind \"Details\" rather than as the headline.\n */\nexport function SessionNotices({ state }: { state: IWsSessionState }): React.ReactElement | null {\n const notices = state.sessionNotices ?? [];\n if (notices.length === 0) return null;\n return (\n <div className=\"pointer-events-none absolute right-4 top-16 z-40 flex w-[min(400px,calc(100%-32px))] flex-col gap-2\">\n {notices.slice(-3).map((notice) => {\n const sentence = noticeSentence(notice);\n return (\n <div\n key={notice.id}\n role=\"alert\"\n className=\"gui-rise pointer-events-auto flex items-start gap-3 rounded-xl bg-popover px-4 py-3 text-[14px] leading-snug text-popover-foreground shadow-xl shadow-black/30\"\n >\n <span className=\"mt-[7px] h-2 w-2 flex-shrink-0 rounded-full bg-destructive\" />\n <span className=\"max-h-40 flex-1 overflow-y-auto whitespace-pre-wrap break-words\">\n {sentence ?? notice.message}\n {sentence && (\n <details className=\"mt-1.5\">\n <summary className=\"cursor-pointer text-[12.5px] text-muted-foreground hover:text-foreground\">\n Details\n </summary>\n <p className=\"mt-1 whitespace-pre-wrap break-words text-[12.5px] text-muted-foreground\">\n {notice.message}\n </p>\n </details>\n )}\n </span>\n <button\n type=\"button\"\n aria-label=\"Dismiss notice\"\n onClick={() => state.dismissSessionNotice?.(notice.id)}\n className=\"-mr-1 rounded-md p-1 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <X size={15} />\n </button>\n </div>\n );\n })}\n </div>\n );\n}\n","'use client';\n\nimport React, { useEffect, useId, useState } from 'react';\n\nimport type { ISettingsChoice } from '@robota-sdk/agent-interface-session';\n\nconst SELECT_CLASS =\n 'min-w-[160px] flex-shrink-0 rounded-lg border border-border bg-raised px-2.5 py-1.5 text-[13.5px] text-foreground';\n\n/**\n * One row of the settings form: a plain label, a pop-up menu of `{id, label, description}` choices\n * (never a bare id) and, under it, the description of whichever choice is currently selected — HIG's\n * \"a short plain description sits under each control.\" Selecting a choice applies AT ONCE.\n */\nexport function SettingsPopupField({\n label,\n note,\n value,\n options,\n onChange,\n disabled = false,\n}: {\n label: string;\n /** A fixed note shown under the control instead of the selected choice's own description. */\n note?: string;\n value: string;\n options: readonly ISettingsChoice[];\n onChange: (value: string) => void;\n disabled?: boolean;\n}): React.ReactElement {\n const id = useId();\n const selected = options.find((option) => option.id === value);\n const description = note ?? selected?.description;\n return (\n <div className=\"flex flex-col gap-1.5 border-b border-border/60 py-3.5 last:border-b-0\">\n <div className=\"flex items-center justify-between gap-4\">\n <label htmlFor={id} className=\"text-[14px] font-medium text-foreground\">\n {label}\n </label>\n <select\n id={id}\n value={options.some((option) => option.id === value) ? value : ''}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value)}\n className={SELECT_CLASS}\n >\n {!options.some((option) => option.id === value) ? (\n <option value=\"\" disabled>\n {value || 'Unset'}\n </option>\n ) : null}\n {options.map((option) => (\n <option key={option.id} value={option.id}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n {description ? (\n <p className=\"text-[12.5px] leading-snug text-muted-foreground\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nconst OTHER = '__other__';\n\n/**\n * The language control: a pop-up of the runtime's recommended languages, plus \"Other…\" — the\n * language is free text (any string the runtime accepts), so the recommended list is a shortcut,\n * never a validated enum. Applies on Enter or on leaving the free-text field.\n */\nexport function SettingsLanguageField({\n label,\n current,\n recommended,\n note,\n onChange,\n disabled = false,\n}: {\n label: string;\n current: string;\n recommended: readonly ISettingsChoice[];\n note: string;\n onChange: (language: string) => void;\n disabled?: boolean;\n}): React.ReactElement {\n const id = useId();\n const isRecommended = recommended.some((choice) => choice.id === current);\n const [showOther, setShowOther] = useState(!isRecommended && current.length > 0);\n const [draft, setDraft] = useState(isRecommended ? '' : current);\n\n // A fresh snapshot (e.g. after a switch away and back) re-derives whether \"Other…\" is showing.\n useEffect(() => {\n const stillRecommended = recommended.some((choice) => choice.id === current);\n setShowOther(!stillRecommended && current.length > 0);\n setDraft(stillRecommended ? '' : current);\n }, [current, recommended]);\n\n function commitDraft(): void {\n const trimmed = draft.trim();\n if (trimmed.length > 0 && trimmed !== current) onChange(trimmed);\n }\n\n return (\n <div className=\"flex flex-col gap-1.5 border-b border-border/60 py-3.5 last:border-b-0\">\n <div className=\"flex items-center justify-between gap-4\">\n <label htmlFor={id} className=\"text-[14px] font-medium text-foreground\">\n {label}\n </label>\n <select\n id={id}\n disabled={disabled}\n value={showOther ? OTHER : isRecommended ? current : ''}\n onChange={(event) => {\n if (event.target.value === OTHER) {\n setShowOther(true);\n return;\n }\n setShowOther(false);\n onChange(event.target.value);\n }}\n className={SELECT_CLASS}\n >\n {!isRecommended && !showOther ? (\n <option value=\"\" disabled>\n {current || 'Unset'}\n </option>\n ) : null}\n {recommended.map((choice) => (\n <option key={choice.id} value={choice.id}>\n {choice.label}\n </option>\n ))}\n <option value={OTHER}>Other…</option>\n </select>\n </div>\n {showOther ? (\n <input\n type=\"text\"\n aria-label={`${label} — other`}\n placeholder=\"Language code, e.g. fr\"\n value={draft}\n disabled={disabled}\n onChange={(event) => setDraft(event.target.value)}\n onBlur={commitDraft}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n commitDraft();\n }\n }}\n className=\"rounded-lg border border-border bg-raised px-2.5 py-1.5 text-[13.5px] text-foreground\"\n />\n ) : null}\n <p className=\"text-[12.5px] leading-snug text-muted-foreground\">{note}</p>\n </div>\n );\n}\n","import React from 'react';\n\nimport { SettingsLanguageField, SettingsPopupField } from './SettingsFields.js';\n\nimport type { ISettingsSnapshot, TSettingsPatch } from '@robota-sdk/agent-interface-session';\n\n/**\n * Language, output style and preset — reusing the exact choices and descriptions the runtime\n * already builds for `/language`, `/output-style` and `/preset` (#3282 §4a). Never internal ids:\n * every pop-up shows the runtime's human labels.\n */\nexport function SettingsGeneralSection({\n snapshot,\n onUpdate,\n onRequestPresetChange,\n}: {\n snapshot: ISettingsSnapshot;\n onUpdate: (patch: TSettingsPatch) => void;\n /** Routed through the parent so it can confirm a preset that would skip every check. */\n onRequestPresetChange: (presetId: string) => void;\n}): React.ReactElement {\n return (\n <section aria-labelledby=\"settings-general-heading\">\n <h3 id=\"settings-general-heading\" className=\"sr-only\">\n General\n </h3>\n <SettingsLanguageField\n label=\"Language\"\n current={snapshot.language.current}\n recommended={snapshot.language.recommended}\n note={snapshot.language.appliesNote}\n onChange={(language) => onUpdate({ field: 'language', language })}\n />\n <SettingsPopupField\n label=\"Output style\"\n value={snapshot.outputStyle.current}\n options={snapshot.outputStyle.choices}\n onChange={(styleId) => onUpdate({ field: 'outputStyle', styleId })}\n />\n <SettingsPopupField\n label=\"Preset\"\n note=\"Applies to this session — it is not saved for the next one.\"\n value={snapshot.preset.current}\n options={snapshot.preset.choices}\n onChange={onRequestPresetChange}\n />\n </section>\n );\n}\n","'use client';\n\nimport { ChevronDown, ChevronRight, RefreshCw } from 'lucide-react';\nimport React, { useId, useState } from 'react';\n\nimport type { ISettingsMcpServer, ISettingsSnapshot } from '@robota-sdk/agent-interface-session';\n\nconst STATUS_LABEL: Readonly<Record<ISettingsMcpServer['status'], string>> = {\n connected: 'Connected',\n failed: 'Failed',\n disabled: 'Disabled',\n};\n\nconst STATUS_CLASS: Readonly<Record<ISettingsMcpServer['status'], string>> = {\n connected: 'text-success',\n failed: 'text-destructive',\n disabled: 'text-muted-foreground',\n};\n\nfunction ServerRow({\n server,\n expanded,\n onToggleExpanded,\n onToggleEnabled,\n}: {\n server: ISettingsMcpServer;\n expanded: boolean;\n onToggleExpanded: () => void;\n onToggleEnabled: (enabled: boolean) => void;\n}): React.ReactElement {\n const labelId = useId();\n const canExpand = server.toolNames.length > 0;\n return (\n <li className=\"border-b border-border/60 py-3 last:border-b-0\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1\">\n <button\n type=\"button\"\n onClick={canExpand ? onToggleExpanded : undefined}\n disabled={!canExpand}\n aria-expanded={canExpand ? expanded : undefined}\n className=\"flex w-full items-center gap-1.5 text-left disabled:cursor-default\"\n >\n {canExpand ? (\n expanded ? (\n <ChevronDown size={14} strokeWidth={1.75} className=\"flex-shrink-0 text-muted-foreground\" />\n ) : (\n <ChevronRight size={14} strokeWidth={1.75} className=\"flex-shrink-0 text-muted-foreground\" />\n )\n ) : (\n <span className=\"w-[14px] flex-shrink-0\" />\n )}\n <span id={labelId} className=\"truncate text-[14px] font-medium text-foreground\">\n {server.name}\n </span>\n </button>\n <p className=\"mt-0.5 text-[12.5px] text-muted-foreground\">\n {server.scopeLabel} · <span className={STATUS_CLASS[server.status]}>{STATUS_LABEL[server.status]}</span>\n {server.status === 'failed' && server.statusReason ? `: ${server.statusReason}` : ''}\n {' · '}\n {server.toolNames.length === 1 ? '1 tool' : `${server.toolNames.length} tools`}\n </p>\n {expanded && canExpand ? (\n <ul className=\"mt-2 flex flex-col gap-0.5 pl-[20px]\">\n {server.toolNames.map((toolName) => (\n <li key={toolName} className=\"truncate font-mono text-[12px] text-muted-foreground\">\n {toolName}\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={server.enabled}\n aria-labelledby={labelId}\n onClick={() => onToggleEnabled(!server.enabled)}\n className={`relative h-6 w-11 flex-shrink-0 rounded-full transition-colors ${\n server.enabled ? 'bg-primary' : 'bg-raised'\n }`}\n >\n <span\n className={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform ${\n server.enabled ? 'translate-x-[22px]' : 'translate-x-0.5'\n }`}\n />\n </button>\n </div>\n </li>\n );\n}\n\n/**\n * The MCP Servers section (#3282 §4 part b-2): every configured server's name, where it is\n * configured, its live connection status and tool count, an enable/disable switch (approve/reject),\n * an expandable list of its tool names, and a section-level Reload. Adding or editing a server is\n * left out — the runtime has no command path that writes MCP config (recorded in\n * `packages/agent-gui-web/docs/SPEC.md`).\n */\nexport function SettingsMcpSection({\n snapshot,\n onToggleServer,\n onReload,\n reloading,\n}: {\n snapshot: ISettingsSnapshot;\n onToggleServer: (serverId: string, enabled: boolean) => void;\n onReload: () => void;\n reloading: boolean;\n}): React.ReactElement {\n const [expandedIds, setExpandedIds] = useState<ReadonlySet<string>>(new Set());\n const { servers } = snapshot.mcp;\n\n function toggleExpanded(id: string): void {\n setExpandedIds((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }\n\n return (\n <section aria-labelledby=\"settings-mcp-heading\" className=\"flex flex-col gap-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 id=\"settings-mcp-heading\" className=\"text-[14px] font-medium text-foreground\">\n MCP Servers\n </h3>\n <button\n type=\"button\"\n onClick={onReload}\n disabled={reloading}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-raised px-2.5 py-1.5 text-[13px] text-foreground hover:bg-hover disabled:opacity-60\"\n >\n <RefreshCw size={13} strokeWidth={1.75} className={reloading ? 'animate-spin' : ''} />\n Reload servers\n </button>\n </div>\n {servers.length === 0 ? (\n <p className=\"text-[13px] leading-snug text-muted-foreground\">No MCP servers are configured.</p>\n ) : (\n <ul>\n {servers.map((server) => (\n <ServerRow\n key={server.id}\n server={server}\n expanded={expandedIds.has(server.id)}\n onToggleExpanded={() => toggleExpanded(server.id)}\n onToggleEnabled={(enabled) => onToggleServer(server.id, enabled)}\n />\n ))}\n </ul>\n )}\n </section>\n );\n}\n","import { Trash2 } from 'lucide-react';\nimport React, { useId } from 'react';\n\nimport { SettingsPopupField } from './SettingsFields.js';\n\nimport type { ISettingsPermissionRule, ISettingsSnapshot } from '@robota-sdk/agent-interface-session';\n\nconst KIND_LABEL: Readonly<Record<ISettingsPermissionRule['kind'], string>> = {\n allow: 'Allow',\n deny: 'Deny',\n ask: 'Ask',\n};\n\nconst KIND_VERB: Readonly<Record<ISettingsPermissionRule['kind'], string>> = {\n allow: 'allowed',\n deny: 'denied',\n ask: 'asked about',\n};\n\nfunction PermissionRuleRow({\n rule,\n onRequestRemove,\n}: {\n rule: ISettingsPermissionRule;\n onRequestRemove: (rule: ISettingsPermissionRule) => void;\n}): React.ReactElement {\n return (\n <li className=\"flex items-center justify-between gap-3 rounded-lg px-2.5 py-2 hover:bg-hover\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-[13.5px] text-foreground\">\n <span className=\"font-medium\">{KIND_LABEL[rule.kind]}</span>{' '}\n <span className=\"font-mono text-[12.5px]\">{rule.pattern}</span>\n </p>\n <p className=\"truncate text-[12px] text-muted-foreground\">{rule.source}</p>\n </div>\n {rule.removable ? (\n <button\n type=\"button\"\n aria-label={`Remove rule: ${KIND_LABEL[rule.kind]} ${rule.pattern}`}\n onClick={() => onRequestRemove(rule)}\n className=\"flex-shrink-0 rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-destructive\"\n >\n <Trash2 size={15} strokeWidth={1.75} />\n </button>\n ) : null}\n </li>\n );\n}\n\n/**\n * Default permission mode, the rules `/permissions` shows, and the sandbox switch (#3282 §4a).\n * \"Skip all checks\" as the mode and a preset that would set it both go through\n * `onRequestModeChange`'s parent-owned confirmation before anything is sent.\n */\nexport function SettingsPermissionsSection({\n snapshot,\n onRequestModeChange,\n onToggleSandbox,\n onRequestRemoveRule,\n}: {\n snapshot: ISettingsSnapshot;\n onRequestModeChange: (mode: string) => void;\n onToggleSandbox: (enabled: boolean) => void;\n onRequestRemoveRule: (rule: ISettingsPermissionRule) => void;\n}): React.ReactElement {\n const sandboxLabelId = useId();\n const { sandbox } = snapshot;\n\n return (\n <section aria-labelledby=\"settings-permissions-heading\" className=\"flex flex-col gap-5\">\n <div>\n <h3 id=\"settings-permissions-heading\" className=\"sr-only\">\n Permissions\n </h3>\n <SettingsPopupField\n label=\"Default permission mode\"\n value={snapshot.permissionMode.current}\n options={snapshot.permissionMode.choices}\n onChange={onRequestModeChange}\n />\n </div>\n\n <div>\n <h4 className=\"mb-1.5 text-[13px] font-medium text-foreground\">Rules</h4>\n {snapshot.permissionRules.length === 0 ? (\n <p className=\"text-[13px] leading-snug text-muted-foreground\">\n No permission rules are configured — the default mode above governs every action.\n </p>\n ) : (\n <ul className=\"space-y-0.5\">\n {snapshot.permissionRules.map((rule) => (\n <PermissionRuleRow key={rule.id} rule={rule} onRequestRemove={onRequestRemoveRule} />\n ))}\n </ul>\n )}\n </div>\n\n <div className=\"flex items-start justify-between gap-4 border-t border-border/60 pt-4\">\n <div className=\"min-w-0\">\n <span id={sandboxLabelId} className=\"text-[14px] font-medium text-foreground\">\n Sandbox\n </span>\n <p className=\"mt-0.5 text-[12.5px] leading-snug text-muted-foreground\">\n {sandbox.description}\n </p>\n {!sandbox.available && sandbox.unavailableReason ? (\n <p className=\"mt-0.5 text-[12.5px] text-destructive\">\n Not available on this machine: {sandbox.unavailableReason}\n </p>\n ) : null}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={sandbox.enabled}\n aria-labelledby={sandboxLabelId}\n disabled={!sandbox.available}\n onClick={() => onToggleSandbox(!sandbox.enabled)}\n className={`relative h-6 w-11 flex-shrink-0 rounded-full transition-colors ${\n sandbox.enabled ? 'bg-primary' : 'bg-raised'\n } ${!sandbox.available ? 'cursor-not-allowed opacity-50' : ''}`}\n >\n <span\n className={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform ${\n sandbox.enabled ? 'translate-x-[22px]' : 'translate-x-0.5'\n }`}\n />\n </button>\n </div>\n </section>\n );\n}\n\n/** Reused by the removal confirmation copy in `SettingsScreen`. */\nexport function describePermissionRuleRemoval(rule: ISettingsPermissionRule): string {\n return `\"${rule.pattern}\" will no longer be ${KIND_VERB[rule.kind]} automatically.`;\n}\n","'use client';\n\nimport { RefreshCw, Trash2 } from 'lucide-react';\nimport React, { useId, useState } from 'react';\n\nimport type { ISettingsPlugin, ISettingsSnapshot } from '@robota-sdk/agent-interface-session';\n\nfunction PluginRow({\n plugin,\n canInstall,\n onToggleEnabled,\n onRequestUninstall,\n}: {\n plugin: ISettingsPlugin;\n canInstall: boolean;\n onToggleEnabled: (enabled: boolean) => void;\n onRequestUninstall: () => void;\n}): React.ReactElement {\n const labelId = useId();\n return (\n <li className=\"flex items-start justify-between gap-3 border-b border-border/60 py-3 last:border-b-0\">\n <div className=\"min-w-0 flex-1\">\n <span id={labelId} className=\"truncate text-[14px] font-medium text-foreground\">\n {plugin.name}\n </span>\n {plugin.description ? (\n <p className=\"mt-0.5 text-[12.5px] leading-snug text-muted-foreground\">{plugin.description}</p>\n ) : null}\n </div>\n <div className=\"flex flex-shrink-0 items-center gap-2\">\n {canInstall ? (\n <button\n type=\"button\"\n aria-label={`Uninstall ${plugin.name}`}\n onClick={onRequestUninstall}\n className=\"rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-destructive\"\n >\n <Trash2 size={15} strokeWidth={1.75} />\n </button>\n ) : null}\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={plugin.enabled}\n aria-labelledby={labelId}\n onClick={() => onToggleEnabled(!plugin.enabled)}\n className={`relative h-6 w-11 flex-shrink-0 rounded-full transition-colors ${\n plugin.enabled ? 'bg-primary' : 'bg-raised'\n }`}\n >\n <span\n className={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform ${\n plugin.enabled ? 'translate-x-[22px]' : 'translate-x-0.5'\n }`}\n />\n </button>\n </div>\n </li>\n );\n}\n\n/**\n * The Plugins section (#3282 §4 part b-2): every installed plugin's name, description and enabled\n * state, an enable/disable switch, a section-level Reload, and — local surfaces only — install and\n * uninstall, since installing runs third-party code. `canInstall` (from the snapshot) mirrors the\n * refusal `/plugin install|uninstall` itself gives a remote device or an observer.\n */\nexport function SettingsPluginsSection({\n snapshot,\n onTogglePlugin,\n onReload,\n reloading,\n onRequestInstall,\n onRequestUninstall,\n}: {\n snapshot: ISettingsSnapshot;\n onTogglePlugin: (pluginId: string, enabled: boolean) => void;\n onReload: () => void;\n reloading: boolean;\n onRequestInstall: (pluginId: string) => void;\n onRequestUninstall: (plugin: ISettingsPlugin) => void;\n}): React.ReactElement {\n const [installDraft, setInstallDraft] = useState('');\n const { plugins, canInstall } = snapshot.plugins;\n const installId = useId();\n\n function submitInstall(): void {\n const trimmed = installDraft.trim();\n if (trimmed.length === 0) return;\n onRequestInstall(trimmed);\n setInstallDraft('');\n }\n\n return (\n <section aria-labelledby=\"settings-plugins-heading\" className=\"flex flex-col gap-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 id=\"settings-plugins-heading\" className=\"text-[14px] font-medium text-foreground\">\n Plugins\n </h3>\n <button\n type=\"button\"\n onClick={onReload}\n disabled={reloading}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-raised px-2.5 py-1.5 text-[13px] text-foreground hover:bg-hover disabled:opacity-60\"\n >\n <RefreshCw size={13} strokeWidth={1.75} className={reloading ? 'animate-spin' : ''} />\n Reload plugins\n </button>\n </div>\n {plugins.length === 0 ? (\n <p className=\"text-[13px] leading-snug text-muted-foreground\">No plugins are installed.</p>\n ) : (\n <ul>\n {plugins.map((plugin) => (\n <PluginRow\n key={plugin.id}\n plugin={plugin}\n canInstall={canInstall}\n onToggleEnabled={(enabled) => onTogglePlugin(plugin.id, enabled)}\n onRequestUninstall={() => onRequestUninstall(plugin)}\n />\n ))}\n </ul>\n )}\n <div className=\"border-t border-border/60 pt-3\">\n {canInstall ? (\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor={installId} className=\"text-[13px] font-medium text-foreground\">\n Install a plugin\n </label>\n <div className=\"flex gap-2\">\n <input\n id={installId}\n type=\"text\"\n placeholder=\"name@marketplace\"\n value={installDraft}\n onChange={(event) => setInstallDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n submitInstall();\n }\n }}\n className=\"min-w-0 flex-1 rounded-lg border border-border bg-raised px-2.5 py-1.5 text-[13.5px] text-foreground\"\n />\n <button\n type=\"button\"\n onClick={submitInstall}\n disabled={installDraft.trim().length === 0}\n className=\"flex-shrink-0 rounded-lg bg-raised px-3 py-1.5 text-[13px] text-foreground hover:bg-hover disabled:opacity-60\"\n >\n Install\n </button>\n </div>\n </div>\n ) : (\n <p className=\"text-[12.5px] leading-snug text-muted-foreground\">\n Installing and uninstalling plugins runs code on this computer, so it only works from the\n desktop app or the page opened here — not from a remote device.\n </p>\n )}\n </div>\n </section>\n );\n}\n","'use client';\n\nimport { Check } from 'lucide-react';\nimport React, { useEffect, useRef, useState } from 'react';\n\nimport { PopupMenu } from './PopupMenu.js';\n\nimport type { ISettingsProviderProfile, ISettingsSnapshot } from '@robota-sdk/agent-interface-session';\nimport type { TModelListSnapshot } from '../hooks/session-client-types.js';\n\nconst BUTTON =\n 'rounded-lg px-2.5 py-1.5 text-[12.5px] font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50';\nconst PRIMARY_BUTTON = `${BUTTON} bg-primary text-primary-foreground hover:opacity-90`;\nconst SECONDARY_BUTTON = `${BUTTON} bg-raised text-foreground hover:bg-hover`;\nconst DESTRUCTIVE_BUTTON = `${BUTTON} text-destructive hover:bg-destructive/10`;\n\n/** The \"Model\" action: a `PopupMenu` of this ONE profile's catalog models (#3282 §4b). */\nfunction ProviderModelButton({\n profile,\n modelList,\n onSelect,\n}: {\n profile: ISettingsProviderProfile;\n modelList: TModelListSnapshot | null;\n onSelect: (profileName: string, modelId: string) => void;\n}): React.ReactElement {\n const [open, setOpen] = useState(false);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const models = modelList?.groups.find((group) => group.profileName === profile.name)?.models ?? [];\n return (\n <span className=\"relative inline-block\">\n <button\n ref={triggerRef}\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n disabled={models.length === 0}\n onClick={() => setOpen(true)}\n className={SECONDARY_BUTTON}\n >\n Model\n </button>\n {open ? (\n <PopupMenu\n label={`Models for ${profile.name}`}\n sections={[\n {\n items: models.map((model) => ({\n key: model.id,\n label: model.label,\n checked: profile.model !== undefined && model.id === profile.model.id,\n onSelect: () => onSelect(profile.name, model.id),\n })),\n },\n ]}\n triggerRef={triggerRef}\n onClose={() => setOpen(false)}\n />\n ) : null}\n </span>\n );\n}\n\nfunction ProviderProfileRow({\n profile,\n modelList,\n onUse,\n onModelChange,\n onCommand,\n onRequestDelete,\n}: {\n profile: ISettingsProviderProfile;\n modelList: TModelListSnapshot | null;\n onUse: (profileName: string) => void;\n onModelChange: (profileName: string, modelId: string) => void;\n onCommand: (args: string) => void;\n onRequestDelete: (profileName: string) => void;\n}): React.ReactElement {\n return (\n <li className=\"rounded-xl border border-border/60 p-3\">\n <div className=\"flex items-start gap-1.5\">\n {profile.current ? (\n <Check size={14} strokeWidth={2.25} className=\"mt-0.5 flex-shrink-0 text-primary\" aria-hidden=\"true\" />\n ) : (\n <span className=\"mt-0.5 w-3.5 flex-shrink-0\" aria-hidden=\"true\" />\n )}\n <div className=\"min-w-0\">\n <p className=\"truncate text-[14px] font-medium text-foreground\">\n {profile.name}\n {profile.current ? <span className=\"sr-only\"> (in use)</span> : null}\n </p>\n <p className=\"truncate text-[12.5px] text-muted-foreground\">\n {profile.providerLabel}\n {profile.model ? ` · ${profile.model.label}` : ' · No model configured'}\n </p>\n {profile.connectionState ? (\n <p className=\"text-[12.5px] text-destructive\">{profile.connectionState}</p>\n ) : null}\n </div>\n </div>\n <div className=\"mt-2.5 flex flex-wrap items-center gap-1.5\">\n <button\n type=\"button\"\n disabled={profile.current}\n onClick={() => onUse(profile.name)}\n className={PRIMARY_BUTTON}\n >\n Use\n </button>\n <ProviderModelButton profile={profile} modelList={modelList} onSelect={onModelChange} />\n <button\n type=\"button\"\n onClick={() => onCommand(`edit ${profile.name}`)}\n className={SECONDARY_BUTTON}\n >\n Edit…\n </button>\n <button\n type=\"button\"\n onClick={() => onCommand(`test ${profile.name}`)}\n className={SECONDARY_BUTTON}\n >\n Test connection\n </button>\n <button\n type=\"button\"\n onClick={() => onCommand(`duplicate ${profile.name}`)}\n className={SECONDARY_BUTTON}\n >\n Duplicate…\n </button>\n <button\n type=\"button\"\n onClick={() => onRequestDelete(profile.name)}\n className={`${DESTRUCTIVE_BUTTON} ml-auto`}\n >\n Delete…\n </button>\n </div>\n </li>\n );\n}\n\n/**\n * The configured provider profiles (#3282 §4b), each row a plain provider name, its model's label,\n * a checkmark on the one in use, and a plain connection state when one is known. Every action goes\n * through the same path the equivalent command does: Use/Model apply through `state.updateSettings`\n * (the same `field` patterns Preset/Mode/Sandbox use); Edit/Test/Duplicate/Add dispatch the raw\n * `/provider` command — driven by the SAME `ask_request` rendering the first-run setup panel and the\n * model menu's former \"Manage providers…\" flow already use, so a masked API key field and a\n * confirmation step need nothing new here.\n */\nexport function SettingsProvidersSection({\n snapshot,\n modelList,\n onRequestModelList,\n onUse,\n onModelChange,\n onCommand,\n onRequestDelete,\n}: {\n snapshot: ISettingsSnapshot;\n modelList: TModelListSnapshot | null;\n onRequestModelList: () => void;\n onUse: (profileName: string) => void;\n onModelChange: (profileName: string, modelId: string) => void;\n /** Dispatches `/provider <args>` (Edit/Test/Duplicate) or `/provider add` (Add) unmodified. */\n onCommand: (args: string) => void;\n onRequestDelete: (profileName: string) => void;\n}): React.ReactElement {\n // The \"Model\" button needs each profile's catalog as soon as this section is visible, not only\n // once a person opens one — `list-models` is a cheap, observer-safe read (#3282 §2).\n // Intentionally empty deps: fetch once per mount, not on every render.\n useEffect(() => {\n onRequestModelList();\n }, []);\n\n const { profiles } = snapshot.providers;\n\n return (\n <section aria-labelledby=\"settings-providers-heading\" className=\"flex flex-col gap-4\">\n <h3 id=\"settings-providers-heading\" className=\"sr-only\">\n Providers & Models\n </h3>\n {profiles.length === 0 ? (\n <p className=\"text-[13.5px] text-muted-foreground\">No providers configured yet.</p>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {profiles.map((profile) => (\n <ProviderProfileRow\n key={profile.name}\n profile={profile}\n modelList={modelList}\n onUse={onUse}\n onModelChange={onModelChange}\n onCommand={onCommand}\n onRequestDelete={onRequestDelete}\n />\n ))}\n </ul>\n )}\n <button\n type=\"button\"\n onClick={() => onCommand('add')}\n className=\"self-start rounded-lg bg-raised px-3 py-1.5 text-[13px] font-medium text-foreground hover:bg-hover\"\n >\n Add provider…\n </button>\n </section>\n );\n}\n","'use client';\n\nimport { ChevronLeft, X } from 'lucide-react';\nimport React, { useEffect, useState } from 'react';\n\nimport { ConfirmDialog, Dialog } from './Dialog.js';\nimport { SettingsGeneralSection } from './SettingsGeneralSection.js';\nimport { SettingsMcpSection } from './SettingsMcpSection.js';\nimport { describePermissionRuleRemoval, SettingsPermissionsSection } from './SettingsPermissionsSection.js';\nimport { SettingsPluginsSection } from './SettingsPluginsSection.js';\nimport { SettingsProvidersSection } from './SettingsProvidersSection.js';\n\nimport type { IWsSessionState } from '../hooks/useSessionClient.js';\nimport type { ISettingsPermissionRule, ISettingsPlugin } from '@robota-sdk/agent-interface-session';\n\ninterface ISettingsSectionDefinition {\n id: string;\n label: string;\n}\n\n/**\n * The Settings screen's sections (#3282 §4). Adding a later section (Advisor) is one entry here plus\n * one component in the switch below — nothing else in this file changes.\n */\nconst SECTIONS: readonly ISettingsSectionDefinition[] = [\n { id: 'general', label: 'General' },\n { id: 'permissions', label: 'Permissions' },\n { id: 'mcp', label: 'MCP Servers' },\n { id: 'plugins', label: 'Plugins' },\n { id: 'providers', label: 'Providers & Models' },\n];\n\ntype TPendingSkipAllChecks = { kind: 'mode' } | { kind: 'preset'; presetId: string };\ntype TPendingPluginAction =\n | { kind: 'install'; pluginId: string }\n | { kind: 'uninstall'; plugin: ISettingsPlugin };\n\n/**\n * A large modal sheet with a section sidebar on the left and content on the right (#3282 §4a) — the\n * GUI's Settings screen, opened by the sidebar's gear button, the desktop app's ⌘, menu item, or\n * `/settings` (its `show-settings` `ui_intent` calls `state.openSettings()`, wired in\n * `useSessionClient`). Every change applies at once; there is no Save button. Esc and the Close\n * button both close it and return focus to whatever opened it (`Dialog` owns that).\n */\nexport function SettingsScreen({ state }: { state: IWsSessionState }): React.ReactElement | null {\n const [activeSectionId, setActiveSectionId] = useState<string>('general');\n // Narrow layout only: true shows the section LIST; false shows the selected section's content,\n // pushed in over the list, with the Back button above returning to it.\n const [showList, setShowList] = useState(true);\n const [pendingSkipAllChecks, setPendingSkipAllChecks] = useState<TPendingSkipAllChecks | null>(\n null,\n );\n const [pendingRuleRemoval, setPendingRuleRemoval] = useState<ISettingsPermissionRule | null>(\n null,\n );\n const [pendingPluginAction, setPendingPluginAction] = useState<TPendingPluginAction | null>(null);\n const [mcpReloading, setMcpReloading] = useState(false);\n const [pluginsReloading, setPluginsReloading] = useState(false);\n // #3282 §4b: the profile a pending \"Delete…\" confirms, or null when none is pending.\n const [pendingProviderDelete, setPendingProviderDelete] = useState<string | null>(null);\n\n // Each time the screen opens, land on the section the opener asked for (`/plugin` → Plugins,\n // \"Manage providers…\" → Providers & Models; the gear and `/settings` ask for none, which lands on\n // General) and, for narrow windows, on the section list.\n useEffect(() => {\n if (!state.settingsOpen) return;\n setActiveSectionId(state.settingsInitialSectionId ?? 'general');\n setShowList(true);\n }, [state.settingsOpen, state.settingsInitialSectionId]);\n\n // A reply to ANY request clears a reload spinner — the common case is the reload's own reply;\n // a snapshot refreshed from elsewhere in the meantime is a reasonable time to stop showing it too.\n // `settingsError` is in the dependency list too: a reload that fails updates the error, not the\n // snapshot, and without it the button would spin forever on a failed reload.\n useEffect(() => {\n setMcpReloading(false);\n setPluginsReloading(false);\n }, [state.settingsSnapshot, state.settingsError]);\n\n if (!state.settingsOpen) return null;\n\n const snapshot = state.settingsSnapshot;\n const activeSection = SECTIONS.find((section) => section.id === activeSectionId) ?? SECTIONS[0];\n\n function selectSection(id: string): void {\n setActiveSectionId(id);\n setShowList(false);\n }\n\n function requestPermissionModeChange(mode: string): void {\n if (snapshot && mode === snapshot.permissionMode.skipsAllChecksMode) {\n setPendingSkipAllChecks({ kind: 'mode' });\n return;\n }\n state.updateSettings({ field: 'permissionMode', mode });\n }\n\n function requestPresetChange(presetId: string): void {\n if (snapshot?.preset.skipsAllChecksPresetIds.includes(presetId)) {\n setPendingSkipAllChecks({ kind: 'preset', presetId });\n return;\n }\n state.updateSettings({ field: 'preset', presetId });\n }\n\n function reloadMcpServers(): void {\n setMcpReloading(true);\n state.updateSettings({ field: 'reloadMcpServers' });\n }\n\n function reloadPlugins(): void {\n setPluginsReloading(true);\n state.updateSettings({ field: 'reloadPlugins' });\n }\n\n function confirmPluginAction(): void {\n if (pendingPluginAction?.kind === 'install') {\n state.updateSettings({ field: 'installPlugin', pluginId: pendingPluginAction.pluginId });\n } else if (pendingPluginAction?.kind === 'uninstall') {\n state.updateSettings({ field: 'uninstallPlugin', pluginId: pendingPluginAction.plugin.id });\n }\n setPendingPluginAction(null);\n }\n\n return (\n <>\n <Dialog\n open\n onClose={() => state.closeSettings()}\n title=\"Settings\"\n panelClassName=\"flex h-[min(680px,85vh)] w-full max-w-3xl flex-col overflow-hidden rounded-2xl bg-card text-card-foreground shadow-[0_8px_30px_-12px_rgb(0_0_0/0.45)] focus:outline-none\"\n >\n <div className=\"flex h-12 flex-shrink-0 items-center gap-1 border-b border-border px-3\">\n {!showList ? (\n <button\n type=\"button\"\n aria-label=\"Back to sections\"\n onClick={() => setShowList(true)}\n className=\"rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground md:hidden\"\n >\n <ChevronLeft size={18} strokeWidth={1.75} />\n </button>\n ) : null}\n <h2 className=\"px-1.5 text-[15px] font-semibold text-foreground\">Settings</h2>\n <button\n type=\"button\"\n aria-label=\"Close Settings\"\n onClick={() => state.closeSettings()}\n className=\"ml-auto rounded-md p-1.5 text-muted-foreground hover:bg-hover hover:text-foreground\"\n >\n <X size={17} strokeWidth={1.75} />\n </button>\n </div>\n\n <div className=\"flex min-h-0 flex-1\">\n <nav\n aria-label=\"Settings sections\"\n className={`w-full flex-shrink-0 overflow-y-auto border-r border-border bg-sidebar px-2 py-3 md:block md:w-[200px] ${\n showList ? 'block' : 'hidden'\n }`}\n >\n <ul className=\"space-y-0.5\">\n {SECTIONS.map((section) => (\n <li key={section.id}>\n <button\n type=\"button\"\n onClick={() => selectSection(section.id)}\n aria-current={section.id === activeSectionId ? 'true' : undefined}\n className={`w-full rounded-lg px-2.5 py-2 text-left text-[13.5px] ${\n section.id === activeSectionId\n ? 'bg-raised font-medium text-foreground'\n : 'text-foreground/85 hover:bg-hover'\n }`}\n >\n {section.label}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n\n <div\n className={`min-h-0 flex-1 overflow-y-auto px-5 py-4 md:block ${\n showList ? 'hidden' : 'block'\n }`}\n >\n {snapshot === null ? (\n state.settingsStatus === 'error' ? (\n <div className=\"flex flex-col items-start gap-2\">\n <p className=\"text-[13.5px] text-destructive\">\n {state.settingsError ?? 'Settings could not be read.'}\n </p>\n <button\n type=\"button\"\n onClick={() => state.openSettings()}\n className=\"rounded-lg bg-raised px-3 py-1.5 text-[13px] text-foreground hover:bg-hover\"\n >\n Try again\n </button>\n </div>\n ) : (\n <p className=\"text-[13.5px] text-muted-foreground\">Loading settings…</p>\n )\n ) : (\n <>\n {state.settingsError ? (\n <p className=\"mb-3 rounded-lg bg-destructive/12 px-3 py-2 text-[13px] text-destructive\">\n {state.settingsError}\n </p>\n ) : null}\n {activeSection.id === 'general' ? (\n <SettingsGeneralSection\n snapshot={snapshot}\n onUpdate={state.updateSettings}\n onRequestPresetChange={requestPresetChange}\n />\n ) : activeSection.id === 'mcp' ? (\n <SettingsMcpSection\n snapshot={snapshot}\n onToggleServer={(serverId, enabled) =>\n state.updateSettings({ field: 'mcpServerEnabled', serverId, enabled })\n }\n onReload={reloadMcpServers}\n reloading={mcpReloading}\n />\n ) : activeSection.id === 'plugins' ? (\n <SettingsPluginsSection\n snapshot={snapshot}\n onTogglePlugin={(pluginId, enabled) =>\n state.updateSettings({ field: 'pluginEnabled', pluginId, enabled })\n }\n onReload={reloadPlugins}\n reloading={pluginsReloading}\n onRequestInstall={(pluginId) => setPendingPluginAction({ kind: 'install', pluginId })}\n onRequestUninstall={(plugin) => setPendingPluginAction({ kind: 'uninstall', plugin })}\n />\n ) : activeSection.id === 'providers' ? (\n <SettingsProvidersSection\n snapshot={snapshot}\n modelList={state.modelList}\n onRequestModelList={state.requestModelList}\n onUse={(profileName) => state.updateSettings({ field: 'providerProfile', profileName })}\n onModelChange={(profileName, modelId) =>\n state.updateSettings({ field: 'providerModel', profileName, modelId })\n }\n onCommand={(args) => state.send({ type: 'command', name: 'provider', args })}\n onRequestDelete={(profileName) => setPendingProviderDelete(profileName)}\n />\n ) : (\n <SettingsPermissionsSection\n snapshot={snapshot}\n onRequestModeChange={requestPermissionModeChange}\n onToggleSandbox={(enabled) => state.updateSettings({ field: 'sandbox', enabled })}\n onRequestRemoveRule={(rule) => setPendingRuleRemoval(rule)}\n />\n )}\n </>\n )}\n </div>\n </div>\n </Dialog>\n\n <ConfirmDialog\n open={pendingSkipAllChecks !== null}\n title=\"Skip all permission checks?\"\n body=\"Every action runs without asking first — file edits, shell commands and network access included. You can turn this back on at any time.\"\n confirmLabel=\"Skip all checks\"\n destructive\n onCancel={() => setPendingSkipAllChecks(null)}\n onConfirm={() => {\n if (pendingSkipAllChecks?.kind === 'mode' && snapshot) {\n state.updateSettings({\n field: 'permissionMode',\n mode: snapshot.permissionMode.skipsAllChecksMode,\n });\n } else if (pendingSkipAllChecks?.kind === 'preset') {\n state.updateSettings({ field: 'preset', presetId: pendingSkipAllChecks.presetId });\n }\n setPendingSkipAllChecks(null);\n }}\n />\n\n <ConfirmDialog\n open={pendingRuleRemoval !== null}\n title=\"Remove this rule?\"\n body={pendingRuleRemoval ? describePermissionRuleRemoval(pendingRuleRemoval) : ''}\n confirmLabel=\"Remove\"\n destructive\n onCancel={() => setPendingRuleRemoval(null)}\n onConfirm={() => {\n if (pendingRuleRemoval) {\n state.updateSettings({\n field: 'removePermissionRule',\n scope: pendingRuleRemoval.scope,\n kind: pendingRuleRemoval.kind,\n pattern: pendingRuleRemoval.pattern,\n });\n }\n setPendingRuleRemoval(null);\n }}\n />\n\n <ConfirmDialog\n open={pendingPluginAction !== null}\n title={pendingPluginAction?.kind === 'uninstall' ? 'Uninstall this plugin?' : 'Install this plugin?'}\n body={pendingPluginAction ? describePluginAction(pendingPluginAction) : ''}\n confirmLabel={pendingPluginAction?.kind === 'uninstall' ? 'Uninstall' : 'Install'}\n destructive\n onCancel={() => setPendingPluginAction(null)}\n onConfirm={confirmPluginAction}\n />\n\n <ConfirmDialog\n open={pendingProviderDelete !== null}\n title={pendingProviderDelete ? `Delete profile \"${pendingProviderDelete}\"?` : ''}\n body=\"This removes the profile and its stored key. This cannot be undone.\"\n confirmLabel=\"Delete\"\n destructive\n onCancel={() => setPendingProviderDelete(null)}\n onConfirm={() => {\n if (pendingProviderDelete) {\n state.updateSettings({ field: 'deleteProviderProfile', profileName: pendingProviderDelete });\n }\n setPendingProviderDelete(null);\n }}\n />\n </>\n );\n}\n\n/** Splits `<name>@<marketplace>` for the confirmation copy; a bare name shows with no source. */\nfunction describePluginAction(action: TPendingPluginAction): string {\n if (action.kind === 'uninstall') {\n return `\"${action.plugin.name}\" will be removed from this computer.`;\n }\n const at = action.pluginId.lastIndexOf('@');\n const name = at === -1 ? action.pluginId : action.pluginId.slice(0, at);\n const source = at === -1 ? undefined : action.pluginId.slice(at + 1);\n return `Install \"${name}\"${source ? ` from ${source}` : ''}. It can run code on this computer.`;\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * Tracks whether `query` currently matches, reactively (#3289 §2 — the narrow-window session sheet\n * below `md`). A host without `window.matchMedia` (an older embedding, or a non-browser test) reports\n * `false` and never subscribes, rather than throwing.\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return undefined;\n const mql = window.matchMedia(query);\n const onChange = (): void => setMatches(mql.matches);\n onChange();\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [query]);\n\n return matches;\n}\n\n/** Tailwind's `md` breakpoint (768px) — below it, the session list is a sheet rather than a column. */\nexport const NARROW_WINDOW_QUERY = '(max-width: 767.98px)';\n","import { useEffect, useRef, useState } from 'react';\n\nimport { AgentActivityPanel } from './AgentActivityPanel.js';\nimport { AgentSwitcherSheet } from './AgentSwitcherSheet.js';\nimport { ProductMark, ProductWordmark } from './Brand.js';\nimport { Composer, GoalBar } from './Composer.js';\nimport { ConversationView } from './ConversationView.js';\nimport { Dialog } from './Dialog.js';\nimport { ExecutionDetailSheet } from './ExecutionDetailSheet.js';\nimport { HelpSheet } from './HelpSheet.js';\nimport { PermissionPrompt } from './PermissionPrompt.js';\nimport { PersonalUsageDashboard } from './PersonalUsageDashboard.js';\nimport { ProjectPanel } from './ProjectPanel.js';\nimport { SessionSidebar, SessionSidebarRail, sessionTitle } from './SessionSidebar.js';\nimport { ConnectionBanner, SessionNotices, SessionTitleBar } from './SessionSurfaceChrome.js';\nimport { SettingsScreen } from './SettingsScreen.js';\nimport { NARROW_WINDOW_QUERY, useMediaQuery } from '../hooks/use-media-query.js';\n\nimport type { IComposerHandle, IPickedFile } from './Composer.js';\nimport type { TSessionView } from './SessionSurfaceChrome.js';\nimport type { IWsSessionState } from '../hooks/useSessionClient.js';\n\n/**\n * The desktop session shell — the GUI analog of the TUI's presentation. Pure presentation over an\n * `IWsSessionState`: no hooks, no transport, no session/command/permission logic — it renders the\n * reconstructed session and forwards user intent through the reducer's `send`/`answer*`. The session\n * sidebar runs the full height on the left once the host has listed its sessions (a host that cannot\n * has none); beside it the title bar, the conversation, the pending question docked above the\n * composer, and the background-activity rail on the right when work runs beside the conversation.\n */\n\n/** The column every part of the conversation shares, so messages, prompt and composer line up. */\nconst COLUMN = 'mx-auto w-full max-w-[760px] px-6';\n\n/**\n * Designed empty state shown before the first turn. Renders in the same slot as `ConversationView`\n * (never together), so it carries the same `main` landmark and label (#3289 §3 review) — nothing\n * above this slot supplies one.\n */\nfunction EmptyState(): React.ReactElement {\n return (\n <main className=\"flex h-full items-center justify-center\" aria-label=\"Conversation\">\n <div className=\"gui-rise flex max-w-[440px] flex-col items-center gap-4 px-8 text-center\">\n <ProductMark size={40} />\n <h2 className=\"text-[26px] font-semibold tracking-[-0.02em] text-foreground\">\n What are we working on?\n </h2>\n <p className=\"text-[15px] leading-relaxed text-muted-foreground\">\n Session connected. Send a message to start — the agent works in the host runtime and\n asks you here before anything that needs your permission.\n </p>\n </div>\n </main>\n );\n}\n\n/**\n * Issue #3282 §3 — shown in place of the conversation while `sessionStatus.setupRequired` holds: the\n * session is running (the GUI itself connected fine), but has no provider to reply with yet. \"Set up\n * provider\" runs `/provider add` as a command; its questions dock above where the composer would be\n * (the same `PermissionPrompt` any ask uses), and the panel clears itself once the session reports a\n * real provider, live — no restart, no reload.\n *\n * Renders in the same slot as `ConversationView`/`EmptyState` (never together), so it carries the\n * page's `main` landmark here too (#3289 §3 review) — nothing above this slot supplies one.\n */\nfunction SetupPanel({\n onSetUp,\n disabled,\n}: {\n onSetUp: () => void;\n disabled: boolean;\n}): React.ReactElement {\n return (\n <main className=\"flex h-full items-center justify-center\" aria-label=\"Set up a provider\">\n <div className=\"gui-rise flex max-w-[440px] flex-col items-center gap-4 px-8 text-center\">\n <ProductMark size={40} />\n <h2 className=\"text-[26px] font-semibold tracking-[-0.02em] text-foreground\">\n Connect a model provider to start.\n </h2>\n <p className=\"text-[15px] leading-relaxed text-muted-foreground\">\n Choose a provider and model to start. Enter a key if your provider requires one. You can\n add more providers or change them later the same way.\n </p>\n <button\n type=\"button\"\n onClick={onSetUp}\n disabled={disabled}\n className=\"rounded-lg bg-primary px-4 py-2 text-[14px] font-medium text-primary-foreground hover:opacity-90 disabled:opacity-60\"\n >\n Set up provider\n </button>\n </div>\n </main>\n );\n}\n\n/**\n * The full desktop layout over an `IWsSessionState`. `surface` is an optional label shown next to the\n * mark (e.g. \"app\").\n */\nexport function SessionSurface({\n state,\n surface,\n personalUsageEnabled = false,\n onReconnect,\n pickFiles,\n getPathForFile,\n onOpenMemoryInEditor,\n}: {\n state: IWsSessionState;\n surface?: string;\n /** Desktop shells opt in; embedded/session-only surfaces keep their existing chat-only contract. */\n personalUsageEnabled?: boolean;\n /**\n * Present only when the host can restart the runtime after the connection is lost for good (issue\n * #3280 §5, the desktop app); absent in the browser, where the banner says how to reopen it instead.\n */\n onReconnect?: () => Promise<void>;\n /** Forwarded to the composer's attach button — present only on the desktop app (#3282 §4d). */\n pickFiles?: () => Promise<readonly IPickedFile[]>;\n /** Forwarded to the composer's drag-and-drop — present only on the desktop app (#3282 §4d). */\n getPathForFile?: (file: File) => string;\n /**\n * The Project panel's memory \"Open in editor\" action — present only when the host can open a file\n * in an external editor (the desktop app, #3282 §4c). Absent in the browser, where the action\n * itself is hidden (only shown \"if the host supports it\").\n */\n onOpenMemoryInEditor?: (path: string) => void;\n}): React.ReactElement {\n const [view, setView] = useState<TSessionView>('chat');\n const composerRef = useRef<IComposerHandle>(null);\n const tasks = state.executionWorkspace?.entries ?? [];\n // The main thread alone is this conversation; the rail earns its width only for work beside it.\n const hasTasks = tasks.some((entry) => entry.kind !== 'main_thread');\n // #3288 §1: the entry open in the detail sheet, resolved fresh from the live snapshot each render\n // (so it reflects the entry's own updates, e.g. a Stop taking effect) rather than a stale copy.\n const openEntry = tasks.find((entry) => entry.id === state.openEntryId) ?? null;\n // A loop's `cancel` means \"stop the loop\" (`/loop stop <id>`) — never cancel-background-task,\n // which for a self-paced loop would only cancel its disposable wake timer, leaving the loop\n // itself active (see IExecutionWorkspaceEntry.loopId).\n const stopExecutionEntry = (entry: { loopId?: string; sourceId: string }): void => {\n if (entry.loopId !== undefined) {\n state.send({ type: 'command', name: 'loop', args: `stop ${entry.loopId}` });\n } else {\n state.send({ type: 'cancel-background-task', taskId: entry.sourceId });\n }\n };\n // #3282 §4 part b-3: the rail also earns its width for a schedule or a goal, even with no running\n // background task/loop beside the conversation — \"Work in progress\" covers all four (issue #3282\n // §4's Expected section), not only execution-workspace entries.\n const currentGoal = state.sessionStatus?.goal ?? null;\n const hasWorkInProgress = hasTasks || state.scheduledTasks.length > 0 || currentGoal !== null;\n const isEmpty =\n state.messages.length === 0 &&\n !state.streamingText &&\n !state.isThinking &&\n state.activeTools.length === 0;\n // #3282 §3: no provider is configured — the session is running (a placeholder stands in for the\n // real one), but there is nothing to reply with yet.\n const setupRequired = state.sessionStatus?.setupRequired === true;\n const hasSessionList =\n (state.sessionListing ?? null) !== null || state.sessionsError?.code === 'list_failed';\n const sidebarOpen = hasSessionList && state.sessionSidebarOpen;\n const usage = personalUsageEnabled && view === 'usage';\n const project = view === 'project';\n const listing = state.sessionListing ?? null;\n const currentRow = listing?.sessions.find((session) => session.id === listing.currentSessionId);\n // A rename seen on this page wins; otherwise the host's listing names the current session.\n const title = state.sessionName ?? (currentRow ? sessionTitle(currentRow) : null);\n // #3289 §2 — below `md` the open sidebar is a sheet over the conversation (the shared `Dialog`,\n // #3331) rather than a column beside it: a dimmed backdrop, Esc, an outside click or choosing a\n // session all close it again, and it traps Tab inside it while open.\n const isNarrow = useMediaQuery(NARROW_WINDOW_QUERY);\n const sheetActive = sidebarOpen && isNarrow;\n const closeSidebar = (): void => state.setSessionSidebarOpen?.(false);\n // The sheet's own opener — the rail's \"Show sessions\" button — unmounts in the SAME update that\n // mounts the sheet (the ternary below swaps `SessionSidebarRail` for `Dialog`), so passing it as\n // `Dialog`'s `restoreFocusTo` would only hand back a node already detached from the document by the\n // time `Dialog` reads it — a `.focus()` on that node is a harmless no-op, not a restore. This effect\n // does the real work instead: a `focusin` listener captures the opener the moment it actually had\n // focus, while the sheet is closed; on close, a control that reappeared under the same `aria-label`\n // (the rail's button, remounted) is as good a return address as the original node.\n const sheetOpenerRef = useRef<HTMLElement | null>(null);\n useEffect(() => {\n if (sheetActive) return undefined;\n const onFocusIn = (event: FocusEvent): void => {\n if (event.target instanceof HTMLElement) sheetOpenerRef.current = event.target;\n };\n document.addEventListener('focusin', onFocusIn);\n return () => document.removeEventListener('focusin', onFocusIn);\n }, [sheetActive]);\n const wasSheetActiveRef = useRef(false);\n useEffect(() => {\n if (wasSheetActiveRef.current && !sheetActive) {\n const opener = sheetOpenerRef.current;\n const label = opener?.getAttribute('aria-label');\n const target =\n opener && document.contains(opener)\n ? opener\n : label\n ? document.querySelector<HTMLElement>(`[aria-label=\"${label}\"]`)\n : null;\n target?.focus();\n }\n wasSheetActiveRef.current = sheetActive;\n }, [sheetActive]);\n // Choosing a session from the sidebar shows its conversation, whichever view was open — and, while\n // it is a narrow-window sheet, closes it too, exactly as a backdrop click or Esc would.\n const sidebarState: IWsSessionState = {\n ...state,\n switchSession: (sessionId) => {\n setView('chat');\n state.switchSession?.(sessionId);\n if (sheetActive) closeSidebar();\n },\n newSession: () => {\n setView('chat');\n state.newSession?.();\n if (sheetActive) closeSidebar();\n },\n };\n const sidebarPanel = (\n <SessionSidebar\n state={sidebarState}\n brand={<ProductWordmark surface={surface} />}\n className={\n sheetActive\n ? 'h-full'\n : 'absolute inset-y-0 left-0 z-30 shadow-2xl shadow-black/40 md:static md:z-auto md:shadow-none'\n }\n />\n );\n\n return (\n <div className=\"agent-ui relative flex h-full bg-background text-foreground\">\n {hasSessionList ? (\n sidebarOpen ? (\n isNarrow ? (\n <Dialog\n open={sheetActive}\n onClose={closeSidebar}\n title=\"Sessions\"\n panelClassName=\"mr-auto flex h-full w-[272px] max-w-[85vw] flex-col self-stretch overflow-hidden rounded-2xl bg-sidebar shadow-2xl shadow-black/40 focus:outline-none\"\n >\n {sidebarPanel}\n </Dialog>\n ) : (\n // Wide windows keep it as a static column beside the conversation.\n sidebarPanel\n )\n ) : (\n <SessionSidebarRail state={sidebarState} />\n )\n ) : null}\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <SessionTitleBar\n status={state.status}\n surface={surface}\n title={title}\n workspace={state.sessionStatus?.workspace}\n showBrand={!sidebarOpen}\n view={view}\n onView={setView}\n personalUsageEnabled={personalUsageEnabled}\n connectionLost={state.connectionLost ?? false}\n />\n\n {usage ? (\n <div className=\"min-h-0 flex-1\">\n <PersonalUsageDashboard state={state} />\n {/* A gated turn waits on this answer, so it shows over whatever view is open. */}\n <PermissionPrompt\n layout=\"modal\"\n prompts={state.pendingPrompts}\n onAnswerPermission={state.answerPermission}\n onAnswerAsk={state.answerAsk}\n ownDriverId={state.ownDriverId}\n />\n </div>\n ) : project ? (\n <div className=\"min-h-0 flex-1\">\n <ProjectPanel state={state} onOpenMemoryInEditor={onOpenMemoryInEditor} />\n <PermissionPrompt\n layout=\"modal\"\n prompts={state.pendingPrompts}\n onAnswerPermission={state.answerPermission}\n onAnswerAsk={state.answerAsk}\n ownDriverId={state.ownDriverId}\n />\n </div>\n ) : (\n <div className=\"flex min-h-0 flex-1\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <ConnectionBanner\n status={state.status}\n connectionLost={state.connectionLost ?? false}\n onReconnect={onReconnect}\n />\n <div className=\"min-h-0 flex-1 overflow-hidden\">\n {setupRequired ? (\n <SetupPanel\n onSetUp={() => state.send({ type: 'command', name: 'provider', args: 'add' })}\n disabled={state.status !== 'connected'}\n />\n ) : isEmpty ? (\n <EmptyState />\n ) : (\n <ConversationView\n messages={state.messages}\n activeTools={state.activeTools}\n streamingText={state.streamingText}\n isThinking={state.isThinking}\n ownDriverId={state.ownDriverId}\n />\n )}\n </div>\n <div\n className={`${COLUMN} relative flex flex-shrink-0 flex-col gap-2 pb-4 before:pointer-events-none before:absolute before:inset-x-0 before:-top-8 before:h-8 before:bg-gradient-to-t before:from-background before:to-transparent`}\n >\n <GoalBar\n status={state.sessionStatus ?? null}\n onStop={() => state.send({ type: 'command', name: 'goal', args: 'cancel' })}\n />\n <PermissionPrompt\n layout=\"dock\"\n prompts={state.pendingPrompts}\n onAnswerPermission={state.answerPermission}\n onAnswerAsk={state.answerAsk}\n ownDriverId={state.ownDriverId}\n onFocusReturn={() => composerRef.current?.focus()}\n />\n {/* #3282 §3: hidden while there is no provider to send to — the setup panel's own\n button is the only way in, so nobody types into a composer that goes nowhere. */}\n {setupRequired ? null : (\n <Composer\n ref={composerRef}\n catalog={state.commandCatalog ?? null}\n status={state.sessionStatus ?? null}\n connected={state.status === 'connected'}\n running={state.isThinking}\n onStop={() => state.send({ type: 'abort' })}\n queued={state.queuedPrompt}\n pickFiles={pickFiles}\n getPathForFile={getPathForFile}\n onCancelQueue={() => {\n state.send({ type: 'cancel-queue' });\n // No push confirms a cleared queue (unlike a resolved prompt); ask, so the row\n // reliably disappears instead of trusting the clear went through.\n state.send({ type: 'get-pending' });\n }}\n onCommand={(name, args) =>\n state.send({ type: 'command', name, ...(args ? { args } : {}) })\n }\n modelList={state.modelList}\n onRequestModelList={() => state.requestModelList()}\n onSilentCommand={(name, args) => state.sendCommandSilently(name, args)}\n // #3282 §4b: \"Manage providers…\" now opens Settings at the Providers & Models\n // section, replacing the model menu's former server-driven profile flow.\n onManageProviders={() => state.openSettings('providers')}\n onSubmit={(prompt) => {\n if (!prompt.startsWith('/')) {\n state.send({ type: 'submit', prompt });\n return;\n }\n const [name, ...rest] = prompt.slice(1).trim().split(/\\s+/u);\n if (!name) return;\n const args = rest.join(' ');\n state.send({ type: 'command', name, ...(args ? { args } : {}) });\n }}\n />\n )}\n </div>\n </div>\n\n {hasWorkInProgress && (\n <aside aria-label=\"Agents\" className=\"flex w-72 flex-shrink-0 overflow-hidden bg-sidebar\">\n <AgentActivityPanel\n tasks={tasks}\n className=\"flex-1\"\n selectedEntryId={state.openEntryId ?? undefined}\n onSelect={(entry) => state.openExecutionDetail(entry.id)}\n onReturnToConversation={() => state.closeExecutionDetail()}\n onStop={stopExecutionEntry}\n schedules={state.scheduledTasks}\n goal={currentGoal}\n onPauseSchedule={state.pauseSchedule}\n onResumeSchedule={state.resumeSchedule}\n onDeleteSchedule={state.deleteSchedule}\n onCancelGoal={() => state.send({ type: 'command', name: 'goal', args: 'cancel' })}\n />\n </aside>\n )}\n\n <ExecutionDetailSheet\n entry={openEntry}\n status={state.executionDetailStatus}\n records={state.executionDetailRecords}\n error={state.executionDetailError}\n complete={state.executionDetailComplete}\n onClose={() => state.closeExecutionDetail()}\n onLoadMore={() => state.loadMoreExecutionDetail()}\n onStop={openEntry ? () => stopExecutionEntry(openEntry) : undefined}\n />\n </div>\n )}\n </div>\n\n <SessionNotices state={state} />\n <SettingsScreen state={state} />\n <AgentSwitcherSheet state={state} />\n <HelpSheet open={state.helpOpen} onClose={() => state.closeHelp()} catalog={state.commandCatalog} />\n </div>\n );\n}\n\n/**\n * A centered chrome frame for the pre-session (loading) and fatal states — reused by app shells. Its\n * content area is this page's `main` landmark (#3289 §3 review): nothing above `CenteredChrome`'s\n * three call sites (starting, fatal, the trust question) supplies one, and none of the three ever\n * render alongside `SessionSurface`'s own. `header` stays a sibling of `main`, not inside it, so it\n * keeps its own implicit `banner` role.\n */\nexport function CenteredChrome({\n tone,\n children,\n}: {\n tone: 'muted' | 'fatal';\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <div className=\"agent-ui flex h-full flex-col bg-background text-foreground\">\n <header className=\"flex h-12 flex-shrink-0 items-center px-5\">\n <ProductWordmark />\n </header>\n <main className=\"flex flex-1 items-center justify-center\">\n <div className=\"gui-rise flex max-w-[520px] flex-col items-center gap-4 px-8 text-center\">\n <span\n className={`h-2.5 w-2.5 rounded-full ${\n tone === 'fatal' ? 'bg-destructive' : 'animate-pulse bg-warning'\n }`}\n />\n <div\n className={`text-[15px] leading-relaxed ${\n tone === 'fatal' ? 'text-foreground' : 'text-muted-foreground'\n }`}\n >\n {children}\n </div>\n </div>\n </main>\n </div>\n );\n}\n","'use client';\n\nimport { useProductIdentity } from '../product-identity.js';\n\nimport React, { useState } from 'react';\n\nimport { useWsSession } from '../hooks/useSessionClient.js';\n\nimport { AgentActivityPanel } from './AgentActivityPanel.js';\nimport { ConversationView } from './ConversationView.js';\n\nconst STATUS_CONFIG: Record<string, { label: string; dot: string; text: string; glow: string }> = {\n connected: {\n label: 'Connected',\n dot: 'bg-accent',\n text: 'text-muted-foreground',\n glow: 'status-glow',\n },\n connecting: {\n label: 'Connecting…',\n dot: 'bg-warning animate-pulse',\n text: 'text-warning',\n glow: '',\n },\n disconnected: { label: 'Disconnected', dot: 'bg-subtle', text: 'text-subtle', glow: '' },\n error: { label: 'Error', dot: 'bg-destructive', text: 'text-destructive', glow: '' },\n};\n\ninterface ISessionMonitorProps {\n /** WebSocket URL. Injected by the HTTP server via <meta name=\"ws-url\">. */\n wsUrl: string;\n className?: string;\n}\n\nexport function SessionMonitor({ wsUrl, className }: ISessionMonitorProps): React.ReactElement {\n const { identity } = useProductIdentity();\n const [url, setUrl] = useState(wsUrl);\n const [inputUrl, setInputUrl] = useState(wsUrl);\n\n const {\n status,\n messages,\n activeTools,\n streamingText,\n isThinking,\n executionWorkspace,\n ownDriverId,\n send,\n } = useWsSession(url);\n const cfg = STATUS_CONFIG[status] ?? STATUS_CONFIG.disconnected;\n\n const backgroundTasks =\n executionWorkspace?.entries.filter(\n (e) => e.kind === 'background_task' && e.visibility !== 'collapsed',\n ) ?? [];\n const hasAgents = backgroundTasks.length > 0;\n\n return (\n <div className={`agent-ui flex flex-col h-full overflow-hidden bg-background ${className ?? ''}`}>\n {/* Header */}\n <div className=\"flex h-12 flex-shrink-0 items-center gap-3 px-5\">\n <div className=\"flex items-center gap-2.5\">\n <span className={`h-2 w-2 rounded-full flex-shrink-0 ${cfg.dot} ${cfg.glow}`} />\n <span className=\"text-[15px] font-semibold text-foreground\">CLI monitor</span>\n <span role=\"status\" className={`text-[13px] ${cfg.text}`}>\n {cfg.label}\n </span>\n </div>\n <div className=\"ml-auto flex items-center gap-2\">\n <input\n aria-label=\"WebSocket URL\"\n className=\"h-8 w-60 rounded-lg bg-raised px-3 font-mono text-[12.5px] text-foreground placeholder:text-subtle focus:outline-none\"\n value={inputUrl}\n onChange={(e) => setInputUrl(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') setUrl(inputUrl);\n }}\n placeholder=\"ws://localhost:7070\"\n />\n <button\n className=\"h-8 rounded-lg bg-primary px-3.5 text-[13px] font-medium text-primary-foreground hover:opacity-90\"\n onClick={() => setUrl(inputUrl)}\n >\n Connect\n </button>\n </div>\n </div>\n\n {/* Main content: conversation + optional agent panel */}\n <div className=\"flex flex-1 overflow-hidden\">\n {/* Left: conversation */}\n <div\n className={`flex flex-col overflow-hidden min-w-0 ${hasAgents ? 'flex-[2]' : 'flex-1'}`}\n >\n <div className=\"flex-1 overflow-hidden\">\n {status === 'connected' ? (\n <ConversationView\n messages={messages}\n activeTools={activeTools}\n streamingText={streamingText}\n isThinking={isThinking}\n ownDriverId={ownDriverId}\n />\n ) : (\n // #3289 §3 review: this renders in the same slot as `ConversationView` (never\n // together), so it carries this page's `main` landmark here too — nothing above this\n // slot supplies one.\n <main className=\"flex h-full items-center justify-center\" aria-label=\"Conversation\">\n <div className=\"flex flex-col items-center gap-3 text-center px-8\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-card\">\n <span className={`h-2.5 w-2.5 rounded-full ${cfg.dot}`} />\n </div>\n <p className=\"max-w-[320px] text-[15px] leading-relaxed text-muted-foreground\">\n {status === 'connecting'\n ? `Connecting to ${url}…`\n : status === 'error'\n ? `Connection error — could not reach ${url}. Check the CLI is running and the URL is correct.`\n : `Run ${identity.cliName} to start the CLI (WS transport starts automatically).`}\n </p>\n </div>\n </main>\n )}\n </div>\n\n <SessionInput\n enabled={status === 'connected'}\n onSubmit={(prompt) => send({ type: 'submit', prompt })}\n />\n </div>\n\n {/* Right: agent activity panel (conditional) */}\n {hasAgents && <AgentActivityPanel tasks={backgroundTasks} className=\"flex-1 bg-sidebar\" />}\n </div>\n </div>\n );\n}\n\nfunction SessionInput({\n enabled,\n onSubmit,\n}: {\n enabled: boolean;\n onSubmit: (prompt: string) => void;\n}): React.ReactElement {\n const [value, setValue] = useState('');\n\n const handleSubmit = (): void => {\n const trimmed = value.trim();\n if (!trimmed) return;\n onSubmit(trimmed);\n setValue('');\n };\n\n return (\n <div className=\"mx-auto flex w-full max-w-[760px] flex-shrink-0 items-end gap-2 px-6 pb-4\">\n <textarea\n aria-label=\"Message\"\n className=\"max-h-[160px] min-h-[48px] flex-1 resize-none rounded-[22px] bg-card px-4 py-3 text-[15px] leading-relaxed text-foreground placeholder:text-subtle [field-sizing:content] focus:outline-none disabled:opacity-60\"\n rows={1}\n placeholder={enabled ? 'Send a message…' : 'Connect to send messages'}\n disabled={!enabled}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n }}\n />\n <button\n className=\"h-12 rounded-[22px] bg-primary px-5 text-[14px] font-medium text-primary-foreground hover:opacity-90 disabled:bg-raised disabled:text-subtle\"\n disabled={!enabled || !value.trim()}\n onClick={handleSubmit}\n >\n Send\n </button>\n </div>\n );\n}\n"],"mappings":"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"}
|