dsh-ntr 0.3.1 → 0.3.3
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/README.en.md +3 -1
- package/README.md +3 -1
- package/cordis.patch.yml +17 -1
- package/lib/bridge.js +2436 -82
- package/lib/client/acp-ui.js +103 -3
- package/lib/client/acp-view.js +24 -4
- package/lib/client/activity-pill.js +156 -0
- package/lib/client/branch-action.js +149 -0
- package/lib/client/composer-mode.js +48 -13
- package/lib/client/context-meter.js +86 -0
- package/lib/client/index.js +279 -16
- package/lib/client/model-seat.js +8 -2
- package/lib/client/panel.js +96 -29
- package/lib/client/released-banner.js +66 -0
- package/lib/client/session-controls.js +38 -13
- package/lib/client/slash-isolation.js +2 -2
- package/lib/client/store.js +380 -13
- package/lib/client.js +1364 -70
- package/lib/client.js.map +1 -1
- package/lib/impl.js +1229 -8
- package/lib/index.js +34 -0
- package/lib/protocol.js +19 -0
- package/lib/tps.js +32 -0
- package/lib/types/bridge.d.ts +850 -18
- package/lib/types/client/acp-ui.d.ts +34 -2
- package/lib/types/client/acp-view.d.ts +13 -2
- package/lib/types/client/activity-pill.d.ts +81 -0
- package/lib/types/client/branch-action.d.ts +47 -0
- package/lib/types/client/composer-mode.d.ts +28 -0
- package/lib/types/client/context-meter.d.ts +51 -0
- package/lib/types/client/model-seat.d.ts +21 -0
- package/lib/types/client/released-banner.d.ts +34 -0
- package/lib/types/client/slash-isolation.d.ts +1 -1
- package/lib/types/client/store.d.ts +108 -1
- package/lib/types/impl.d.ts +94 -7
- package/lib/types/protocol.d.ts +145 -0
- package/lib/types/tps.d.ts +17 -0
- package/package.json +2 -1
- package/src/bridge.ts +2754 -86
- package/src/client/acp-ui.tsx +123 -2
- package/src/client/acp-view.tsx +30 -5
- package/src/client/activity-pill.tsx +201 -0
- package/src/client/branch-action.tsx +178 -0
- package/src/client/composer-mode.tsx +59 -13
- package/src/client/context-meter.tsx +128 -0
- package/src/client/index.tsx +298 -15
- package/src/client/model-seat.tsx +28 -3
- package/src/client/panel.tsx +157 -40
- package/src/client/primitives.d.ts +27 -0
- package/src/client/released-banner.tsx +97 -0
- package/src/client/session-controls.tsx +34 -11
- package/src/client/slash-isolation.ts +2 -1
- package/src/client/store.ts +412 -14
- package/src/impl.ts +1251 -13
- package/src/index.ts +33 -0
- package/src/protocol.ts +145 -0
- package/src/tps.ts +40 -0
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useUi","useSyncExternalStore","useRef","STYLE","styleMounted","useUi","useSyncExternalStore","useState","useRef","useSyncExternalStore","useState","boundSession","useUi","useState","useState","memo","useUi","useRef","useUi","useState"],"sources":["../src/protocol.ts","../src/client/store.ts","../src/client/acp-ui.tsx","../src/client/panel.tsx","../src/client/composer-mode.tsx","../src/client/agent-lock-anim.ts","../src/client/session-controls.tsx","../src/client/acp-view.tsx","../src/client/settings-dialog.tsx","../src/client/permission-dock.tsx","../src/client/model-seat.tsx","../src/client/slash-isolation.ts","../src/client/index.tsx"],"sourcesContent":["/**\n * dsh-ntr shared contracts (browser-safe: types only, zero runtime code).\n *\n * Two planes:\n * - `AcpEvent`: host → browser, one JSON frame per `session/update` fold,\n * permission round-trip, agent process status, and turn lifecycle.\n * - RPC payloads: browser → host unary calls on the `/acp` Connection channel.\n *\n * Security invariants encoded here (enforced host-side in impl.ts):\n * - A SESSION never names a program to spawn: `session/start` and the\n * conversation↔agent binding only select a configured agent id and a cwd\n * that must resolve inside the agent's allowed roots. Naming a program is\n * a CONFIGURATION act instead — the browser may do it through `agent/save`\n * / `agent/test`, both of which run the command through `validateAgentDef`\n * (a command line, not a bare path) and both of which really spawn.\n * - Agent environment variables flow config → child process only.\n */\n\nimport type { SessionUpdate } from '@agentclientprotocol/sdk'\n\n// ---- Configuration (host side, from the profile layer) ----\n\n/** One pre-configured ACP agent executable. */\nexport interface AcpAgentDef {\n /** Stable id the browser selects by. `^[a-z0-9][a-z0-9._-]*$`. */\n id: string\n /** Display name (defaults to the id). */\n title?: string\n /**\n * Executable spawned over stdio, as a full command line (`codebuddy --acp`),\n * parsed by `validateAgentDef`. The browser MAY set it — that is exactly\n * what the settings panel's single command box does, through `agent/save`\n * (persist, then probe) and `agent/test` (spawn a throwaway handshake). A\n * SESSION never can: `session/start` and the conversation binding only\n * select a configured agent id (see the module header).\n */\n command: string\n /** Argument vector (no shell interpretation). */\n args?: string[]\n /**\n * npm package installed globally (`npm install -g`) when `command` cannot be\n * resolved on the host PATH, right before the spawn is attempted. Unset → a\n * missing command just fails with ENOENT. Name-only charset is enforced by\n * `validateAgentDef` (no shell metacharacters can ever reach the installer).\n */\n npmPackage?: string\n /** Child environment: the inherited host env, with these keys layered on top. */\n env?: Record<string, string>\n /** Default absolute workspace directory for new sessions. */\n cwd?: string\n /** Absolute roots a session cwd may resolve under. Defaults to `[cwd]`. */\n allowedCwdRoots?: string[]\n /**\n * Which probed `category: model` choices this agent offers as `agentId@choice`\n * variants in the plugin's own ACP seats (the chips/picker in the chat\n * composer and the ACP-mode model seat). Those menus have no search, so the\n * full catalog is a list storm nobody can navigate: a real pi agent publishes\n * 500 choices, omp 581. Variants are **opt-in** — unset or empty offers none\n * of them, and only the listed choice values are added. The bare `agentId`\n * entry (the agent's own default model) is always offered regardless, so an\n * agent is never unreachable. Filtering only affects listing — hidden\n * variants still resolve and stream (see `agent/catalog`).\n *\n * ACP models are deliberately absent from DSH's native model selector — see\n * {@link AgentCatalogResult} for why that is structural, not cosmetic.\n */\n exposedModels?: string[]\n /**\n * Auto-discovery for {@link exposedModels}, ON by default: every model the\n * agent NEWLY publishes (present in an observed catalog but absent from the\n * one known before it) is appended to `exposedModels` automatically. It never\n * touches models the user already had selected, and the very first catalog\n * observation is a baseline (nothing is \"new\" yet). `false` is the explicit\n * opt-out and the only value persisted; unset means \"on\". Flipped from the\n * settings card's lightweight `agent/set-auto-discover-models` RPC — never by\n * `agent/save`, which restarts the process for connection changes.\n */\n autoDiscoverModels?: boolean\n /**\n * How long this agent's process may sit READY with no live session and\n * nothing in flight before the host releases it (kills the child). Unset →\n * the host default (30 minutes). `0` = never release, and only agents with\n * no live session are ever candidates (see `AgentBridge.releaseIfIdle`):\n * probing / post-save spawns are what leak processes, since they hold no\n * session at all. Nothing is lost either way — a released session is\n * resumed with `session/load` on the next send.\n */\n idleTimeoutMs?: number\n /**\n * MCP servers this agent should connect to, in the plugin's NORMALIZED shape\n * (see {@link AcpMcpServerDef}) — not the ACP wire shape.\n *\n * WHY A NORMALIZED SHAPE: the ACP wire union is asymmetric — its stdio\n * variant carries NO `type` field while http/sse do, and `env`/`headers` are\n * `[{name,value}]` arrays. A hand-written config that guessed the wire\n * shape wrong is accepted by the agent and then SILENTLY DROPPED (measured:\n * a stdio entry carrying `type:'stdio'` never connects). The normalized\n * shape has one explicit discriminant, so the wire conversion happens in\n * exactly one place (bridge.ts) and TypeScript checks it against the SDK's\n * `McpServer` union.\n *\n * An agent's OWN MCP configuration (e.g. `~/.codebuddy/.mcp.json`) is read\n * by the agent itself and needs no declaration here — this field only adds\n * servers ON TOP of that.\n */\n mcpServers?: AcpMcpServerDef[]\n}\n\n/** How an MCP server is reached. `acp` (agent→client `mcp/message`) is NOT supported. */\nexport type AcpMcpTransport = 'stdio' | 'http' | 'sse'\n\n/**\n * One MCP server, in the plugin's normalized shape.\n *\n * The ACP-transport variant (agent calls back into the client over\n * `mcp/message`) is deliberately absent: the client side of that method is\n * not in the ACP SDK's stable surface, and no agent advertises the\n * capability. `validateAgentDef` rejects it with that reason.\n */\nexport type AcpMcpServerDef =\n | {\n /** Namespaces the agent's tools as `mcp__<name>__<tool>`; unique per agent. */\n name: string\n transport: 'stdio'\n /** Executable to spawn for this server. */\n command: string\n /** Arguments for the command. */\n args?: string[]\n /** Environment for the server process (layered over the agent's own env). */\n env?: Record<string, string>\n }\n | {\n /** Namespaces the agent's tools as `mcp__<name>__<tool>`; unique per agent. */\n name: string\n transport: 'http' | 'sse'\n /** The server's URL. */\n url: string\n /** Extra request headers (e.g. authorization). */\n headers?: Record<string, string>\n }\n\nexport interface AcpPluginConfig {\n agents: AcpAgentDef[]\n /** Per-session transcript ring bound (default 600 entries). */\n transcriptMaxEntries?: number\n /**\n * DEAD CONFIG — nothing in this plugin reads it. `validateConfig` still\n * accepts it and the host entry still threads it into the `BridgeManager`\n * constructor, but boot no longer probes (`bootstrapCatalogs` is\n * intentionally empty) and `agent/catalog` is pure memory, so there is no\n * automatic spawn left for it to switch off. Catalogs fill only from a\n * gesture: the agent's own `session/new` / `session/load`, the settings\n * card's `agent/probe-models` button, or an on-disk cache. Kept only so\n * that removing it can be a separate cross-file change (impl.ts, bridge.ts\n * and the host entry all touch it today).\n */\n probeModelsOnBoot?: boolean\n /**\n * Budget for one agent startup step (`initialize`, `session/new`, and the\n * `session/load` of a resume). An agent that does not answer within this\n * window is killed and the request fails with `agent_timeout` instead of\n * hanging forever. Default 60000 ms.\n */\n startupTimeoutMs?: number\n /**\n * How long a permission ask may sit unanswered before the deadline fires.\n *\n * ONE window for both places an ask can live — the conversation's approval\n * card and the settings panel's ledger. They ask the same question (\"is\n * anyone there?\"), so they get the same answer; two windows only made the\n * settings card look like two unrelated knobs.\n *\n * `0` means 不等待 — the ask is settled the moment it arrives, without asking\n * anyone. That rung belongs to the `auto` MODE: it answers the ask, so the\n * agent continues. Under `abort` the same zero would refuse every ask on\n * arrival (the agent stops at every gate), so `abort` never runs it — the\n * host normalizes it to the shortest real wait. Never expiring is the `wait`\n * MODE's business, so the two never overlap. Anything else is the wait\n * before the deadline fires. Default 300000 ms (5 minutes).\n *\n * When it fires, {@link AcpPluginConfig.permissionTimeoutMode} decides what\n * happens.\n *\n * Editable from the settings page (`ui/approval-deadlines-set`), which\n * persists an override in the plugin's own state file and applies it to the\n * running bridges without restarting anything; the value here is the\n * deployment baseline that override falls back to. See {@link AcpApprovalDeadlines}.\n */\n permissionTimeoutMs?: number\n /**\n * LEGACY: the old conversation-route window, kept so an existing\n * `cordis.patch.yml` still says what it meant. Folded into\n * {@link AcpPluginConfig.permissionTimeoutMs} when that key is absent, and\n * ignored when it is present.\n */\n nativeApprovalTimeoutMs?: number\n /**\n * LEGACY: the old ledger-route window, kept for the same reason as\n * {@link AcpPluginConfig.nativeApprovalTimeoutMs}.\n */\n permissionFallbackTimeoutMs?: number\n /**\n * What an expired approval deadline DOES. `wait` (default) never expires\n * at all; `abort` answers `cancelled`; `auto` answers with the option the\n * agent's own kinds imply and lets the agent continue.\n *\n * The default is `wait`: a permission ask is a question FOR A HUMAN, and\n * refusing it because the human looked away is worse than waiting — the\n * ask stays visible, so nobody is left wondering. `abort` and `auto` are\n * opt-in for deployments that would rather have a bounded turn.\n *\n * `auto` is unattended operation — the plugin may approve something no human\n * looked at — so it is opt-in and the outcome is announced. `wait` is the\n * \"hang visibly until I come back\" mode: the ask stays pending and the turn\n * stays open.\n *\n * Editable from the settings page, like {@link nativeApprovalTimeoutMs}.\n */\n permissionTimeoutMode?: PermissionTimeoutMode\n /**\n * After `session/cancel`, how long to wait for the agent to settle the turn\n * before the host force-finalizes it. An agent that ignores the cancel\n * notification must not wedge the session in `running` forever.\n * Default 15000 ms; `0` disables the fallback.\n */\n cancelFallbackMs?: number\n /**\n * How long to wait before resending a prompt the agent answered with\n * `cancelled` although no cancel was requested for that turn. Some agents\n * (observed on codebuddy 2.161.4) clean up cancel state asynchronously, and\n * a prompt landing inside that cleanup window is answered with `cancelled`\n * without generating a turn — the user's message is silently swallowed.\n * The delay must outlast the agent's cleanup window. Default 600 ms.\n */\n staleCancelRetryDelayMs?: number\n /**\n * How many times a stale-cancelled prompt is resent before the host gives\n * up and reports the `cancelled` stop as-is. Default 2.\n */\n staleCancelMaxRetries?: number\n /**\n * Inactivity watchdog for a turn the agent owes an answer: how long the turn\n * may stay SILENT before the host finalizes it as stalled.\n *\n * The clock is `LiveSession.lastAgentActivityAt`, refreshed by ANY\n * `session/update`, at every turn start, at each segment start (the host\n * handing the turn back after a card), and when a permission ask arrives or\n * is resolved. The watchdog only READS it while the turn is watched: an OPEN\n * agent segment (always true on the `/acp`-RPC path, which has no driver\n * stream) and no pending approval. The host-card execution phase and an\n * unanswered ask are exempt by that gate, not because the clock is frozen —\n * they legitimately move it too.\n *\n * Firing finalizes the TURN only (an `agent/notice` plus the usual turn\n * end); it does NOT kill the agent process. The wedged `session/prompt` is\n * abandoned, and the next message reclaims the session: parked with\n * `releaseAll` and resumed with `session/load`, or reset outright when the\n * agent declares no `loadSession` (the context is gone and the user is\n * told).\n *\n * Default 600000 ms (10 minutes); `0` disables the WHOLE watchdog, the tool\n * window included.\n */\n turnStallMs?: number\n /**\n * The longer silence window applied while ANY ACP tool call is still active\n * by `toolCallId` — parallel calls are tracked as a set, so the window lasts\n * until the LAST of them finishes. The agent is working on its own clock in\n * there, so the plain turn window would call a healthy long-running tool\n * stalled.\n *\n * Default 1800000 ms (30 minutes); `0` falls back to\n * {@link AcpPluginConfig.turnStallMs}. It matches\n * `TOOL_OUTPUT_WAIT_MS = 30 * 60 * 1000` in `presets/acp/acp-guard.mjs`, so\n * the two halves of one turn agree on how long a tool may take.\n */\n turnStallToolMs?: number\n /**\n * How often the watchdog scans for silent turns, in milliseconds. A tuning /\n * test knob only: NO settings-page control exists for it — and none for the\n * two windows above either; the whole watchdog is configured in the patch\n * file. Default 30000 ms.\n */\n turnStallTickMs?: number\n}\n\n// ---- Host → browser events (`/api/acp/stream`, SSE) ----\n\nexport type AgentProcessState = 'stopped' | 'starting' | 'ready' | 'error'\n\n/** Agent facts the browser may render (never includes env/argv secrets beyond argv itself, which is config-visible). */\n/**\n * State of the ACP-mode agent preset in the HARNESS's own preset roster.\n *\n * Nothing is installed any more: the preset ships as this package's bundle\n * patch (`presets/acp/acp.patch.yml`), which the harness composes when it\n * builds the profile, so the only question left is whether the roster carries\n * the declaration and can compose a session from it — which is exactly what a\n * `preset-<id>` row that failed to activate reports as `problem`.\n */\nexport interface AcpPresetStatus {\n /** Preset id (a session's `agentPreset` value; matched by the browser half). */\n id: string\n /** The declaration inside this package, for copy and debugging. */\n source: string\n /** Whether the roster carries the preset and it can compose a session. */\n installed: boolean\n /** The harness's own diagnostic when the declaration exists but is unusable. */\n problem?: string\n}\n\nexport interface AgentView {\n id: string\n title: string\n command: string\n args: string[]\n cwd?: string\n allowedCwdRoots: string[]\n state: AgentProcessState\n /** Last stderr lines when state is `error` (diagnostics). */\n stderrTail?: string[]\n /** `initialize` result, once the process is ready. */\n agentInfo?: { name?: string; title?: string; version?: string }\n agentCapabilities?: unknown\n liveSessions: string[]\n /**\n * The window the host actually honors for this agent (its own\n * `idleTimeoutMs`, else the host default). Resolved, not raw, so the panel\n * can show a concrete number instead of guessing at a default; `0` = never\n * released. Absent on an older host half.\n */\n idleTimeoutMs?: number\n}\n\n/**\n * Why the host released a session's agent process. The browser turns this into\n * one runtime-log line per cause; `idle` stays silent (expected lifecycle).\n */\nexport type AcpReleaseCause = 'idle' | 'workspace' | 'resume-failed' | 'stalled' | 'died'\n\nexport type AcpEvent =\n | { type: 'agent/status'; agentId: string; state: AgentProcessState; error?: string; agent?: AgentView }\n | { type: 'agent/notice'; agentId: string; level: 'info' | 'error'; text: string }\n | { type: 'session/created'; sessionId: string; session: SessionView }\n | { type: 'session/meta'; sessionId: string; session: SessionView }\n | { type: 'session/closed'; sessionId: string; reason: 'closed' | 'agent-stopped' | 'agent-died' }\n | {\n /** The agent process died: the session is kept resumable, not destroyed. */\n type: 'session/released'\n sessionId: string\n session: SessionView\n reason: string\n /** Host's release-cause classification; absent on an older host half. */\n cause?: AcpReleaseCause\n }\n | {\n /**\n * The session was resumed through `session/load` on a fresh agent\n * process; the agent replays its history as fresh `session/update`s, so\n * the browser must drop local blocks and the seq watermark first.\n */\n type: 'session/reset'\n sessionId: string\n session: SessionView\n }\n | { type: 'session/update'; sessionId: string; seq: number; update: SessionUpdate }\n /**\n * One agent's learned surface changed on the host because a real session\n * published new `configOptions` or `availableCommands` (its `session/new` /\n * `session/load`, a `config_option_update`, or an\n * `available_commands_update`). The browser must drop its cached seat\n * catalog for that agent and re-read `agent/catalog`; without this, a\n * conversation opened before the agent's first real session keeps rendering\n * yesterday's model list (user report, 2026-10-19) — and a conversation\n * opened after it would miss the agent's slash commands until a reload.\n */\n | { type: 'catalog/updated'; agentId: string; updatedAt: number }\n /**\n * One agent's model visibility changed outside `agent/save`: the settings\n * panel edited `exposedModels`, the auto-discovery switch was flipped, or an\n * observed catalog contributed newly published models. The browser patches\n * its copy of the agent definition (the settings card) and re-reads\n * `agent/catalog` (the composer's seat list). `exposedModels: []` means the\n * same as unset — no `agentId@choice` variants.\n */\n | { type: 'agent/exposure'; agentId: string; exposedModels: string[]; autoDiscoverModels: boolean }\n /**\n * One outstanding ask, broadcast ONCE when it is created and replayed as-is\n * to every freshly connected client (see `permission/synced`).\n *\n * Every ACP permission ask carries everything the conversation card needs:\n * who owns it (`dshSessionId`), what it is about (`toolCall` + the derived\n * `toolName` / `reason`), the options to render verbatim, and the deadline in\n * force. The host never answers on its own outside an expired `abort` /\n * `auto` deadline, so this frame (and its replay) is the answer entry.\n */\n | {\n type: 'permission/request'\n requestId: string\n /** ACP session id the ask belongs to. */\n sessionId: string\n /** DSH chat session id; absent for `/acp` RPC-only sessions. */\n dshSessionId?: string\n /** Raw ACP `ToolCallUpdate` the agent sent — never summarized away. */\n toolCall: unknown\n /** Host-derived human name (`permissionToolName`, `_meta` fallback included). */\n toolName: string\n /** One-line detail from the agent's declared input, when it has any. */\n reason?: string\n options: PermissionOptionView[]\n /** What an unanswered ask does when its deadline fires. */\n timeoutMode: PermissionTimeoutMode\n /** Epoch ms of the deadline; absent in `wait` mode (no deadline). */\n deadlineAt?: number\n }\n | { type: 'permission/resolved'; requestId: string; sessionId: string; outcome: PermissionOutcome }\n /**\n * An ask whose deadline fired and was settled by the plugin itself — refused\n * (`abort`), or answered with the option the agent's own kinds imply\n * (`auto`).\n *\n * This is the conversation's record of the expiry: the card it was answering\n * is gone, so without this frame the user would see it vanish with nothing\n * said about it.\n */\n | {\n type: 'permission/expired'\n sessionId: string\n toolCall: unknown\n /** How long the ask waited before the deadline fired. */\n timeoutMs: number\n /** What the plugin answered with. */\n outcome: PermissionOutcome\n /** The picked option's label, when one was picked (`auto` mode). */\n optionName?: string\n }\n /**\n * Terminator of the pending-permission REPLAY a freshly connected client\n * receives (see the `/api/acp/stream` route). A `permission/request` frame is\n * broadcast once, when the ask is created — a client that connects later (page\n * reload, a second tab, a stream outage) would otherwise never learn the ask\n * exists, and the settings-panel ledger card is the only answer entry for it.\n * The replay re-sends every outstanding ask and then this frame, whose\n * `requestIds` is the authoritative live set: the client drops any pending ask\n * of its own that is not in it (one resolved while it was disconnected).\n */\n | { type: 'permission/synced'; requestIds: string[] }\n | { type: 'turn/start'; sessionId: string; turnId: string; text: string }\n | { type: 'turn/end'; sessionId: string; turnId: string; stopReason?: string; error?: string }\n /**\n * A DSH chat conversation was locked to its agent at the start of its first\n * ACP turn. Addressed by the CHAT session id (the browser may not know the\n * ACP session yet). The composer plays its base→tag hand-off on this frame;\n * a `session/chat-list` read is only the fallback (it can arrive a turn late).\n */\n | { type: 'chat/agent-locked'; dshSessionId: string; agentId: string }\n /**\n * The provisional lock of a first turn that delivered no visible output was\n * rolled back: the conversation may switch agents again, and its base must\n * return. The failed ACP session is closed with it.\n */\n | { type: 'chat/agent-unlocked'; dshSessionId: string }\n\nexport interface PermissionOptionView {\n optionId: string\n name: string\n kind: string\n}\n\nexport type PermissionOutcome = { outcome: 'selected'; optionId: string } | { outcome: 'cancelled' }\n\n/** Derived session snapshot for replay/reconnect (source of truth stays in the transcript). */\nexport interface SessionView {\n sessionId: string\n agentId: string\n acpSessionId: string\n cwd: string\n title?: string\n modes?: { currentModeId?: string; availableModes?: Array<{ id: string; name?: string; description?: string }> }\n configOptions?: unknown[]\n /** The agent's slash commands, every field it declared — not just the two the picker draws. */\n availableCommands?: Array<{ name: string; description?: string; [key: string]: unknown }>\n plan?: unknown[]\n usage?: { used: number; size: number; cost?: { amount: number; currency: string } }\n running: boolean\n closed: boolean\n /**\n * The owning agent process died and the session awaits a `session/load`\n * resume. Resuming happens implicitly on the next prompt (\"send to\n * recover\") when the agent declares the `loadSession` capability.\n */\n released?: boolean\n /** Highest transcript seq included in this snapshot (for gap detection). */\n seq: number\n}\n\n// ---- Browser → host RPC (`/acp` channel; payloads below) ----\n\nexport type RpcResult<T> = {\n ok: true\n value: T\n} | {\n ok: false\n /** Mirrors the host `ConnectionRpcFailure` shape (details is transport-mandated). */\n error: { code: string; message: string; details: object }\n}\n\nexport interface AgentsListResult {\n agents: AgentView[]\n}\nexport type SessionPromptResult = { accepted: true; turnId: string }\nexport type TranscriptResult = { session: SessionView; entries: Array<{ seq: number; update: SessionUpdate }> }\nexport type SessionsListResult = { sessions: SessionView[] }\n\n/**\n * One ACP agent session currently backing a DSH chat session, looked up by the\n * chat session id the composer already knows. This is what lets the browser\n * expose an agent's OWN mode list, model, thought level AND slash commands in\n * the conversation (the ACP-native controls), independently of DSH's model\n * selection and command catalog.\n */\nexport interface ChatSessionView {\n agentId: string\n /** Display name of the agent definition, when it differs from the id. */\n agentTitle?: string\n /** Host-minted session id (stable across an agent restart). */\n sessionId: string\n title?: string\n /**\n * The adapter model id this session was last aligned to\n * (`agentId` / `agentId@choice`), as recorded by the host half. Absent until\n * the first turn or an explicit switch.\n */\n modelId?: string\n /** The reasoning effort in force, when the agent reports one. */\n reasoningEffort?: string\n /** The agent's declared modes, when it has any (`session/new` / `session/load`). */\n modes?: SessionView['modes']\n /**\n * The slash commands the agent declared (`available_commands_update`). The\n * composer's `/` menu shows THESE in an ACP conversation — the host's own\n * command catalog describes commands this runtime would execute, which the\n * agent can never see.\n */\n availableCommands?: SessionView['availableCommands']\n /** The agent process died; the session awaits a `session/load` resume. */\n released?: boolean\n}\nexport type ChatSessionsListResult = { sessions: ChatSessionView[] }\n\n/**\n * The `session/chat-list` answer: the backing ACP sessions, plus whether that\n * DSH chat session runs the ACP agent preset (host-read from the session log).\n * The composer surfaces fall back to the latter when the client runtime's\n * session rows carry no `agentPreset` — on such hosts it is the only fact\n * source they have.\n */\nexport interface ChatSessionListResult extends ChatSessionsListResult {\n acpMode?: boolean\n /**\n * The agent this conversation is LOCKED to, once its first ACP turn has run.\n *\n * The composer's agent base retires at that point and the composer-edge tag\n * shows this agent; the tag is display-only because a different agent would\n * have none of the conversation's context. The lock is persisted with the\n * conversation record, so it outlives the ACP session (and a host restart),\n * and it is reported even when the agent is no longer in the roster — the\n * conversation still belongs to it.\n */\n lockedAgentId?: string\n /**\n * The conversation's persisted pick (agent + model + thought level), when one\n * exists. This is what the composer reads BEFORE the conversation has an ACP\n * session, so a model chosen up front shows up as the current one instead of\n * appearing to have been forgotten.\n */\n choice?: AcpConversationChoice\n}\n\n/**\n * One ACP session's folded transcript, addressed by its DSH chat session.\n *\n * This is the data behind the conversation's ACP view tab. The native chat\n * plane renders what the LLM adapter emits, so the agent's prose and thinking\n * ride there — but a fuller card (a tool call with its arguments, a plan, a\n * unified diff) has no chunk vocabulary in the adapter's output, so it is\n * rendered here instead. This is how the browser reaches it without knowing an\n * ACP session id up front. See docs/acp-convergence.md §1.2.\n */\nexport interface ChatTranscriptView {\n session: SessionView\n /**\n * Raw ACP `session/update` payloads in seq order (the fold happens\n * client-side). `unknown` is deliberate, matching `TranscriptResult`: the\n * host's buffer is transport-agnostic and must not import the SDK.\n */\n entries: Array<{ seq: number; update: unknown }>\n}\nexport type ChatTranscriptResult = { views: ChatTranscriptView[] }\nexport interface ChatTranscriptPayload { dshSessionId: string; afterSeq?: number }\n\nexport interface SessionStartPayload { agentId: string; cwd?: string }\nexport interface SessionPromptPayload { sessionId: string; text: string }\nexport interface SessionIdPayload { sessionId: string }\nexport interface PermissionRespondPayload { requestId: string; optionId?: string }\nexport interface SessionModeSetPayload { sessionId: string; modeId: string }\nexport interface SessionConfigSetPayload { sessionId: string; configId: string; value: string | boolean }\nexport interface TranscriptGetPayload { sessionId: string; afterSeq?: number }\nexport interface AgentIdPayload { agentId: string }\n/**\n * The plugin's OWN default ACP agent (the panel's ★ gesture): which agent an\n * ACP-mode conversation starts on. Plugin-private state — the host never sees a\n * model route here, and nothing in this plugin writes DSH's model selection\n * (see docs/acp-convergence.md §2.2).\n */\nexport interface DefaultAgentSetPayload { agentId?: string }\n/** `default/agent`: the plugin's own default agent id, or `undefined` when unset. */\nexport type DefaultAgentResult = { agentId?: string }\n/** `plugin/version`: the installed host package's version, for the settings badge. */\nexport type PluginVersionResult = { version?: string }\n\n/**\n * Where the plugin's own update check stands. The host half only ever reports\n * facts it can read from the harness plugin manager and the npm registry — it\n * never downloads, swaps files, or restarts itself.\n */\nexport type PluginUpdateStatus =\n | 'unsupported' | 'not-installed' | 'source-install' | 'managed'\n | 'current' | 'available' | 'applying' | 'busy'\n | 'applied' | 'restart-required' | 'failed'\n\n/**\n * One update-check answer, shared by `plugin/update-check` and\n * `plugin/update-apply`.\n */\nexport interface PluginUpdateState {\n status: PluginUpdateStatus\n /** 运行中(内存里)的插件版本 */\n current: string\n /** 磁盘上装着的版本(listBundles().version),可读时才有 */\n installed?: string\n /** profile registry 链上最新的稳定版 */\n latest?: string\n /** latest 的 npm 发布时间 ISO */\n publishedAt?: string\n /** 实际按顺序问过的 registry */\n registries?: string[]\n /** 失败/额外说明(英文技术信息,UI 自己负责本地化) */\n message?: string\n}\n\n/**\n * How the composer-edge agent tag shows liveness (the blue halo). `breath`\n * pulses the halo at the speed below; `steady` shows a constant glow.\n * Plugin-private display preference, persisted with the agent registry.\n */\nexport interface AcpTagHalo {\n mode: 'breath' | 'steady'\n speed: 'slow' | 'normal' | 'fast'\n}\n/** Default when nothing was ever saved: the breathing halo at normal speed. */\nexport const DEFAULT_TAG_HALO: AcpTagHalo = { mode: 'breath', speed: 'normal' }\n/** `ui/tag-halo`: read the halo preference. */\nexport type TagHaloResult = { halo: AcpTagHalo }\n/** `ui/tag-halo-set`: write the halo preference. */\nexport interface TagHaloSetPayload { halo: AcpTagHalo }\n\n/** The approval deadline the settings page can edit, by field name. ONE window. */\nexport type ApprovalDeadlineKey = 'permissionTimeoutMs'\n\n/**\n * What an EXPIRED approval deadline does. Three modes, and only three — an\n * unanswered ask either gets answered for you, keeps waiting, or is refused.\n *\n * - `abort`: answer `cancelled`. The operation is refused and the agent\n * continues with that answer.\n * - `auto`: answer the ask with the option the AGENT's own kinds imply\n * (`allow_always` → `allow_once` → `reject_once` → `reject_always` → the\n * first option), and the agent CONTINUES executing. This is unattended\n * operation: the plugin may end up approving something nobody looked at,\n * which is why it is opt-in and why the outcome is announced.\n * - `wait` (default): never expire. The ask stays pending, visibly, until a\n * human answers it or the session is cancelled.\n *\n * Same three modes as OmniTerm dev's `acp_perm_timeout_mode`, under the same\n * names. The plugin's own default is `wait`, not `abort` and not `auto`: a\n * permission ask is a question for a human, so the safe default is to keep\n * waiting rather than to refuse (or worse, approve) on their behalf.\n */\nexport type PermissionTimeoutMode = 'abort' | 'auto' | 'wait'\n\n/**\n * What the settings page's approval-deadline card shows: the values in force,\n * plus the facts that explain why a value the user did not pick is winning.\n *\n * `configured` is present only when `cordis.patch.yml` names a key — a settings\n * override outranks it, and the card must say so rather than silently showing\n * the override. `configuredMode` is the same fact about the mode.\n */\nexport interface AcpApprovalDeadlines {\n /**\n * The one wait window, in ms. `0` means 不等待 — the ask is settled on\n * arrival, without asking anyone, and only the `auto` mode runs it.\n * Never expiring is the `mode`'s business.\n *\n * It covers BOTH places an ask can live (the conversation's approval card\n * and the settings panel's ledger): one question, one answer, one number.\n */\n permissionTimeoutMs: number\n /** What an expired deadline does. Always present: `wait` is the default. */\n mode: PermissionTimeoutMode\n /** The config-file value for the window, when the file names one. */\n configured?: Partial<Record<ApprovalDeadlineKey, number>>\n /** The config-file baseline for `mode`, when the file names one. */\n configuredMode?: PermissionTimeoutMode\n}\n/**\n * `ui/approval-deadlines`: read the deadlines in force. `deadlines` is absent\n * on a host half from before the card existed, which hides the card rather than\n * showing a number the browser invented.\n */\nexport type ApprovalDeadlinesResult = { deadlines?: AcpApprovalDeadlines }\n/**\n * One write to the approval-deadline settings: the window, and the mode.\n * Omitted keys keep their current value — a card that only moved the mode\n * must not reset the number.\n */\nexport interface ApprovalDeadlinePatch {\n permissionTimeoutMs?: number\n mode?: PermissionTimeoutMode\n}\n/**\n * `ui/approval-deadlines-set`: write the deadline, or the mode.\n * `reset: true` drops the persisted override so the `cordis.patch.yml`\n * baseline (or the built-in default) applies again.\n */\nexport interface ApprovalDeadlinesSetPayload extends ApprovalDeadlinePatch {\n reset?: boolean\n}\n\n/**\n * `session/chat-bind`: point one DSH chat conversation at an ACP agent. The\n * plugin's own conversation↔agent mapping — deliberately NOT a DSH model\n * selection, which is what polluting the deployment default and getting\n * refused by the GUI's catalog gate both came from.\n */\nexport interface ChatBindPayload { dshSessionId: string; agentId: string }\n/** `session/chat-model-set`: switch the bound agent's model (`agentId@choice`). */\nexport interface ChatModelSetPayload { dshSessionId: string; modelId: string }\n/** `session/chat-effort-set`: switch (or clear, by omitting) the agent's effort; `agentId` names the agent a not-yet-bound conversation is served by (the plugin default). */\nexport interface ChatEffortSetPayload { dshSessionId: string; effort?: string; agentId?: string }\n/** `session/chat-config-set`: set one of the agent's extra config options. */\nexport interface ChatConfigSetPayload { dshSessionId: string; configId: string; value: string | boolean; agentId?: string }\n\n/** Host-detected agent presence and tested defaults. */\nexport interface DetectedAgentInfo {\n id: string\n name: string\n description?: string\n detected: boolean\n executablePath?: string\n defaultCommand: string\n defaultArgs: string[]\n currentConfig?: AcpAgentDef\n}\n\nexport type AgentDetectResult = { agents: DetectedAgentInfo[] }\n\nexport interface AgentSavePayload {\n id: string\n title?: string\n command: string\n args?: string[]\n npmPackage?: string\n cwd?: string\n allowedCwdRoots?: string[]\n env?: Record<string, string>\n exposedModels?: string[]\n /** Opt out of auto-discovery with `false`; absent keeps the default (on). See {@link AcpAgentDef.autoDiscoverModels}. */\n autoDiscoverModels?: boolean\n /** Absent → keep the host default (30 minutes); `0` = never release. */\n idleTimeoutMs?: number\n /** MCP servers to connect to, in the normalized shape. See {@link AcpAgentDef.mcpServers}. */\n mcpServers?: AcpMcpServerDef[]\n}\n\n/** Result of `agent/test`: spawn + initialize a throwaway process from form values, then tear it down. */\nexport interface AgentTestResult {\n ok: boolean\n /** Failure reason when `ok` is false (spawn ENOENT, install failure, initialize timeout, …). */\n error?: string\n /** The agent's `initialize` identity, when the handshake completed. */\n agentInfo?: { name?: string; title?: string; version?: string }\n /** Wall-clock cost of the whole test, including a triggered npm install. */\n durationMs: number\n /** Tail of the child's stderr (and installer output), newest last. */\n stderr: string[]\n}\n\nexport interface AgentExposedModelsPayload {\n agentId: string\n /** Absent → clear the filter (expose no variants); array (even empty) → expose exactly these. */\n exposedModels?: string[]\n}\n\n// ---- Model catalog (host-side cache of ACP `configOptions`) ----\n\n/** One selectable value of a `model` / `thought_level` config option. */\nexport interface AcpCatalogChoice {\n value: string\n name: string\n description?: string\n /**\n * Label of the `SessionConfigSelectGroup` this choice arrived under\n * (commonly the vendor); absent when the agent ships a flat option list.\n * Deliberately never guessed from name/value — vendors cross-list models,\n * so only the agent's own grouping is trustworthy.\n */\n group?: string\n}\n\n/** A parsed `category: model | thought_level` select option for one agent. */\nexport interface AcpCatalogOption {\n configId: string\n name: string\n /**\n * `'boolean'` for an on/off toggle: `currentValue` is the string `'true'` /\n * `'false'` (the catalog stays string-typed so every existing reader keeps\n * working) and `choices` is empty. Absent means the ordinary select.\n */\n kind?: 'boolean'\n /** Currently selected value at parse time (may be absent from `choices`). */\n currentValue: string | null\n choices: AcpCatalogChoice[]\n}\n\n/**\n * The per-category catalog derived from one agent's `configOptions`.\n *\n * `mode` is the agent's own permission/working mode published as a\n * `category: 'mode'` select. It duplicates the `modes` field of `session/new`\n * on the agents observed so far (omp, codebuddy), but it is the only copy a\n * conversation can read BEFORE it has a live session — which is exactly the\n * window in which the composer must still show the control (user report,\n * 2026-10-18: the mode select vanished on a conversation set up ahead of its\n * first message).\n *\n * `extras` are the agent's REMAINING select options (temperature, vendor\n * switches, `model_config` entries, anything unknown). They have no dedicated\n * control in the composer, so they ride the \"advanced\" base to the left of the\n * model chip. Keeping them here — rather than in the browser's raw\n * `configOptions` — is what lets a conversation with no live session show and\n * set them.\n */\nexport interface AcpAgentCatalog {\n model?: AcpCatalogOption\n thought?: AcpCatalogOption\n mode?: AcpCatalogOption\n extras?: AcpCatalogOption[]\n}\n\n/**\n * Everything one DSH chat conversation has chosen for its ACP agent — the\n * plugin's own per-conversation record, persisted with the agent registry.\n *\n * Every field is a PICK, not a live-session fact: a conversation can be set up\n * before it has ever run a turn (that is the whole point — choosing the agent,\n * model and thought level BEFORE the first message rather than discovering you\n * cannot). Nothing here is a DSH model route (see the module header): these\n * values are applied to the agent's `session/new` / `session/set_config_option`\n * on the next turn, and re-applied when a session is recreated or resumed.\n */\nexport interface AcpConversationChoice {\n /** The ACP agent serving this conversation. */\n agentId: string\n /**\n * Whether this conversation has LOCKED its agent: its first ACP turn has\n * STARTED. From then on the agent cannot be switched — a different agent has\n * none of the context — so the composer hides its picker and the host\n * refuses `session/chat-bind` to another agent (`agent_locked`). The lock is\n * provisional until that turn produces a visible block: a first turn that\n * delivers nothing is rolled back and the conversation is free again. Once\n * delivered, the lock is permanent; start a new conversation to use another\n * agent.\n */\n locked?: boolean\n /**\n * The agent's model as an adapter model id (`agentId` = the agent's default,\n * `agentId@choice` = an explicit variant). Absent means \"the agent's default\".\n */\n modelId?: string\n /** The agent's own `thought_level` value. Absent means \"the agent's default\". */\n effort?: string\n /**\n * The agent's own working mode (`session/set_mode`: `plan` / `acceptEdits` /\n * …). Absent means \"whatever the agent starts on\".\n *\n * This is the agent's vocabulary for HOW it works, distinct from the model and\n * the thought level, but it sits in the same composer bar — so it belongs to\n * the same \"remember what the user last chose here\" contract rather than being\n * the one control that resets every time the process restarts.\n */\n mode?: string\n /**\n * The agent's extra `configOptions`, by `configId` (the \"advanced\" base).\n * Absent means \"none chosen\"; individual keys absent mean \"the agent's own\n * current value\". These are the same values `session/set_config_option`\n * accepts, persisted per conversation so a pick survives a restart and is\n * re-applied when its ACP session is recreated or resumed.\n */\n extraConfigs?: Record<string, string | boolean>\n}\n\n/** Result of `agent/probe-models`: the freshly probed (or cached) catalog. */\nexport interface AgentProbeModelsResult {\n agentId: string\n catalog: AcpAgentCatalog\n updatedAt: number\n}\n\n/** One of the agent's extra select options, shaped for the composer's advanced menu. */\nexport interface AgentCatalogExtraOption {\n configId: string\n /** The agent's own option name (falls back to the config id host-side). */\n name: string\n /**\n * `'boolean'` for an on/off toggle (`currentValue` is `'true'` / `'false'`,\n * `choices` is empty and the composer renders a checkbox row). Absent means\n * the ordinary select. Kept optional so cached catalogs written before\n * toggles existed still read.\n */\n kind?: 'boolean'\n /** The value the agent reports as current, when it reports one. */\n currentValue?: string\n choices: Array<{ id: string; name: string; description?: string }>\n}\n\n/**\n * Result of `agent/catalog`: the ACP model surface for ONE agent.\n *\n * THIS IS THE ONLY PLACE ACP MODELS EXIST. DSH builds its provider/model\n * catalog per *request*, not per session — `buildModelCatalog(ctx)` takes no\n * session id, so every session's model directory is that same global list. A\n * provider that advertises models therefore advertises them to every\n * conversation, including the ones where the external agent has no tool\n * registration, no runtime context and is not the model talking at all. The\n * `acp` route consequently advertises nothing (`LlmAdapter.listModels` is\n * advisory — exact ids still resolve and stream), and the plugin's own seats\n * read this per-agent view instead. There is no per-session seam on the host\n * to filter the global catalog with, so \"advertise nothing\" is the only\n * structurally honest isolation.\n *\n * Read-only and memory-only: it never spawns and never starts a probe, not\n * even for an agent with no cached catalog — that case is reported honestly\n * (`probed: false`, `base` entry only). A catalog fills only from a gesture:\n * the agent's own `session/new` / `session/load`, the settings card's\n * `agent/probe-models` button, or an on-disk cache. (Boot-time probing used\n * to exist and no longer does — see `BridgeManager.bootstrapCatalogs`.)\n */\nexport interface AgentCatalogResult {\n agentId: string\n /** Agent display name (`title` ?? id). */\n title: string\n /**\n * The bare-`agentId` entry: the route that means \"let the agent choose its own\n * model\". Its `name` spells out the model that route CURRENTLY resolves to\n * (`<agent> · <model>`) — the bare route is not a model, so naming it after the\n * agent alone left the checked row in the picker saying nothing about which\n * model was in force.\n */\n base: { id: string; name: string; description?: string }\n /** The `agentId@choice` variants this agent exposes (`exposedModels` applied). */\n variants: Array<{ id: string; name: string; description?: string }>\n /**\n * Reasoning efforts from the agent's own `thought_level` option, when it\n * advertises one. Agent-wide by construction (a thought level is not a\n * property of one model), which is why it rides here and not on `variants`.\n */\n reasoning?: LlmModelReasoningInfo\n /**\n * The model the agent itself reports as selected (`model.currentValue`).\n *\n * This is what the model chip shows for a conversation that has not picked\n * anything yet: without it the chip has no label and therefore does not\n * render at all, so an agent the user simply has not tuned yet looks like an\n * agent with no model axis (user report, 2026-10-18). Its absence is\n * meaningful — an agent that publishes no `category: 'model'` select has no\n * model axis, and the chip stays away.\n *\n * NOTE the difference from `base`: `base` is a ROUTE (bare `agentId`), this is\n * a MODEL VALUE (a choice id). The picker needs both — one to name the route,\n * one to mark the row the conversation is actually on.\n */\n currentModel?: { value: string; name: string; description?: string }\n /**\n * The agent's own MODE options (`category: 'mode'`, e.g. codebuddy's\n * `Always Ask` … `Bypass Permissions`).\n *\n * The live composer select reads the `modes` field of `session/new` /\n * `session/load`; this copy exists for the window before one exists. It is\n * the same list on every agent observed so far (omp, codebuddy), and it is\n * the only source a conversation set up ahead of its first message has.\n */\n mode?: {\n /** The agent's current mode id, when it reports one. */\n currentValue?: string\n choices: Array<{ id: string; name: string; description?: string }>\n }\n /**\n * The agent's remaining select options — everything that is not the model,\n * the thought level, or the mode. Empty/absent means the composer shows no\n * advanced base.\n */\n extras?: AgentCatalogExtraOption[]\n /**\n * The slash commands this agent declared in its most recent session\n * (`available_commands_update`), cached per agent.\n *\n * A live session reports its own list through `session/chat-list`, and that\n * one always wins. This copy exists for the window BEFORE a conversation has\n * an ACP session: typing `/` in a fresh NTR conversation must list the\n * agent's commands, the way the native menu lists DSH's — waiting for the\n * first message to discover them is what makes the menu look empty.\n */\n commands?: SessionView['availableCommands']\n /** When those commands were last observed. */\n commandsUpdatedAt?: number\n /** A probe has produced a catalog for this agent (`false` → base entry only). */\n probed: boolean\n /** When the cached catalog was last refreshed. */\n updatedAt?: number\n}\n\n// ---- LLM Streaming & Adapter types (shared with DSH core LLM service) ----\n\nexport interface LlmModelInfo {\n provider: string\n id: string\n name: string\n description?: string\n inputModalities?: readonly string[]\n}\n\n/** One reasoning effort advertised by a model (mirrors DSH `LlmReasoningEffortInfo`). */\nexport interface LlmReasoningEffortInfo {\n id: string\n name: string\n description?: string\n}\n\n/** Reasoning metadata (mirrors DSH `LlmModelReasoningInfo`): `defaultEffort` must be one of `efforts`. */\nexport interface LlmModelReasoningInfo {\n efforts: readonly LlmReasoningEffortInfo[]\n defaultEffort?: string\n}\n\nexport interface LlmResolvedModelInfo extends LlmModelInfo {\n context?: { contextWindow: number }\n defaultMaxTokens?: number\n reasoning?: LlmModelReasoningInfo\n}\n\nexport interface LlmProviderInfo {\n id: string\n name: string\n}\n\nexport type StreamChunk =\n | { type: 'block-start'; index: number; blockType: 'text' | 'reasoning' | 'tool-call' | 'tool-result' }\n | { type: 'text-delta'; index: number; text: string }\n | { type: 'reasoning-delta'; index: number; text: string }\n | { type: 'tool-call-delta'; index: number; id: string; name?: string; argumentsDelta: string }\n | { type: 'block-end'; index: number; block: unknown }\n | { type: 'usage'; usage: { inputTokens: number; outputTokens: number } }\n | {\n type: 'finish'\n reason:\n | { kind: 'stop' }\n | { kind: 'aborted'; failure?: { message: string; code: string } }\n | { kind: 'error'; failure?: { message: string; code: string } }\n }\n\n/**\n * Provenance of one message in a host-built request. The host tags every\n * message, and the tag is the only way to tell the user's own words apart from\n * the context sections the harness injects around them (`agent-instructions`,\n * `skill-catalog`, the `@deepseek-ai/dsh-system-prompt` runtime snapshot, tool\n * results) — see AGENTS.md rule 16.\n */\nexport interface GenerateMessageSource {\n kind?: string\n plugin?: string\n form?: string\n [k: string]: unknown\n}\n\nexport interface GenerateMessage {\n role: 'system' | 'user' | 'assistant'\n content: Array<{ type: string; text?: string; [k: string]: unknown }>\n /** Host provenance; absent sources are treated as real user input (hand-built callers). */\n source?: GenerateMessageSource\n /** Stable identity of the durable message, when the host built the request from a session log. */\n id?: string\n}\n\nexport interface GenerateOptions {\n provider: string\n model: string\n /** Adapter-owned reasoning effort selected for this exact model. */\n reasoningEffort?: string\n messages: GenerateMessage[]\n system?: string\n signal?: AbortSignal\n sessionId?: string\n /**\n * Auxiliary (non-conversation) call classification, e.g. `session-title` or\n * `compaction`. Ordinary turns leave it unset; an ACP bridge must never turn\n * one of these into a remote agent turn.\n */\n purpose?: string\n}\n\nexport interface PreparedAdapterCall {\n readonly model: LlmResolvedModelInfo\n stream(options: GenerateOptions): AsyncIterable<StreamChunk>\n}\n\nexport interface LlmAdapterLike {\n providerInfo?(provider: string): LlmProviderInfo\n providerRetryPolicy?(provider: string): unknown\n imageRequestPricing?(provider: string, model: string): unknown\n listModels?(provider: string): Promise<readonly LlmModelInfo[]>\n resolveModel?(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>\n prepareCall?(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>\n stream(options: GenerateOptions): AsyncIterable<StreamChunk>\n}\n\n","/**\n * Browser-side state store for the ACP panel. Owns: the `/acp` RPC calls, the\n * `/api/acp/stream` EventSource, per-session block folding (raw ACP\n * `session/update` → render blocks), and the pending permission set.\n *\n * Deliberately framework-free: React binds through `useSyncExternalStore`.\n */\n\nimport type { AcpApprovalDeadlines, AcpConversationChoice, AcpEvent, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentSavePayload, AgentTestResult, AgentView, ChatSessionView, ChatTranscriptView, ApprovalDeadlinesSetPayload, ApprovalDeadlinesResult, DetectedAgentInfo, PermissionTimeoutMode, PluginUpdateState, PluginVersionResult, SessionView } from '../protocol.ts'\nimport { DEFAULT_TAG_HALO } from '../protocol.ts'\n\n/** One slash command the ACP agent declared, whole row (the menu draws name, description and `input.hint`). */\nexport type AgentCommand = NonNullable<ChatSessionView['availableCommands']>[number]\n\n/** The unary RPC face (narrowed `ctx.connection.rpc.call`). */\nexport type RpcCall = (channel: string, endpoint: string, payload: unknown) => Promise<\n | { ok: true; value: unknown }\n | { ok: false; error: { code: string; message: string } }\n>\n\nexport type Block =\n | { kind: 'user'; text: string }\n | { kind: 'agent'; messageId?: string; text: string }\n | { kind: 'thought'; text: string }\n | {\n kind: 'tool'\n toolCallId: string\n title: string\n status: string\n toolKind?: string\n lines: string[]\n /**\n * Structured ACP diffs (`content: [{type:'diff'}]`). Kept apart from\n * {@link lines} because a diff is the one tool payload worth rendering as\n * a diff: flattening it to `diff <path>` throws away everything that makes\n * an edit reviewable.\n */\n diffs?: Array<{ path: string; oldText: string | null; newText: string }>\n /** Text items the card cannot render (diff output, an unknown content kind's JSON). */\n extras?: string[]\n /**\n * The call's arguments, as the agent sent them (`rawInput` / `arguments`).\n * Rendered read-only under the header: the real model's view of \"it ran\n * `ls -la`\" is that line, and the old fold dropped it entirely.\n */\n input?: string\n /** The call's other named fields (`kind`, `status`, `locations`, `terminalId`, `meta`), one `key: value` per line. */\n details?: string[]\n /** The whole notification this block was last written from, pretty-printed. */\n raw?: string\n }\n | { kind: 'plan'; entries: Array<{ content: string; status: string; priority?: string }> }\n /**\n * Any other ACP notification, kept whole: an available-commands list, a mode\n * change, or a kind newer than this build. `text` is the readable fold and\n * `raw` the untouched payload — an agent's word the ACP view cannot format is\n * still an agent's word.\n */\n | { kind: 'event'; event: string; text: string; raw?: string; status?: string }\n | { kind: 'notice'; tone: 'info' | 'error'; text: string }\n\n/**\n * One permission ask waiting for a human. Every field is copied VERBATIM from\n * the host's `permission/request` frame: the host owns what the ask is called\n * (`toolName` / `reason`), which options the agent offered (order included),\n * and whether the ask carries a deadline. The browser does not derive a title\n * from `toolCall` and does not second-guess the agent's option labels.\n */\nexport interface PendingPermission {\n requestId: string\n /** ACP session id. */\n sessionId: string\n /** DSH chat session id; absent for `/acp` RPC-only sessions (settings-only ask). */\n dshSessionId?: string\n toolName: string\n reason?: string\n options: Array<{ optionId: string; name: string; kind: string }>\n timeoutMode: PermissionTimeoutMode\n /** Epoch ms; absent in `wait` mode. */\n deadlineAt?: number\n}\n\n/** Returned by {@link deadlineRemainingLabel} once the deadline has passed. */\nexport const PERMISSION_DEADLINE_EXPIRED = 'expired'\n\n/**\n * The asks that belong to one DSH chat conversation. An ask without a\n * `dshSessionId` belongs to no conversation (an `/acp` RPC-only session), so it\n * is deliberately absent from every conversation's dock and stays in the\n * settings-panel ledger.\n */\nexport function permissionsForSession(\n permissions: readonly PendingPermission[],\n dshSessionId: string,\n): PendingPermission[] {\n return permissions.filter(permission => permission.dshSessionId === dshSessionId)\n}\n\n/**\n * Compact countdown for one ask, or `undefined` when there is nothing to count\n * down (`wait` mode, or a frame without a deadline). Pure so the formatting is\n * testable without a clock: the dock passes `Date.now()` on its tick.\n *\n * Past the deadline the answer is {@link PERMISSION_DEADLINE_EXPIRED} rather\n * than a negative number — the host has already armed the expiry, and a card\n * counting backwards would claim a deadline that no longer exists.\n */\nexport function deadlineRemainingLabel(\n deadlineAt: number | undefined,\n now: number,\n mode: PermissionTimeoutMode,\n): string | undefined {\n if (mode === 'wait' || deadlineAt === undefined) return undefined\n const remainingMs = deadlineAt - now\n if (remainingMs <= 0) return PERMISSION_DEADLINE_EXPIRED\n const seconds = Math.ceil(remainingMs / 1000)\n if (seconds < 60) return `${String(seconds)}s`\n const minutes = Math.floor(seconds / 60)\n const rest = seconds % 60\n return rest === 0 ? `${String(minutes)}m` : `${String(minutes)}m ${String(rest)}s`\n}\n\n/**\n * One observed ACP transcript, keyed by ACP session id. This is the ACP view\n * tab's data source: the native chat plane can only render text/reasoning\n * (that is the LLM adapter's entire chunk vocabulary), so the real tool calls,\n * plans and diffs live here and nowhere else.\n */\nexport interface ChatTranscript {\n /** The DSH chat session this ACP session belongs to (its refetch address). */\n owner: string\n session: SessionView\n blocks: Block[]\n /** Highest folded transcript seq (gap detection for the live stream). */\n seq: number\n}\n\n/**\n * One line of the settings page's ACP runtime log. Agent-side notices and\n * session-release reasons land here (bounded, append order oldest→newest);\n * failures of the settings page's OWN actions stay in {@link UiState.notice}.\n */\nexport interface AcpLogEntry {\n id: number\n at: number\n level: 'info' | 'error'\n text: string\n agentId?: string\n}\n\n/** Cap on retained runtime-log lines; older entries fall off the front. */\nexport const LOG_MAX_ENTRIES = 200\n\nexport interface UiState {\n open: boolean\n /**\n * Bumped by {@link AcpStore.refreshLocale}. Labels resolve while rendering,\n * but memoized transcript rows compare props — this counter is what tells\n * them the language switched.\n */\n localeRev: number\n stream: 'init' | 'live' | 'down'\n agents: AgentView[]\n /**\n * Whether the host has answered `agents/list` at least once in this page.\n * `agents: []` alone cannot tell \"no agent configured\" from \"not read yet\";\n * a surface that offers a setup entry on an empty roster must wait for this.\n */\n agentsLoaded: boolean\n sessions: SessionView[]\n activeSessionId?: string\n blocks: Block[]\n permissions: PendingPermission[]\n busy: boolean\n /**\n * The settings page's one-line banner for its OWN action failures. Agent-side\n * runtime messages do not land here; they append to {@link logs} instead.\n */\n notice?: string\n /** Agent-side runtime log, append order oldest→newest, capped at {@link LOG_MAX_ENTRIES}. */\n logs: AcpLogEntry[]\n /** Lines appended since the user last opened the log; reset by {@link AcpStore.markLogsRead}. */\n logUnread: number\n /**\n * The plugin's OWN default ACP agent (the panel's ★ gesture) — which agent a\n * new ACP-mode conversation starts on. NOT the deployment default model: that\n * one is global to every mode, and nothing in this bundle writes it.\n */\n defaultAgent?: string\n detectedAgents: DetectedAgentInfo[]\n detecting: boolean\n /**\n * Outcome of the last explicit re-detect (`重新检测与刷新`). Detection\n * usually finds exactly what the panel already renders — without this line\n * the button reads as dead. Set only by an explicit click, not the ambient\n * resync, and cleared again when that click's detection fails (a stale success\n * line next to the failure notice reads as if nothing happened).\n */\n detectSummary?: string\n /** Probed model catalogs per agent id (from `agent/probe-models`). */\n modelCatalogs: Record<string, AgentProbeModelsResult>\n /**\n * Per-agent ACP model surfaces (from `agent/catalog`) — the data source for\n * the conversation-side chips and the ACP-mode model seat. Kept apart from\n * {@link modelCatalogs} on purpose: that one is the panel's full probe\n * result (every choice an agent published), while this is the filtered,\n * ready-to-render list the seats offer.\n */\n seatCatalogs: Record<string, AgentCatalogResult>\n probingModels: Record<string, boolean>\n /**\n * Last probe failure per agent id, rendered next to the button that\n * triggered it. `notice` alone is not enough: it lives at the top of the\n * settings panel, so a probe failing below the fold (missing cwd, an agent\n * that never answers, …) reads as a dead button.\n */\n probeErrors: Record<string, string>\n /**\n * ACP agent sessions backing the chat session the composer control sits in.\n * Only meaningful together with {@link chatSessionsFor}: the control reads it\n * by chat session id, so a stale list from another conversation never renders.\n */\n chatSessions: ChatSessionView[]\n /** The DSH chat session id {@link chatSessions} was loaded for. */\n chatSessionsFor?: string\n /**\n * The agent tag's halo preference (settings-editable display choice). The\n * tag reads it per render; the host answer in `resync` is the authority and\n * a write lands optimistically first.\n */\n tagHalo: AcpTagHalo\n /**\n * The approval deadlines in force, as the settings card edits them. Absent on\n * a host half from before the card existed, which hides the card rather than\n * showing a number this browser invented. Writes land optimistically and the\n * host answer (returned by the same route) is the authority.\n */\n approvalDeadlines?: AcpApprovalDeadlines\n /**\n * Per-chat-session \"does this conversation run the ACP preset\" verdicts, as\n * reported by `session/chat-list` (host-read from the session log). The\n * composer surfaces fall back to this when the client runtime's session rows\n * carry no `agentPreset` (older hosts); a session never asked about is simply\n * absent.\n */\n acpMode: Record<string, boolean>\n /**\n * Per-chat-session ACP agent binding, as reported by `session/chat-list`'s\n * `boundAgentId` (the host's intent map plus its live-session fallback): who\n * serves this conversation's NEXT message. `session/chat-bind` writes the\n * optimistic entry here the moment the click succeeds — the live session\n * list cannot reflect a switch until a turn materializes a session for the\n * new agent, and the chip would otherwise keep naming the previous agent.\n */\n chatBoundAgent: Record<string, string>\n /**\n * Per-chat-session agent lock, as reported by `chat/agent-locked` (the live\n * path) or `session/chat-list`'s `lockedAgentId` (the fallback): the first\n * ACP turn has STARTED, so this conversation belongs to that agent and\n * cannot be switched. The lock is provisional until that turn shows\n * something; `chat/agent-unlocked` removes it when it delivers nothing. The\n * composer hides its agent base and shows a read-only tag for this agent.\n * The id is kept even when the agent has been deleted from the roster — the\n * conversation is still locked to it.\n */\n chatLockedAgent: Record<string, string>\n /**\n * Per-chat-session serial for a lock THIS page saw arrive — bumped by the\n * `chat/agent-locked` frame (the live path, sent when the first turn starts)\n * or by a later `session/chat-list` answer that first reports `lockedAgentId`\n * (the fallback when the frame was missed). A rolled-back first turn followed\n * by a retry therefore gets a fresh serial, and its ceremony plays again.\n *\n * This is the animation's trigger, and the reason it cannot replay on a page\n * load: a conversation that is already locked when its list is first read\n * has no entry here. The composer's base and its edge tag\n * (`agent-lock-anim.ts`) play the shatter/condense hand-off only for a serial\n * they have not consumed.\n */\n agentLockReveal: Record<string, number>\n /**\n * The conversation's persisted pick — model and thought level — as reported by\n * `session/chat-list`'s `choice`, and written optimistically the moment a\n * picker commits one.\n *\n * This is the record that makes a conversation usable BEFORE it has ever run\n * a turn: without it the model / thought-level chips can only reflect a live\n * ACP session, which does not exist until the first message has already gone\n * out on whatever the agent's defaults were.\n */\n chatChoice: Record<string, AcpConversationChoice>\n /**\n * The model-seat shadow gate's own account of what it last decided, and why\n * (`src/client/index.tsx`'s `syncAcpMode`). Read-only, human-facing, and\n * rendered by the ACP view: every branch of that gate ends with the native\n * model seat either shadowed or not, and without this line the four ways it\n * can end \"not shadowed\" are indistinguishable from the outside — which is\n * exactly how \"the ACP base still has not been replaced\" became unreadable.\n */\n seatDiag: string\n /**\n * Diagnostic summary from the permission-seat blanking gate (`conversation.input.permission`).\n */\n permissionSeatDiag: string\n /**\n * Why slash isolation is not in force, when it is not (`idle` while it is).\n * Isolation degrades OPEN — an unrecognized source shape leaves the host's\n * commands in charge — so without this line the only symptom would be a menu\n * that quietly lists the wrong commands.\n */\n slashDiag: string\n /**\n * Observed ACP transcripts keyed by ACP session id. Only sessions the\n * conversation's ACP view asked for are watched (live `session/update`\n * frames fold straight into them), and the map is capped so a long browsing\n * session cannot accumulate every transcript it ever looked at.\n */\n chatTranscripts: Record<string, ChatTranscript>\n /**\n * State of the host's ACP-mode agent preset IN THE HARNESS ROSTER (the entry\n * the new-session picker shows). `undefined` when the host serves no roster\n * (`preset/status` answering `null`), in which case the panel hides the block.\n */\n preset?: AcpPresetStatus\n /**\n * The installed host package's version (`plugin/version`), rendered as the\n * settings page badge. Absent on a host half without the endpoint, which\n * hides the badge rather than naming a version this browser invented.\n */\n pluginVersion?: string\n /**\n * The running version and its update status (`plugin/update-check`). Absent\n * on a host half without the endpoint, which hides the update entry instead\n * of offering an install this browser cannot perform.\n */\n pluginUpdate?: PluginUpdateState\n}\n\ninterface Listener { (): void }\n\nconst MAX_BLOCKS = 400\n/** How many observed ACP transcripts stay live at once (insertion-ordered eviction). */\nconst MAX_WATCHED = 6\n\n/** Raw JSON budget for one event block; the cut is marked. */\nconst RAW_MAX = 256_000\n\n/**\n * How long a fetched update state stays fresh. The settings panel asks on every\n * mount; without this, every open would hit the registry. A forced check (the\n * card's own button) always goes out.\n */\nconst PLUGIN_UPDATE_TTL_MS = 10 * 60 * 1000\n\n/** Pretty-printed JSON of an agent message (never throws, cuts only at {@link RAW_MAX} with a marker). */\nexport function jsonText(value: unknown): string {\n let text: string\n try {\n text = JSON.stringify(value, null, 2) ?? String(value)\n } catch {\n text = String(value)\n }\n return text.length > RAW_MAX\n ? `${text.slice(0, RAW_MAX)}\\n… [truncated ${String(text.length - RAW_MAX)} chars]`\n : text\n}\n\n/**\n * The call's arguments as the agent sent them. Accepts every spelling seen in\n * the wild (`rawInput` / `arguments`, string / object / array) and never\n * invents one: a call with no input gets `undefined`.\n */\nexport function toolInputOf(update: Record<string, unknown>): string | undefined {\n for (const key of ['rawInput', 'arguments'] as const) {\n const value = update[key]\n if (typeof value === 'string') {\n if (value !== '') return value\n continue\n }\n if (Array.isArray(value)) {\n if (value.length > 0) return JSON.stringify(value, null, 2)\n continue\n }\n if (typeof value === 'object' && value !== null) return JSON.stringify(value, null, 2)\n }\n return undefined\n}\n\n/** The call's other named fields, one `key: value` per line. */\nexport function toolDetailsOf(update: Record<string, unknown>): string[] {\n const lines: string[] = []\n for (const key of ['kind', 'status', 'locations', 'terminalId', 'meta'] as const) {\n const value = update[key]\n if (value === undefined || value === null || value === '') continue\n if (key === 'locations' && Array.isArray(value) && value.length === 0) continue\n lines.push(`${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`)\n }\n return lines\n}\n\n/** Text a `text` / `thinking` chunk's `content` carries (string, object, or array of blocks). */\nfunction chunkText(content: unknown): string {\n if (typeof content === 'string') return content\n if (Array.isArray(content)) {\n return content.map(item => {\n if (typeof item === 'string') return item\n if (typeof item === 'object' && item !== null) {\n const row = item as Record<string, unknown>\n return typeof row.text === 'string' ? row.text : JSON.stringify(item)\n }\n return ''\n }).join('')\n }\n if (typeof content === 'object' && content !== null) {\n const row = content as Record<string, unknown>\n if (typeof row.text === 'string') return row.text\n return JSON.stringify(content)\n }\n return ''\n}\n\n/**\n * One tool call's `content`, split into what the card renders as text\n * ({@link lines}), the structured edits it renders as diffs, and everything\n * else as its own readable text ({@link extras}). Nothing is dropped: a diff is\n * ALSO shown as text (header plus `-`/`+` lines) because the model's view of a\n * tool result is text, and a kind this build cannot render becomes its full\n * JSON.\n */\nfunction splitToolContent(content: unknown): { lines: string[]; diffs?: Array<{ path: string; oldText: string | null; newText: string }>; extras: string[] } {\n const lines: string[] = []\n const extras: string[] = []\n const diffs: Array<{ path: string; oldText: string | null; newText: string }> = []\n if (!Array.isArray(content)) {\n if (typeof content === 'string' && content !== '') lines.push(content)\n return { lines, extras }\n }\n for (const item of content) {\n if (typeof item === 'string') {\n if (item !== '') lines.push(item)\n continue\n }\n if (typeof item !== 'object' || item === null) continue\n const row = item as Record<string, unknown>\n if (row.type === 'content') {\n const inner = row.content as Record<string, unknown> | undefined\n if (typeof inner === 'string') {\n lines.push(inner)\n } else if (inner?.type === 'text') {\n lines.push(typeof inner.text === 'string' ? inner.text : JSON.stringify(item))\n } else if (inner?.type === 'diff' && typeof inner.path === 'string') {\n const diff = { path: inner.path, oldText: typeof inner.oldText === 'string' ? inner.oldText : null, newText: typeof inner.newText === 'string' ? inner.newText : '' }\n diffs.push(diff)\n lines.push(diffText(diff))\n } else {\n extras.push(`${String(inner?.type ?? 'content')} ${JSON.stringify(item)}`)\n }\n continue\n }\n if (row.type === 'diff' && typeof row.path === 'string' && typeof row.newText === 'string') {\n const diff = { path: row.path, oldText: typeof row.oldText === 'string' ? row.oldText : null, newText: row.newText }\n diffs.push(diff)\n lines.push(diffText(diff))\n continue\n }\n if (row.type === 'text' && typeof row.text === 'string') {\n lines.push(row.text)\n continue\n }\n extras.push(`${String(row.type ?? 'content')} ${JSON.stringify(item)}`)\n }\n return { lines, extras, ...(diffs.length === 0 ? {} : { diffs }) }\n}\n\n/** A structured edit as plain text: its path, then `-`/`+` lines with three lines of context. */\nfunction diffText(diff: { path: string; oldText: string | null; newText: string }): string {\n const split = (text: string | null): string[] => text === null || text === '' ? [] : (text.endsWith('\\n') ? text.slice(0, -1) : text).split('\\n')\n const oldLines = split(diff.oldText)\n const newLines = split(diff.newText)\n let head = 0\n while (head < oldLines.length && head < newLines.length && oldLines[head] === newLines[head]) head++\n let tail = 0\n while (\n tail < oldLines.length - head\n && tail < newLines.length - head\n && oldLines[oldLines.length - 1 - tail] === newLines[newLines.length - 1 - tail]\n ) tail++\n const out: string[] = [diff.path]\n for (let i = Math.max(0, head - 3); i < head; i++) out.push(` ${oldLines[i] ?? ''}`)\n for (let i = head; i < oldLines.length - tail; i++) out.push(`- ${oldLines[i] ?? ''}`)\n for (let i = head; i < newLines.length - tail; i++) out.push(`+ ${newLines[i] ?? ''}`)\n for (let i = oldLines.length - tail; i < oldLines.length; i++) out.push(` ${oldLines[i] ?? ''}`)\n return out.join('\\n')\n}\n\n/** The readable fold of a notification the view has no card of its own for. */\nexport function describeEvent(kind: string, update: Record<string, unknown>): string {\n if (kind === 'available_commands_update') {\n const commands = Array.isArray(update.availableCommands) ? update.availableCommands : []\n return commands.map(command => {\n const row = (typeof command === 'object' && command !== null ? command : {}) as Record<string, unknown>\n const name = typeof row.name === 'string' ? row.name : ''\n const description = typeof row.description === 'string' && row.description !== '' ? ` — ${row.description}` : ''\n return `${name}${description}`\n }).join('\\n')\n }\n if (kind === 'plan') {\n const entries = Array.isArray(update.entries) ? update.entries : []\n return entries.map(entry => {\n const row = (typeof entry === 'object' && entry !== null ? entry : {}) as Record<string, unknown>\n return `[${String(row.status ?? 'pending')}] ${String(row.content ?? '')}`\n }).join('\\n')\n }\n if (kind === 'current_mode_update' && typeof update.currentModeId === 'string') return update.currentModeId\n return ''\n}\n\nexport const DEFAULT_KNOWN_PRESETS: DetectedAgentInfo[] = [\n {\n id: 'pi',\n name: 'Pi Coding Agent',\n description: 'Automata / Pi Coding Agent with native ACP bridge support',\n detected: false,\n defaultCommand: 'pi-acp',\n defaultArgs: [],\n },\n {\n id: 'omp',\n name: 'Oh My Pi',\n description: 'Oh My Pi (pi-based coding agent) — run as `omp acp` over stdio',\n detected: false,\n defaultCommand: 'omp',\n defaultArgs: ['acp'],\n },\n // Hand copy of the host's `AGENT_PRESETS` entry (src/bridge.ts): the panel\n // falls back to this list before the first `agents/detect` round-trip.\n {\n id: 'codebuddy',\n name: 'CodeBuddy Code',\n description: 'Tencent CodeBuddy Code — run as `codebuddy --acp` over stdio',\n detected: false,\n defaultCommand: 'codebuddy',\n defaultArgs: ['--acp'],\n },\n {\n id: 'claude',\n name: 'Claude Code',\n description: 'Claude Agent SDK adapter — run as `npx -y @agentclientprotocol/claude-agent-acp` over stdio',\n detected: false,\n defaultCommand: 'npx',\n defaultArgs: ['-y', '@agentclientprotocol/claude-agent-acp'],\n },\n {\n id: 'codex',\n name: 'Codex',\n description: 'OpenAI Codex adapter — run as `npx -y @agentclientprotocol/codex-acp` over stdio',\n detected: false,\n defaultCommand: 'npx',\n defaultArgs: ['-y', '@agentclientprotocol/codex-acp'],\n },\n {\n id: 'gemini',\n name: 'Gemini CLI',\n description: 'Google Gemini CLI — run as `gemini --experimental-acp` over stdio',\n detected: false,\n defaultCommand: 'gemini',\n defaultArgs: ['--experimental-acp'],\n },\n {\n id: 'opencode',\n name: 'OpenCode',\n description: 'OpenCode — run as `opencode acp` over stdio',\n detected: false,\n defaultCommand: 'opencode',\n defaultArgs: ['acp'],\n },\n {\n id: 'cursor',\n name: 'Cursor CLI',\n description: 'Cursor CLI — run as `agent acp` over stdio (legacy alias `cursor-agent acp`)',\n detected: false,\n defaultCommand: 'agent',\n defaultArgs: ['acp'],\n },\n {\n id: 'qwen',\n name: 'Qwen Code',\n description: 'Qwen Code — run as `qwen --experimental-acp` over stdio',\n detected: false,\n defaultCommand: 'qwen',\n defaultArgs: ['--experimental-acp'],\n },\n {\n id: 'kiro',\n name: 'Kiro',\n description: 'Kiro CLI — run as `kiro-cli acp` over stdio',\n detected: false,\n defaultCommand: 'kiro-cli',\n defaultArgs: ['acp'],\n },\n {\n id: 'qoder',\n name: 'Qoder',\n description: 'Qoder CLI — run as `qodercli --acp` over stdio',\n detected: false,\n defaultCommand: 'qodercli',\n defaultArgs: ['--acp'],\n },\n {\n id: 'qoder-cn',\n name: 'Qoder CN',\n description: 'Qoder CLI (China) — run as `qoderclicn --acp` over stdio',\n detected: false,\n defaultCommand: 'qoderclicn',\n defaultArgs: ['--acp'],\n },\n]\n\nexport class AcpStore {\n private state: UiState = {\n open: false,\n localeRev: 0,\n stream: 'init',\n agents: [],\n agentsLoaded: false,\n sessions: [],\n blocks: [],\n permissions: [],\n busy: false,\n logs: [],\n logUnread: 0,\n detectedAgents: [...DEFAULT_KNOWN_PRESETS],\n detecting: false,\n modelCatalogs: {},\n seatCatalogs: {},\n probingModels: {},\n probeErrors: {},\n chatSessions: [],\n tagHalo: { ...DEFAULT_TAG_HALO },\n approvalDeadlines: undefined,\n chatTranscripts: {},\n acpMode: {},\n chatBoundAgent: {},\n chatLockedAgent: {},\n agentLockReveal: {},\n chatChoice: {},\n seatDiag: 'idle',\n permissionSeatDiag: 'idle',\n slashDiag: 'idle',\n }\n\n private readonly listeners = new Set<Listener>()\n private source?: EventSource\n /** Monotonic id source for {@link AcpLogEntry}s (never reset by `clearLogs`). */\n private logSeq = 0\n private readonly lastSeq = new Map<string, number>()\n /** Sessions with a finalized turn (dedups the cancel-fallback + late real turn/end pair). */\n private readonly openTurns = new Set<string>()\n /** In-flight `session/chat-list` reads, keyed by chat session id. */\n private readonly chatSessionReads = new Map<string, { dropped: boolean; done: Promise<void> }>()\n /**\n * Chat sessions whose list has answered at least once in this page. The\n * FIRST answer only seeds the lock; a lock appearing on a later answer is a\n * transition this page actually witnessed, and only that one is announced via\n * {@link UiState.agentLockReveal}.\n */\n private readonly chatListLoaded = new Set<string>()\n /**\n * Conversations whose provisional first-turn lock the host rolled back\n * (`chat/agent-unlocked`, no visible output in the first turn). A\n * `session/chat-list` read that started BEFORE the rollback can still name\n * `lockedAgentId`; it must not revive the lock. A new `chat/agent-locked`\n * frame clears the entry (the user retried and the turn really started).\n */\n private readonly chatUnlocked = new Set<string>()\n /**\n * Sessions whose ACP-mode verdict a committed `agent-preset/selected` frame\n * settled. A frame settles the verdict; the host's log read can lag a switch\n * by one (the appended row is not yet visible to a fresh read, and the seat\n * taking the model cell re-mounts readers), so a read may only settle a\n * session no frame has spoken for — otherwise a stale answer would regress a\n * verdict the runtime already told us the truth about.\n *\n * The ONE exception is the gate's own re-ask ({@link loadChatSessions} with\n * `settleVerdict`): a verdict the gate has held as its only fact source for\n * longer than its TTL is exactly the case where the frame path has gone\n * quiet, and without an authorized re-read a switch the runtime never framed\n * could never be corrected.\n */\n private readonly acpModeLive = new Set<string>()\n /**\n * Chat sessions whose host read came back WITHOUT a verdict (no `acpMode`\n * field, or the read failed). The seat gate consults this to retract its\n * one-shot ask ledger entry, so a read that could not answer costs one retry\n * instead of leaving the native model seat visible for the rest of the page.\n */\n private readonly acpModeUnsettled = new Set<string>()\n /** In-flight `agent/catalog` reads, keyed by agent id. */\n private readonly seatCatalogReads = new Set<string>()\n /** In-flight `agent/cached-models` reads, keyed by agent id. */\n private readonly cachedModelReads = new Set<string>()\n /** Counts behind the last successful explicit re-detect, so a locale switch can re-render its summary. */\n private detectCounts: { total: number; installed: number } | undefined\n /** When {@link UiState.pluginUpdate} was last read from the host (epoch ms). */\n private pluginUpdateFetchedAt = 0\n /**\n * The shared in-flight `plugin/update-check`. The settings section mounts\n * twice (both entries) and the update popover asks too; without this, two\n * mounts in the same tick each fire their own registry read.\n */\n private pluginUpdateInflight?: Promise<PluginUpdateState | undefined>\n /** Re-entry guard for `plugin/update-apply` (a double click is one install). */\n private pluginUpdateApplying = false\n /**\n * Update read generation. An apply bumps it so a check that was already in\n * flight (started before the install) cannot land afterwards and overwrite\n * the install's verdict with the pre-install `available`.\n */\n private pluginUpdateGeneration = 0\n\n constructor(private readonly rpc: RpcCall, private readonly locale: (zh: string, en: string) => string) {}\n\n // ---- React bridge ----\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n getSnapshot(): UiState {\n return this.state\n }\n\n private mutate(patch: Partial<UiState>, blocksChanged = false): void {\n this.state = {\n ...this.state,\n ...patch,\n blocks: blocksChanged ? [...this.state.blocks] : patch.blocks ?? this.state.blocks,\n }\n for (const listener of [...this.listeners]) listener()\n }\n\n /**\n * Build the log patch for one line. Does NOT mutate: the caller merges it\n * into the single `mutate` call it was already making for the same event.\n */\n private logPatch(level: 'info' | 'error', text: string, agentId?: string): Pick<UiState, 'logs' | 'logUnread'> {\n const entry: AcpLogEntry = {\n id: ++this.logSeq,\n at: Date.now(),\n level,\n text,\n ...(agentId === undefined ? {} : { agentId }),\n }\n return {\n logs: [...this.state.logs, entry].slice(-LOG_MAX_ENTRIES),\n logUnread: this.state.logUnread + 1,\n }\n }\n\n // ---- Streaming update coalescing ----\n\n /**\n * Live `session/update` frames waiting to be folded. Agents stream at token\n * granularity — hundreds of frames a second with usage ticks interleaved —\n * and folding plus notifying listeners per frame re-rendered every store\n * consumer per chunk. Frames are coalesced into ONE fold + ONE mutate per\n * animation frame instead; the order inside a batch is preserved, and the\n * transcript watermark (`lastSeq`) advances per batch.\n */\n private readonly pendingUpdates: Array<{ sessionId: string; seq: number; update: unknown }> = []\n private updateFlushArmed = false\n\n /** Fold everything pending now (idempotent; the scheduler clears its flag). */\n flushUpdates(): void {\n const batch = this.pendingUpdates.splice(0, this.pendingUpdates.length)\n this.updateFlushArmed = false\n if (batch.length === 0) return\n const bySession = new Map<string, Array<{ seq: number; update: unknown }>>()\n for (const item of batch) {\n const list = bySession.get(item.sessionId)\n if (list === undefined) bySession.set(item.sessionId, [item])\n else list.push(item)\n }\n let chatTranscripts = this.state.chatTranscripts\n let blocksChanged = false\n for (const [sessionId, items] of bySession) {\n const last = items[items.length - 1]\n // The observed transcript folds every frame (a watched session's cards\n // and prose); blocks are copied only when something actually changed, so\n // a usage-only tick costs one record object, not a re-render payload.\n const record = chatTranscripts[sessionId]\n if (record !== undefined) {\n let changed = false\n for (const item of items) {\n if (foldUpdate(record.blocks, item.update as Record<string, unknown>)) changed = true\n }\n chatTranscripts = {\n ...chatTranscripts,\n [sessionId]: { ...record, seq: last.seq, ...(changed ? { blocks: [...record.blocks] } : {}) },\n }\n }\n if (sessionId !== this.state.activeSessionId) {\n this.lastSeq.set(sessionId, last.seq)\n continue\n }\n const seen = this.lastSeq.get(sessionId)\n const contiguous = seen === undefined\n || (items[0].seq === seen + 1 && items.every((item, index) => index === 0 || item.seq === items[index - 1].seq + 1))\n this.lastSeq.set(sessionId, last.seq)\n if (!contiguous) {\n void this.loadTranscript(sessionId) // gap detected → replay\n continue\n }\n for (const item of items) {\n if (foldUpdate(this.state.blocks, item.update as Record<string, unknown>)) blocksChanged = true\n }\n }\n const patch: Partial<UiState> = {}\n if (chatTranscripts !== this.state.chatTranscripts) patch.chatTranscripts = chatTranscripts\n if (blocksChanged) patch.blocks = [...this.state.blocks]\n if (Object.keys(patch).length > 0) this.mutate(patch)\n }\n\n private armUpdateFlush(): void {\n if (this.updateFlushArmed) return\n this.updateFlushArmed = true\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => { this.updateFlushArmed = false; this.flushUpdates() })\n return\n }\n // No frame scheduler (a plain Node runtime): fold at once, keeping the\n // same one-mutate-per-batch contract for non-browser consumers.\n this.flushUpdates()\n }\n\n // ---- Transport ----\n\n /** Open the shared event stream once; schedule first snapshot fetch. */\n start(): void {\n if (this.source !== undefined) return\n this.connectStream()\n }\n\n private connectStream(): void {\n const source = new EventSource('/api/acp/stream')\n source.onopen = () => {\n void this.resync()\n this.mutate({ stream: 'live' })\n }\n source.onerror = () => {\n this.mutate({ stream: 'down' })\n }\n source.onmessage = raw => {\n let event: AcpEvent\n try {\n event = JSON.parse(String(raw.data)) as AcpEvent\n } catch {\n return\n }\n void this.applyEvent(event)\n }\n this.source = source\n }\n\n private async call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n const result = await this.rpc('/acp', endpoint, payload)\n if (!result.ok) throw new Error(result.error.message)\n return result.value as T\n }\n\n /** Snapshot refresh used on panel open and after a stream outage. */\n async resync(): Promise<void> {\n try {\n const [{ agents }, { sessions }, defaultAgentInfo, detectResult, presetResult, tagHaloResult, deadlinesResult, versionResult] = await Promise.all([\n this.call<{ agents: AgentView[] }>('agents/list'),\n this.call<{ sessions: SessionView[] }>('sessions/list'),\n // The plugin's OWN default ACP agent — a plugin-private id, not a DSH\n // model route (see `defaultAgent` on the host half).\n this.call<{ agentId?: string }>('default/agent').catch(() => ({} as { agentId?: string })),\n this.call<{ agents: DetectedAgentInfo[] }>('agents/detect').catch(() => null),\n // `null` (host without a preset roster) hides the panel block.\n this.call<{ preset: AcpPresetStatus | null }>('preset/status').catch(() => null),\n // Host without the endpoint (older half) keeps the local default.\n this.call<{ halo: AcpTagHalo }>('ui/tag-halo').catch(() => ({ halo: { ...DEFAULT_TAG_HALO } })),\n // `undefined` (older half) hides the deadline card instead of showing a\n // number this browser made up.\n this.call<ApprovalDeadlinesResult>('ui/approval-deadlines').catch(() => ({ deadlines: undefined })),\n // The settings badge's data source; older halves answer `unknown\n // endpoint`, which hides the badge.\n this.call<PluginVersionResult>('plugin/version').catch(() => null),\n ])\n const detectedAgents = (detectResult && detectResult.agents.length > 0)\n ? detectResult.agents\n : (this.state.detectedAgents.length > 0 ? this.state.detectedAgents : [...DEFAULT_KNOWN_PRESETS])\n // Absent on an older host: keep the settings-panel card (degraded path).\n this.mutate({\n agents,\n agentsLoaded: true,\n sessions,\n defaultAgent: defaultAgentInfo?.agentId,\n detectedAgents,\n preset: presetResult?.preset ?? undefined,\n tagHalo: tagHaloResult?.halo ?? { ...DEFAULT_TAG_HALO },\n approvalDeadlines: deadlinesResult.deadlines,\n pluginVersion: versionResult?.version !== undefined && versionResult.version !== '' ? versionResult.version : undefined,\n })\n const active = this.state.activeSessionId ?? sessions[0]?.sessionId\n if (active !== undefined) await this.loadTranscript(active)\n } catch (error) {\n this.mutate({ notice: String(error instanceof Error ? error.message : error) })\n }\n }\n\n /**\n * Read the plugin's update state (`plugin/update-check`). Throttled to ten\n * minutes unless `force`: the settings panel asks on mount, and every\n * remount must not hit the registry.\n *\n * An older host serves no such endpoint (`unknown endpoint`). That is a\n * normal degradation, not an error worth a notice: the answer is `undefined`\n * and the panel simply keeps its update entry hidden.\n */\n async checkPluginUpdate(force = false): Promise<PluginUpdateState | undefined> {\n // One read for every caller while it is in flight, force included: the\n // settings page and the popover mounting together must not hit the\n // registry twice.\n if (this.pluginUpdateInflight !== undefined) return this.pluginUpdateInflight\n const current = this.state.pluginUpdate\n if (!force && current !== undefined && Date.now() - this.pluginUpdateFetchedAt < PLUGIN_UPDATE_TTL_MS) {\n return current\n }\n const generation = this.pluginUpdateGeneration\n const inflight = this.readPluginUpdate(generation)\n this.pluginUpdateInflight = inflight\n try {\n return await inflight\n } finally {\n this.pluginUpdateInflight = undefined\n }\n }\n\n private async readPluginUpdate(generation: number): Promise<PluginUpdateState | undefined> {\n try {\n const pluginUpdate = await this.call<PluginUpdateState>('plugin/update-check')\n // An install started while this read was in flight: its answer is\n // already stale, and the install's verdict owns the state now.\n if (generation !== this.pluginUpdateGeneration) return undefined\n this.pluginUpdateFetchedAt = Date.now()\n this.mutate({ pluginUpdate })\n return pluginUpdate\n } catch {\n return undefined\n }\n }\n\n /**\n * Install one exact version (`plugin/update-apply`). The status flips to\n * `applying` first so the badge answers the click; the host's answer then\n * becomes the state.\n *\n * A failed call does NOT prove the install did not happen: on an HMR host\n * the update uninstalls the plugin before reinstalling it, which can cut the\n * response. Whatever the error, the disk may already carry the new version,\n * so the state becomes `restart-required` (the one neutral verdict for \"the\n * running code is not the disk's\") with `installed` dropped (unknown, not a\n * fact to display) and the notice asks for a reload rather than restoring\n * `available` and pretending nothing changed.\n */\n async applyPluginUpdate(version: string): Promise<PluginUpdateState | undefined> {\n if (this.pluginUpdateApplying) return undefined\n this.pluginUpdateApplying = true\n // Any check already in flight predates the install; its answer must not\n // land on top of the install's verdict.\n this.pluginUpdateGeneration += 1\n const previous = this.state.pluginUpdate\n if (previous !== undefined) {\n this.mutate({ pluginUpdate: { ...previous, status: 'applying' } })\n }\n try {\n const pluginUpdate = await this.call<PluginUpdateState>('plugin/update-apply', { version })\n this.pluginUpdateFetchedAt = Date.now()\n this.mutate({ pluginUpdate })\n return pluginUpdate\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n // The disk state is UNKNOWN here: the install may have completed before\n // the response was cut. Keep `latest` for display, drop `installed` so no\n // surface can claim a disk version we never read, and reset the throttle\n // so the next check actually asks the host instead of serving this\n // unknown verdict for ten minutes.\n const interrupted: PluginUpdateState = previous !== undefined\n ? { ...previous, status: 'restart-required', installed: undefined, message }\n : { status: 'restart-required', current: this.state.pluginVersion ?? '?', message }\n this.pluginUpdateFetchedAt = 0\n this.mutate({\n pluginUpdate: interrupted,\n notice: this.locale(\n '更新请求已中断;请刷新页面确认当前版本。',\n 'The update request was interrupted. Reload the page to confirm the installed version.',\n ),\n })\n return undefined\n } finally {\n // Invalidate reads that STARTED during the install: their \"applying\"\n // answer would otherwise land on top of this call's final verdict.\n this.pluginUpdateGeneration += 1\n this.pluginUpdateApplying = false\n }\n }\n\n async detectAgents(): Promise<void> {\n this.mutate({ detecting: true })\n try {\n const result = await this.call<{ agents: DetectedAgentInfo[] }>('agents/detect')\n const agents = result.agents.length > 0 ? result.agents : [...DEFAULT_KNOWN_PRESETS]\n const installed = agents.filter(agent => agent.detected).length\n this.detectCounts = { total: agents.length, installed }\n this.mutate({\n detectedAgents: agents,\n detecting: false,\n detectSummary: this.detectSummaryOf(agents.length, installed),\n })\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agents/detect')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以激活主机检测功能。', 'Host running older version. Please restart dsh web to enable agent detection.')\n : msg\n this.detectCounts = undefined\n this.mutate({\n detecting: false,\n notice,\n detectedAgents: this.state.detectedAgents.length > 0 ? this.state.detectedAgents : [...DEFAULT_KNOWN_PRESETS],\n // A failed re-detect must not keep showing the previous run's success line.\n detectSummary: undefined,\n })\n }\n }\n\n private detectSummaryOf(total: number, installed: number): string {\n return this.locale(\n `已检测 ${String(total)} 个预设:${String(installed)} 个已安装,其余可从「添加智能体」启用。`,\n `Detected ${String(total)} presets: ${String(installed)} installed, enable the rest via \"Add agent\".`,\n )\n }\n\n async saveAgent(payload: AgentSavePayload): Promise<void> {\n try {\n await this.call('agent/save', payload)\n await this.resync()\n // The host probes the catalog right after a save; pull the fresh result\n // so the agent card fills in without a second click.\n void this.probeModels(payload.id)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /**\n * Dry-run the connection form: the host spawns a throwaway process from\n * these exact values, handshakes, and tears it down. A reachable-but-broken\n * agent arrives as `{ ok: false }`, not a thrown error; only transport or\n * validation failures reject.\n */\n async testAgent(payload: AgentSavePayload): Promise<AgentTestResult> {\n try {\n return await this.call<AgentTestResult>('agent/test', payload)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/test')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以启用连接测试功能。', 'Host running older version. Please restart dsh web to enable connection testing.')\n : msg\n this.mutate({ notice })\n throw error\n }\n }\n\n /**\n * Seed one agent's model panes from the host's PERSISTENT catalog cache\n * (`agent/cached-models`, memory-only: it never spawns).\n *\n * The settings card used to learn a catalog only from `agent/probe-models`,\n * so its panes stayed blank until the user clicked 「检测可用模型」 on every\n * visit — including the models already configured, which are known from the\n * agent definition and the on-disk catalog. This read fills both panes on\n * mount; the probe stays the explicit REFRESH (add what the agent newly\n * publishes, drop what it stopped advertising).\n */\n async loadCachedModels(agentId: string): Promise<void> {\n if (this.state.modelCatalogs[agentId] !== undefined || this.cachedModelReads.has(agentId)) return\n this.cachedModelReads.add(agentId)\n try {\n const result = await this.call<AgentProbeModelsResult | null>('agent/cached-models', { agentId })\n // `null` = this agent never published a catalog. A probe may have landed\n // while this read was in flight; a fresh probe always wins.\n if (result === null || result === undefined || this.state.modelCatalogs[agentId] !== undefined) return\n this.mutate({ modelCatalogs: { ...this.state.modelCatalogs, [agentId]: result } })\n } catch {\n // Older host without the endpoint, or an agent deleted meanwhile: the\n // panes fall back to the selection alone, exactly as before a probe.\n } finally {\n this.cachedModelReads.delete(agentId)\n }\n }\n\n /** Ask the host to probe one agent's model catalog (spawn + throwaway session). */\n async probeModels(agentId: string): Promise<void> {\n const previousError = this.state.probeErrors[agentId]\n this.mutate({\n probingModels: { ...this.state.probingModels, [agentId]: true },\n probeErrors: this.clearProbeError(agentId),\n })\n try {\n const result = await this.call<AgentProbeModelsResult>('agent/probe-models', { agentId })\n // The seat list is a filtered view of the same catalog: drop the cached\n // one (in the same patch) and re-read below, so a fresh probe lands in an\n // open conversation too.\n const seatCatalogs = { ...this.state.seatCatalogs }\n delete seatCatalogs[agentId]\n this.mutate({\n modelCatalogs: { ...this.state.modelCatalogs, [agentId]: result },\n seatCatalogs,\n probingModels: { ...this.state.probingModels, [agentId]: false },\n // A retry after the user fixed the problem must not leave the old\n // failure pinned at the top of the panel. Only drop the notice when it\n // is still ours — anything newer belongs to someone else.\n ...(previousError !== undefined && this.state.notice === previousError ? { notice: undefined } : {}),\n })\n void this.loadAgentCatalog(agentId)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/probe-models')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以激活模型检测功能。', 'Host running older version. Please restart dsh web to enable model detection.')\n : msg\n this.mutate({\n notice,\n probingModels: { ...this.state.probingModels, [agentId]: false },\n probeErrors: { ...this.state.probeErrors, [agentId]: notice },\n })\n }\n }\n\n private clearProbeError(agentId: string): Record<string, string> {\n if (this.state.probeErrors[agentId] === undefined) return this.state.probeErrors\n const next = { ...this.state.probeErrors }\n delete next[agentId]\n return next\n }\n\n /**\n * Read one agent's ACP model surface (`agent/catalog`) — what the composer\n * chips and the ACP-mode model seat render. Cached per agent, because the\n * list only changes when the agent's catalog is re-probed or its exposure\n * filter is edited (both of which drop the cache below), and deduped because\n * every ACP surface asks for it on mount.\n *\n * The host answers from memory, so this is safe to call from a render effect;\n * a probe it may trigger runs in the background on the host side.\n */\n async loadAgentCatalog(agentId: string): Promise<void> {\n if (this.state.seatCatalogs[agentId] !== undefined || this.seatCatalogReads.has(agentId)) return\n this.seatCatalogReads.add(agentId)\n try {\n const result = await this.call<AgentCatalogResult>('agent/catalog', { agentId })\n this.mutate({ seatCatalogs: { ...this.state.seatCatalogs, [agentId]: result } })\n } catch {\n // An older host half, or an agent that was deleted meanwhile: the seats\n // fall back to the roster's own title and the bare agent route, which is\n // exactly what they show before a catalog exists.\n } finally {\n this.seatCatalogReads.delete(agentId)\n }\n }\n\n /** Drop one agent's cached seat catalog (its model surface just changed). */\n private invalidateSeatCatalog(agentId: string): void {\n if (this.state.seatCatalogs[agentId] === undefined) return\n const next = { ...this.state.seatCatalogs }\n delete next[agentId]\n this.mutate({ seatCatalogs: next })\n }\n\n /**\n * Persist the ACP-seat model filter for one agent. `undefined` clears the\n * filter — same as an empty list, i.e. no `agentId@choice` variants, only\n * the agent's base entry. Uses the dedicated lightweight RPC so the agent\n * process and its live sessions are never restarted.\n */\n async setExposedModels(agentId: string, exposedModels: string[] | undefined): Promise<void> {\n try {\n await this.call('agent/set-exposed-models', {\n agentId,\n ...(exposedModels === undefined ? {} : { exposedModels }),\n })\n await this.resync()\n // The seats render the filtered list, so an edited filter has to be\n // re-read or the open conversation keeps offering the old set.\n this.invalidateSeatCatalog(agentId)\n void this.loadAgentCatalog(agentId)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/set-exposed-models')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以启用模型筛选功能。', 'Host running older version. Please restart dsh web to enable model filtering.')\n : msg\n this.mutate({ notice })\n throw error\n }\n }\n\n async deleteAgent(agentId: string): Promise<void> {\n try {\n await this.call('agent/delete', { agentId })\n await this.resync()\n // Nothing should keep offering a deleted agent's models (a conversation\n // that was routed to it falls back to its roster entry).\n this.invalidateSeatCatalog(agentId)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /**\n * Persist the auto-discovery switch for one agent. Lightweight like\n * {@link setExposedModels}: no process restart, and the current selection is\n * left exactly as it is.\n */\n async setAutoDiscoverModels(agentId: string, autoDiscoverModels: boolean): Promise<void> {\n try {\n await this.call('agent/set-auto-discover-models', { agentId, autoDiscoverModels })\n await this.resync()\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/set-auto-discover-models')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以启用自动发现功能。', 'Host running older version. Please restart dsh web to enable auto-discovery.')\n : msg\n this.mutate({ notice })\n throw error\n }\n }\n\n // ---- Panel lifecycle ----\n\n toggle(): void {\n const nextOpen = !this.state.open\n this.mutate({ open: nextOpen })\n if (nextOpen) {\n this.start()\n void this.resync()\n }\n }\n\n close(): void {\n this.mutate({ open: false })\n }\n\n selectSession(sessionId: string): void {\n this.mutate({ activeSessionId: sessionId })\n void this.loadTranscript(sessionId)\n }\n\n private async loadTranscript(sessionId: string): Promise<void> {\n try {\n const { session, entries } = await this.call<{ session: SessionView; entries: Array<{ seq: number; update: Record<string, unknown> }> }>(\n 'transcript/get',\n { sessionId },\n )\n // An answer BEHIND what this page already folded must not replace it.\n //\n // The host fills its transcript from the notification pipeline, which the\n // SDK paces independently of the `session/prompt` response, so a read\n // taken right after a turn can still be missing the agent's last chunk.\n // `lastSeq` already refuses to go backwards; the blocks must not either,\n // or switching away and back inside that window truncates a turn the user\n // just watched finish. A resumed session replays as fresh updates, but\n // only after `session/reset` zeroes the watermark — so this cannot swallow\n // a legitimate replay.\n const seen = this.lastSeq.get(sessionId) ?? 0\n if ((entries.at(-1)?.seq ?? 0) < seen) return\n const blocks: Block[] = []\n for (const entry of entries) {\n foldUpdate(blocks, entry.update)\n this.lastSeq.set(sessionId, Math.max(this.lastSeq.get(sessionId) ?? 0, entry.seq))\n }\n const sessions = this.state.sessions.some(s => s.sessionId === sessionId)\n ? this.state.sessions.map(s => (s.sessionId === sessionId ? session : s))\n : [...this.state.sessions, session]\n this.mutate({ sessions, blocks, activeSessionId: sessionId })\n } catch {\n this.mutate({ blocks: [] })\n }\n }\n\n // ---- Actions ----\n\n async startSession(agentId: string, cwd?: string): Promise<void> {\n try {\n const { sessionId } = await this.call<{ sessionId: string }>('session/start', { agentId, ...(cwd === undefined || cwd === '' ? {} : { cwd }) })\n await this.resync()\n this.selectSession(sessionId)\n } catch (error) {\n this.flashError(error)\n }\n }\n\n async sendPrompt(text: string): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined || text.trim() === '') return\n this.mutate({ busy: true })\n try {\n await this.call('session/prompt', { sessionId, text })\n } catch (error) {\n this.flashError(error)\n } finally {\n this.mutate({ busy: false })\n }\n }\n\n async cancelActive(): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/cancel', { sessionId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n async closeActive(): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/close', { sessionId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n async setMode(modeId: string): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/mode-set', { sessionId, modeId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Load the ACP agent sessions backing one DSH chat session. The composer\n * control calls this per chat session (and again whenever a turn settles, so\n * the binding a fresh conversation just created shows up).\n */\n /**\n * Load the ACP agent sessions backing one DSH chat session. The composer\n * control calls this per chat session (and again whenever a turn settles, so\n * the binding a fresh conversation just created shows up).\n *\n * `settleVerdict` is the seat gate's own re-ask and the ONLY read allowed to\n * settle a verdict a frame had already settled (see {@link acpModeLive}): the\n * gate holds such a verdict as its only fact source while the session row\n * stays silent, and a host read is the one place a switch the runtime never\n * framed can still be learned from. Composer readers pass nothing and keep\n * the frame's authority.\n */\n loadChatSessions(dshSessionId: string, options?: { settleVerdict?: boolean }): Promise<void> {\n // Two composer entries read this (the agent-mode control and the ACP\n // cluster) and both re-read when a turn settles; one round-trip answers\n // for both instead of two racing ones. A caller arriving mid-read waits\n // for THAT read rather than returning immediately: the slash source's\n // one-shot \"do I have a verdict yet?\" ask is only useful if it can\n // receive the answer of a read already in flight.\n const inFlight = this.chatSessionReads.get(dshSessionId)\n if (inFlight !== undefined) return inFlight.done\n const read: { dropped: boolean; done: Promise<void> } = { dropped: false, done: Promise.resolve() }\n this.chatSessionReads.set(dshSessionId, read)\n read.done = this.readChatSessionList(dshSessionId, options, read)\n return read.done\n }\n\n private async readChatSessionList(\n dshSessionId: string,\n options: { settleVerdict?: boolean } | undefined,\n read: { dropped: boolean },\n ): Promise<void> {\n const settle = options?.settleVerdict === true || !this.acpModeLive.has(dshSessionId)\n try {\n const { sessions, acpMode, boundAgentId, lockedAgentId, choice } = await this.call<{ sessions: ChatSessionView[]; acpMode?: boolean; boundAgentId?: string; lockedAgentId?: string; choice?: AcpConversationChoice }>('session/chat-list', { dshSessionId, refresh: true })\n // A committed `agent-preset/selected` frame may have settled this\n // session's verdict while the read was in flight; the host answer then\n // describes the log as of BEFORE that switch and must not overwrite it.\n if (read.dropped) return\n // The first answer for a conversation only seeds what is already true: a\n // lock reported there belongs to an earlier page. A lock that appears on\n // a LATER answer is the first-turn transition unfolding in front of the\n // user, and only that one is announced to the hand-off animation.\n const firstAnswer = !this.chatListLoaded.has(dshSessionId)\n this.chatListLoaded.add(dshSessionId)\n // The lock is one-way, so a read that no longer reports one is an older\n // host half, not an unlock: keep what we already know in that case. The\n // one explicit unlock is the host's rollback frame, tracked separately.\n // A read that AGREES there is no lock clears the rollback marker, so a\n // later legitimate lock (even with its frame missed) is accepted again.\n if (lockedAgentId === undefined) this.chatUnlocked.delete(dshSessionId)\n const reportedLock = lockedAgentId !== undefined && this.chatUnlocked.has(dshSessionId)\n ? undefined\n : lockedAgentId\n const chatLockedAgent = reportedLock === undefined\n ? this.state.chatLockedAgent\n : { ...this.state.chatLockedAgent, [dshSessionId]: reportedLock }\n const witnessedLock = !firstAnswer\n && reportedLock !== undefined\n && this.state.chatLockedAgent[dshSessionId] === undefined\n const agentLockReveal = witnessedLock\n ? { ...this.state.agentLockReveal, [dshSessionId]: (this.state.agentLockReveal[dshSessionId] ?? 0) + 1 }\n : this.state.agentLockReveal\n this.mutate({\n chatSessions: sessions,\n chatSessionsFor: dshSessionId,\n chatLockedAgent,\n agentLockReveal,\n // The host's binding (intent + live-session fallback) outranks the\n // optimistic entry this store wrote on the click; a host without the\n // field (older half) simply leaves the optimistic value standing.\n ...(boundAgentId === undefined\n ? {}\n : { chatBoundAgent: { ...this.state.chatBoundAgent, [dshSessionId]: boundAgentId } }),\n // The persisted pick (model + thought level) — the host's record,\n // present before any ACP session exists. Merged over the optimistic\n // entry so a value a click just wrote is not erased by a read that\n // started before the click landed.\n ...(choice === undefined\n ? {}\n : { chatChoice: { ...this.state.chatChoice, [dshSessionId]: choice } }),\n // The seat gate's fallback fact source: absent on an older host half,\n // which then simply never takes the seat over (the allowed failure).\n // A frame-settled verdict outranks any read, whenever it lands: the\n // host's log read can lag a switch by one, and taking the model seat\n // re-mounts readers, so a stale answer would otherwise regress it.\n ...(acpMode === undefined || !settle\n ? {}\n : { acpMode: { ...this.state.acpMode, [dshSessionId]: acpMode === true } }),\n })\n // A read that answered but carried no verdict has NOT settled this\n // session. The gate's ask ledger is one-shot per id, so unless the id is\n // retracted here it stays marked \"asked\" forever and the gate never asks\n // again — one silent round-trip and the native model seat is permanent\n // for that session. The gate re-derives this from `hasAcpModeVerdict`\n // on its next evaluation (every store mutation re-runs it), which is\n // what makes the fallback recoverable instead of a single coin flip.\n if (acpMode === undefined && settle) {\n this.acpModeUnsettled.add(dshSessionId)\n }\n } catch {\n // A host without this surface (older build) has nothing to show; the\n // control simply renders nothing rather than an error nobody can act on.\n if (read.dropped) return\n this.mutate({ chatSessions: [], chatSessionsFor: dshSessionId })\n if (settle) {\n this.acpModeUnsettled.add(dshSessionId)\n }\n } finally {\n if (this.chatSessionReads.get(dshSessionId) === read) this.chatSessionReads.delete(dshSessionId)\n }\n }\n\n /**\n * Abandon the in-flight `session/chat-list` read for one session (or all of\n * them): a committed `agent-preset/selected` frame carries the authoritative\n * verdict, and the late answer to an ask that predates the frame describes\n * the log from before the switch — it must not land in the store.\n */\n abandonChatSessionRead(dshSessionId?: string): void {\n if (dshSessionId === undefined) {\n for (const read of this.chatSessionReads.values()) read.dropped = true\n this.chatSessionReads.clear()\n return\n }\n const read = this.chatSessionReads.get(dshSessionId)\n if (read === undefined) return\n read.dropped = true\n this.chatSessionReads.delete(dshSessionId)\n }\n\n /**\n * The slash commands the LIVE ACP session declared for one chat session, as\n * read by {@link loadChatSessions} (`available_commands_update` →\n * `session/chat-list`).\n *\n * `undefined` covers both \"no answer has landed yet\" and \"there is no live\n * session (or it has not declared anything)\" — in every one of those cases\n * the caller falls back to the agent-level cache ({@link agentCommands}),\n * which is what makes `/` list commands before the first message exists.\n */\n chatSessionCommands(dshSessionId: string): readonly AgentCommand[] | undefined {\n if (this.state.chatSessionsFor !== dshSessionId) return undefined\n return this.state.chatSessions[0]?.availableCommands\n }\n\n /**\n * The slash commands the agent declared in its most recent session, from\n * `agent/catalog` (host-cached per agent, see `AgentCatalogResult.commands`).\n * The pre-first-turn source for the `/` menu.\n */\n agentCommands(agentId: string | undefined): readonly AgentCommand[] | undefined {\n if (agentId === undefined) return undefined\n return this.state.seatCatalogs[agentId]?.commands\n }\n\n /**\n * Which agent serves one conversation as far as this page can tell, without\n * reading any catalog: live ACP session → agent lock → host/optimistic\n * binding → persisted pick → plugin default. Used to find the agent whose\n * cached commands the `/` menu should offer before a session exists.\n */\n chatAgentFor(dshSessionId: string): string | undefined {\n if (this.state.chatSessionsFor === dshSessionId) {\n const live = this.state.chatSessions[0]\n if (live !== undefined) return live.agentId\n }\n // The lock outlives the session: after the first turn it is the durable\n // answer even when the ACP session is gone.\n return this.state.chatLockedAgent[dshSessionId]\n ?? this.state.chatBoundAgent[dshSessionId]\n ?? this.state.chatChoice[dshSessionId]?.agentId\n ?? this.state.defaultAgent\n }\n\n /**\n * Record the verdict a committed `agent-preset/selected` frame carries, folded\n * from the frame's own preset field ({@link acpModeFromPresetEvent}). The\n * frame is authoritative: it marks the session live-settled, so no later host\n * read may regress it (see {@link acpModeLive}) — the gate's own TTL re-ask\n * being the one authorized exception. Same no-op-when-unchanged discipline as\n * the other accessors, so a frame restating the held verdict costs no\n * listener churn.\n */\n noteAcpMode(dshSessionId: string, acpMode: boolean): void {\n this.acpModeLive.add(dshSessionId)\n if (this.state.acpMode[dshSessionId] === acpMode) return\n this.mutate({ acpMode: { ...this.state.acpMode, [dshSessionId]: acpMode } })\n }\n\n /**\n * Record the model-seat gate's last decision (see the `seatDiag` field). The\n * gate owns the string; the store only carries it so the ACP view can render\n * *why* the native seat is or is not shadowed. No-op when unchanged, so the\n * gate can call this on every evaluation without churning listeners.\n */\n noteSeatDiag(text: string): void {\n if (this.state.seatDiag === text) return\n this.mutate({ seatDiag: text })\n }\n\n /**\n * Record the permission-seat gate's last decision (`conversation.input.permission`).\n */\n notePermissionSeatDiag(text: string): void {\n if (this.state.permissionSeatDiag === text) return\n this.mutate({ permissionSeatDiag: text })\n }\n\n /**\n * Record the slash-isolation seam's last verdict (`idle` = in force, or no\n * problem to report). No-op when unchanged, like the seat lines.\n */\n noteSlashDiag(text: string): void {\n if (this.state.slashDiag === text) return\n this.mutate({ slashDiag: text })\n }\n\n /**\n * Whether a chat session's host read produced a verdict the seat gate can\n * rely on. `false` means \"asked but not answered\" — the read returned no\n * `acpMode` field or failed outright — and is the signal for the gate to\n * retract its one-shot ask ledger entry for this id so it can ask again.\n *\n * A verdict handed down by a committed `agent-preset/selected` frame is\n * authoritative and outranks everything (see {@link acpModeLive}); such a\n * session always answers `true` here, settled or not.\n */\n hasAcpModeVerdict(dshSessionId: string): boolean {\n if (this.acpModeLive.has(dshSessionId)) return true\n if (this.acpModeUnsettled.has(dshSessionId)) {\n this.acpModeUnsettled.delete(dshSessionId)\n return false\n }\n return this.state.acpMode[dshSessionId] !== undefined\n }\n\n /**\n * Drop one chat session's ACP-mode verdict — or the whole map. The preset\n * switch a session committed is announced by the host (`agent-preset/selected`)\n * and folded by the runtime into its own rows, but NOT into this map, so the\n * gate re-asks instead of trusting a verdict the session log has moved past.\n */\n invalidateAcpMode(dshSessionId?: string): void {\n if (dshSessionId === undefined) {\n // Dropping the whole map also re-opens every session to host reads.\n this.acpModeLive.clear()\n if (Object.keys(this.state.acpMode).length === 0) return\n this.mutate({ acpMode: {} })\n return\n }\n this.acpModeLive.delete(dshSessionId)\n if (this.state.acpMode[dshSessionId] === undefined) return\n const acpMode = { ...this.state.acpMode }\n delete acpMode[dshSessionId]\n this.mutate({ acpMode })\n }\n\n /**\n * Switch the ACP agent's own mode for one chat session (`session/set_mode`),\n * then re-read the binding so the control reflects what the agent actually\n * reports (a rejected switch must not leave an optimistic lie on screen).\n */\n async setChatSessionMode(dshSessionId: string, modeId: string, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-mode-set', { dshSessionId, modeId, ...(agentId === undefined ? {} : { agentId }) })\n } catch (error) {\n this.flashError(error)\n }\n await this.loadChatSessions(dshSessionId)\n }\n\n // ---- ACP transcript observation (the conversation's ACP view) ----\n\n /**\n * Start (or refresh) observation of every ACP transcript behind one DSH chat\n * session. Blocks are rebuilt from scratch — the host buffer is the record,\n * and this view is the only consumer that needs the raw agent stream.\n */\n async loadChatTranscript(dshSessionId: string): Promise<void> {\n let views: ChatTranscriptView[]\n try {\n ({ views } = await this.call<{ views: ChatTranscriptView[] }>(\n 'session/chat-transcript',\n { dshSessionId },\n ))\n } catch {\n // An older host half (or a session with no ACP binding): nothing to\n // observe, and no error worth flashing for a read-only side view.\n return\n }\n // Everything this chat session used to have, minus what it still has: a\n // closed or replaced ACP session must not linger as a ghost tab body.\n const merged: Record<string, ChatTranscript> = {}\n for (const [id, record] of Object.entries(this.state.chatTranscripts)) {\n if (record.owner !== dshSessionId) merged[id] = record\n }\n for (const view of views) {\n const blocks: Block[] = []\n for (const entry of view.entries) foldUpdate(blocks, entry.update as Record<string, unknown>)\n merged[view.session.sessionId] = {\n owner: dshSessionId,\n session: view.session,\n blocks,\n seq: view.entries.length > 0 ? view.entries[view.entries.length - 1]?.seq ?? 0 : 0,\n }\n }\n this.mutate({ chatTranscripts: this.evictTranscripts(merged) })\n }\n\n /** Observed transcript for one ACP session id (undefined → nobody is watching it). */\n transcriptOf(acpSessionId: string): ChatTranscript | undefined {\n return this.state.chatTranscripts[acpSessionId]\n }\n\n /** The observed transcript of one chat session (the live binding wins over a released one). */\n chatTranscriptOf(dshSessionId: string): ChatTranscript | undefined {\n const owned = Object.values(this.state.chatTranscripts).filter(record => record.owner === dshSessionId)\n return owned.find(record => !record.session.released) ?? owned[0]\n }\n\n /** Keep the resident transcript map at its bound, newest insertion first. */\n private evictTranscripts(map: Record<string, ChatTranscript>): Record<string, ChatTranscript> {\n const ids = Object.keys(map)\n if (ids.length <= MAX_WATCHED) return map\n const out: Record<string, ChatTranscript> = {}\n for (const id of ids.slice(ids.length - MAX_WATCHED)) {\n const value = map[id]\n if (value !== undefined) out[id] = value\n }\n return out\n }\n\n /** Re-publish an observed transcript's session snapshot (mode / released flips). */\n private watchedSessionPatch(sessionId: string, session: SessionView): { chatTranscripts?: Record<string, ChatTranscript> } {\n const current = this.state.chatTranscripts[sessionId]\n if (current === undefined) return {}\n return { chatTranscripts: { ...this.state.chatTranscripts, [sessionId]: { ...current, session } } }\n }\n\n /** Mirror the running flag onto a transcript the ACP view is showing. */\n private markWatchedRunning(sessionId: string, running: boolean): void {\n const current = this.state.chatTranscripts[sessionId]\n if (current === undefined || current.session.running === running) return\n this.mutate({\n chatTranscripts: {\n ...this.state.chatTranscripts,\n [sessionId]: { ...current, session: { ...current.session, running } },\n },\n })\n }\n\n async setConfigOption(configId: string, value: string): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/config-set', { sessionId, configId, value })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Answer one ask (`optionId` absent = cancel it). A failure is reported\n * through the store's notice — the settings panel renders that — AND\n * rethrown, so the conversation dock can pin the message next to the card\n * that failed instead of leaving it on a settings page the user may not\n * have open. Callers that cannot show it (the settings panel) swallow it\n * after the notice.\n */\n async respondPermission(requestId: string, optionId?: string): Promise<void> {\n try {\n await this.call('permission/respond', { requestId, ...(optionId === undefined ? {} : { optionId }) })\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n async stopAgent(agentId: string): Promise<void> {\n try {\n await this.call('agent/stop', { agentId })\n await this.resync()\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Set the plugin's OWN default ACP agent (the panel's ★ gesture): which agent\n * an ACP-mode conversation starts on. Plugin-private state — the host keeps it\n * in its own agent registry file, and nothing here touches DSH's model\n * selection (see docs/acp-convergence.md §2.2).\n */\n async setDefaultAgent(agentId: string | undefined): Promise<void> {\n try {\n await this.call('default/agent-set', { ...(agentId === undefined ? {} : { agentId }) })\n this.mutate({ defaultAgent: agentId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Write the agent tag's halo preference (settings card). The value is\n * validated host-side; this side writes optimistically first so the tag's\n * look changes with the click, and reverts to the host answer on a failure.\n */\n async setTagHalo(halo: AcpTagHalo): Promise<void> {\n const previous = this.state.tagHalo\n this.mutate({ tagHalo: halo })\n try {\n await this.call('ui/tag-halo-set', { halo })\n } catch (error) {\n this.mutate({ tagHalo: previous })\n this.flashError(error)\n }\n }\n\n /**\n * Write the approval deadline (settings card), or drop the persisted override\n * with `reset` so the `cordis.patch.yml` baseline applies again.\n *\n * The host answer — the same route returns the deadlines now in force — is the\n * authority, so an optimistic write that the host trims (out of range, or\n * merged with a value this browser did not know about) self-corrects instead\n * of drifting from what runs.\n */\n async setApprovalDeadline(ms: number): Promise<void> {\n await this.writeApprovalDeadlines({ permissionTimeoutMs: ms })\n }\n\n /**\n * Write the mode: what an expired deadline DOES (`abort` refuses, `auto`\n * answers with the option the agent's own kinds imply, `wait` never\n * expires). Same write path as the window, so the host answer stays\n * the authority.\n */\n async setApprovalTimeoutMode(mode: PermissionTimeoutMode): Promise<void> {\n await this.writeApprovalDeadlines({ mode })\n }\n\n /** Drop the persisted override: the config file (or the built-in default) decides again. */\n async resetApprovalDeadlines(): Promise<void> {\n await this.writeApprovalDeadlines({ reset: true })\n }\n\n private async writeApprovalDeadlines(payload: ApprovalDeadlinesSetPayload): Promise<void> {\n const previous = this.state.approvalDeadlines\n // Optimistic only where it is safe: the effective numbers and the mode move\n // now, and the override facts (`configured`) stay as the host last reported\n // them. A failure restores the whole view.\n if (previous !== undefined) {\n this.mutate({\n approvalDeadlines: {\n ...previous,\n ...(payload.permissionTimeoutMs === undefined ? {} : { permissionTimeoutMs: payload.permissionTimeoutMs }),\n ...(payload.mode === undefined ? {} : { mode: payload.mode }),\n },\n })\n }\n try {\n const result = await this.call<ApprovalDeadlinesResult>('ui/approval-deadlines-set', payload)\n this.mutate({ approvalDeadlines: result.deadlines })\n } catch (error) {\n this.mutate({ approvalDeadlines: previous })\n this.flashError(error)\n }\n }\n\n /**\n * Bind one DSH chat session to an ACP agent. This is the plugin's own\n * conversation↔agent mapping: it does NOT write a DSH model route, so it\n * cannot pollute the deployment default model and cannot be refused by the\n * GUI's catalog gate (the retired `session/model-unavailable` failure).\n *\n * The mapping is host-side and keyed by chat session id; the browser learns\n * the binding back through `session/chat-list`.\n */\n async bindChatAgent(dshSessionId: string, agentId: string): Promise<void> {\n try {\n await this.call('session/chat-bind', { dshSessionId, agentId })\n // Optimistic: the host recorded the intent, so the NEXT message runs the\n // picked agent — but the live-session list (`chatSessions`) cannot show\n // that until the turn materializes its session. Recording here is what\n // makes the chip and the picker's checkmark move on the click itself.\n this.mutate({ chatBoundAgent: { ...this.state.chatBoundAgent, [dshSessionId]: agentId } })\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /** Switch the bound agent's model (`agentId@choice`) for one chat session. */\n async setChatSessionModel(dshSessionId: string, modelId: string, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-model-set', { dshSessionId, modelId })\n // Optimistic for the same reason the binding is: the host recorded the\n // pick, and a conversation with no ACP session yet would otherwise show\n // the agent's default until its first turn created one.\n this.writeChoice(dshSessionId, choice => ({ ...choice, modelId }), agentId)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /** Switch the bound agent's reasoning effort for one chat session. */\n async setChatSessionEffort(dshSessionId: string, effort: string | undefined, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-effort-set', {\n dshSessionId,\n ...(effort === undefined ? {} : { effort }),\n ...(agentId === undefined ? {} : { agentId }),\n })\n this.writeChoice(dshSessionId, choice => {\n const { effort: _dropped, ...rest } = choice\n return effort === undefined ? rest : { ...rest, effort }\n }, agentId)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /** Set one of the bound agent's EXTRA config options for one chat session. */\n async setChatSessionConfig(dshSessionId: string, configId: string, value: string | boolean, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-config-set', {\n dshSessionId,\n configId,\n value,\n ...(agentId === undefined ? {} : { agentId }),\n })\n this.writeChoice(dshSessionId, choice => ({\n ...choice,\n extraConfigs: { ...choice.extraConfigs, [configId]: value },\n }), agentId)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /**\n * Patch one conversation's optimistic pick. The agent half is required by the\n * record (a model or thought level belongs to an agent). Its owner is the\n * route the picker used, else the recorded pick, the optimistic binding, or\n * the plugin default — that last one really serves a never-bound conversation\n * (the guard routes to it), so waiting for `session/chat-list` would leave the\n * chip behind the click. Only a conversation with none of those is left alone.\n */\n private writeChoice(dshSessionId: string, update: (choice: AcpConversationChoice) => AcpConversationChoice, agentId?: string): void {\n const current = this.state.chatChoice[dshSessionId]\n // The plugin default is a real route (the host applies it to a conversation\n // nobody bound), so an optimistic pick in a fresh conversation belongs to\n // it — without this the host recorded the pick but the chip waited for the\n // next `session/chat-list`.\n const owner = agentId ?? current?.agentId ?? this.state.chatBoundAgent[dshSessionId] ?? this.state.defaultAgent\n if (owner === undefined) return\n const next = update({ ...current, agentId: owner })\n this.mutate({ chatChoice: { ...this.state.chatChoice, [dshSessionId]: { ...next, agentId: owner } } })\n }\n\n /**\n * Localize one string. BOUND to the instance in the constructor: the ACP\n * view hands this reference to memoized rows, so it must stay stable across\n * renders or every row re-renders on every store notification.\n */\n label: (zh: string, en: string) => string = (zh: string, en: string): string => this.locale(zh, en)\n\n /**\n * Republish the snapshot after a locale switch. Every `label()` resolves\n * against the current language while rendering, so a notification is all\n * that is needed — except the detect summary, which the store owns as an\n * already-resolved string and rebuilds from its counts here.\n */\n refreshLocale(): void {\n const counts = this.detectCounts\n this.mutate({\n localeRev: this.state.localeRev + 1,\n ...(counts === undefined ? {} : { detectSummary: this.detectSummaryOf(counts.total, counts.installed) }),\n })\n }\n\n clearNotice(): void {\n this.mutate({ notice: undefined })\n }\n\n /** Drop every retained runtime-log line (the unread counter restarts too). */\n clearLogs(): void {\n this.mutate({ logs: [], logUnread: 0 })\n }\n\n /** The user looked at the log: clear the unread badge. */\n markLogsRead(): void {\n if (this.state.logUnread === 0) return\n this.mutate({ logUnread: 0 })\n }\n\n private flashError(error: unknown): void {\n this.mutate({ notice: error instanceof Error ? error.message : String(error) })\n }\n\n // ---- Event application ----\n\n private applyEvent(event: AcpEvent): Promise<void> {\n switch (event.type) {\n case 'agent/notice': {\n this.mutate(this.logPatch(event.level, event.text, event.agentId))\n return Promise.resolve()\n }\n case 'agent/status': {\n if (event.agent !== undefined) {\n const agents = this.state.agents.some(a => a.id === event.agent?.id)\n ? this.state.agents.map(a => (a.id === event.agent?.id ? event.agent : a))\n : [...this.state.agents, event.agent]\n this.mutate({ agents })\n } else {\n this.mutate({})\n }\n return Promise.resolve()\n }\n case 'session/created': {\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : [...this.state.sessions, event.session]\n this.mutate({ sessions, activeSessionId: this.state.activeSessionId ?? event.sessionId })\n return Promise.resolve()\n }\n case 'session/meta': {\n // Host re-published the session view after mode/config changes.\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : this.state.sessions\n this.mutate({ sessions, ...this.watchedSessionPatch(event.sessionId, event.session) })\n return Promise.resolve()\n }\n case 'session/closed': {\n const closingSession = this.state.sessions.find(s => s.sessionId === event.sessionId)\n const sessions = this.state.sessions.filter(s => s.sessionId !== event.sessionId)\n const active = this.state.activeSessionId === event.sessionId ? sessions[0]?.sessionId : this.state.activeSessionId\n this.openTurns.delete(event.sessionId)\n const closedTranscripts = { ...this.state.chatTranscripts }\n delete closedTranscripts[event.sessionId]\n // The composer's chat-list view must lose the closed row too: a stale\n // entry would keep reading as a live session (the agent tag's halo\n // breathes on `!released`) until some future turn re-reads the list.\n const chatSessions = this.state.chatSessions.filter(s => s.sessionId !== event.sessionId)\n this.mutate({\n sessions,\n activeSessionId: active,\n chatTranscripts: closedTranscripts,\n ...(chatSessions.length !== this.state.chatSessions.length ? { chatSessions } : {}),\n permissions: this.state.permissions.filter(p => p.sessionId !== event.sessionId),\n ...(event.reason === 'agent-died'\n ? this.logPatch('error', this.locale('会话因 agent 进程退出而终止', 'session ended: agent process died'), closingSession?.agentId)\n : {}),\n })\n return Promise.resolve()\n }\n case 'session/released': {\n // The agent process died: keep the session visible (marked released)\n // instead of dropping it — the next prompt resumes it via session/load.\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : [...this.state.sessions, event.session]\n this.openTurns.delete(event.sessionId)\n // The composer's own list (`session/chat-list` views) must flip too, or\n // the agent tag's live halo keeps breathing for a process that is gone:\n // nothing else re-reads this list until the next turn settles.\n const chatSessions = this.state.chatSessions.some(s => s.sessionId === event.sessionId)\n ? this.state.chatSessions.map(s => (s.sessionId === event.sessionId ? { ...s, released: true } : s))\n : this.state.chatSessions\n // One runtime-log line per release cause; `idle` stays silent on\n // purpose (an expected lifecycle transition, not a fault).\n const cause = event.cause ?? 'died'\n let log: Partial<Pick<UiState, 'logs' | 'logUnread'>> = {}\n if (cause === 'workspace') {\n log = this.logPatch('info', this.locale(\n 'agent 为切换工作区重启,会话已挂起;再次发送消息将自动恢复。',\n 'agent restarted for its workspace; the session is paused and will resume on your next message.',\n ), event.session.agentId)\n } else if (cause === 'resume-failed') {\n log = this.logPatch('error', this.locale(\n '会话恢复失败;再次发送消息将重试恢复。',\n 'session resume failed; your next message will retry it.',\n ), event.session.agentId)\n } else if (cause === 'stalled') {\n log = this.logPatch('error', this.locale(\n 'agent 未响应卡死的回合,会话已挂起;再次发送消息将自动恢复。',\n 'agent did not acknowledge the stalled turn; the session is paused and will resume on your next message.',\n ), event.session.agentId)\n } else if (cause === 'died') {\n log = this.logPatch('error', this.locale(\n 'agent 进程已退出,会话已挂起;再次发送消息将自动恢复。',\n 'agent process died; the session is paused and will resume on your next message.',\n ), event.session.agentId)\n }\n this.mutate({\n sessions,\n ...(chatSessions !== this.state.chatSessions ? { chatSessions } : {}),\n permissions: this.state.permissions.filter(p => p.sessionId !== event.sessionId),\n // An observed transcript keeps its buffer but flips to released, so\n // the ACP view can say \"会恢复\" instead of silently looking dead.\n ...this.watchedSessionPatch(event.sessionId, event.session),\n ...log,\n })\n return Promise.resolve()\n }\n case 'session/reset': {\n // The session was resumed on a fresh process and the agent is about to\n // replay its history as fresh updates: drop local blocks and the seq\n // watermark so the replay folds in from a clean slate.\n this.lastSeq.set(event.sessionId, 0)\n this.openTurns.delete(event.sessionId)\n const isActive = event.sessionId === this.state.activeSessionId\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : [...this.state.sessions, event.session]\n // The agent is about to replay its whole history as fresh updates:\n // an observed transcript must drop both its blocks and its watermark\n // or the replay would be read as a gap and refetched forever.\n const watched = this.state.chatTranscripts[event.sessionId]\n this.mutate({\n sessions,\n ...(isActive ? { blocks: [] } : {}),\n ...(watched === undefined ? {} : {\n chatTranscripts: {\n ...this.state.chatTranscripts,\n [event.sessionId]: { ...watched, session: event.session, blocks: [], seq: 0 },\n },\n }),\n })\n return Promise.resolve()\n }\n case 'session/update': {\n // Coalesced (see `pendingUpdates`): one fold + one mutate per frame.\n this.pendingUpdates.push({ sessionId: event.sessionId, seq: event.seq, update: event.update })\n this.armUpdateFlush()\n return Promise.resolve()\n }\n case 'catalog/updated': {\n // The host learned a new catalog, or a new slash-command list, from a\n // real session (its session/new, session/load, a config_option_update,\n // or an available_commands_update). The seat list is a cached\n // projection of both, so drop and re-read it — deduped by\n // `loadAgentCatalog`, and memory-only on the host, so this never starts\n // a probe.\n this.invalidateSeatCatalog(event.agentId)\n return this.loadAgentCatalog(event.agentId)\n }\n case 'agent/exposure': {\n // The host changed an agent's model visibility outside `agent/save`:\n // the settings card edited it, the auto-discovery switch was flipped,\n // or newly published models were appended automatically. Patch the\n // settings card's copy of the definition AND drop the seat cache — the\n // variants `agent/catalog` answers are a projection of this filter.\n const exposedModels = event.exposedModels.length === 0 ? undefined : [...event.exposedModels]\n const detectedAgents = this.state.detectedAgents.map(info =>\n info.id === event.agentId && info.currentConfig !== undefined\n ? {\n ...info,\n currentConfig: {\n ...info.currentConfig,\n exposedModels: exposedModels === undefined ? undefined : exposedModels,\n autoDiscoverModels: event.autoDiscoverModels,\n },\n }\n : info)\n this.invalidateSeatCatalog(event.agentId)\n this.mutate({ detectedAgents })\n return this.loadAgentCatalog(event.agentId)\n }\n case 'permission/request': {\n // Verbatim copy of the host's frame. The host decides what the ask is\n // called (`toolName` / `reason`) and which options exist; deriving a\n // title from `toolCall` here used to make the card say something the\n // host never said.\n const permission: PendingPermission = {\n requestId: event.requestId,\n sessionId: event.sessionId,\n ...(event.dshSessionId === undefined ? {} : { dshSessionId: event.dshSessionId }),\n toolName: event.toolName,\n ...(event.reason === undefined ? {} : { reason: event.reason }),\n options: event.options,\n timeoutMode: event.timeoutMode,\n ...(event.deadlineAt === undefined ? {} : { deadlineAt: event.deadlineAt }),\n }\n this.mutate({\n permissions: [...this.state.permissions.filter(p => p.requestId !== event.requestId), permission],\n })\n return Promise.resolve()\n }\n case 'permission/resolved': {\n this.mutate({ permissions: this.state.permissions.filter(p => p.requestId !== event.requestId) })\n return Promise.resolve()\n }\n case 'permission/synced': {\n // Terminator of the reconnect replay: the host just re-sent every\n // outstanding ask, and this set is the authoritative live list. Drop\n // anything this client still holds from before the disconnect — an ask\n // resolved while it was away is gone host-side, and answering it would\n // only earn `unknown_request`.\n const live = new Set(event.requestIds)\n this.mutate({ permissions: this.state.permissions.filter(p => live.has(p.requestId)) })\n return Promise.resolve()\n }\n case 'chat/agent-locked': {\n // The host locks a conversation at the START of its first ACP turn and\n // says so at once; this frame is what makes the hand-off animation run\n // at the start rather than on the turn-settled list read. It is emitted\n // only on the transition, so it always announces a new ceremony — even\n // if a racing list read already seeded the lock itself.\n this.chatUnlocked.delete(event.dshSessionId)\n this.mutate({\n chatLockedAgent: { ...this.state.chatLockedAgent, [event.dshSessionId]: event.agentId },\n chatBoundAgent: { ...this.state.chatBoundAgent, [event.dshSessionId]: event.agentId },\n agentLockReveal: { ...this.state.agentLockReveal, [event.dshSessionId]: (this.state.agentLockReveal[event.dshSessionId] ?? 0) + 1 },\n })\n return Promise.resolve()\n }\n case 'chat/agent-unlocked': {\n // The first turn delivered nothing and the host rolled the lock back.\n // The base must return; the failed ACP session is closed host-side, so\n // drop a cached list that still names it and let the next read confirm.\n this.chatUnlocked.add(event.dshSessionId)\n const chatLockedAgent = { ...this.state.chatLockedAgent }\n delete chatLockedAgent[event.dshSessionId]\n this.mutate({\n chatLockedAgent,\n ...(this.state.chatSessionsFor === event.dshSessionId ? { chatSessions: [] } : {}),\n })\n return Promise.resolve()\n }\n case 'turn/start': {\n this.openTurns.add(event.sessionId)\n this.markWatchedRunning(event.sessionId, true)\n if (event.sessionId === this.state.activeSessionId) {\n this.state.blocks.push({ kind: 'user', text: event.text })\n trimBlocks(this.state.blocks)\n this.mutate({ sessions: this.state.sessions.map(s => (s.sessionId === event.sessionId ? { ...s, running: true } : s)) })\n }\n return Promise.resolve()\n }\n case 'turn/end': {\n // One turn may end twice (force-finalized after an ignored cancel, or\n // settled by the host after a crash, then the real response arrives);\n // only the first turn/end per open turn is applied.\n if (!this.openTurns.has(event.sessionId)) return Promise.resolve()\n this.openTurns.delete(event.sessionId)\n this.markWatchedRunning(event.sessionId, false)\n if (event.sessionId === this.state.activeSessionId) {\n if (event.error !== undefined) {\n this.state.blocks.push({ kind: 'notice', tone: 'error', text: event.error })\n } else if (event.stopReason !== undefined && event.stopReason !== 'end_turn') {\n this.state.blocks.push({ kind: 'notice', tone: 'info', text: `stop: ${event.stopReason}` })\n }\n trimBlocks(this.state.blocks)\n this.mutate({ sessions: this.state.sessions.map(s => (s.sessionId === event.sessionId ? { ...s, running: false } : s)) })\n }\n return Promise.resolve()\n }\n default:\n return Promise.resolve()\n }\n }\n}\n\n/** Merge a raw ACP update into the block list (mutates in place). */\n/**\n * Fold one transcript update into a block list. IMMUTABLE per block: the ACP\n * view memoizes rows on the block object, so an appended chunk replaces the\n * tail block instead of mutating it (array copies stay the caller's job, one\n * per folded batch). Returns whether any block changed — a usage-only tick\n * folds to `false` and the batch skips copying the array.\n *\n * `raw` is a lazy getter: pretty-printing a tool payload costs up to\n * {@link RAW_MAX} characters of JSON per call, and progress ticks restate\n * whole calls at high frequency — the cost is paid once, by the view that\n * actually shows the card, never by the fold loop.\n */\nexport function foldUpdate(blocks: Block[], update: Record<string, unknown>): boolean {\n const kind = String(update.sessionUpdate ?? '')\n const content = update.content as Record<string, unknown> | undefined\n switch (kind) {\n case 'user_message_chunk':\n case 'agent_message_chunk':\n case 'agent_thought_chunk': {\n const targetKind = kind === 'user_message_chunk' ? 'user' : kind === 'agent_message_chunk' ? 'agent' : 'thought'\n const text = chunkText(content)\n if (text === '') return false\n const messageId = typeof update.messageId === 'string' ? update.messageId : undefined\n const last = blocks[blocks.length - 1]\n if (targetKind === 'agent' && last?.kind === 'agent' && ((messageId !== undefined && last.messageId === messageId) || (messageId === undefined && last.messageId === undefined))) {\n blocks[blocks.length - 1] = { ...last, text: last.text + text }\n } else if (targetKind !== 'agent' && last?.kind === targetKind) {\n blocks[blocks.length - 1] = { ...last, text: last.text + text }\n } else if (targetKind === 'agent') {\n blocks.push({ kind: 'agent', ...(messageId === undefined ? {} : { messageId }), text })\n } else {\n blocks.push({ kind: targetKind, text })\n }\n return true\n }\n case 'tool_call': {\n let rawOf: string | undefined\n blocks.push({\n kind: 'tool',\n toolCallId: String(update.toolCallId ?? ''),\n title: String(update.title ?? ''),\n status: String(update.status ?? 'pending'),\n ...(typeof update.kind === 'string' ? { toolKind: update.kind } : {}),\n ...splitToolContent(update.content),\n ...toolInputFields(update),\n get raw() { return rawOf ??= jsonText(update) },\n })\n return true\n }\n case 'tool_call_update': {\n const id = String(update.toolCallId ?? '')\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i]\n if (block?.kind !== 'tool' || block.toolCallId !== id) continue\n // An update restates the call in whole, but every field it does NOT\n // carry keeps the block's previous value (the restatement that only\n // brings a result must not wipe the arguments the card shows).\n const parsed = splitToolContent(update.content)\n const input = toolInputOf(update)\n const details = toolDetailsOf(update)\n let rawOf: string | undefined\n blocks[i] = {\n kind: 'tool',\n toolCallId: id,\n title: typeof update.title === 'string' && update.title !== '' ? update.title : block.title,\n status: typeof update.status === 'string' ? update.status : block.status,\n ...(typeof update.kind === 'string' ? { toolKind: update.kind } : block.toolKind === undefined ? {} : { toolKind: block.toolKind }),\n lines: parsed.lines.length > 0 ? parsed.lines : block.lines,\n ...(parsed.diffs === undefined\n ? block.diffs === undefined ? {} : { diffs: block.diffs }\n : { diffs: parsed.diffs }),\n ...(parsed.extras.length > 0 ? { extras: parsed.extras } : block.extras === undefined ? {} : { extras: block.extras }),\n ...(input === undefined ? block.input === undefined ? {} : { input: block.input } : { input }),\n ...(details.length > 0 ? { details } : block.details === undefined ? {} : { details: block.details }),\n get raw() { return rawOf ??= jsonText(update) },\n }\n return true\n }\n // Update for a tool call that rolled out of the transcript window.\n let rawOf: string | undefined\n blocks.push({\n kind: 'tool',\n toolCallId: id,\n title: id,\n status: String(update.status ?? 'completed'),\n ...splitToolContent(update.content),\n ...toolInputFields(update),\n get raw() { return rawOf ??= jsonText(update) },\n })\n return true\n }\n case 'plan': {\n const entries = Array.isArray(update.entries)\n ? (update.entries as Array<Record<string, unknown>>).map(e => ({\n content: String(e.content ?? ''),\n status: String(e.status ?? 'pending'),\n ...(typeof e.priority === 'string' ? { priority: e.priority } : {}),\n }))\n : []\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i]\n if (block?.kind !== 'plan') continue\n blocks[i] = { ...block, entries }\n return true\n }\n blocks.push({ kind: 'plan', entries })\n return true\n }\n default: {\n // Every remaining notification kind is kept, not dropped: a command list,\n // a mode change, or a kind newer than this build. Bookkeeping frames that\n // say nothing new (`config_option_update` / `usage_update` /\n // `session_info_update`) ride the SessionView snapshot — they are state,\n // not stream content, and a row per tick would be noise.\n if (kind === '' || STATE_ONLY_UPDATES.has(kind)) return false\n let rawOf: string | undefined\n blocks.push({ kind: 'event', event: kind, text: describeEvent(kind, update), get raw() { return rawOf ??= jsonText(update) } })\n return true\n }\n }\n}\n\n/**\n * The input / detail fields a tool block carries from an update, spread-ready.\n * Kept apart from the content split so `tool_call` and `tool_call_update` share\n * one spelling of \"when does a field appear\".\n */\nfunction toolInputFields(update: Record<string, unknown>): Record<string, unknown> {\n const fields: Record<string, unknown> = {}\n const input = toolInputOf(update)\n if (input !== undefined) fields.input = input\n const details = toolDetailsOf(update)\n if (details.length > 0) fields.details = details\n return fields\n}\n\n/** Notifications that update the SessionView snapshot instead of the block stream. */\nexport const STATE_ONLY_UPDATES: ReadonlySet<string> = new Set([\n 'config_option_update',\n 'usage_update',\n 'session_info_update',\n])\n\nfunction trimBlocks(blocks: Block[]): void {\n if (blocks.length > MAX_BLOCKS) blocks.splice(0, blocks.length - MAX_BLOCKS)\n}\n","/**\n * Shared browser-side kit for the ACP *conversation* surfaces: the composer\n * base (agent / model / thought level), its picker and the ACP view tab.\n *\n * Everything here is deliberately structural. The ACP plugin's client half may\n * not import the host runtime or any client package at runtime — the seats it\n * takes (`conversation.input.model`, `conversation.input.overlay`,\n * `conversation.view`) are declared by `dsh-client-ui-conversation`, and the\n * services it reads (`sessions`) are resolved by the entry in `index.tsx` and\n * handed over as plain functions. That is what keeps this bundle loadable in a\n * deployment that composes only a subset of the official client modules: an\n * absent service yields `undefined` and the surface hides itself instead of\n * breaking the plugin.\n *\n * NO MODEL DIRECTORY. This bundle used to read and write the host's per-session\n * model directory (`ctx.modelDirectories`) so an ACP agent could be expressed as\n * a route. That axis is retired: ACP is not a DSH provider, the route write\n * polluted the deployment default, and the GUI's catalog gate then refused the\n * plugin's own route with `session/model-unavailable`. The agent identity now\n * lives in this plugin's own state (`session/chat-list`), reached by its own\n * RPC — see docs/acp-convergence.md §2.2.\n *\n * THE ONE DUPLICATED CODEC: `splitAcpModelId` mirrors the host's\n * `splitAdapterModelId` (src/impl.ts). It cannot be imported from there — the\n * browser half must not pull host-plane modules into the bundle — and the\n * shared contract file is types-only by rule. Keep the two in step: they are\n * the only place that knows how `agentId` and `agentId@choice` are spelled.\n */\n\nimport type { CSSProperties, ReactNode } from 'react'\nimport { useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport type { AcpStore, UiState } from './store.ts'\n\n// ---- Host-facing structural types ----\n\n/** One adapter-provided reasoning effort (DSH `ModelReasoningEffort`). */\nexport interface AcpReasoningEffort {\n id: string\n name: string\n description?: string\n}\n\n/**\n * One model inside a provider group of the agent's OWN catalog (from\n * `agent/catalog`). Deliberately NOT DSH's model catalog: an ACP agent's models\n * are the agent's business, and DSH's directory has no per-session seam to\n * filter them with anyway (see the module header).\n */\nexport interface AcpModelEntry {\n id: string\n name: string\n description?: string\n reasoning?: { efforts: readonly AcpReasoningEffort[]; defaultEffort?: string }\n}\n\n/** One provider group inside an agent's own model catalog. */\nexport interface AcpModelGroup {\n id: string\n name: string\n models: readonly AcpModelEntry[]\n}\n\n/** The session's command + projection face (`SessionFace`), narrowed to what the cluster reads. */\nexport interface AcpSessionFace {\n command(line: string): Promise<\n { ok: true; value: { matched: boolean } } | { ok: false; error: { code: string; message: string } }\n >\n projections: {\n faceOf(key: string): {\n getSnapshot(): unknown\n subscribe(listener: () => void): () => void\n }\n }\n}\n\n/** What `index.tsx` hands every ACP conversation entry. */\nexport interface AcpChatInjected {\n acp: AcpStore\n /** Resolve the session's command/projection face; `undefined` before the session materializes. */\n faceFor(sessionId: string): AcpSessionFace | undefined\n}\n\n// ---- The session standard kit (framework-provided, declared structurally) ----\n\n/**\n * The slice of the client runtime's `ConversationSnapshot` our composer entries\n * read: `running` is the only field we watch, and the only re-read trigger we\n * have for the host's ACP chat-session binding (a fresh conversation gets its\n * binding when its first turn creates one, and no SSE frame announces that).\n */\nexport interface AcpSessionSnapshot {\n running: boolean\n}\n\n/**\n * `SessionStandardProps.useSession`: the selector hook the renderer binds per\n * session binding (see `standardKit` in `dsh-client-ui-renderer` —\n * `standard['sessionId'] = info.sessionId` and every `hooks` source becomes a\n * `use<Name>` selector hook).\n *\n * Declared structurally, like every other host-facing type here, because the\n * client runtime that merges `SessionStandardProps` is not part of a plugin's\n * dependency closure: `PropsRuntime<K>` resolves to `object` in a plugin's own\n * build, so the type cannot name these members even though the runtime hands\n * them over. THE MEMBERS ARE `sessionId` + `useSession` — owners pass neither,\n * and there is no `session` prop on any of these seats (reading one was a\n * shipped crash: `Cannot read properties of undefined (reading 'sessionId')`).\n */\nexport interface AcpSessionHook {\n <S>(selector: (snapshot: AcpSessionSnapshot) => S): S\n}\n\n// ---- The shared adapter-model codec (see the module header) ----\n\n/** Split `agentId` / `agentId@choice`; mirrors the host's `splitAdapterModelId`. */\nexport function splitAcpModelId(model: string): { agentId: string; choice?: string } {\n const at = model.indexOf('@')\n if (at <= 0) return { agentId: model }\n return { agentId: model.slice(0, at), choice: model.slice(at + 1) }\n}\n\n/**\n * Build `agentId` / `agentId@choice`; mirrors the host's `buildAdapterModelId`.\n *\n * The inverse of {@link splitAcpModelId}, and a hand copy for the same reason:\n * the browser half cannot import the host plane. Needed where the browser has a\n * bare choice value (the agent's own current model, as `agent/catalog` reports\n * it) and must compare it against adapter model ids.\n */\nexport function buildAcpModelId(agentId: string, choice?: string): string {\n return choice === undefined || choice === '' ? agentId : `${agentId}@${choice}`\n}\n\n/**\n * Whether a form value looks like an absolute path on some platform the plugin\n * runs on: POSIX (`/…`), a Windows drive path (`C:\\…`, `C:/…`), or a UNC path\n * (`\\\\host\\share…`, `//host/share…`). Mirrors the host's `isAbsolutePath`\n * (src/impl.ts) — the browser must pre-reject exactly what `validateAgentDef`\n * would reject, or a save the form enables dies on the host\n * (tests/acp-view.spec.ts asserts equivalence on a shared sample set).\n *\n * The extended-length `\\\\?\\…` / `//?/…` family is rejected here too: the host's\n * workspace guard cannot interpret it, so a config written that way could never\n * resolve a cwd.\n */\nexport function isAbsolutePathLike(p: string): boolean {\n if (p.startsWith('\\\\\\\\?\\\\') || p.startsWith('//?/')) return false\n return p.startsWith('/') || /^[A-Za-z]:[\\\\/]/.test(p) || p.startsWith('\\\\\\\\')\n}\n\n// ---- The connection form's command line (hand copy of src/impl.ts) ----\n\n/**\n * Hand copies of the host's `tokenizeCommandLine` / `splitCommandLine` /\n * `quoteCommandToken` (src/impl.ts). The 命令 field is ONE command line — the\n * whole thing a user would paste into a terminal — and the panel has to parse\n * it exactly the way `validateAgentDef` will, or the \"程序 / 参数\" preview under\n * the field would lie about what the host is about to run. Same discipline as\n * `splitAcpModelId`: browser half cannot import the host plane, so\n * tests/acp-view.spec.ts asserts equivalence on a shared sample set.\n */\nexport function tokenizeCommandLine(raw: string): string[] {\n const tokens: string[] = []\n let current = ''\n let quoted = false\n let touched = false\n let quote: '\"' | \"'\" | undefined\n for (let index = 0; index < raw.length; index += 1) {\n const char = raw[index] as string\n if (quote !== undefined) {\n if (char === '\\\\' && quote === '\"' && raw[index + 1] === '\"') {\n current += '\"'\n index += 1\n continue\n }\n if (char === quote) {\n quote = undefined\n continue\n }\n current += char\n continue\n }\n if (char === '\"' || char === \"'\") {\n quote = char\n touched = true\n continue\n }\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') {\n if (touched || current !== '') tokens.push(current)\n current = ''\n touched = false\n continue\n }\n current += char\n touched = true\n }\n if (touched || current !== '') tokens.push(current)\n return tokens\n}\n\n/** First token = program, rest = arguments. Mirrors the host's `splitCommandLine`. */\nexport function splitCommandLine(raw: string): { command: string; args: string[] } {\n const tokens = tokenizeCommandLine(raw)\n return { command: tokens[0] ?? '', args: tokens.slice(1) }\n}\n\n/** Quote one token for a command line — only when it needs it. */\nexport function quoteCommandToken(token: string): string {\n if (token !== '' && !/[\\s\"']/.test(token)) return token\n return `\"${token.replaceAll('\"', '\\\\\"')}\"`\n}\n\n/** Join a parsed command back into the single editable field (round-trips through `splitCommandLine`). */\nexport function formatCommandLine(command: string, args: readonly string[] = []): string {\n return [command, ...args].map(quoteCommandToken).join(' ')\n}\n\n// ---- React bindings ----\n\n/** Subscribe to the ACP store (the panel and every chat surface share this). */\nexport function useUi(acp: AcpStore): UiState {\n return useSyncExternalStore(cb => acp.subscribe(cb), () => acp.getSnapshot())\n}\n\n\n// ---- The ACP-mode model seat (shadowing the native one) ----\n\n/**\n * The ACP agent preset's id. THREE places must agree on it (no type system\n * spans them): the declaration's `config.id` in `presets/acp/acp.patch.yml`\n * (this bundle's own patch, carried by package.json `dsh.bundle.patch` — no\n * install step), the host half's `ACP_PRESET_ID` (`src/bridge.ts`), and this\n * constant (the browser cannot read the host plane). `acp` is the value the\n * session list reports for an ACP-mode session and the same string the host's\n * own `sessionAgentPreset` resolver reads back from the session log.\n */\nexport const ACP_PRESET_ID = 'acp'\n\n/**\n * The bits of the client session list this plugin reads.\n *\n * Structural on purpose: the client runtime package is not a dependency of\n * this bundle, so the list is resolved as a plain observable at call time and\n * an unexpected shape simply reads as \"no ACP session\" — which leaves every ACP\n * surface hidden rather than broken.\n *\n * TWO SHAPES ship in the wild. DSH 0.1.7-rc.2's session controller\n * (`dsh-api-session-controller`) builds `{ ids, byId, phase,\n * projectionsBySession }` with NO `current` field at all — the staged session\n * is the row whose `retainedBy.mainView` count is positive (the same rule\n * `dsh-client-ui-session` uses to pick its main view), and the session's\n * preset rides `projectionValues.agentPreset` (folded from the\n * `agent-preset/selected` log event; the value is the preset's `id`).\n * Older runtimes carried a literal `current` plus a top-level `agentPreset`\n * on the row and a `currentAddress` marker for addressed subagent routes.\n */\nexport interface AcpSessionsSnapshot {\n /** Old runtimes only: the currently selected session id. */\n current?: string\n /** Old runtimes only: set when the current route is an addressed subagent session. */\n currentAddress?: unknown\n /** List rows by session id (both runtime shapes). */\n byId?: Record<string, AcpSessionsRow | undefined>\n /**\n * rc.2 shape: row ids in display order. A TIE-BREAK hint, never the\n * candidate set: `byId` is a superset of `ids` (the session controller adds\n * catalog-only and retained-scope rows to `byId` without giving them an\n * `ids` entry), so walking `ids` alone can miss the row the main view\n * actually retains. See {@link stagedSessionOf}.\n */\n ids?: readonly string[]\n phase?: string\n}\n\n/** One session-list row, narrowed to what the gate reads (both shapes). */\nexport interface AcpSessionsRow {\n id?: string\n /** Old runtimes: the preset at the top level. */\n agentPreset?: string\n /** rc.2: session projections, `agentPreset` among them. */\n projectionValues?: { agentPreset?: string } | undefined\n /** rc.2: per-surface retain counts; `mainView > 0` marks the staged session. */\n retainedBy?: { mainView?: number } | undefined\n /** rc.2: `\"subagent\"` marks addressed subagent rows. */\n origin?: string\n}\n\n/**\n * The preset the row records, under either runtime spelling. `undefined` means\n * the row is silent (projections not loaded yet, or a runtime without presets)\n * — never \"definitely not ACP\".\n */\nexport function rowAgentPreset(row: AcpSessionsRow | undefined): string | undefined {\n return row?.agentPreset ?? row?.projectionValues?.agentPreset\n}\n\n/** The session the composer is staged on, derived per runtime shape. */\nexport interface AcpStagedSession {\n sessionId: string\n row?: AcpSessionsRow\n /** Addressed subagent route: it is not a conversation this plugin may drive. */\n subagent: boolean\n}\n\n/**\n * Which session the composer is currently staged on.\n *\n * Old runtimes answer from `snapshot.current` (with `currentAddress` as the\n * subagent marker). rc.2 has no `current`: the staged session is the row the\n * main view retains (`retainedBy.mainView > 0`) — the exact rule\n * `dsh-client-ui-session` applies, walked in the list's own `ids` order — and a\n * row with `origin === \"subagent\"` is an addressed subagent route. `undefined`\n * when nothing is staged yet (the list has not loaded, or no row holds the main\n * view): callers must stand down rather than guess.\n */\nexport function stagedSessionOf(snapshot: AcpSessionsSnapshot | undefined): AcpStagedSession | undefined {\n if (snapshot === undefined) return undefined\n if (snapshot.current !== undefined) {\n return {\n sessionId: snapshot.current,\n row: snapshot.byId?.[snapshot.current],\n subagent: snapshot.currentAddress !== undefined,\n }\n }\n const byId = snapshot.byId ?? {}\n // Candidate set is `byId`, NOT `ids`. In rc.2 the controller projects\n // `ids` from the host list only, then appends two fallback families to\n // `byId` without an `ids` entry: subagent-catalog children of a projected\n // parent, and live retained scopes carrying a subagent address. A retained\n // scope of either family can be the row holding the main view, so an\n // `ids`-only walk would report \"nothing is staged\" and every ACP surface\n // (this gate included) would stand down on a real ACP conversation.\n //\n // `ids` still matters: it is the display order, so when several rows retain\n // the main view (multi-view layouts) the one the list shows first wins —\n // the same row `dsh-client-ui-session` puts on screen. Everything outside\n // `ids` sorts last, in `byId` key order.\n const order = new Map<string, number>()\n for (const [index, id] of (snapshot.ids ?? []).entries()) order.set(id, index)\n const rank = (id: string): number => order.get(id) ?? Number.MAX_SAFE_INTEGER\n const candidates = Object.keys(byId).sort((a, b) => rank(a) - rank(b))\n const sessionId = candidates.find(id => (byId[id]?.retainedBy?.mainView ?? 0) > 0)\n if (sessionId === undefined) return undefined\n const row = byId[sessionId]\n return { sessionId, row, subagent: row?.origin === 'subagent' }\n}\n\n/** Whether the staged session runs the ACP agent preset. */\nexport function stagedIsAcpMode(\n snapshot: AcpSessionsSnapshot | undefined,\n acpMode?: (sessionId: string) => boolean | undefined,\n): boolean {\n const staged = stagedSessionOf(snapshot)\n if (staged === undefined || staged.subagent) return false\n const listed = rowAgentPreset(staged.row)\n if (listed !== undefined) return listed === ACP_PRESET_ID\n return acpMode?.(staged.sessionId) === true\n}\n\n/**\n * The ACP-mode verdict a committed `agent-preset/selected` frame carries. The\n * frame is `[sessionId, agentPreset]` and the runtime broadcasts it the moment\n * a switch commits — before the appended session-log row is visible to a fresh\n * `session/chat-list` cold read, so re-reading the log from that frame folds the\n * PREVIOUS preset and the surface would lag one switch behind until a reload.\n * The frame's own preset is therefore the live path's fact source; the host read\n * only seeds a session this tab has never seen a frame for.\n */\nexport function acpModeFromPresetEvent(agentPreset: unknown): boolean {\n return agentPreset === ACP_PRESET_ID\n}\n\n/** The session list store as this bundle reads it (structural, see {@link AcpSessionsSnapshot}). */\nexport interface SessionListSource {\n getSnapshot(): AcpSessionsSnapshot | undefined\n subscribe(listener: () => void): () => void\n}\n\n// ---- The one open menu (composer overlay slot) ----\n\n/** Which picker the composer overlay is showing. */\nexport type AcpMenuKind = 'agent' | 'model' | 'thought' | 'advanced'\n\ninterface MenuState {\n sessionId: string\n kind: AcpMenuKind\n /** The control that opened the picker; the popup docks to it. */\n trigger?: HTMLElement\n /** Last pick failure, rendered inside the open picker (its own retry surface). */\n error?: string\n}\n\nlet menuState: MenuState | null = null\nconst menuListeners = new Set<() => void>()\n\nfunction emitMenu(): void {\n for (const listener of [...menuListeners]) listener()\n}\n\n/** Toggle-style open: pressing the same chip twice closes its menu. */\nexport function openAcpMenu(sessionId: string, kind: AcpMenuKind, trigger?: HTMLElement): void {\n menuState = menuState !== null && menuState.sessionId === sessionId && menuState.kind === kind\n ? null\n : { sessionId, kind, ...trigger === undefined ? {} : { trigger } }\n emitMenu()\n}\n\n/** Close whatever picker is open (Escape, pick, outside pointer). */\nexport function closeAcpMenu(): void {\n if (menuState === null) return\n menuState = null\n emitMenu()\n}\n\n/**\n * Report a pick failure inside the open picker. The menu stays open on\n * purpose: a failed switch must not leave the user staring at a closed chip\n * with no idea whether it took effect.\n */\nexport function setAcpMenuError(error?: string): void {\n if (menuState === null) return\n menuState = { ...menuState, ...(error === undefined ? { error: undefined } : { error }) }\n emitMenu()\n}\n\n/** Subscribe to the open-picker state (both the chips and the overlay read it). */\nexport function useAcpMenu(): MenuState | null {\n return useSyncExternalStore(\n cb => {\n menuListeners.add(cb)\n return () => { menuListeners.delete(cb) }\n },\n () => menuState,\n )\n}\n\n// ---- The NTR settings dialog ----\n\n/**\n * Open state of the frame-wide NTR settings dialog (`settings-dialog.tsx`).\n *\n * Module-level because the two ends live in unrelated slot trees: the trigger\n * is the composer's agent base (a session-scoped model-seat entry) and the\n * dialog itself rides the root `shell.overlay` layer. A React context cannot\n * span that boundary, so this is the same tiny external-store seam the menu\n * uses above.\n */\nlet settingsDialogOpen = false\nconst settingsDialogListeners = new Set<() => void>()\n\nfunction emitSettingsDialog(): void {\n for (const listener of [...settingsDialogListeners]) listener()\n}\n\n/** Open the NTR settings dialog (the empty agent roster's way forward). */\nexport function openAcpSettingsDialog(): void {\n if (settingsDialogOpen) return\n settingsDialogOpen = true\n emitSettingsDialog()\n}\n\n/** Close the NTR settings dialog (close button, mask click, Escape). */\nexport function closeAcpSettingsDialog(): void {\n if (!settingsDialogOpen) return\n settingsDialogOpen = false\n emitSettingsDialog()\n}\n\n/** Current dialog state, for tests and non-React readers. */\nexport function acpSettingsDialogIsOpen(): boolean {\n return settingsDialogOpen\n}\n\n/** Subscribe to the dialog state (the root overlay entry reads it). */\nexport function useAcpSettingsDialogOpen(): boolean {\n return useSyncExternalStore(\n cb => {\n settingsDialogListeners.add(cb)\n return () => { settingsDialogListeners.delete(cb) }\n },\n acpSettingsDialogIsOpen,\n )\n}\n\n// ---- Menu presentation ----\n\n/** One selectable row of a picker. */\nexport interface AcpMenuItem {\n id: string\n label: string\n detail?: string\n /** Current value (rendered with the accent + a check). */\n active?: boolean\n disabled?: boolean\n /** Leading status dot color (agent process state). */\n dot?: string\n /**\n * Render as a checkbox row (`menuitemcheckbox`) rather than a radio row.\n * The advanced base uses it for the agent's boolean config options: one row\n * whose check IS the current value, whose `id` is the value a click sets.\n */\n checkbox?: boolean\n}\n\n/** One titled block of rows. */\nexport interface AcpMenuSection {\n id: string\n title?: string\n note?: string\n items: readonly AcpMenuItem[]\n}\n\n/** Props of the picker shell rendered into `conversation.input.overlay`. */\nexport interface AcpMenuProps {\n /** Menu model; `undefined` renders nothing (closed). */\n model?: {\n title: string\n note?: string\n sections: readonly AcpMenuSection[]\n error?: string\n loading?: boolean\n }\n /** The control that opened the menu; the popup docks above it (see `placeMenu`). */\n anchor?: HTMLElement | null\n onPick(itemId: string, sectionId: string): void\n onClose(): void\n}\n\n/**\n * Pure popup placement, viewport coordinates: the menu docks with its right\n * edge under the trigger's, `gap`px above it — and only then clamps to the\n * viewport (flip below when the top has no room, slide left at the right edge).\n * Separated from the DOM measurement so the geometry stays testable.\n */\nexport function placeMenu(\n trigger: { left: number; right: number; top: number; bottom: number },\n menu: { width: number; height: number },\n viewport: { width: number; height: number },\n gap = 8,\n): { x: number; y: number } {\n const x = Math.min(\n Math.max(gap, trigger.right - menu.width),\n Math.max(gap, viewport.width - menu.width - gap),\n )\n const above = trigger.top - menu.height - gap\n let y = above >= gap ? above : trigger.bottom + gap\n if (y + menu.height > viewport.height - gap) {\n y = Math.max(gap, viewport.height - menu.height - gap)\n }\n return { x, y }\n}\n\n/**\n * The picker shell. It rides the composer's overlay anchor (the card's top\n * edge) — the seat the platform reserves for menus, so the popup is never\n * clipped by the card's own scrollport — and then docks itself to the control\n * that opened it (`anchor`): the seat or a chip is a dropdown trigger, and a\n * menu that materializes elsewhere on the card reads as \"nothing happened\".\n */\nexport function AcpMenu({ model, anchor, onPick, onClose }: AcpMenuProps) {\n const rootRef = useRef<HTMLDivElement>(null)\n useLayoutEffect(() => {\n const node = rootRef.current\n if (node === null || model === undefined) return\n const place = (): void => {\n try {\n if (anchor === undefined || anchor === null || !anchor.isConnected) {\n node.style.transform = ''\n return\n }\n const menuRect = node.getBoundingClientRect()\n const triggerRect = anchor.getBoundingClientRect()\n const { x, y } = placeMenu(\n triggerRect,\n menuRect,\n { width: window.innerWidth, height: window.innerHeight },\n )\n // Translate from wherever CSS parked the popup: the offset parent is\n // the platform's zero-height overlay anchor, so plain left/top would\n // need its exact geometry — measuring the delta needs none.\n node.style.transform = 'none'\n const base = node.getBoundingClientRect()\n node.style.transform = `translate(${x - base.left}px, ${y - base.top}px)`\n } catch { /* placement must never break the menu */ }\n }\n place()\n window.addEventListener('resize', place)\n return () => { window.removeEventListener('resize', place) }\n }, [model, anchor])\n useEffect(() => {\n if (model === undefined) return\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.stopPropagation()\n onClose()\n }\n }\n const onPointerDown = (event: PointerEvent): void => {\n if (!(event.target instanceof Node)) return\n if (rootRef.current?.contains(event.target)) return\n // Clicking the composer card itself must not close the menu (the chips\n // live there); everything else outside does.\n if (rootRef.current?.closest('[data-composer-card]')?.contains(event.target) === true) return\n onClose()\n }\n document.addEventListener('keydown', onKey, true)\n document.addEventListener('pointerdown', onPointerDown, true)\n return () => {\n document.removeEventListener('keydown', onKey, true)\n document.removeEventListener('pointerdown', onPointerDown, true)\n }\n }, [model, onClose])\n\n if (model === undefined) return null\n return (\n <div ref={rootRef} className=\"ntr-acp-menu\" role=\"menu\" aria-label={model.title}>\n <div className=\"ntr-acp-menu-head\">\n <span className=\"ntr-acp-menu-title\">{model.title}</span>\n {model.loading && <span className=\"ntr-acp-menu-spin\" aria-hidden>…</span>}\n </div>\n {model.note !== undefined && <div className=\"ntr-acp-menu-note\">{model.note}</div>}\n {model.error !== undefined && <div className=\"ntr-acp-menu-error\">{model.error}</div>}\n <div className=\"ntr-acp-menu-body\">\n {model.sections.map(section => (\n <div key={section.id} className=\"ntr-acp-menu-section\">\n {section.title !== undefined && <div className=\"ntr-acp-menu-section-title\">{section.title}</div>}\n {section.note !== undefined && <div className=\"ntr-acp-menu-note\">{section.note}</div>}\n {section.items.map(item => (\n <button\n key={`${section.id}/${item.id}`}\n type=\"button\"\n role={item.checkbox === true ? 'menuitemcheckbox' : 'menuitemradio'}\n aria-checked={item.active === true}\n className={`ntr-acp-menu-item${item.active === true ? ' is-active' : ''}`}\n disabled={item.disabled === true}\n onClick={() => { onPick(item.id, section.id) }}\n >\n {item.dot !== undefined && <span className=\"ntr-acp-dot\" style={{ background: item.dot }} aria-hidden />}\n {item.checkbox === true && (\n <span className={`ntr-acp-check${item.active === true ? ' is-on' : ''}`} aria-hidden>{item.active === true ? '✓' : ''}</span>\n )}\n <span className=\"ntr-acp-menu-item-label\">{item.label}</span>\n {item.active === true && item.checkbox !== true && <span className=\"ntr-acp-tick\" aria-hidden>✓</span>}\n {item.detail !== undefined && <span className=\"ntr-acp-menu-item-detail\">{item.detail}</span>}\n </button>\n ))}\n </div>\n ))}\n {model.sections.every(section => section.items.length === 0) && (\n <div className=\"ntr-acp-menu-empty\">—</div>\n )}\n </div>\n </div>\n )\n}\n\n// ---- Chips ----\n\n/**\n * One composer chip. With `onClick` it is the trigger of a picker; without it\n * it is a static nameplate (the locked agent on the composer edge) — same\n * shape and no button semantics, so it does not read as a control that does\n * nothing. Neither variant draws a caret: the chips are a compact status row,\n * and the disclosure arrow added visual noise without carrying information.\n *\n * `className` exists for the lock hand-off: the base needs the shatter\n * keyframes (`ntr-acp-shatter`) while it is still mounted, and that class must\n * ride the chip itself to keep the cluster's `:last-child` layout rule intact.\n */\nexport function AcpChip(props: {\n label: string\n title?: string\n active?: boolean\n onClick?: (trigger: HTMLElement) => void\n children?: ReactNode\n style?: CSSProperties\n className?: string\n}) {\n const onClick = props.onClick\n const className = `ntr-acp-chip${onClick === undefined ? ' is-static' : ''}${props.active === true ? ' is-active' : ''}${props.className === undefined ? '' : ` ${props.className}`}`\n if (onClick === undefined) {\n return (\n <span className={className} title={props.title} style={props.style}>\n {props.children}\n <span className=\"ntr-acp-chip-label\">{props.label}</span>\n </span>\n )\n }\n return (\n <button\n type=\"button\"\n className={className}\n title={props.title}\n aria-haspopup=\"menu\"\n onClick={(event) => { onClick(event.currentTarget) }}\n style={props.style}\n >\n {props.children}\n <span className=\"ntr-acp-chip-label\">{props.label}</span>\n </button>\n )\n}\n\n/**\n * The debris layer of the lock hand-off (see `agent-lock-anim.ts`).\n *\n * `out` scatters the base's fragments; `in` plays the same particles backwards\n * under the arriving tag, so the plate reads as condensed from what the base\n * broke into. Purely decorative: `aria-hidden`, no pointer events, and the\n * whole layer is gone once the phase ends.\n */\ninterface AcpShard {\n /** Resting position inside the chip, in percent. */\n x: number\n y: number\n width: number\n height: number\n /**\n * Scatter offset and spin for the outbound half. The base sits at the RIGHT\n * of the composer bar while the tag hangs near its left edge, so the debris\n * drifts leftward: it reads as the same cloud that later streams in from the\n * right to re-form the tag (see `inX` / `inY`).\n */\n dx: number\n dy: number\n /** Where the inbound half starts: to the right of the tag, off toward the base. */\n inX: number\n inY: number\n rotate: number\n scale: number\n delay: number\n}\n\nconst ACP_SHARDS: readonly AcpShard[] = [\n { x: 8, y: 30, width: 5, height: 7, dx: -42, dy: -22, inX: 150, inY: -18, rotate: -120, scale: 0.35, delay: 0 },\n { x: 18, y: 55, width: 4, height: 5, dx: -28, dy: 24, inX: 118, inY: 24, rotate: 90, scale: 0.55, delay: 20 },\n { x: 28, y: 20, width: 6, height: 4, dx: -20, dy: -30, inX: 196, inY: -10, rotate: -45, scale: 0.45, delay: 10 },\n { x: 38, y: 65, width: 5, height: 6, dx: -14, dy: 28, inX: 132, inY: 30, rotate: 140, scale: 0.4, delay: 30 },\n { x: 48, y: 35, width: 4, height: 8, dx: -36, dy: -26, inX: 210, inY: -4, rotate: 75, scale: 0.5, delay: 0 },\n { x: 56, y: 60, width: 6, height: 5, dx: -24, dy: 24, inX: 164, inY: 20, rotate: -90, scale: 0.55, delay: 25 },\n { x: 66, y: 25, width: 4, height: 4, dx: -30, dy: -20, inX: 128, inY: -24, rotate: 120, scale: 0.4, delay: 5 },\n { x: 74, y: 58, width: 5, height: 7, dx: -18, dy: 20, inX: 182, inY: 14, rotate: -140, scale: 0.5, delay: 15 },\n { x: 84, y: 32, width: 4, height: 5, dx: -10, dy: -14, inX: 112, inY: -32, rotate: 60, scale: 0.4, delay: 20 },\n { x: 13, y: 45, width: 3, height: 4, dx: -34, dy: 6, inX: 206, inY: 8, rotate: 180, scale: 0.3, delay: 35 },\n { x: 43, y: 48, width: 3, height: 3, dx: -26, dy: 16, inX: 176, inY: 4, rotate: -60, scale: 0.3, delay: 40 },\n { x: 88, y: 50, width: 3, height: 4, dx: -8, dy: 8, inX: 142, inY: 26, rotate: 160, scale: 0.3, delay: 35 },\n]\n\nexport function AcpShatterParts(props: { mode: 'out' | 'in' }) {\n return (\n <span className={`ntr-acp-shards is-${props.mode}`} aria-hidden>\n {ACP_SHARDS.map((shard, index) => (\n <span\n key={index}\n className=\"ntr-acp-shard\"\n style={{\n left: `${shard.x}%`,\n top: `${shard.y}%`,\n width: shard.width,\n height: shard.height,\n animationDelay: `${shard.delay}ms`,\n '--ntr-shard-dx': `${shard.dx}px`,\n '--ntr-shard-dy': `${shard.dy}px`,\n '--ntr-shard-in-x': `${shard.inX}px`,\n '--ntr-shard-in-y': `${shard.inY}px`,\n '--ntr-shard-rotate': `${shard.rotate}deg`,\n '--ntr-shard-scale': shard.scale,\n } as CSSProperties}\n />\n ))}\n </span>\n )\n}\n\n// ---- Styles ----\n\nconst STYLE = `\n.ntr-acp-cluster { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; flex-wrap: nowrap; }\n/* The last chip is the cluster's fixed right-hand anchor: it never shrinks\n below its label, while the chips to its left absorb the squeeze. Before the\n lock that is the agent base; after it retires, the model / thought pair\n inherits the edge. Without this a long model name could compress the agent\n base into invisibility — the \"selector disappeared\" symptom. */\n.ntr-acp-cluster > .ntr-acp-chip:last-child { flex: none; }\n/* The agent tag after the base retires. It rides the composer card's top edge:\n the overlay anchor is a zero-height absolute box at that edge, so translateY\n (-50%) puts half the tag above the card and half inside it — a nameplate\n hanging on the box. Above the menu's siblings but below the picker (z 100). */\n.ntr-acp-tag { position: absolute; top: 0; left: 24px; z-index: 90; transform: translateY(-50%); display: inline-flex; max-width: calc(100% - 48px); }\n.ntr-acp-tag > .ntr-acp-chip { font-size: 11px; line-height: 1.35; padding: 1px 8px; background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-1, #fff)); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); }\n.ntr-acp-tag > .ntr-acp-chip.is-static:hover { background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-1, #fff)); }\n.ntr-acp-chip { position: relative; display: inline-flex; align-items: center; gap: 5px; max-width: 220px; font: inherit; font-size: 12px; line-height: 1.4; padding: 3px 8px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }\n.ntr-acp-chip:hover { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-chip.is-static { cursor: default; }\n.ntr-acp-chip.is-static:hover { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.ntr-acp-chip.is-active { border-color: var(--dsw-alias-state-business-primary, #2563eb); color: var(--dsw-alias-state-business-primary, #2563eb); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 10%, transparent); }\n.ntr-acp-chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n/* The lock hand-off (src/client/agent-lock-anim.ts): the outgoing agent base\n breaks into debris for 460ms, then the edge tag condenses from the same\n particles. Both halves read one shared clock in that module; these durations\n must stay equal to SHATTER_MS / FORM_MS there. */\n.ntr-acp-shards { position: absolute; inset: 0; display: block; pointer-events: none; overflow: visible; }\n.ntr-acp-shard { position: absolute; border-radius: 1px; background: currentColor; opacity: 0; }\n.ntr-acp-shards.is-out .ntr-acp-shard { animation: ntr-acp-shard-out 460ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }\n.ntr-acp-shards.is-in .ntr-acp-shard { animation: ntr-acp-shard-in 540ms cubic-bezier(0.16, 0.84, 0.28, 1) both; }\n.ntr-acp-chip.ntr-acp-shatter { animation: ntr-acp-shatter-chip 460ms cubic-bezier(0.4, 0, 0.8, 0.4) both; pointer-events: none; }\n.ntr-acp-tag.is-forming > .ntr-acp-chip { animation: ntr-acp-form-chip 540ms cubic-bezier(0.16, 0.84, 0.28, 1) both; }\n/* A live agent's nameplate breathes: a soft blue halo pulses while the ACP\n session serving this conversation is up and not released (agentTagAlive in\n session-controls.tsx). The period comes from the settings card through the\n --ntr-halo-period variable on the tag span; the base shadow from the plain\n tag chip rides inside every keyframe so elevation never flickers. */\n.ntr-acp-tag.is-alive > .ntr-acp-chip {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 40%, transparent);\n animation: ntr-acp-tag-breath var(--ntr-halo-period, 3.2s) ease-in-out infinite;\n}\n/* Steady mode: the same halo, constant. Same rule as reduced-motion below. */\n.ntr-acp-tag.is-alive.is-steady > .ntr-acp-chip {\n animation: none;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 6px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 35%, transparent), 0 0 14px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 20%, transparent);\n}\n@keyframes ntr-acp-tag-breath {\n 0%, 100% { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 4px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 18%, transparent); }\n 50% { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 9px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 50%, transparent), 0 0 18px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 28%, transparent); }\n}\n@keyframes ntr-acp-shatter-chip {\n 0% { opacity: 1; transform: none; filter: blur(0); }\n 22% { opacity: 1; transform: scale(1.05); filter: blur(0); }\n 55% { opacity: 0.55; filter: blur(1.5px); }\n 100% { opacity: 0; transform: scale(0.84); filter: blur(4px); }\n}\n@keyframes ntr-acp-form-chip {\n 0% { opacity: 0; transform: scale(0.55) translateY(2px); filter: blur(6px); }\n 60% { opacity: 1; filter: blur(0); }\n 100% { opacity: 1; transform: none; filter: blur(0); }\n}\n@keyframes ntr-acp-shard-out {\n 0% { opacity: 0; transform: translate(0, 0) scale(1) rotate(0deg); }\n 18% { opacity: 0.95; }\n 100% { opacity: 0; transform: translate(var(--ntr-shard-dx), var(--ntr-shard-dy)) scale(var(--ntr-shard-scale)) rotate(var(--ntr-shard-rotate)); }\n}\n@keyframes ntr-acp-shard-in {\n 0% { opacity: 0; transform: translate(var(--ntr-shard-in-x), var(--ntr-shard-in-y)) scale(var(--ntr-shard-scale)) rotate(var(--ntr-shard-rotate)); }\n 22% { opacity: 0.9; }\n 70% { opacity: 0.5; }\n 100% { opacity: 0; transform: translate(0, 0) scale(0.35) rotate(0deg); }\n}\n@media (prefers-reduced-motion: reduce) {\n .ntr-acp-shards { display: none; }\n .ntr-acp-chip.ntr-acp-shatter, .ntr-acp-tag.is-forming > .ntr-acp-chip { animation: none; }\n /* No pulsing, but the alive state stays visible as a static halo. */\n .ntr-acp-tag.is-alive > .ntr-acp-chip {\n animation: none;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 6px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 35%, transparent);\n }}\n.ntr-acp-seat { display: inline-flex; align-items: center; gap: 6px; max-width: 260px; font: inherit; font-size: 12px; line-height: 1.4; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }\n.ntr-acp-seat:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-seat:disabled { opacity: 0.5; cursor: default; }\n.ntr-acp-seat.is-open { border-color: var(--dsw-alias-state-business-primary, #2563eb); color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-seat.is-detached { border-color: var(--dsw-alias-state-warn-label, #d97706); color: var(--dsw-alias-state-warn-label, #d97706); }\n.ntr-acp-seat-main { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-seat-sub { color: var(--dsw-alias-label-secondary, #6b7280); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }\n.ntr-acp-tick { margin-left: auto; color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-check { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 13px; height: 13px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: 3px; font-size: 10px; line-height: 1; color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-check.is-on { border-color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-menu { position: absolute; bottom: calc(100% + 8px); left: 8px; z-index: 100; width: min(360px, 100%); max-height: min(420px, 60vh); display: flex; flex-direction: column; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16); font-size: 12px; overflow: hidden; }\n.ntr-acp-menu-head { padding: 8px 10px 6px; font-weight: 600; display: flex; align-items: center; gap: 6px; }\n.ntr-acp-menu-title { flex: 1; }\n.ntr-acp-menu-spin { opacity: 0.5; }\n.ntr-acp-menu-note { padding: 0 10px 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); line-height: 1.5; }\n.ntr-acp-menu-error { margin: 0 10px 6px; padding: 6px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 11px; }\n.ntr-acp-menu-body { overflow: auto; padding: 0 4px 6px; }\n.ntr-acp-menu-section { display: flex; flex-direction: column; padding-top: 4px; }\n.ntr-acp-menu-section-title { padding: 6px 8px 4px; font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-menu-item { display: flex; align-items: center; gap: 6px; font: inherit; font-size: 12px; text-align: left; padding: 6px 8px; border: none; border-radius: 6px; background: none; color: inherit; cursor: pointer; width: 100%; }\n.ntr-acp-menu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-menu-item:disabled { opacity: 0.45; cursor: default; }\n.ntr-acp-menu-item.is-active { font-weight: 600; }\n.ntr-acp-menu-item-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-menu-item-detail { color: var(--dsw-alias-label-secondary, #6b7280); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; text-align: right; }\n.ntr-acp-menu-empty { padding: 10px; text-align: center; color: var(--dsw-alias-label-secondary, #6b7280); }\n/* ---- ACP view tab ---- */\n.ntr-acp-view { display: flex; flex-direction: column; height: 100%; min-height: 0; font-size: 13px; line-height: 1.6; }\n.ntr-acp-view-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2, #e5e7eb); font-size: 12px; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-view-agent { font-weight: 600; color: inherit; }\n.ntr-acp-view-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; direction: rtl; text-align: left; }\n.ntr-acp-view-head .ntr-acp-more { margin-left: auto; }\n.ntr-acp-stream { flex: 1; min-height: 0; overflow: auto; padding: 12px 14px 24px; display: flex; flex-direction: column; gap: 10px; }\n.ntr-acp-badge { font-size: 11px; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; }\n.ntr-acp-badge.is-done { color: color-mix(in srgb, var(--dsw-alias-state-success-primary, #22c55e) 72%, var(--dsw-alias-label-primary, #111827)); background: var(--dsw-alias-state-success-tertiary, #dcfce7); }\n.ntr-acp-badge.is-busy { color: var(--dsw-alias-state-business-primary, #1d4ed8); background: var(--dsw-alias-state-business-tertiary, #dbeafe); }\n.ntr-acp-badge.is-warn { color: var(--dsw-alias-state-warn-label, #b45309); background: var(--dsw-alias-state-warn-tertiary, #fef3c7); }\n.ntr-acp-badge.is-fail { color: var(--dsw-alias-state-error-primary, #b91c1c); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); }\n.ntr-acp-badge.is-idle { color: var(--dsw-alias-label-secondary, #6b7280); background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.ntr-acp-bubble { max-width: 88%; padding: 8px 12px; border-radius: 12px; white-space: pre-wrap; word-break: break-word; }\n.ntr-acp-bubble.is-user { align-self: flex-end; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 12%, transparent); }\n.ntr-acp-bubble.is-agent { align-self: flex-start; background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.ntr-acp-inline-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 1px 4px; border-radius: 4px; background: color-mix(in srgb, currentColor 10%, transparent); }\n.ntr-acp-code { margin: 6px 0; padding: 8px 10px; border-radius: 8px; background: #0f172a; color: #e2e8f0; overflow: auto; font-size: 12px; line-height: 1.5; }\n.ntr-acp-thought { border-left: 3px solid var(--dsw-alias-border-l2, #d1d5db); padding-left: 8px; color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12px; }\n.ntr-acp-thought > summary { cursor: pointer; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; }\n.ntr-acp-thought-body { white-space: pre-wrap; padding: 4px 0 2px; }\n.ntr-acp-tool { border: 1px solid var(--dsw-alias-border-l2, #e5e7eb); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }\n.ntr-acp-tool.is-fail { border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }\n.ntr-acp-tool-head { display: flex; align-items: center; gap: 8px; padding: 7px 10px; font-size: 12px; }\n.ntr-acp-tool-title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-tool-body { margin: 0; padding: 8px 10px; border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); background: var(--dsw-alias-bg-layer-2, #f9fafb); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 340px; overflow: auto; }\n.ntr-acp-tool-input { background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #111827); max-height: 240px; }\n.ntr-acp-tool-meta { font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-diff { border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); }\n.ntr-acp-diff-head { display: flex; align-items: center; gap: 8px; padding: 5px 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; background: var(--dsw-alias-bg-layer-2, #f9fafb); }\n.ntr-acp-diff-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-diff-stats .is-add { color: var(--dsw-alias-state-success-primary, #15803d); }\n.ntr-acp-diff-stats .is-del { color: var(--dsw-alias-state-error-primary, #b91c1c); }\n.ntr-acp-diff-body { max-height: 360px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; }\n.ntr-acp-diff-row { display: flex; gap: 8px; padding: 0 10px; white-space: pre; }\n.ntr-acp-diff-row.is-add { background: var(--dsw-alias-file-diff-added-bg, #dcfce7); }\n.ntr-acp-diff-row.is-del { background: var(--dsw-alias-file-diff-deleted-bg, #fee2e2); }\n.ntr-acp-diff-row.is-ctx { color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-diff-sign { width: 1ch; flex: none; opacity: 0.7; }\n.ntr-acp-diff-text { overflow-x: auto; }\n.ntr-acp-diff-note { padding: 4px 10px 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-more { font: inherit; font-size: 11.5px; padding: 3px 10px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }\n.ntr-acp-more:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-more:disabled { opacity: 0.5; cursor: default; }\n.ntr-acp-more.is-primary { border-color: transparent; background: var(--dsw-alias-button-primary-fill, #2563eb); color: var(--dsw-alias-label-primary-foreground, #fff); }\n.ntr-acp-tool > .ntr-acp-more, .ntr-acp-empty-actions .ntr-acp-more { margin: 6px 10px; }\n.ntr-acp-plan { border: 1px solid var(--dsw-alias-border-l2, #e5e7eb); border-radius: 10px; padding: 8px 10px; background: var(--dsw-alias-bg-layer-1, #fff); }\n.ntr-acp-plan-head { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); margin-bottom: 4px; }\n.ntr-acp-plan-row { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; padding: 1px 0; }\n.ntr-acp-plan-row.is-completed { color: var(--dsw-alias-label-secondary, #6b7280); text-decoration: line-through; }\n.ntr-acp-plan-row.is-in_progress { font-weight: 600; }\n.ntr-acp-plan-glyph { width: 1ch; flex: none; }\n.ntr-acp-plan-prio { margin-left: auto; font-size: 10px; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-notice { padding: 6px 10px; border-radius: 8px; font-size: 12px; }\n.ntr-acp-notice.is-error { background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); }\n.ntr-acp-notice.is-info { background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-empty { margin: auto; max-width: 460px; text-align: center; display: flex; flex-direction: column; gap: 10px; padding: 24px; }\n.ntr-acp-empty-title { font-size: 14px; font-weight: 600; }\n.ntr-acp-empty-body { margin: 0; color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12.5px; line-height: 1.7; }\n.ntr-acp-empty-hint { color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12px; }\n.ntr-acp-seat-diag { font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace); opacity: 0.72; word-break: break-word; }\n.ntr-acp-empty-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }\n/* ---- Pending permission asks (conversation.input.dock) ---- */\n/* The dock slot is the full conversation column, but the composer card inside\n it is narrower (InputBar: side clearance + card max width). Mirror that\n formula so each ask lines up with the input box instead of spanning the\n column. The list owns a bounded height and scrolls so a burst of asks cannot\n push the composer off screen. */\n.ntr-permission-dock {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px));\n max-width: var(--dsh-composer-card-max-width, 920px);\n margin: 0 auto;\n display: flex; flex-direction: column; gap: 6px;\n max-height: min(38vh, 320px); overflow-y: auto;\n font-size: 12px; line-height: 1.5;\n}\n.ntr-permission-card { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); }\n.ntr-permission-head { display: flex; align-items: baseline; gap: 8px; min-width: 0; }\n.ntr-permission-tool { flex: none; font-weight: 600; }\n.ntr-permission-reason { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-permission-deadline { flex: none; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); font-variant-numeric: tabular-nums; }\n.ntr-permission-deadline.is-expired { color: var(--dsw-alias-state-warn-label, #b45309); font-weight: 600; }\n.ntr-permission-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }\n.ntr-permission-option, .ntr-permission-cancel { font: inherit; font-size: 12px; line-height: 1.4; padding: 4px 10px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; white-space: normal; text-align: left; }\n.ntr-permission-option:hover:not(:disabled), .ntr-permission-cancel:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-permission-option:disabled, .ntr-permission-cancel:disabled { opacity: 0.5; cursor: default; }\n.ntr-permission-option:focus-visible, .ntr-permission-cancel:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #2563eb); outline-offset: 2px; }\n.ntr-permission-cancel { margin-left: auto; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-permission-error { padding: 4px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 11px; word-break: break-word; }\n/* ---- NTR settings dialog (shell.overlay) ---- */\n/* Frame-wide surface opened from the agent base's empty-roster menu. The\n overlay layer itself is click-through, so the layer opts back in and blocks\n the app while it is up. */\n.ntr-acp-dialog-layer { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; pointer-events: auto; }\n.ntr-acp-dialog-mask { position: absolute; inset: 0; background: var(--dsw-alias-bg-mask-1, rgba(0, 0, 0, 0.4)); }\n.ntr-acp-dialog {\n position: relative; display: flex; flex-direction: column; overflow: hidden;\n width: min(920px, calc(100vw - 48px)); height: min(820px, calc(100vh - 48px));\n background: var(--dsw-alias-bg-layer-2, #fff); color: inherit;\n border-radius: 24px; box-shadow: var(--dsw-elevation-prominent, 0 24px 60px rgba(0, 0, 0, 0.25));\n}\n.ntr-acp-dialog-head { flex: none; display: flex; align-items: center; gap: 8px; padding: 16px 16px 8px 24px; }\n.ntr-acp-dialog-title { flex: 1; font-size: 16px; font-weight: 600; }\n.ntr-acp-dialog-close { font: inherit; font-size: 14px; line-height: 1; width: 30px; height: 30px; padding: 0; border: none; border-radius: 999px; background: none; color: inherit; cursor: pointer; }\n.ntr-acp-dialog-close:hover { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-dialog-body { flex: 1; min-height: 0; overflow: auto; }\n/* The section brings its own page scroll for the settings shell's content\n column; the dialog body is the scroll owner here. */\n.ntr-acp-dialog-body > .dwa-root { max-width: none; overflow: visible; }\n`\n\nlet styleMounted = false\n\n/** Mount the shared stylesheet once (same pattern as the settings panel's). */\nexport function ensureAcpStyle(): void {\n if (styleMounted || typeof document === 'undefined') return\n styleMounted = true\n const node = document.createElement('style')\n node.textContent = STYLE\n document.head.appendChild(node)\n}\n","/**\n * React surface for ACP: the settings configuration section.\n *\n * Scope is deliberately narrow — this is a CONFIGURATION surface. Runtime\n * interaction lives in the conversation: every permission ask with a chat\n * identity is answered on a card above that conversation's composer\n * (`permission-dock.tsx`). The ledger card here is the cross-session mirror —\n * asks whose conversation is not open, or which have no chat identity at all —\n * and is where those are answered (see `permissionLedgerCard`).\n *\n * Each configured agent is one compact row (status + current model + badges)\n * that expands into the full connection form and model catalog. The row stays\n * mounted and the body toggles `display`, so editing a form and collapsing it\n * never loses the in-progress values.\n * All business state lives in the injected AcpStore.\n */\n\nimport { useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { formatCommandLine, isAbsolutePathLike, splitCommandLine } from './acp-ui.tsx'\nimport { DEFAULT_KNOWN_PRESETS, LOG_MAX_ENTRIES } from './store.ts'\nimport type { AcpStore, UiState } from './store.ts'\nimport type { AcpAgentDef, AcpApprovalDeadlines, AcpCatalogChoice, AcpMcpServerDef, AgentSavePayload, AgentView, DetectedAgentInfo, PermissionTimeoutMode, PluginUpdateState } from '../protocol.ts'\n\ninterface WithStore { acp: AcpStore }\n\n/**\n * Mirrors `AGENT_ID_PATTERN` in impl.ts — the client cannot import host-side\n * modules, so this copy must stay in sync (same deal as the preset copies,\n * constraint 7).\n */\nconst AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/\n\n/** Preset ids (drives the「自定义」badge); mirrors AGENT_PRESETS via store copies. */\nconst PRESET_IDS = new Set(DEFAULT_KNOWN_PRESETS.map(preset => preset.id))\n\n/** Seed for the from-scratch creation card: no preset, no tested default. */\nconst NEW_AGENT_SEED: DetectedAgentInfo = {\n id: '',\n name: '',\n detected: false,\n defaultCommand: '',\n defaultArgs: [],\n}\n\n/** How long a chip edit is coalesced before the lightweight exposure RPC fires. */\nconst EXPOSURE_SAVE_DEBOUNCE_MS = 400\n\nconst STYLE = `\n.dwa-root {\n --ntr-gap-1: 4px; --ntr-gap-2: 8px; --ntr-gap-3: 12px; --ntr-gap-4: 16px;\n --ntr-radius: 10px; --ntr-radius-sm: 7px;\n --ntr-font: 13px; --ntr-font-sm: 12px; --ntr-font-xs: 11px;\n --ntr-line: 1px solid var(--dsw-alias-border-l2, #e5e7eb);\n --ntr-muted: var(--dsw-alias-label-secondary, #6b7280);\n --ntr-danger: var(--dsw-alias-state-error-primary, #dc2626);\n --ntr-warn: var(--dsw-alias-state-warn-label, #b45309);\n --ntr-success: color-mix(in srgb, var(--dsw-alias-state-success-primary, #16a34a) 72%, var(--dsw-alias-label-primary, #111827));\n --ntr-accent: var(--dsw-alias-button-primary-fill, #2563eb);\n width: 100%; max-width: 960px; box-sizing: border-box; margin: 0 auto;\n padding: 28px 32px; min-width: 0; container-type: inline-size; font-size: var(--ntr-font); line-height: 1.55;\n}\n.dwa-root [hidden] { display: none !important; }\n.dwa-root button, .dwa-root input, .dwa-root select, .dwa-root textarea { font-family: inherit; }\n.dwa-root input, .dwa-root textarea { min-width: 0; }\n.dwa-tabs { display: flex; gap: 24px; flex-wrap: wrap; border-bottom: var(--ntr-line); margin-bottom: 24px; }\n.dwa-tabs > button { font: inherit; color: var(--ntr-muted); background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 0 0 12px; min-height: 40px; white-space: nowrap; cursor: pointer; }\n.dwa-tabs > button:hover { color: var(--dsw-alias-label-primary, #111827); }\n.dwa-tabs > button[aria-pressed=\"true\"] { color: var(--dsw-alias-label-primary, #111827); border-bottom-color: currentColor; font-weight: 550; }\n.dwa-detail-tabs { margin: 4px 0 20px; gap: 20px; }\n.dwa-detail-tabs > button { font-size: var(--ntr-font-sm); }\n.dwa-row-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.dwa-notice { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }\n.dwa-notice > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.dwa-empty { text-align: center; padding: 40px 16px; color: var(--ntr-muted); }\n.dwa-empty h3 { font-size: 16px; font-weight: 550; color: var(--dsw-alias-label-primary, #111827); margin: 0 0 8px; }\n.dwa-empty .dwa-btn { margin-top: 20px; }\n.dwa-health { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 24px; color: var(--ntr-muted); font-size: var(--ntr-font-sm); flex-wrap: wrap; }\n.dwa-health-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.dwa-health-status > span:last-child:not(:nth-child(2)) { padding-left: 12px; border-left: var(--ntr-line); }\n.dwa-back { margin-bottom: 16px; }\n.dwa-root button:focus-visible, .dwa-root input:focus-visible, .dwa-root select:focus-visible {\n outline: 2px solid var(--ntr-accent); outline-offset: 2px;\n}\n.dwa-page-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 28px; }\n.dwa-page-heading { min-width: 0; }\n.dwa-page-title { margin: 0; font-size: 20px; line-height: 1.35; font-weight: 550; letter-spacing: -0.02em; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.dwa-page-description { margin: 5px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-sm); }\n.dwa-btn {\n min-height: 32px; box-sizing: border-box; font: inherit; border-radius: var(--ntr-radius-sm);\n border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-1, #fff);\n padding: 5px 12px; cursor: pointer; color: inherit; font-size: var(--ntr-font-sm);\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n white-space: nowrap; transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;\n}\n.dwa-btn:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); border-color: var(--dsw-alias-border-l1, #9ca3af); }\n.dwa-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); border-color: transparent; font-weight: 550; }\n.dwa-btn-primary:hover { background: var(--ntr-accent); border-color: transparent; opacity: 0.88; }\n.dwa-btn-secondary { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.dwa-btn-quiet { background: transparent; border-color: transparent; color: var(--ntr-muted); }\n.dwa-btn-quiet:hover { border-color: transparent; color: var(--dsw-alias-label-primary, #111827); }\n.dwa-btn-danger { color: var(--ntr-danger); border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }\n.dwa-btn-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, #fee2e2); }\n.dwa-section { margin-bottom: 24px; }\n.dwa-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }\n.dwa-section-title { font-size: 13px; font-weight: 550; margin: 0; display: flex; align-items: center; gap: 8px; }\n.dwa-section-copy { margin: 3px 0 0; font-size: var(--ntr-font-xs); color: var(--ntr-muted); }\n.dwa-card { border: var(--ntr-line); border-radius: var(--ntr-radius); padding: 16px; background: var(--dsw-alias-bg-layer-1, #fff); font-size: var(--ntr-font); line-height: 1.55; }\n.dwa-card-warn { border-color: color-mix(in srgb, var(--ntr-warn) 30%, var(--dsw-alias-border-l2, #e5e7eb)); background: var(--dsw-alias-state-warn-tertiary, #fffbeb); }\n.dwa-mode-banner { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 16px; align-items: start; margin-bottom: 24px; padding: 14px 16px; }\n.dwa-mode-title { display: flex; align-items: center; gap: 8px; font-weight: 650; }\n.dwa-mode-copy { grid-column: 1 / -1; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-mode-source { display: block; margin-top: 5px; overflow-wrap: anywhere; opacity: 0.85; }\n.dwa-badge { font-size: var(--ntr-font-xs); line-height: 1.4; border-radius: 999px; padding: 2px 8px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font-weight: 550; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }\n.dwa-badge-success { background: var(--dsw-alias-state-success-tertiary, #dcfce7); color: var(--ntr-success); }\n.dwa-badge-warn { background: var(--dsw-alias-state-warn-tertiary, #fef3c7); color: var(--ntr-warn); }\n.dwa-badge-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }\n/* The update badge is a button (it expands the update card): strip the\n browser's default button chrome, keep the badge look. */\n.dwa-update-badge { border: 0; font-family: inherit; cursor: pointer; }\n.dwa-update-badge:hover { opacity: 0.85; }\n/* The version badge doubles as the update entry: same badge look, no chrome. */\n.dwa-version-btn { border: 0; font-family: inherit; cursor: pointer; }\n.dwa-version-btn:hover { opacity: 0.85; }\n.dwa-version { font-family: var(--ds-font-family-code, monospace); }\n/* The update card sits between the page header and the permission mirror. */\n.dwa-update-card { margin-bottom: 16px; }\n.dwa-update-summary { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-weight: 550; }\n.dwa-update-copy { margin: 6px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-update-message { margin: 6px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); white-space: pre-wrap; overflow-wrap: anywhere; }\n.dwa-update-actions { margin-top: 10px; }\n.dwa-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }\n.dwa-agent-list { display: flex; flex-direction: column; border-top: var(--ntr-line); }\n.dwa-agent-card { border-bottom: var(--ntr-line); min-width: 0; }\n.dwa-agent-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; align-items: center; gap: 14px; padding: 16px 0; }\n.dwa-agent-avatar { display: grid; place-items: center; width: 38px; height: 38px; border: var(--ntr-line); border-radius: 9px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font: 12px var(--ds-font-family-code, monospace); }\n.dwa-caret { font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; width: 1em; }\n.dwa-caret-btn { font: inherit; border: none; background: var(--dsw-alias-bg-layer-2, #f3f4f6); width: 28px; height: 28px; padding: 0; border-radius: var(--ntr-radius-sm); display: inline-flex; align-items: center; justify-content: center; color: var(--ntr-muted); cursor: pointer; }\n.dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); color: inherit; }\n.dwa-agent-summary { min-width: 0; }\n.dwa-agent-titleline { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }\n.dwa-agent-name { font-size: 13px; font-weight: 550; overflow-wrap: anywhere; }\n.dwa-agent-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 4px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-agent-meta-separator { opacity: 0.45; }\n.dwa-agent-body { padding: 0 0 24px 52px; }\n.dwa-subsection { min-width: 0; }\n.dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: stretch; }\n.dwa-form-col { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }\n.dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-label-primary, #374151); }\n.dwa-hint { font-size: var(--ntr-font-xs); line-height: 1.55; color: var(--ntr-muted); }\n.dwa-error { font-size: var(--ntr-font-xs); color: var(--ntr-danger); }\n.dwa-input { min-height: 36px; font: inherit; font-size: var(--ntr-font-sm); padding: 7px 10px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; width: 100%; box-sizing: border-box; }\n.dwa-select { min-height: 32px; font: inherit; font-size: var(--ntr-font-sm); padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; }\n.dwa-input:hover, .dwa-select:hover { border-color: var(--dsw-alias-border-l1, #9ca3af); }\n.dwa-input:focus, .dwa-select:focus { border-color: var(--ntr-accent); }\n.dwa-input-mono, .dwa-mono { font-family: var(--ds-font-family-code, ui-monospace), SFMono-Regular, Menlo, Monaco, Consolas, monospace; }\n.dwa-stderr { max-height: 120px; overflow: auto; font-family: monospace; font-size: var(--ntr-font-xs); white-space: pre-wrap; color: var(--ntr-warn); margin: 12px 0 0; }\n.dwa-default-hint { background: var(--dsw-alias-bg-layer-2, #f9fafb); border: var(--ntr-line); border-radius: var(--ntr-radius-sm); padding: 10px 12px; font-size: var(--ntr-font-sm); display: flex; align-items: center; justify-content: space-between; margin: 12px 0 14px; gap: 12px; }\n.dwa-form-actions { display: flex; align-items: center; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: var(--ntr-line); flex-wrap: wrap; }\n.dwa-form-actions .dwa-btn-primary { min-width: 88px; }\n.dwa-form-actions > .dwa-hint:first-child { margin-right: auto; }\n.dwa-form-actions > .dwa-hint:last-child:not(:first-child) { width: 100%; }\n.dwa-menu-wrap { position: relative; }\n.dwa-menu-wrap > .dwa-btn { width: 32px; padding: 0; }\n.dwa-menu { position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 190px; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); padding: 4px; display: flex; flex-direction: column; }\n.dwa-menu-item { font: inherit; font-size: var(--ntr-font-sm); text-align: left; border: none; background: none; padding: 7px 10px; border-radius: 5px; cursor: pointer; color: inherit; }\n.dwa-menu-item:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-menu-item-danger { color: var(--ntr-danger); }\n.dwa-menu-confirm { display: flex; align-items: center; gap: 4px; padding: 4px; }\n.dwa-add-panel .dwa-agent-row { display: none; }\n.dwa-add-panel .dwa-agent-body { padding: 0 0 24px; }\n.dwa-add-panel .dwa-agent-card { border: 0; }\n.dwa-add-content { padding-top: 10px; }\n.dwa-chip { min-height: 30px; font: inherit; font-size: var(--ntr-font-sm); border: var(--ntr-line); border-radius: 999px; padding: 4px 12px; background: var(--dsw-alias-bg-layer-1, #fff); cursor: pointer; color: inherit; }\n.dwa-chip:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-chip-active { border-color: var(--ntr-accent); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); font-weight: 550; }\n.dwa-chip-list { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }\n.dwa-switch { display: inline-flex; align-items: center; gap: 7px; font-size: var(--ntr-font-sm); cursor: pointer; user-select: none; }\n.dwa-inline-setting { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-2, #f9fafb); }\n.dwa-inline-setting .dwa-switch { flex-shrink: 0; margin-top: 1px; }\n.dwa-setting-copy { color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n/* Stacked, not side by side: the rows carry name, id and credit, and a\n half-width pane truncated them at every realistic settings width. Each list\n gets the whole column instead. */\n.dwa-model-transfer { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }\n/* Tall enough for a comfortable selection (about ten rows) before scrolling;\n the cap keeps the two stacked panes from pushing the rest of the card away. */\n.dwa-model-pane { min-width: 0; max-height: 360px; display: flex; flex-direction: column; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }\n.dwa-model-pane-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; border-bottom: var(--ntr-line); font-size: var(--ntr-font-xs); font-weight: 600; color: var(--dsw-alias-label-secondary, #4b5563); }\n.dwa-model-pane-head .dwa-btn { margin-left: auto; min-height: 26px; padding: 2px 8px; font-size: var(--ntr-font-xs); }\n.dwa-model-pane-head .dwa-caret-btn { flex: none; width: 22px; height: 22px; background: transparent; }\n.dwa-model-pane-head .dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); }\n.dwa-model-pane.is-collapsed .dwa-model-list { display: none; }\n.dwa-model-list { display: flex; flex-direction: column; gap: 2px; padding: 5px; flex: 1 1 auto; min-height: 0; overflow-y: auto; }\n.dwa-model-section { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; font-size: 12px; }\n.dwa-model-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n/* The actions group keeps 「检测可用模型」 and the filter together and wraps as\n one unit; the status line takes the leftover space on the first row, so a\n narrow panel puts [detect][search] on its own row starting under the label\n instead of floating them to the right edge. */\n.dwa-model-toolbar-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.dwa-model-status { margin-left: auto; white-space: nowrap; }\n.dwa-model-search { width: 200px; min-height: 30px; }\n.dwa-model-empty { padding: 6px; }\n.dwa-model-row { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 38px; border: none; background: none; padding: 7px 8px; border-radius: 5px; cursor: pointer; color: inherit; font: inherit; font-size: var(--ntr-font-sm); text-align: left; }\n.dwa-model-row:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-model-row.is-on { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); }\n.dwa-model-row-mark { width: 12px; flex: none; text-align: center; opacity: 0.75; }\n.dwa-model-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n/* Name and credit keep their room; the id is the only cell that collapses. */\n.dwa-model-row-desc { flex: 0 0 auto; max-width: 45%; opacity: 0.75; font-size: var(--ntr-font-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n/* The trailing 14px is reserved for the fade mask: while the id fits, the\n gradient only covers empty padding; once it overflows, its tail dissolves\n instead of ending in a hard ellipsis. armModelIdMarquee reads the same\n padding to scroll the full text under the pointer. */\n.dwa-model-row-value { flex: 0 1 auto; flex-shrink: 1000; box-sizing: border-box; min-width: 0; max-width: 45%; padding-right: 14px; opacity: 0.55; font-size: 10px; overflow: hidden; white-space: nowrap; mask-image: linear-gradient(to right, #000 calc(100% - 14px), transparent); -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 14px), transparent); }\n.dwa-model-row-value-text { display: inline-block; white-space: nowrap; }\n.dwa-model-row:hover .dwa-model-row-value-text { animation: dwa-model-id-marquee var(--dwa-id-duration, 1.4s) ease-in-out 0.2s infinite alternate; }\n@keyframes dwa-model-id-marquee {\n from { transform: translateX(0); }\n to { transform: translateX(var(--dwa-id-shift, 0px)); }\n}\n.dwa-model-row-remove { flex: none; opacity: 0.5; padding: 0 2px; }\n.dwa-model-row:hover .dwa-model-row-remove { opacity: 1; }\n.dwa-seg { display: inline-flex; gap: 4px; padding: 4px; box-sizing: border-box; border-radius: var(--ntr-radius-sm); border: var(--ntr-line); background: var(--dsw-alias-bg-layer-2, #f3f4f6); vertical-align: middle; }\n.dwa-seg > button { min-height: 32px; font: inherit; font-size: var(--ntr-font-sm); color: var(--ntr-muted); border: 1px solid transparent; border-radius: 4px; background: transparent; padding: 4px 12px; cursor: pointer; white-space: nowrap; }\n.dwa-seg > button:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-seg > button.is-on { background: var(--dsw-alias-bg-layer-1, #fff); border: var(--ntr-line); color: var(--dsw-alias-label-primary, #111827); font-weight: 550; }\n.dwa-preferences { border-top: var(--ntr-line); }\n.dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 0; }\n.dwa-preference-row + .dwa-preference-row { border-top: var(--ntr-line); }\n/* A control that needs the whole card: the label sits above it, not beside\n it. A right-aligned segmented control squeezed its labels into two lines\n each (中/止, 自动推/进), which read as broken rather than as choices. */\n.dwa-preference-block { padding: 12px 0; }\n.dwa-preference-block + .dwa-preference-block { border-top: var(--ntr-line); }\n.dwa-preference-label { font-size: var(--ntr-font-sm); font-weight: 600; }\n.dwa-preference-copy { margin-top: 2px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-seg-wide { display: flex; width: 100%; margin-top: 10px; }\n.dwa-seg-wide > button { flex: 1; }\n/* The wait window, as a slider: the number is the headline, the rungs are the\n scale under the track. */\n.dwa-deadline-value { margin-top: 8px; font-size: 20px; font-weight: 600; }\n.dwa-deadline-range { width: 100%; margin-top: 6px; accent-color: var(--ntr-accent); }\n.dwa-deadline-range:disabled { opacity: 0.4; }\n.dwa-deadline-ticks { display: flex; justify-content: space-between; margin-top: 2px; color: var(--ntr-muted); font-size: 10px; }\n.dwa-deadline-ticks > span { white-space: nowrap; }\n/* The facts that make a number here meaningless, in warning ink rather than\n the muted gray every other hint uses: the value the config file is\n outranking, on the row it belongs to. */\n.dwa-deadline-flag { margin-top: 4px; font-size: var(--ntr-font-xs); font-weight: 550; color: var(--ntr-warn); }\n.dwa-deadline-warn { margin-top: 10px; padding: 9px 12px; border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-secondary, #fffbeb); border-radius: var(--ntr-radius-sm); font-size: var(--ntr-font-xs); line-height: 1.55; }\n.dwa-deadline-reset { display: flex; justify-content: flex-end; margin-top: 8px; }\n/* Runtime log: one bounded list, newest first. The time column never wraps;\n the message is the only cell that may break anywhere. */\n.dwa-log-list { list-style: none; margin: 0; padding: 0; border: var(--ntr-line); border-radius: var(--ntr-radius); overflow: hidden; }\n.dwa-log-row { display: flex; align-items: baseline; gap: 8px; padding: 6px 10px; font-size: var(--ntr-font-sm); border-top: var(--ntr-line); min-width: 0; }\n.dwa-log-row:first-child { border-top: 0; }\n.dwa-log-row.is-error { background: var(--dsw-alias-state-error-tertiary, #fef2f2); }\n.dwa-log-time { color: var(--ntr-muted); white-space: nowrap; }\n.dwa-log-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.dwa-log-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ntr-danger); display: inline-block; margin-left: 6px; }\n.dwa-log-level.is-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }\n.dwa-log-level.is-error { background: var(--dsw-alias-state-error-tertiary, #fef2f2); color: var(--ntr-danger); }\n@media (max-width: 640px) {\n .dwa-root { padding: 20px 16px; }\n}\n@container (max-width: 640px) {\n .dwa-page-header { align-items: flex-start; flex-wrap: wrap; }\n .dwa-agent-body { padding-left: 0; }\n .dwa-agent-row { grid-template-columns: 32px minmax(0, 1fr) auto; gap: 10px; }\n .dwa-agent-avatar { width: 32px; height: 32px; }\n .dwa-section-heading { flex-wrap: wrap; }\n .dwa-tabs { gap: 20px; }\n .dwa-notice { flex-wrap: wrap; }\n .dwa-seg-wide > button { padding-inline: 5px; }\n .dwa-model-toolbar-actions { width: 100%; }\n .dwa-model-search { flex: 1 1 auto; width: auto; }\n .dwa-preference-row { align-items: flex-start; flex-direction: column; }\n .dwa-default-hint { align-items: flex-start; flex-direction: column; }\n /* The slider's scale: up to eight rungs with full unit words (\"15 分钟\")\n need a smaller face before they can collide on a narrow panel. The rung\n count follows the mode (自动推进 adds 不等待), and the row is a flex\n space-between, so a shorter scale just spreads wider. */\n .dwa-deadline-ticks { font-size: 9px; }\n}\n@media (prefers-reduced-motion: reduce) {\n .dwa-root *, .dwa-root *::before, .dwa-root *::after { transition: none !important; }\n .dwa-model-row:hover .dwa-model-row-value-text { animation: none !important; }\n}\n`\n\nlet styleMounted = false\nfunction ensureStyle(): void {\n if (styleMounted || typeof document === 'undefined') return\n styleMounted = true\n const node = document.createElement('style')\n node.textContent = STYLE\n document.head.appendChild(node)\n}\n\nfunction useUi(acp: AcpStore) {\n return useSyncExternalStore(cb => acp.subscribe(cb), () => acp.getSnapshot())\n}\n\nfunction stateVisual(state: AgentView['state'], acp: AcpStore): { color: string; text: string; title?: string } {\n switch (state) {\n case 'ready': return { color: 'var(--dsw-alias-state-success-primary, #22c55e)', text: acp.label('已连接', 'Connected') }\n case 'starting': return { color: 'var(--dsw-alias-state-warn-primary, #eab308)', text: acp.label('启动中', 'Starting') }\n case 'error': return { color: 'var(--dsw-alias-state-error-primary, #ef4444)', text: acp.label('错误', 'Error') }\n // A configured-but-resting agent is the normal state, not a fault: the\n // process is not kept resident and connects on demand the first time a DSH\n // chat turn uses it. Surfacing a runtime lifecycle here (\"stopped\"/\"idle\")\n // read as broken; the user's real question at a glance is \"is it set up?\",\n // which the config-time 测试连接 (agent/test) already answers. So the\n // resting state just says 已配置.\n default: return {\n color: 'var(--dsw-alias-state-idle-primary, #9ca3af)',\n text: acp.label('已配置', 'Configured'),\n title: acp.label(\n '已配置,进程不常驻。在 DSH 聊天中首次使用时自动连接;展开区可用「测试连接」验证命令能否正常起来。',\n 'Configured; the process is not kept resident. It connects automatically the first time a DSH chat turn uses it — use “Test connection” in the expanded panel to verify the command starts.',\n ),\n }\n }\n}\n\n/** Bilingual label for the event-stream state shown in the toolbar. */\nfunction streamText(stream: 'init' | 'live' | 'down', acp: AcpStore): string {\n if (stream === 'live') return acp.label('事件流已连接', 'Event stream live')\n if (stream === 'down') return acp.label('事件流已断开', 'Event stream down')\n return acp.label('事件流连接中', 'Event stream connecting')\n}\n\n/**\n * Canonical comparable form of an exposure filter. Unset and empty mean the\n * same thing host-side (no `agentId@choice` variants), so they share a key —\n * treating them as different would leave a row permanently dirty.\n */\nfunction exposureKey(exposed: readonly string[] | undefined): string {\n return exposed === undefined || exposed.length === 0 ? 'none' : `set:${exposed.join('\\u0000')}`\n}\n\nfunction sameStringArray(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index])\n}\n\nfunction sameStringMap(a: Record<string, string>, b: Record<string, string>): boolean {\n const keys = Object.keys(a)\n if (keys.length !== Object.keys(b).length) return false\n return keys.every(key => a[key] === b[key])\n}\n\n/**\n * Canonical comparable form of the MCP server list. Unset and empty mean the\n * same thing host-side (no extra MCP servers), so they share a key — treating\n * them as different would leave a card permanently dirty.\n *\n * The list is compared by CANONICAL JSON (object keys sorted, the servers\n * themselves sorted by name): reformatting the text — reordering keys,\n * re-indenting, listing the servers in another order — is not a change. The\n * ARGV array inside a server keeps its order, because there it is the command\n * line. The browser half needs no copy of the host's per-server validation: a\n * list the host would reject simply never matches, and the host's own error is\n * what the user sees.\n */\nfunction mcpKey(servers: readonly AcpMcpServerDef[] | undefined): string {\n if (servers === undefined || servers.length === 0) return 'none'\n const canonical = servers\n .map(canonicalJson)\n .sort((a, b) => {\n const nameA = String((a as { name?: unknown }).name ?? '')\n const nameB = String((b as { name?: unknown }).name ?? '')\n return nameA < nameB ? -1 : nameA > nameB ? 1 : 0\n })\n return JSON.stringify(canonical)\n}\n\n/** One value in canonical JSON form: object keys sorted, arrays kept in order. */\nfunction canonicalJson(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(canonicalJson)\n if (value === null || typeof value !== 'object') return value\n const entries = Object.entries(value as Record<string, unknown>)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([key, entry]) => [key, canonicalJson(entry)])\n return Object.fromEntries(entries)\n}\n\n/**\n * The MCP server list as the form holds it: the raw JSON text the user typed.\n *\n * Parsing happens on the way OUT (`buildPayload`), never on the way in, so a\n * half-typed value never throws inside a render. An unparseable text yields\n * `undefined`, which the form reports as invalid (`mcpInvalid`) and refuses to\n * save — better than sending a truncated list.\n */\nfunction parseMcpServers(text: string): AcpMcpServerDef[] | undefined {\n const trimmed = text.trim()\n if (trimmed === '') return undefined\n try {\n const parsed: unknown = JSON.parse(trimmed)\n if (!Array.isArray(parsed)) return undefined\n return parsed as AcpMcpServerDef[]\n } catch {\n return undefined\n }\n}\n\n/** Whether the MCP server text parses as a list at all (the host validates the rest). */\nfunction mcpTextValid(text: string): boolean {\n const trimmed = text.trim()\n if (trimmed === '') return true\n try {\n return Array.isArray(JSON.parse(trimmed))\n } catch {\n return false\n }\n}\n\n/**\n * Detected presets split by configured-ness, plus configured agents missing\n * from detection (custom / legacy persisted defs) synthesized into card\n * entries so they stay manageable.\n */\nfunction partitionAgents(\n detectedAgents: readonly DetectedAgentInfo[],\n agents: readonly AgentView[],\n): { configured: Array<{ info: DetectedAgentInfo; synthesized: boolean }>; available: DetectedAgentInfo[] } {\n const configured: Array<{ info: DetectedAgentInfo; synthesized: boolean }> = []\n const available: DetectedAgentInfo[] = []\n const agentIds = new Set(agents.map(a => a.id))\n const detectedIds = new Set(detectedAgents.map(d => d.id))\n for (const info of detectedAgents) {\n if (agentIds.has(info.id) || info.currentConfig !== undefined) configured.push({ info, synthesized: false })\n else available.push(info)\n }\n for (const agent of agents) {\n if (!detectedIds.has(agent.id)) {\n configured.push({\n info: {\n id: agent.id,\n name: agent.title,\n detected: false,\n defaultCommand: agent.command,\n defaultArgs: [...agent.args],\n },\n synthesized: true,\n })\n }\n }\n return { configured, available }\n}\n\n/**\n * The persisted definition a save would replace. `currentConfig` is the full\n * def (env / npmPackage / exposedModels included); the synthesized fallback\n * covers only the fields an `AgentView` carries, so anything else compares as\n * \"changed\" — the honest answer when the host never reported it.\n */\n/**\n * The idle-release window in effect for an agent: an unset per-agent field is\n * resolved by the HOST and reported back on `AgentView`, so filling it from\n * there keeps \"unset\" and \"explicitly the host default\" the same state —\n * merely opening the form must never read as an unsaved change.\n */\nfunction resolvedIdleTimeoutMs(def: AcpAgentDef, activeAgent: AgentView | undefined): AcpAgentDef {\n if (def.idleTimeoutMs !== undefined || activeAgent?.idleTimeoutMs === undefined) return def\n return { ...def, idleTimeoutMs: activeAgent.idleTimeoutMs }\n}\n\nfunction persistedDef(detected: DetectedAgentInfo, activeAgent: AgentView | undefined): AcpAgentDef | undefined {\n if (detected.currentConfig !== undefined) return resolvedIdleTimeoutMs(detected.currentConfig, activeAgent)\n if (activeAgent === undefined) return undefined\n return {\n id: activeAgent.id,\n title: activeAgent.title,\n command: activeAgent.command,\n args: [...activeAgent.args],\n ...(activeAgent.cwd === undefined ? {} : { cwd: activeAgent.cwd }),\n allowedCwdRoots: [...activeAgent.allowedCwdRoots],\n env: {},\n ...(activeAgent.idleTimeoutMs === undefined ? {} : { idleTimeoutMs: activeAgent.idleTimeoutMs }),\n }\n}\n\n/**\n * Equivalence test between the form payload and what is already persisted.\n * Order-insensitive on purpose: `env` is a map and `args`/`allowedCwdRoots`\n * keep their order, but a reordered allow-list is not a change worth warning\n * about; `exposedModels === undefined` means \"offer no variants\", which is the\n * same state as an empty list.\n */\nexport function payloadEquals(payload: AgentSavePayload, current: AcpAgentDef): boolean {\n if (payload.command !== current.command) return false\n if ((payload.title ?? payload.id) !== (current.title ?? current.id)) return false\n if (!sameStringArray(payload.args ?? [], current.args ?? [])) return false\n if ((payload.npmPackage ?? '') !== (current.npmPackage ?? '')) return false\n if ((payload.cwd ?? '') !== (current.cwd ?? '')) return false\n if (!sameStringArray(payload.allowedCwdRoots ?? [], current.allowedCwdRoots ?? [])) return false\n if (!sameStringMap(payload.env ?? {}, current.env ?? {})) return false\n if (exposureKey(payload.exposedModels) !== exposureKey(current.exposedModels)) return false\n if (mcpKey(payload.mcpServers) !== mcpKey(current.mcpServers)) return false\n if ((payload.autoDiscoverModels !== false) !== (current.autoDiscoverModels !== false)) return false\n // An absent field means \"use the host default\" — which is what `current`\n // already resolved to (see `resolvedIdleTimeoutMs`), so it is not a change.\n if (payload.idleTimeoutMs !== undefined && payload.idleTimeoutMs !== current.idleTimeoutMs) return false\n return true\n}\n\n/**\n * The settings panel's permission-ledger rule.\n *\n * Pending asks are shown in the conversation card while that conversation is\n * open; this card is the cross-session mirror for the ones it cannot show —\n * asks belonging to another conversation, and `/acp` RPC-only asks with no\n * chat identity at all. It therefore appears whenever ANY ask is pending: a\n * pending entry with no conversation card would otherwise have no answer\n * surface anywhere.\n */\nexport function permissionLedgerCard(ui: Pick<UiState, 'permissions'>): { visible: boolean } {\n return { visible: ui.permissions.length > 0 }\n}\n\n/**\n * The ONE wait window, as the rungs the slider snaps to.\n *\n * Shortest wait first, and `0` (不等待) FIRST: the slider reads left to right\n * as \"wait longer\", and \"do not wait at all\" is the shortest wait there is.\n * Never expiring is NOT on this scale — that is the `wait` MODE's business,\n * and having 「一直等待」 in both places made the card say the same thing twice\n * in two ways.\n *\n * A window the config file names that is not on this ladder (45 seconds, say)\n * still runs as configured: the card prints the exact number and parks the\n * thumb on the nearest rung, so the slider never pretends a number the host\n * is not using.\n */\nconst DEADLINE_LADDER: readonly number[] = [\n 0,\n 30_000,\n 60_000,\n 300_000,\n 900_000,\n 1_800_000,\n 3_600_000,\n 21_600_000,\n]\n\n/**\n * The shortest REAL wait — the first rung above 不等待.\n *\n * Hand-copied from the host half (`APPROVAL_DEADLINE_MIN_WAIT_MS` in\n * `src/impl.ts`): the browser half may not import the host module, so the\n * equivalence is pinned by a test instead (see `tests/acp-view.spec.ts`).\n */\nexport const DEADLINE_MIN_WAIT_MS = 30_000\n\n/**\n * The window the card shows under a mode.\n *\n * 不等待 (`0`) is the **auto** mode's rung: it answers with the option the\n * agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —\n * refusing every ask the instant it arrives stops the agent at every gate it\n * meets, which is not a timeout but a wall. So under `abort` the window floors\n * to the shortest real wait, and the card never shows a zero it cannot run.\n *\n * `undefined` (the `wait` mode) stays undefined: there is no window at all.\n */\nexport function deadlineValueFor(mode: PermissionTimeoutMode, ms: number | undefined): number | undefined {\n if (ms === undefined) return undefined\n return mode === 'abort' ? Math.max(ms, DEADLINE_MIN_WAIT_MS) : ms\n}\n\n/**\n * The rungs a mode offers.\n *\n * 不等待 (`0`) is the **auto** mode's rung: it answers with the option the\n * agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —\n * refusing every ask the instant it arrives stops the agent at every gate it\n * meets, which is not a timeout but a wall. So `abort` starts at the shortest\n * real wait.\n */\nfunction deadlineLadder(mode: PermissionTimeoutMode): readonly number[] {\n return mode === 'auto' ? DEADLINE_LADDER : DEADLINE_LADDER.filter(ms => ms > 0)\n}\n\n/**\n * The rung the slider shows for a window. Exact hits keep their rung; anything\n * else takes the nearest one. A tie goes to the SHORTER wait: the thumb must\n * never promise more patience than the window has.\n */\nexport function deadlineSliderIndex(ms: number, mode: PermissionTimeoutMode = 'auto'): number {\n const ladder = deadlineLadder(mode)\n const exact = ladder.indexOf(ms)\n if (exact >= 0) return exact\n let best = 0\n let bestDistance = Number.POSITIVE_INFINITY\n for (let i = 0; i < ladder.length; i++) {\n const distance = Math.abs(ms - ladder[i])\n if (distance < bestDistance) {\n bestDistance = distance\n best = i\n }\n }\n return best\n}\n\n/** The three modes, in the order a user reads them: refuse, answer, wait. */\nconst DEADLINE_MODES: readonly PermissionTimeoutMode[] = ['abort', 'auto', 'wait']\n\n/** `5 分钟` / `30 秒` / `不等待` — the readout and the tick label, one formatter. */\nfunction deadlineText(ms: number, acp: AcpStore): string {\n if (ms <= 0) return acp.label('不等待', 'No wait')\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return acp.label(`${String(seconds)} 秒`, `${String(seconds)}s`)\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return acp.label(`${String(minutes)} 分钟`, `${String(minutes)}m`)\n const hours = Math.round(minutes / 60)\n return acp.label(`${String(hours)} 小时`, `${String(hours)}h`)\n}\n\n/** The mode's label — one formatter, so the picker and the override note agree. */\nfunction modeText(mode: PermissionTimeoutMode, acp: AcpStore): string {\n if (mode === 'abort') return acp.label('超时中止', 'Abort')\n if (mode === 'auto') return acp.label('自动推进', 'Auto')\n return acp.label('一直等待', 'Wait')\n}\n\n/**\n * The mode block: what happens when nobody answers.\n *\n * Three values and no more — refuse, answer, wait — so the picker is a\n * segmented control, not a dropdown. The buttons stretch across the whole\n * card: a right-aligned segmented control squeezed the labels into two lines\n * each (中/止, 自动推/进), which read as broken rather than as choices.\n *\n * The copy under the buttons says what the SELECTED mode does, in one\n * sentence — the labels alone (\"超时中止\") cannot. `auto` carries a warning of\n * its own: it approves things nobody looked at.\n */\nfunction DeadlineModeBlock(props: {\n acp: AcpStore\n mode: PermissionTimeoutMode\n /** Set when the deployment baseline is outranked here. */\n override?: string\n}) {\n const { acp, mode, override } = props\n const copy = mode === 'wait'\n ? acp.label(\n '一直等待应答,不设超时时间。',\n 'Wait until you answer — no deadline.',\n )\n : mode === 'auto'\n ? acp.label(\n '超过设定时间未应答,自动代替你选择一个选项让 agent 继续执行。',\n 'Past the deadline, an option is chosen for you and the agent continues.',\n )\n : acp.label(\n '超过设定时间未应答,自动拒绝该操作,agent 继续执行。',\n 'Past the deadline, the operation is refused and the agent continues.',\n )\n return (\n <div className=\"dwa-preference-block\">\n <div className=\"dwa-preference-label\">{acp.label('超时处理方式', 'When nobody answers')}</div>\n <div className=\"dwa-seg dwa-seg-wide\">\n {DEADLINE_MODES.map(option => (\n <button\n key={option}\n type=\"button\"\n className={mode === option ? 'is-on' : ''}\n onClick={() => void acp.setApprovalTimeoutMode(option)}\n >\n {modeText(option, acp)}\n </button>\n ))}\n </div>\n <div className=\"dwa-preference-copy\">{copy}</div>\n {mode === 'auto' && (\n <div className=\"dwa-deadline-warn\">\n {acp.label(\n '请确保你的 agent 值得信任,请谨慎选择。',\n 'Only pick this if you trust your agent — choose it carefully.',\n )}\n </div>\n )}\n {override !== undefined && (\n <div className=\"dwa-deadline-flag\">⚠ {override}</div>\n )}\n </div>\n )\n}\n\n/**\n * The duration block: how long to wait before the deadline fires.\n *\n * ONE slider, because there is ONE window: an ask in the conversation and an\n * ask in the settings ledger are the same question, so they get the same\n * answer. The slider's rungs are the windows people actually pick; a window\n * the config file names outside them still runs as configured (the readout\n * prints the exact number and the thumb parks on the nearest rung).\n *\n * `value === undefined` is the `wait` mode: there is no deadline at all, so\n * the scale has nothing to say — the slider is not drawn, and the readout\n * names the state that is actually in force. The stored window is NOT touched\n * by that (the mode write carries only the mode), so switching back restores\n * it.\n */\nfunction DeadlineSliderBlock(props: {\n acp: AcpStore\n /** undefined in `wait` mode: no window, so no scale. */\n value: number | undefined\n /** The mode the window runs under — it decides which rungs exist. */\n mode: PermissionTimeoutMode\n /** Set when the deployment baseline is outranked here. */\n override?: string\n}) {\n const { acp, value, mode, override } = props\n // 不等待 belongs to `auto`. Under `abort` the window floors to the shortest\n // real wait, so the card never shows a zero it cannot run.\n const effective = deadlineValueFor(mode, value)\n const ladder = deadlineLadder(mode)\n const index = effective === undefined ? 0 : deadlineSliderIndex(effective, mode)\n const copy = effective === undefined\n ? acp.label(\n '选择「一直等待」时不设超时时长。',\n '“Wait” sets no deadline.',\n )\n : effective <= 0\n ? acp.label(\n '不设等待时间,请求到达即按上方所选的方式处理。',\n 'No wait: the ask is settled on arrival, by whatever you picked above.',\n )\n : acp.label(\n '超过该时长仍未应答,将按上方所选的方式处理。',\n 'Past this, whatever you picked above is what happens.',\n )\n return (\n <div className=\"dwa-preference-block\">\n <div className=\"dwa-preference-label\">{acp.label('等待时长', 'How long to wait')}</div>\n <div className=\"dwa-deadline-value\">\n {effective === undefined ? acp.label('一直等待', 'Always') : deadlineText(effective, acp)}\n </div>\n {effective !== undefined && (\n <>\n <input\n className=\"dwa-deadline-range\"\n type=\"range\"\n min={0}\n max={ladder.length - 1}\n step={1}\n value={index}\n aria-label={acp.label('等待时长', 'How long to wait')}\n onChange={event => {\n const next = ladder[Number(event.target.value)]\n if (next !== undefined && next !== effective) void acp.setApprovalDeadline(next)\n }}\n />\n <div className=\"dwa-deadline-ticks\">\n {ladder.map(rung => (\n <span key={rung}>{deadlineText(rung, acp)}</span>\n ))}\n </div>\n </>\n )}\n <div className=\"dwa-preference-copy\">{copy}</div>\n {override !== undefined && (\n <div className=\"dwa-deadline-flag\">⚠ {override}</div>\n )}\n </div>\n )\n}\n\n/**\n * The facts the card has to show, decided from what the host reported.\n *\n * The card carries TWO controls, and they are orthogonal: one window (how\n * long) and one mode (what happens). An override can sit on either of them,\n * so each is decided on its own.\n *\n * `overridden` is what gates the reset button. A baseline that AGREES with the\n * running value is not an override, and offering a reset for it would be a\n * button that does nothing (observed live).\n */\nexport function approvalDeadlineFacts(deadlines: AcpApprovalDeadlines): {\n /** The config-file window the card's window outranks — undefined when\n * nothing is being outranked. */\n baseline: number | undefined\n /** The config-file mode the card's mode outranks, when it differs. */\n modeBaseline: PermissionTimeoutMode | undefined\n /** A reset is only honest when something is actually being outranked. */\n overridden: boolean\n} {\n const configured = deadlines.configured?.permissionTimeoutMs\n const baseline = configured === undefined || configured === deadlines.permissionTimeoutMs\n ? undefined\n : configured\n const modeBaseline = deadlines.configuredMode !== undefined && deadlines.configuredMode !== deadlines.mode\n ? deadlines.configuredMode\n : undefined\n return {\n baseline,\n modeBaseline,\n overridden: baseline !== undefined || modeBaseline !== undefined,\n }\n}\n\n/**\n * The approval-deadline card.\n *\n * TWO controls, and they are orthogonal: one window (how long) and one mode\n * (what happens). One window covers both places an ask can wait — the\n * conversation's approval card and the settings panel's ledger — because they\n * ask the same question and the answer should not differ by where the card\n * happens to be drawn.\n *\n * The two controls are ONE system, and the card says so in a sentence: the\n * window says how long to wait, the mode says what happens when nobody\n * answered. That sentence is the thing the previous three-row layout kept\n * missing.\n *\n * The facts that make a number here meaningless are shown, not swallowed: the\n * deployment baseline this card outranks. Each rides on the control it belongs\n * to, and the reset button appears only when something is actually being\n * outranked.\n */\nfunction ApprovalDeadlinesCard(props: { acp: AcpStore; ui: UiState }) {\n const { acp, ui } = props\n const deadlines = ui.approvalDeadlines\n // Older host half: no card rather than a number this browser invented.\n if (deadlines === undefined) return null\n const facts = approvalDeadlineFacts(deadlines)\n const baselineNote = facts.baseline === undefined\n ? undefined\n : acp.label(\n `配置文件(cordis.patch.yml)设为 ${deadlineText(facts.baseline, acp)},此处设置优先。`,\n `The config file (cordis.patch.yml) asks for ${deadlineText(facts.baseline, acp)}; the value set here wins.`,\n )\n const modeNote = facts.modeBaseline === undefined\n ? undefined\n : acp.label(\n `配置文件(cordis.patch.yml)设为「${modeText(facts.modeBaseline, acp)}」,此处设置优先。`,\n `The config file (cordis.patch.yml) asks for \"${modeText(facts.modeBaseline, acp)}\"; the value set here wins.`,\n )\n // In `wait` mode there is no deadline at all, so the slider shows the one\n // thing that is true: no deadline.\n const waitMode = deadlines.mode === 'wait'\n return (\n <section className=\"dwa-section\">\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('审批时限', 'Approval deadlines')}</h3>\n <p className=\"dwa-section-copy\">\n {acp.label(\n '权限请求的等待时长与超时处理方式。',\n 'How long to wait for a permission request, and what happens when nobody answers.',\n )}\n </p>\n </div>\n </div>\n <div className=\"dwa-preferences\">\n <DeadlineModeBlock acp={acp} mode={deadlines.mode} override={modeNote} />\n <DeadlineSliderBlock\n acp={acp}\n value={waitMode ? undefined : deadlines.permissionTimeoutMs}\n mode={deadlines.mode}\n override={baselineNote}\n />\n {facts.overridden && (\n <div className=\"dwa-deadline-reset\">\n <button type=\"button\" className=\"dwa-btn dwa-btn-secondary\" onClick={() => void acp.resetApprovalDeadlines()}>\n {acp.label('恢复配置文件的值', 'Use config file value')}\n </button>\n </div>\n )}\n </div>\n </section>\n )\n}\n\ntype SettingsPage = 'agents' | 'add' | 'approvals' | 'appearance' | 'logs' | 'diagnostics'\n\n/**\n * Local wall-clock `HH:MM:SS` for one runtime-log line. Hand-built on purpose:\n * `toLocaleTimeString` may drop seconds or pick a 12-hour clock depending on\n * the browser locale, which makes the column ragged and ambiguous.\n */\nfunction logClock(at: number): string {\n const date = new Date(at)\n return [date.getHours(), date.getMinutes(), date.getSeconds()]\n .map(value => String(value).padStart(2, '0'))\n .join(':')\n}\n\n/** The states that carry an update badge next to the version badge. */\nfunction updateBadgeText(update: PluginUpdateState, acp: AcpStore): string | undefined {\n if (update.status === 'available') {\n return acp.label(`可更新 v${update.latest ?? update.current}`, `Update v${update.latest ?? update.current}`)\n }\n if (update.status === 'applying') return acp.label('更新中…', 'Updating…')\n if (update.status === 'applied') {\n const installed = update.installed ?? update.latest ?? update.current\n return acp.label(`已更新 v${installed}`, `Updated v${installed}`)\n }\n if (update.status === 'restart-required') {\n // Only a KNOWN disk version can be named. An interrupted install leaves\n // `installed` unset, and a badge would have to invent a version.\n if (update.installed === undefined) return undefined\n // Neutral: the disk version can be newer OR older than the running one.\n return acp.label(`待重启 v${update.installed}`, `Restart for v${update.installed}`)\n }\n return undefined\n}\n\n/**\n * The version badge's update companion. Every badge is a button, `applying`\n * included: the card is where the install's outcome is read, so the running\n * install must be able to open it too. States without anything to act on get\n * no badge at all — the version badge itself still opens the card.\n */\nfunction PluginUpdateBadge(props: { acp: AcpStore; update: PluginUpdateState; open: boolean; onToggle: () => void }) {\n const { acp, update, open, onToggle } = props\n const text = updateBadgeText(update, acp)\n if (text === undefined) return null\n return (\n <button\n type=\"button\"\n className=\"dwa-badge dwa-badge-info dwa-update-badge\"\n aria-expanded={open}\n aria-controls=\"ntr-plugin-update-card\"\n onClick={onToggle}\n >\n {text}\n </button>\n )\n}\n\n/**\n * The update card. In-page, no dialog: the settings page already owns the one\n * action (install the exact version the host reported), and a modal on top of\n * a settings section would hide the version it is updating.\n *\n * The copy stays to one line per state (constraint 33); the host's raw error\n * (`message`) is the only long text, and it wraps on its own line so a stack\n * trace or a registry error is readable without stretching the card.\n */\nfunction PluginUpdateCard(props: { acp: AcpStore; update: PluginUpdateState; onClose: () => void }) {\n const { acp, update, onClose } = props\n const applying = update.status === 'applying'\n const latest = update.latest ?? update.current\n const onDisk = update.installed !== undefined && update.installed !== update.current\n ? update.installed\n : undefined\n const published = update.publishedAt !== undefined && !Number.isNaN(new Date(update.publishedAt).getTime())\n ? new Date(update.publishedAt).toLocaleDateString()\n : undefined\n const copy = update.status === 'available'\n ? acp.label('更新会卸载并重装插件,之后可能需要重启 DSH。', 'Updating uninstalls and reinstalls the plugin, and may require restarting DSH.')\n : update.status === 'applied'\n ? acp.label('已应用,刷新页面即可使用新版本。', 'Applied. Reload the page to use the new version.')\n : update.status === 'restart-required'\n ? update.installed !== undefined\n ? acp.label(\n `磁盘已是 v${update.installed},重启 DSH 后生效(桌面 App 退出重开;命令行重启 dsh web)。`,\n `On disk v${update.installed}. Restart DSH to apply (quit and reopen the app, or restart dsh web).`,\n )\n : acp.label(\n '更新可能已完成,请刷新页面确认当前版本。',\n 'The update may already be complete. Refresh the page to confirm the current version.',\n )\n : update.status === 'busy'\n ? acp.label('有进行中的对话,结束后才能更新。', 'Finish the running conversation before updating.')\n : update.status === 'failed'\n ? acp.label('更新失败。', 'Update failed.')\n : update.status === 'current'\n ? acp.label('已是最新版本。', 'Up to date.')\n : update.status === 'source-install'\n ? acp.label(\n '源码或本地安装,无法通过 registry 更新。',\n 'Source or local install; update it through its own checkout.',\n )\n : update.status === 'managed'\n ? acp.label('此安装由宿主管理,不能在这里更新。', 'This install is managed by the host and cannot be updated here.')\n : undefined\n const showMessage = (update.status === 'failed' || update.status === 'busy' || update.status === 'managed')\n && update.message !== undefined\n const apply = async (): Promise<void> => {\n if (update.latest === undefined) return\n try {\n await acp.applyPluginUpdate(update.latest)\n } catch {\n // The store keeps the \"disk may have changed\" state and writes its own notice.\n }\n }\n return (\n <div\n id=\"ntr-plugin-update-card\"\n className=\"dwa-card dwa-update-card\"\n role=\"group\"\n aria-label={acp.label('插件更新', 'Plugin update')}\n >\n <div className=\"dwa-update-summary\">\n <span>{acp.label(`当前 v${update.current}`, `Current v${update.current}`)}</span>\n {onDisk !== undefined && <span className=\"dwa-hint\">{acp.label(`磁盘 v${onDisk}`, `On disk v${onDisk}`)}</span>}\n {update.latest !== undefined && (\n <>\n <span aria-hidden=\"true\">→</span>\n <span>{acp.label(`最新 v${latest}`, `Latest v${latest}`)}</span>\n {published !== undefined && <span className=\"dwa-hint\">{published}</span>}\n </>\n )}\n </div>\n {copy !== undefined && <p className=\"dwa-update-copy\">{copy}</p>}\n {showMessage && <div className=\"dwa-update-message\">{update.message}</div>}\n <div className=\"dwa-row-actions dwa-update-actions\">\n {update.status === 'available' && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" onClick={() => { void apply() }}>\n {acp.label('更新', 'Update')}\n </button>\n )}\n {applying && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" disabled>\n {acp.label('正在更新…', 'Updating…')}\n </button>\n )}\n {update.status !== 'applying' && (\n <button type=\"button\" className=\"dwa-btn\" onClick={onClose}>{acp.label('关闭', 'Close')}</button>\n )}\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" disabled={applying} onClick={() => void acp.checkPluginUpdate(true)}>\n {acp.label('重新检查', 'Check again')}\n </button>\n </div>\n </div>\n )\n}\n\n/**\n * The card is drawn for every state a host can report about this install,\n * except the two where there is no install to talk about at all.\n */\nfunction updateCardVisible(update: PluginUpdateState | undefined, open: boolean): boolean {\n if (update === undefined || !open) return false\n return update.status !== 'unsupported' && update.status !== 'not-installed'\n}\n\nexport function AcpSettingsSection(props: WithStore) {\n const acp = props.acp\n ensureStyle()\n const ui = useUi(acp)\n const ledger = permissionLedgerCard(ui)\n const [page, setPage] = useState<SettingsPage>('agents')\n const [addOpen, setAddOpen] = useState(false)\n const [addTarget, setAddTarget] = useState<DetectedAgentInfo | undefined>(undefined)\n const [updateOpen, setUpdateOpen] = useState(false)\n // The store throttles this to one registry read per ten minutes, so every\n // remount (both settings entries mount this section) is cheap.\n useEffect(() => {\n void acp.checkPluginUpdate()\n }, [acp])\n // Opening the log is the read receipt. The store does not mutate at zero,\n // so this cannot loop.\n useEffect(() => {\n if (page === 'logs' && ui.logUnread > 0) acp.markLogsRead()\n }, [acp, page, ui.logUnread])\n const { configured, available } = partitionAgents(ui.detectedAgents, ui.agents)\n const selected = addTarget ?? available[0] ?? NEW_AGENT_SEED\n const openAdd = (): void => {\n setAddOpen(true)\n setPage('add')\n }\n const presetProblem = ui.preset !== undefined && (!ui.preset.installed || ui.preset.problem !== undefined)\n const update = ui.pluginUpdate\n // A source install cannot be replaced by this browser; the version badge\n // says so on hover. Neutral wording: a checkout may be git or not.\n const versionTitle = update?.status === 'source-install'\n ? acp.label('源码或本地安装,无法通过 registry 更新。', 'Source or local install; update it through its own checkout.')\n : undefined\n // The running version is the update entry: every state with a real install\n // report can open the card, including \"up to date\" (which offers a re-check).\n const versionOpensUpdate = update !== undefined\n && update.status !== 'unsupported'\n && update.status !== 'not-installed'\n\n return (\n <div className=\"dwa-root\">\n <header className=\"dwa-page-header\">\n <div className=\"dwa-page-heading\">\n <h2 className=\"dwa-page-title\" aria-label={acp.label('NTR 设置', 'NTR settings')}>\n {acp.label('NTR 设置', 'NTR settings')}\n {ui.pluginVersion !== undefined && (\n versionOpensUpdate\n ? (\n <button\n type=\"button\"\n className=\"dwa-badge dwa-version dwa-version-btn\"\n title={versionTitle}\n aria-expanded={updateOpen}\n aria-controls=\"ntr-plugin-update-card\"\n onClick={() => setUpdateOpen(open => !open)}\n >\n {`v${ui.pluginVersion}`}\n </button>\n )\n : <span className=\"dwa-badge dwa-version\" title={versionTitle}>{`v${ui.pluginVersion}`}</span>\n )}\n {update !== undefined && (\n <PluginUpdateBadge\n acp={acp}\n update={update}\n open={updateOpen}\n onToggle={() => setUpdateOpen(open => !open)}\n />\n )}\n </h2>\n <p className=\"dwa-page-description\">\n {acp.label('连接智能体,设置你的对话偏好。', 'Connect agents and set your conversation preferences.')}\n </p>\n </div>\n {page === 'agents' && configured.length > 0 && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" onClick={openAdd}>\n {acp.label('添加智能体', 'Add agent')}\n </button>\n )}\n </header>\n\n {update !== undefined && updateCardVisible(update, updateOpen) && (\n <PluginUpdateCard acp={acp} update={update} onClose={() => setUpdateOpen(false)} />\n )}\n\n {/* The cross-session mirror: the conversation dock answers the asks\n belonging to the open conversation; this card answers the rest. */}\n {ledger.visible && (\n <div className=\"dwa-card dwa-card-warn\" style={{ marginBottom: 16 }}>\n <div style={{ fontWeight: 600, marginBottom: 4 }}>\n {acp.label('待处理权限请求', 'Pending Permission Requests')}\n </div>\n <div className=\"dwa-hint\" style={{ marginBottom: 8 }}>\n {acp.label(\n '对话打开时在那边的卡片上回答;这里兜底显示其他会话的询问。',\n 'Answer in the conversation card while that conversation is open; this list covers the asks whose conversation is not.',\n )}\n </div>\n {ui.permissions.map(permission => (\n <div key={permission.requestId} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, flexWrap: 'wrap' }}>\n <span style={{ flex: 1, minWidth: 160 }}>\n <span style={{ fontWeight: 600 }}>{permission.toolName}</span>\n {permission.reason !== undefined && (\n <span style={{ color: 'var(--ntr-muted)' }}> · {permission.reason}</span>\n )}\n </span>\n {permission.options.map(option => (\n <button\n key={option.optionId}\n type=\"button\"\n className=\"dwa-btn\"\n onClick={() => { void acp.respondPermission(permission.requestId, option.optionId).catch(() => undefined) }}\n >\n {option.name}\n </button>\n ))}\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" onClick={() => { void acp.respondPermission(permission.requestId).catch(() => undefined) }}>\n {acp.label('取消', 'Cancel')}\n </button>\n </div>\n ))}\n </div>\n )}\n\n {ui.notice !== undefined && (\n <div className=\"dwa-card dwa-card-warn\" style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ flex: 1 }}>{ui.notice}</span>\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" aria-label={acp.label('关闭通知', 'Dismiss notice')} onClick={() => acp.clearNotice()}>✕</button>\n </div>\n )}\n\n {presetProblem && (\n <div className=\"dwa-card dwa-card-warn dwa-notice\" role=\"status\">\n <span>{acp.label('NTR 模式需要检查,当前可能无法开始对话。', 'NTR mode needs attention; new conversations may be unavailable.')}</span>\n <button type=\"button\" className=\"dwa-btn\" onClick={() => setPage('diagnostics')}>{acp.label('查看诊断', 'View diagnostics')}</button>\n </div>\n )}\n <nav className=\"dwa-tabs\" aria-label={acp.label('NTR 设置分类', 'NTR settings categories')}>\n <button type=\"button\" aria-pressed={page === 'agents'} onClick={() => setPage('agents')}>{acp.label('智能体', 'Agents')}</button>\n {ui.approvalDeadlines !== undefined && <button type=\"button\" aria-pressed={page === 'approvals'} onClick={() => setPage('approvals')}>{acp.label('审批时限', 'Approval deadlines')}</button>}\n <button type=\"button\" aria-pressed={page === 'appearance'} onClick={() => setPage('appearance')}>{acp.label('对话外观', 'Appearance')}</button>\n <button type=\"button\" aria-pressed={page === 'logs'} onClick={() => setPage('logs')}>\n {acp.label('日志', 'Logs')}\n {ui.logUnread > 0 && <span className=\"dwa-log-dot\" aria-hidden=\"true\" />}\n </button>\n </nav>\n {page === 'diagnostics' && (\n <section className=\"dwa-section\">\n <h3 className=\"dwa-section-title\">{acp.label('连接诊断', 'Connection diagnostics')}</h3>\n <div className=\"dwa-preference-row\"><span>{acp.label('宿主事件流', 'Host event stream')}</span><span className=\"dwa-hint\">{streamText(ui.stream, acp)}</span></div>\n <div className=\"dwa-row-actions\">\n <button type=\"button\" className=\"dwa-btn\" onClick={() => { void acp.resync(); void acp.detectAgents() }} disabled={ui.detecting}>\n {ui.detecting ? acp.label('检测中...', 'Detecting...') : acp.label('重新检测与刷新', 'Re-detect & Refresh')}\n </button>\n {ui.detectSummary !== undefined && <span className=\"dwa-hint\" role=\"status\">{ui.detectSummary}</span>}\n </div>\n </section>\n )}\n {page === 'diagnostics' && ui.preset !== undefined && (\n <div className=\"dwa-card dwa-mode-banner\">\n <div className=\"dwa-mode-title\">\n {acp.label('NTR 模式', 'NTR mode')}\n <span className=\"dwa-badge\">{ui.preset.id}</span>\n </div>\n <span className={`dwa-badge ${ui.preset.installed ? 'dwa-badge-success' : 'dwa-badge-warn'}`}>\n {ui.preset.installed ? acp.label('已就绪', 'Ready') : acp.label('需检查', 'Needs attention')}\n </span>\n <div className=\"dwa-mode-copy\">\n {ui.preset.installed\n ? acp.label('已加入宿主模式列表,新建会话时选择「NTR 模式」即可使用。', 'Added to the harness roster. Pick “NTR mode” when creating a session.')\n : acp.label('宿主模式列表中没有可用的「NTR 模式」,请检查预设声明。', 'The harness roster has no usable “NTR mode”. Check the preset declaration.')}\n <code className=\"dwa-mode-source dwa-mono\">{ui.preset.source}</code>\n </div>\n {ui.preset.problem !== undefined && (\n <div className=\"dwa-hint\" style={{ gridColumn: '1 / -1', color: 'var(--ntr-warn)' }}>\n {acp.label(`宿主报告:${ui.preset.problem}`, `Harness reported: ${ui.preset.problem}`)}\n </div>\n )}\n </div>\n )}\n\n {/* Keep editors mounted so navigation preserves unsaved values. */}\n <section className=\"dwa-section\" hidden={page !== 'agents'} aria-label={acp.label('已接入的智能体', 'Connected agents')}>\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">\n {acp.label('已接入的智能体', 'Connected agents')}\n {ui.agentsLoaded && <span className=\"dwa-badge\">{configured.length}</span>}\n </h3>\n </div>\n {configured.length > 0 && <span className=\"dwa-hint\">{acp.label('默认项用于新的 NTR 会话', 'The default serves new NTR conversations')}</span>}\n </div>\n\n {!ui.agentsLoaded && <div className=\"dwa-empty\" role=\"status\">{acp.label('正在读取智能体…', 'Loading agents…')}</div>}\n {ui.agentsLoaded && configured.length === 0 && (\n <div className=\"dwa-empty\">\n <h3>{acp.label('接入第一个智能体', 'Connect your first agent')}</h3>\n <p className=\"dwa-section-copy\">{acp.label('选择已有预设,或填写自己的 ACP 启动命令。', 'Choose a preset or enter your own ACP launch command.')}</p>\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" onClick={openAdd}>{acp.label('添加智能体', 'Add agent')}</button>\n </div>\n )}\n\n <div className=\"dwa-agent-list\">\n {configured.map(({ info, synthesized }) => {\n const activeAgent = ui.agents.find(a => a.id === info.id)\n return (\n <AgentRow\n key={info.id}\n detected={info}\n activeAgent={activeAgent}\n acp={acp}\n isDefault={ui.defaultAgent === info.id}\n synthesized={synthesized}\n active={page === 'agents'}\n />\n )\n })}\n </div>\n </section>\n\n <section className=\"dwa-section\" hidden={page !== 'add'} aria-label={acp.label('添加智能体', 'Add agent')}>\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet dwa-back\" onClick={() => setPage('agents')}>{acp.label('返回智能体列表', 'Back to agents')}</button>\n <div className=\"dwa-add-panel\">\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('添加智能体', 'Add agent')}</h3>\n <p className=\"dwa-section-copy\">{acp.label('从预设快速启用,或创建自定义连接。', 'Enable a preset or create a custom connection.')}</p>\n </div>\n </div>\n {addOpen && (\n <div className=\"dwa-add-content\">\n <div className=\"dwa-chip-list\">\n {available.map(info => (\n <button\n key={info.id}\n type=\"button\"\n className={`dwa-chip${selected.id === info.id ? ' dwa-chip-active' : ''}`}\n onClick={() => setAddTarget(info)}\n title={info.description}\n >\n {info.name}\n </button>\n ))}\n <button\n type=\"button\"\n className={`dwa-chip${selected.id === '' ? ' dwa-chip-active' : ''}`}\n onClick={() => setAddTarget(NEW_AGENT_SEED)}\n >\n {acp.label('自定义', 'Custom')}\n </button>\n </div>\n {available.length === 0 && selected.id === '' && (\n <div className=\"dwa-hint\" style={{ marginBottom: 8 }}>\n {acp.label('没有更多可启用的预设 Agent。', 'No more detected presets to enable.')}\n </div>\n )}\n <AgentRow\n key={selected.id}\n detected={selected}\n acp={acp}\n isDefault={false}\n synthesized={false}\n newCustom={selected.id === ''}\n active={page === 'add'}\n onCreated={() => {\n setAddTarget(undefined)\n setAddOpen(false)\n setPage('agents')\n }}\n />\n </div>\n )}\n </div>\n </section>\n\n <div hidden={page !== 'approvals'}><ApprovalDeadlinesCard acp={acp} ui={ui} /></div>\n\n <section className=\"dwa-section\" hidden={page !== 'appearance'} aria-label={acp.label('对话外观', 'Appearance')}>\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('对话外观', 'Conversation appearance')}</h3>\n <p className=\"dwa-section-copy\">{acp.label('设置对话中 NTR 挂牌的进程存活提示。', 'Choose how the NTR tag indicates a running agent process.')}</p>\n </div>\n </div>\n <div className=\"dwa-preferences\">\n <div className=\"dwa-preference-row\">\n <div>\n <div className=\"dwa-preference-label\">{acp.label('挂牌光晕', 'Tag halo')}</div>\n <div className=\"dwa-preference-copy\">{acp.label('呼吸为脉动光,常亮为恒定光。', 'Breathe pulses gently; steady keeps a constant glow.')}</div>\n </div>\n <div className=\"dwa-seg\">\n {(['breath', 'steady'] as const).map(mode => (\n <button\n key={mode}\n type=\"button\"\n className={ui.tagHalo.mode === mode ? 'is-on' : ''}\n onClick={() => void acp.setTagHalo({ ...ui.tagHalo, mode })}\n >\n {mode === 'breath' ? acp.label('呼吸', 'Breathe') : acp.label('常亮', 'Steady')}\n </button>\n ))}\n </div>\n </div>\n {ui.tagHalo.mode === 'breath' && (\n <div className=\"dwa-preference-row\">\n <div>\n <div className=\"dwa-preference-label\">{acp.label('呼吸频率', 'Breathing speed')}</div>\n <div className=\"dwa-preference-copy\">{acp.label('控制光晕的脉动速度。', 'Controls how quickly the halo pulses.')}</div>\n </div>\n <div className=\"dwa-seg\">\n {(['slow', 'normal', 'fast'] as const).map(speed => (\n <button\n key={speed}\n type=\"button\"\n className={ui.tagHalo.speed === speed ? 'is-on' : ''}\n onClick={() => void acp.setTagHalo({ ...ui.tagHalo, speed })}\n >\n {speed === 'slow' ? acp.label('慢速', 'Slow') : speed === 'normal' ? acp.label('正常', 'Normal') : acp.label('快速', 'Fast')}\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </section>\n {/* Conditional on purpose: this list can hold 200 rows, and keeping it\n mounted through a `hidden` page would re-render every line on each\n `session/update`. It carries no local state, so remounting is free. */}\n {page === 'logs' && (\n <section className=\"dwa-section\" aria-label={acp.label('运行日志', 'Runtime log')}>\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('运行日志', 'Runtime log')}</h3>\n <p className=\"dwa-section-copy\">\n {acp.label(\n `agent 运行消息,最多 ${String(LOG_MAX_ENTRIES)} 条。`,\n `Agent runtime messages, up to ${String(LOG_MAX_ENTRIES)}.`,\n )}\n </p>\n </div>\n {ui.logs.length > 0 && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" onClick={() => acp.clearLogs()}>\n {acp.label('清空', 'Clear')}\n </button>\n )}\n </div>\n {ui.logs.length === 0\n ? <div className=\"dwa-empty\" role=\"status\">{acp.label('暂无运行日志。', 'No runtime messages yet.')}</div>\n : (\n <ul className=\"dwa-log-list\">\n {[...ui.logs].reverse().map(entry => (\n <li key={entry.id} className={entry.level === 'error' ? 'dwa-log-row is-error' : 'dwa-log-row'}>\n <span className=\"dwa-log-time dwa-mono\">{logClock(entry.at)}</span>\n <span className={`dwa-badge dwa-log-level ${entry.level === 'error' ? 'is-error' : 'is-info'}`}>\n {entry.level === 'error' ? acp.label('错误', 'Error') : acp.label('信息', 'Info')}\n </span>\n {entry.agentId !== undefined && <span className=\"dwa-badge\">{entry.agentId}</span>}\n <span className=\"dwa-log-text\">{entry.text}</span>\n </li>\n ))}\n </ul>\n )}\n </section>\n )}\n <footer className=\"dwa-health\">\n <div className=\"dwa-health-status\">\n <span className=\"dwa-dot\" style={{ background: ui.stream === 'live' ? 'var(--ntr-success)' : ui.stream === 'down' ? 'var(--ntr-danger)' : 'var(--ntr-muted)' }} />\n <span>{streamText(ui.stream, acp)}</span>\n {ui.preset !== undefined && <span>{ui.preset.installed && !presetProblem ? acp.label('NTR 模式已就绪', 'NTR mode ready') : acp.label('NTR 模式需检查', 'NTR mode needs attention')}</span>}\n </div>\n {page !== 'diagnostics' && <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" onClick={() => setPage('diagnostics')}>{acp.label('查看诊断', 'View diagnostics')}</button>}\n </footer>\n </div>\n )\n}\n\n/** One row per agent: compact when collapsed, the full form when expanded. */\nfunction AgentRow({\n detected,\n activeAgent,\n acp,\n isDefault,\n synthesized,\n active,\n newCustom = false,\n onCreated,\n}: {\n detected: DetectedAgentInfo\n activeAgent?: AgentView\n acp: AcpStore\n isDefault: boolean\n synthesized: boolean\n active: boolean\n /** From-scratch creation: the id is editable and no preset defaults exist. */\n newCustom?: boolean\n /** Fired after a creation-card save succeeds (the list re-derives its selection). */\n onCreated?: () => void\n}) {\n const ui = useUi(acp)\n const configured = activeAgent !== undefined || detected.currentConfig !== undefined\n // A configured agent starts collapsed; a creation card opens straight away\n // (nothing to summarise yet).\n const [expanded, setExpanded] = useState(!configured)\n const [detailPage, setDetailPage] = useState<'models' | 'connection' | 'advanced'>(configured ? 'models' : 'connection')\n const [form, setForm] = useState<AgentForm>(() => initialForm(detected, activeAgent))\n const [saving, setSaving] = useState(false)\n const [savedSuccess, setSavedSuccess] = useState(false)\n const [testing, setTesting] = useState(false)\n const [testResult, setTestResult] = useState<{ ok: boolean; text: string } | undefined>(undefined)\n const patch = (next: Partial<AgentForm>): void => setForm(prev => ({ ...prev, ...next }))\n\n const serverExposureKey = exposureKey(detected.currentConfig?.exposedModels)\n useEffect(() => {\n const server = detected.currentConfig?.exposedModels\n setForm(prev => ({ ...prev, exposed: server === undefined ? undefined : [...server] }))\n // Re-sync the editor only when the persisted filter actually changes, so\n // background resyncs never clobber in-progress checkbox edits.\n }, [serverExposureKey])\n\n const serverAutoDiscover = detected.currentConfig?.autoDiscoverModels !== false\n useEffect(() => {\n // The switch saves itself through the lightweight RPC, so the host is the\n // truth; this keeps the form (and therefore a later full save) in step.\n setForm(prev => ({ ...prev, autoDiscoverModels: serverAutoDiscover }))\n }, [serverAutoDiscover])\n\n const trimmedId = form.agentId.trim()\n const idValid = !newCustom || AGENT_ID_PATTERN.test(trimmedId)\n const idTaken = newCustom && trimmedId !== '' && ui.agents.some(a => a.id === trimmedId)\n const cwdInvalid = form.cwd.trim() !== '' && !isAbsolutePathLike(form.cwd.trim())\n const rootValues = form.roots.map(root => root.trim()).filter(root => root !== '')\n const rootsInvalid = form.rootsOpen && (rootValues.length === 0 || rootValues.some(root => !isAbsolutePathLike(root)))\n const idleInvalid = !idleValid(form.idleStr)\n const mcpInvalid = !mcpTextValid(form.mcpText)\n const formIncomplete = splitCommandLine(form.commandLine).command === '' || !idValid || idTaken || cwdInvalid || rootsInvalid || idleInvalid || mcpInvalid\n\n const payload = buildPayload(form, detected, newCustom)\n const current = persistedDef(detected, activeAgent)\n const clearingIdleOverride = payload.idleTimeoutMs === undefined && detected.currentConfig?.idleTimeoutMs !== undefined\n const dirty = current !== undefined && (!payloadEquals(payload, current) || clearingIdleOverride)\n // A save stops the child and kills every live session (see BridgeManager.saveAgent).\n const showSaveWarning = dirty && activeAgent !== undefined && activeAgent.state !== 'stopped'\n\n const handleUseDefault = (): void => patch({ commandLine: formatCommandLine(detected.defaultCommand, detected.defaultArgs) })\n\n const handleSave = async (): Promise<void> => {\n setSaving(true)\n setSavedSuccess(false)\n try {\n await acp.saveAgent(payload)\n // A save from the creation card resets the list's selection: the agent\n // now lives in the configured list, and keeping the same card mounted\n // would render it twice. Configured rows pass no `onCreated`, so this is\n // a no-op for them.\n onCreated?.()\n if (newCustom) return\n setSavedSuccess(true)\n setTimeout(() => setSavedSuccess(false), 2000)\n } catch {\n // Error handled by store\n } finally {\n setSaving(false)\n }\n }\n\n const handleTest = async (): Promise<void> => {\n setTesting(true)\n setTestResult(undefined)\n try {\n const result = await acp.testAgent(payload)\n setTestResult(result.ok\n ? {\n ok: true,\n text: acp.label('连接成功', 'Connected') + (result.agentInfo?.name !== undefined ? ` · ${result.agentInfo.name}` : '')\n + (result.agentInfo?.version !== undefined ? ` v${result.agentInfo.version}` : '')\n + ` · ${String(result.durationMs)}ms`,\n }\n : { ok: false, text: result.error ?? acp.label('未知错误', 'Unknown error') })\n } catch (error) {\n setTestResult({ ok: false, text: error instanceof Error ? error.message : String(error) })\n } finally {\n setTesting(false)\n }\n }\n\n const state = activeAgent !== undefined ? stateVisual(activeAgent.state, acp) : undefined\n const displayName = newCustom\n ? acp.label('新建自定义 Agent', 'New Custom Agent')\n : (activeAgent?.title ?? detected.currentConfig?.title ?? detected.name)\n const stderr = activeAgent?.state === 'error' ? (activeAgent.stderrTail ?? []) : []\n const errorSummary = stderr.length > 0 ? stderr[stderr.length - 1] : undefined\n const selectedModelCount = form.exposed?.length ?? 0\n const isPreset = PRESET_IDS.has(detected.id)\n // An error row opens itself so the stderr tail is never behind a click.\n const open = expanded || (activeAgent?.state === 'error' && configured)\n\n return (\n <div className={`dwa-agent-card${open ? ' is-open' : ''}`}>\n <div className=\"dwa-agent-row\">\n <span className=\"dwa-agent-avatar\" aria-hidden>{displayName.slice(0, 2).toUpperCase()}</span>\n <div className=\"dwa-agent-summary\">\n <div className=\"dwa-agent-titleline\">\n <span className=\"dwa-agent-name\" title={detected.executablePath ?? detected.description}>{displayName}</span>\n {isDefault && <span className=\"dwa-badge dwa-badge-info\">{acp.label('默认', 'Default')}</span>}\n {!isPreset && !newCustom && <span className=\"dwa-badge\">{acp.label('自定义', 'Custom')}</span>}\n </div>\n <div className=\"dwa-agent-meta\">\n {state !== undefined && (\n <span title={state.title ?? activeAgent?.state} style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>\n <span className=\"dwa-dot\" style={{ background: state.color }} />\n {state.text}\n </span>\n )}\n {configured && (\n <>\n {state !== undefined && <span className=\"dwa-agent-meta-separator\">·</span>}\n <span>{acp.label(`${String(selectedModelCount)} 个已选模型`, `${String(selectedModelCount)} selected models`)}</span>\n </>\n )}\n {!synthesized && !newCustom && !detected.detected && (\n <span style={{ color: 'var(--ntr-warn)' }} title={detected.description}>\n · {acp.label('未在 PATH 中检测到', 'Not in PATH')}\n </span>\n )}\n </div>\n {errorSummary !== undefined && (\n <div className=\"dwa-error dwa-mono\" style={{ marginTop: 6, wordBreak: 'break-all' }}>{errorSummary}</div>\n )}\n </div>\n <div className=\"dwa-row-actions\">\n {configured && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" aria-expanded={open} aria-label={acp.label(`${open ? '收起' : '管理'} ${displayName}`, `${open ? 'Collapse' : 'Manage'} ${displayName}`)} onClick={() => setExpanded(value => !value)}>\n {open ? acp.label('收起', 'Collapse') : acp.label('管理', 'Manage')}\n </button>\n )}\n {activeAgent?.state === 'error' && (\n <button className=\"dwa-btn\" disabled={testing} onClick={() => void handleTest()}>\n {testing ? acp.label('测试中...', 'Testing...') : acp.label('测试', 'Test')}\n </button>\n )}\n {configured && (\n <RowMenu\n acp={acp}\n canStop={activeAgent !== undefined && activeAgent.state !== 'stopped'}\n onStop={() => void acp.stopAgent(detected.id)}\n // The store already surfaced the failure as a notice; swallowing it\n // here only prevents an unhandled rejection.\n onRemove={async () => { await acp.deleteAgent(detected.id).catch(() => { /* notice shown by the store */ }) }}\n />\n )}\n </div>\n </div>\n\n {/* display-toggled (not conditionally mounted) so an in-progress edit\n survives collapsing the row. */}\n <div className=\"dwa-agent-body\" style={open ? undefined : { display: 'none' }}>\n {stderr.length > 0 && <pre className=\"dwa-stderr\">{stderr.join('\\n')}</pre>}\n <nav className=\"dwa-tabs dwa-detail-tabs\" aria-label={acp.label(`${displayName} 配置`, `${displayName} settings`)}>\n {configured && <button type=\"button\" aria-pressed={detailPage === 'models'} onClick={() => setDetailPage('models')}>{acp.label('模型', 'Models')}</button>}\n <button type=\"button\" aria-pressed={detailPage === 'connection'} onClick={() => setDetailPage('connection')}>{acp.label('连接', 'Connection')}</button>\n <button type=\"button\" aria-pressed={detailPage === 'advanced'} onClick={() => setDetailPage('advanced')}>{acp.label('高级', 'Advanced')}</button>\n </nav>\n {configured && (\n <section className=\"dwa-subsection\" hidden={detailPage !== 'models'}>\n <ModelCatalogBlock\n agentId={detected.id}\n detected={detected}\n acp={acp}\n active={active && open && detailPage === 'models'}\n exposed={form.exposed}\n onExposedChange={exposed => patch({ exposed })}\n autoDiscover={form.autoDiscoverModels}\n onAutoDiscoverChange={autoDiscoverModels => patch({ autoDiscoverModels })}\n isDefault={isDefault}\n />\n </section>\n )}\n <section className=\"dwa-subsection\" hidden={detailPage === 'models'}>\n <AgentDetails\n acp={acp}\n advancedOpen={detailPage === 'advanced'}\n dirty={dirty}\n detected={detected}\n form={form}\n patch={patch}\n isConfigured={configured}\n newCustom={newCustom}\n formIncomplete={formIncomplete}\n idValid={idValid}\n idTaken={idTaken}\n cwdInvalid={cwdInvalid}\n rootsInvalid={rootsInvalid}\n idleInvalid={idleInvalid}\n mcpInvalid={mcpInvalid}\n saving={saving}\n savedSuccess={savedSuccess}\n testing={testing}\n testResult={testResult}\n showSaveWarning={showSaveWarning}\n onUseDefault={handleUseDefault}\n onSave={() => void handleSave()}\n onTest={() => void handleTest()}\n />\n </section>\n </div>\n </div>\n )\n}\n\n/** Everything the connection form edits, in one object (kept in the row). */\ninterface AgentForm {\n agentId: string\n title: string\n /**\n * ONE command line — program first, arguments after, quoting to keep a path\n * with spaces in one piece (`splitCommandLine`). The old split into\n * `command` + `argsStr` invited the single most common misconfiguration\n * there is: pasting `codebuddy --acp` into the program box, which the host\n * then looked for as a file literally named `codebuddy --acp`.\n */\n commandLine: string\n npmPackage: string\n cwd: string\n /** Advanced: edit every allowed root, not just the single workspace dir. */\n rootsOpen: boolean\n roots: string[]\n envRows: Array<{ key: string; value: string }>\n exposed?: string[]\n /** Auto-append models the agent newly publishes to {@link exposed}. */\n autoDiscoverModels: boolean\n /** Raw text of the idle-release window in ms ('' = host default, '0' = never). */\n idleStr: string\n /**\n * Raw JSON text of the agent's MCP server list (the plugin's normalized\n * shape — every entry carries a `transport`). '' = no extra MCP servers.\n *\n * Kept as TEXT so a half-typed value survives editing instead of being\n * silently reshaped; it is parsed on the way out.\n */\n mcpText: string\n}\n\n/**\n * Hand copy of `DEFAULT_TUNING.idleTimeoutMs` (src/bridge.ts): the browser\n * cannot import the host plane, and this number is only ever placeholder text\n * for a NOT-yet-configured agent (a configured one renders the host's resolved\n * value from `AgentView.idleTimeoutMs`). Keep the two in sync.\n */\nconst DEFAULT_IDLE_RELEASE_MS = 1_800_000\n\n/**\n * Hand copy of `DEFAULT_TUNING.probeIdleTimeoutMs` (src/bridge.ts). A process\n * that has never hosted a session — the model probe / post-save probe — lives\n * by THIS window whatever the agent's own field says, so the placeholder has to\n * name both windows. Keep the two in sync.\n */\nconst DEFAULT_PROBE_RELEASE_MS = 300_000\n\nexport function initialForm(detected: DetectedAgentInfo, activeAgent: AgentView | undefined): AgentForm {\n const config = detected.currentConfig\n const roots = [...(activeAgent?.allowedCwdRoots ?? config?.allowedCwdRoots ?? [])]\n return {\n agentId: detected.id,\n title: activeAgent?.title ?? config?.title ?? detected.name,\n commandLine: formatCommandLine(\n activeAgent?.command ?? config?.command ?? detected.defaultCommand,\n activeAgent?.args ?? config?.args ?? detected.defaultArgs,\n ),\n npmPackage: config?.npmPackage ?? '',\n cwd: activeAgent?.cwd ?? config?.cwd ?? '',\n // An agent already carrying more than one root opens in advanced mode:\n // single-root mode narrows the allow-list to `cwd` on save, which must\n // never happen as a side effect of merely opening the form.\n rootsOpen: roots.length > 1,\n roots,\n envRows: Object.entries(config?.env ?? {}).map(([key, value]) => ({ key, value })),\n exposed: config?.exposedModels === undefined ? undefined : [...config.exposedModels],\n autoDiscoverModels: config?.autoDiscoverModels !== false,\n // Prefer the host's resolved value: it is the one actually in effect.\n idleStr: idleText(activeAgent?.idleTimeoutMs ?? config?.idleTimeoutMs),\n // The MCP list comes back from the host in its NORMALIZED shape (defaults\n // filled in), so re-serializing it reproduces exactly what is stored —\n // that is what keeps the card from being permanently dirty.\n mcpText: config?.mcpServers === undefined ? '' : JSON.stringify(config.mcpServers, null, 2),\n }\n}\n\nfunction idleText(value: number | undefined): string {\n return value === undefined ? '' : String(value)\n}\n\n/** `MM-DD HH:mm`, local time — compact enough for the catalog's status line. */\nfunction formatStamp(ms: number): string {\n const date = new Date(ms)\n const pad = (value: number): string => String(value).padStart(2, '0')\n return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/**\n * Assemble the save/test payload.\n *\n * Workspace rules (P0-1): a non-empty single-root `cwd` also pins\n * `allowedCwdRoots` to exactly that path — an explicit narrowing, so saving\n * from the panel can never silently widen the sandbox. An empty `cwd` sends\n * neither field and the host falls back to `~/.dsh/dsh-ntr/workspaces`. Advanced\n * mode sends the edited roots as-is (deduplicated).\n */\n/** `''` = host default, `0` = never release, otherwise a sane millisecond window. */\nfunction idleValid(raw: string): boolean {\n const text = raw.trim()\n if (text === '') return true\n if (!/^\\d+$/.test(text)) return false\n const value = Number(text)\n return value === 0 || (value >= 1_000 && value <= 86_400_000)\n}\n\nexport function buildPayload(form: AgentForm, detected: DetectedAgentInfo, newCustom: boolean): AgentSavePayload {\n const env: Record<string, string> = {}\n for (const row of form.envRows) {\n const key = row.key.trim()\n if (key === '') continue\n env[key] = row.value\n }\n const cwd = form.cwd.trim()\n const idleRaw = form.idleStr.trim()\n // The field is a command line; the host validates it the same way, so what\n // the preview under the input shows is what will actually be spawned.\n const parsed = splitCommandLine(form.commandLine)\n // An unparseable MCP text yields `undefined` here and `mcpInvalid` in the\n // form, which disables saving — so this never throws inside a render.\n const mcpServers = parseMcpServers(form.mcpText)\n const payload: AgentSavePayload = {\n id: newCustom ? form.agentId.trim() : detected.id,\n ...(form.title.trim() === '' ? {} : { title: form.title.trim() }),\n command: parsed.command,\n args: parsed.args,\n ...(form.npmPackage.trim() === '' ? {} : { npmPackage: form.npmPackage.trim() }),\n env,\n ...(form.exposed === undefined ? {} : { exposedModels: form.exposed }),\n // Auto-discovery is ON by default: only an unchecked switch records the\n // opt-out, otherwise a full save would silently re-enable it.\n ...(form.autoDiscoverModels ? {} : { autoDiscoverModels: false }),\n // `''` sends nothing: the host then applies its own default.\n ...(idleRaw === '' || !idleValid(idleRaw) ? {} : { idleTimeoutMs: Number(idleRaw) }),\n // The MCP list is OPTIONAL: absent = the agent connects only to the\n // servers it configures for itself.\n ...(mcpServers === undefined ? {} : { mcpServers }),\n }\n if (form.rootsOpen) {\n if (cwd !== '') payload.cwd = cwd\n const roots = [...new Set(form.roots.map(root => root.trim()).filter(root => root !== ''))]\n if (roots.length > 0) payload.allowedCwdRoots = roots\n return payload\n }\n if (cwd !== '') {\n payload.cwd = cwd\n payload.allowedCwdRoots = [cwd]\n }\n return payload\n}\n\n/** The expanded body: connection form, workspace allow-list, env, actions. */\nfunction AgentDetails({\n acp,\n advancedOpen,\n dirty,\n detected,\n form,\n patch,\n isConfigured,\n newCustom,\n formIncomplete,\n idValid,\n idTaken,\n cwdInvalid,\n rootsInvalid,\n idleInvalid,\n mcpInvalid,\n saving,\n savedSuccess,\n testing,\n testResult,\n showSaveWarning,\n onUseDefault,\n onSave,\n onTest,\n}: {\n acp: AcpStore\n advancedOpen: boolean\n dirty: boolean\n detected: DetectedAgentInfo\n form: AgentForm\n patch: (next: Partial<AgentForm>) => void\n isConfigured: boolean\n newCustom: boolean\n formIncomplete: boolean\n idValid: boolean\n idTaken: boolean\n cwdInvalid: boolean\n rootsInvalid: boolean\n idleInvalid: boolean\n mcpInvalid: boolean\n saving: boolean\n savedSuccess: boolean\n testing: boolean\n testResult?: { ok: boolean; text: string }\n showSaveWarning: boolean\n onUseDefault: () => void\n onSave: () => void\n onTest: () => void\n}) {\n // The same parse the host will run (`validateAgentDef` → `splitCommandLine`).\n // Showing it is what turns \"the first box swallowed my arguments\" into a\n // visible, fixable state instead of a spawn error.\n const parsedCommand = splitCommandLine(form.commandLine)\n const commandPreview = parsedCommand.command === ''\n ? acp.label('填整条命令,例如:codebuddy --acp', 'Type the whole command, e.g. codebuddy --acp')\n : `${acp.label('程序', 'Program')}: ${parsedCommand.command}`\n + (parsedCommand.args.length === 0 ? '' : ` ${acp.label('参数', 'Arguments')}: ${parsedCommand.args.join(' ')}`)\n return (\n <div>\n <div hidden={advancedOpen}>\n {newCustom && (\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('Agent ID(唯一标识,创建后不可改)', 'Agent ID (unique, immutable after creation)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n aria-label={acp.label('Agent ID', 'Agent ID')}\n value={form.agentId}\n onChange={e => patch({ agentId: e.target.value })}\n placeholder=\"e.g. my-agent\"\n />\n <span className={idValid && !idTaken ? 'dwa-hint' : 'dwa-error'}>\n {idTaken\n ? acp.label('该 ID 已被现有 Agent 占用。', 'This id is already taken by a configured agent.')\n : idValid\n ? acp.label('小写字母或数字开头,可用 a-z 0-9 . _ -,最长 64 字符。', 'Starts with a lowercase letter or digit; a-z 0-9 . _ - allowed; max 64 chars.')\n : acp.label('ID 格式不合法:小写字母或数字开头,可用 a-z 0-9 . _ -。', 'Invalid id: start with a lowercase letter or digit; only a-z 0-9 . _ - allowed.')}\n </span>\n </div>\n </div>\n )}\n\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('显示名', 'Display Name')}</label>\n <input\n className=\"dwa-input\"\n aria-label={acp.label('显示名', 'Display Name')}\n value={form.title}\n onChange={e => patch({ title: e.target.value })}\n placeholder={detected.name === '' ? acp.label('我的 Agent', 'My Agent') : detected.name}\n />\n </div>\n </div>\n\n {/* ONE field on purpose: the program and its arguments are one command\n line, exactly as a user would type them. Splitting them into two boxes\n made \"paste the whole command into the first box\" the default mistake\n (the host then looked for a file named `codebuddy --acp`); here the\n same paste is correct, and the parse preview below shows what will\n actually run (same parser the host's `validateAgentDef` uses). */}\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n aria-label={acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)')}\n value={form.commandLine}\n onChange={e => patch({ commandLine: e.target.value })}\n placeholder={detected.defaultCommand === ''\n ? 'e.g. codebuddy --acp'\n : formatCommandLine(detected.defaultCommand, detected.defaultArgs)}\n />\n <span className=\"dwa-hint\">{commandPreview}</span>\n <span className=\"dwa-hint\">\n {acp.label(\n '含空格的程序路径请加引号。',\n 'Quote the program path if it contains spaces.',\n )}\n </span>\n </div>\n </div>\n\n <WorkspaceBlock acp={acp} form={form} patch={patch} cwdInvalid={cwdInvalid} rootsInvalid={rootsInvalid} />\n </div>\n\n <div hidden={!advancedOpen}>\n {!newCustom && (\n <div className=\"dwa-default-hint\">\n <div>\n <span style={{ fontWeight: 600, marginRight: 6 }}>\n {acp.label('官方测试默认命令:', 'Tested Default Command:')}\n </span>\n <code className=\"dwa-input-mono\" style={{ background: 'var(--dsw-alias-markdown-inline-code, #0000000a)', padding: '2px 6px', borderRadius: 4 }}>\n {detected.defaultCommand} {detected.defaultArgs.join(' ')}\n </code>\n </div>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n onClick={onUseDefault}\n title={acp.label('把命令与参数重置为官方验证值', 'Reset command and arguments to the tested default')}\n >\n {acp.label('使用预设值', 'Use Preset')}\n </button>\n </div>\n )}\n\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('npm 包名(PATH 中找不到时自动安装)', 'npm Package (auto-installed when missing from PATH)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n value={form.npmPackage}\n onChange={e => patch({ npmPackage: e.target.value })}\n placeholder={acp.label('原生支持 ACP 的 agent 无需填写', 'Not needed for agents with native ACP support')}\n />\n </div>\n </div>\n\n {/* Idle release: scoped to agents with NO live session (see\n AgentBridge.releaseIfIdle), so it reclaims the processes that the\n model probe / post-save probe leave behind without ever costing a\n chat a cold start. TWO windows exist (AgentBridge.currentIdleTimeoutMs):\n this field is the chat window of a process that HAS hosted a session;\n a never-used process (a probe) always gets the short probe window and\n ignores this field — which is why the copy below names both. */}\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('空闲释放(毫秒)', 'Idle Release (ms)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n aria-label={acp.label('空闲释放(毫秒)', 'Idle Release (ms)')}\n value={form.idleStr}\n onChange={e => patch({ idleStr: e.target.value })}\n placeholder={acp.label(\n `留空 = 宿主默认(承载会话 ${String(DEFAULT_IDLE_RELEASE_MS / 60_000)} 分钟 / 探测进程 ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} 分钟)`,\n `Blank = host default (${String(DEFAULT_IDLE_RELEASE_MS / 60_000)} min for a chat process / ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} min for a probe)`,\n )}\n />\n <span className={idleInvalid ? 'dwa-error' : 'dwa-hint'}>\n {idleInvalid\n ? acp.label(\n '必须是 0(永不释放)或 [1000, 86400000] 之间的整数毫秒。',\n 'Must be 0 (never release) or an integer in [1000, 86400000] ms.',\n )\n : (\n <>\n {acp.label(\n '承载过会话的进程空闲后挂起;0 不释放,探测进程固定 5 分钟。',\n 'Park idle chat processes; 0 keeps them alive. Probe processes always release after 5 minutes.',\n )}\n </>\n )}\n </span>\n </div>\n </div>\n\n <div className=\"dwa-form-row\" style={{ flexDirection: 'column', gap: 6 }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <span className=\"dwa-form-label\">{acp.label('环境变量', 'Environment Variables')}</span>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n onClick={() => patch({ envRows: [...form.envRows, { key: '', value: '' }] })}\n >\n {acp.label('添加', 'Add')}\n </button>\n </div>\n {form.envRows.map((row, index) => (\n <div key={index} style={{ display: 'flex', gap: 6, alignItems: 'center' }}>\n <input\n className=\"dwa-input dwa-input-mono\"\n style={{ flex: 2 }}\n value={row.key}\n onChange={e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, key: e.target.value } : r)) })}\n placeholder=\"KEY\"\n />\n <input\n className=\"dwa-input dwa-input-mono\"\n style={{ flex: 3 }}\n value={row.value}\n onChange={e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, value: e.target.value } : r)) })}\n placeholder=\"VALUE\"\n />\n <button\n type=\"button\"\n className=\"dwa-btn\"\n title={acp.label('删除该变量', 'Remove this variable')}\n onClick={() => patch({ envRows: form.envRows.filter((_, i) => i !== index) })}\n >\n ✕\n </button>\n </div>\n ))}\n {form.envRows.length === 0 && (\n <span className=\"dwa-hint\">\n {acp.label('无自定义变量;子进程继承宿主环境。', 'No custom variables; the child inherits the host environment.')}\n </span>\n )}\n </div>\n\n <div className=\"dwa-form-row\" style={{ flexDirection: 'column', gap: 6 }}>\n <span className=\"dwa-form-label\">{acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)')}</span>\n <textarea\n className=\"dwa-input dwa-input-mono\"\n style={{ minHeight: 96, resize: 'vertical' }}\n aria-label={acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)')}\n value={form.mcpText}\n onChange={e => patch({ mcpText: e.target.value })}\n spellCheck={false}\n placeholder={'[\\n { \"name\": \"example\", \"transport\": \"stdio\", \"command\": \"/abs/path/to/server\", \"args\": [\"--mcp\"] }\\n]'}\n />\n {mcpInvalid && (\n <span className=\"dwa-error\">\n {acp.label('必须是一个 JSON 数组;每个条目写明 name 与 transport。', 'Must be a JSON array; every entry needs a name and a transport.')}\n </span>\n )}\n <span className=\"dwa-hint\">\n {acp.label(\n '额外追加给智能体的 MCP 服务器,不影响其已有配置。',\n 'Additional MCP servers for the agent; its own configuration is unchanged.',\n )}\n </span>\n </div>\n </div>\n\n {!advancedOpen && (mcpInvalid || idleInvalid) && <p className=\"dwa-error\" role=\"alert\">{acp.label('请检查「高级」中的无效配置。', 'Check the invalid settings in Advanced.')}</p>}\n {advancedOpen && (!idValid || idTaken || cwdInvalid || rootsInvalid || parsedCommand.command === '') && <p className=\"dwa-error\" role=\"alert\">{acp.label('请检查「连接」中的未完成或无效配置。', 'Check the incomplete or invalid settings in Connection.')}</p>}\n <div className=\"dwa-form-actions\">\n <span className=\"dwa-hint\">{isConfigured ? dirty ? acp.label('有未保存的更改', 'Unsaved changes') : acp.label('配置已保存', 'Settings saved') : ''}</span>\n <button\n className=\"dwa-btn\"\n onClick={onTest}\n disabled={testing || saving || formIncomplete}\n title={acp.label('用当前表单值拉起一次性进程并完成 ACP 握手,不保存配置', 'Spawns a throwaway process from the form values and completes the ACP handshake; nothing is saved')}\n >\n {testing ? acp.label('测试中...', 'Testing...') : acp.label('测试连接', 'Test Connection')}\n </button>\n\n <button\n className=\"dwa-btn dwa-btn-primary\"\n onClick={onSave}\n disabled={saving || testing || formIncomplete || (isConfigured && !dirty)}\n >\n {saving\n ? acp.label('保存中...', 'Saving...')\n : savedSuccess\n ? acp.label('✓ 已保存', '✓ Saved')\n : isConfigured\n ? acp.label('保存更改', 'Save changes')\n : acp.label('保存并启用', 'Save & Enable')}\n </button>\n\n {showSaveWarning && (\n <span className=\"dwa-hint\" style={{ color: 'var(--ntr-warn)' }}>\n ⚠ {acp.label('保存会停止该 Agent 进程并中断进行中的会话', 'Saving stops this agent process and interrupts live sessions')}\n </span>\n )}\n </div>\n\n {testResult !== undefined && (\n <div\n className=\"dwa-mono\"\n style={{\n marginTop: 8,\n fontSize: 12,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-all',\n color: testResult.ok ? 'var(--ntr-success)' : 'var(--ntr-danger)',\n }}\n >\n {testResult.ok ? '✓ ' : '✕ '}{testResult.text}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Workspace editor. Single-root mode is one directory; the advanced switch\n * reveals the full allow-list, because the host enforces every root the agent\n * may touch (and refuses anything else with `cwd_denied`).\n */\nfunction WorkspaceBlock({\n acp,\n form,\n patch,\n cwdInvalid,\n rootsInvalid,\n}: {\n acp: AcpStore\n form: AgentForm\n patch: (next: Partial<AgentForm>) => void\n cwdInvalid: boolean\n rootsInvalid: boolean\n}) {\n return (\n <div className=\"dwa-form-row\" style={{ flexDirection: 'column', gap: 6 }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>\n <span className=\"dwa-form-label\">{acp.label('工作空间根目录', 'Workspace Roots')}</span>\n <label className=\"dwa-hint\" style={{ display: 'inline-flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={form.rootsOpen}\n onChange={e => patch({ rootsOpen: e.target.checked })}\n style={{ margin: 0 }}\n />\n {acp.label('白名单根目录(高级)', 'Root allow-list (advanced)')}\n </label>\n </div>\n\n <div className=\"dwa-form-col\">\n <input\n className=\"dwa-input\"\n value={form.cwd}\n onChange={e => patch({ cwd: e.target.value })}\n placeholder={acp.label('默认 ~/.dsh/dsh-ntr/workspaces', 'Default: ~/.dsh/dsh-ntr/workspaces')}\n />\n {cwdInvalid && (\n <span className=\"dwa-error\">{acp.label('必须是绝对路径(如 /work 或 C:\\\\work)', 'Must be an absolute path (e.g. /work or C:\\\\work)')}</span>\n )}\n <span className=\"dwa-hint\">\n {acp.label(\n '聊天使用会话工作区;此处用于直连会话或宿主未提供工作区时。',\n 'Chats use their session workspace; this applies to direct sessions or when the host provides no workspace.',\n )}\n </span>\n {/* The effective allow-list stays visible in single-root mode too:\n saving there deliberately narrows it to this one directory. */}\n {!form.rootsOpen && (\n <span className=\"dwa-hint\">\n {acp.label('当前白名单:', 'Current allow-list: ')}\n {form.roots.length === 0 ? acp.label('(未设置)', '(unset)') : form.roots.join('、')}\n {form.cwd.trim() === '' ? '' : acp.label(';保存时将收窄为工作空间目录', '; saving narrows it to the workspace directory')}\n </span>\n )}\n </div>\n\n {form.rootsOpen && (\n <div className=\"dwa-form-col\" style={{ gap: 4 }}>\n {form.roots.map((root, index) => (\n <div key={index} style={{ display: 'flex', gap: 6, alignItems: 'center' }}>\n <input\n className=\"dwa-input dwa-input-mono\"\n value={root}\n onChange={e => patch({ roots: form.roots.map((r, i) => (i === index ? e.target.value : r)) })}\n placeholder=\"/absolute/path\"\n />\n <button\n type=\"button\"\n className=\"dwa-btn\"\n title={acp.label('删除该根目录', 'Remove this root')}\n onClick={() => patch({ roots: form.roots.filter((_, i) => i !== index) })}\n >\n ✕\n </button>\n </div>\n ))}\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n onClick={() => patch({ roots: [...form.roots, ''] })}\n >\n {acp.label('添加根目录', 'Add root')}\n </button>\n <span className=\"dwa-hint\">\n {acp.label('留空的工作空间则使用宿主默认目录。', 'An empty workspace falls back to the host default directory.')}\n </span>\n </div>\n {rootsInvalid && (\n <span className=\"dwa-error\">\n {acp.label('至少需要一个绝对路径根目录', 'At least one absolute root is required')}\n </span>\n )}\n </div>\n )}\n\n <span className=\"dwa-hint\">\n {acp.label(\n '白名单之外的路径会被宿主拒绝(cwd_denied);当前 DSH 会话自己的工作区始终被信任。',\n 'Paths outside the allow-list are refused by the host (cwd_denied); the current DSH session\\u2019s own workspace is always trusted.',\n )}\n </span>\n </div>\n )\n}\n\n/** Row overflow menu: the dangerous actions, plus the inline remove confirm. */\nfunction RowMenu({\n acp,\n canStop,\n onStop,\n onRemove,\n}: {\n acp: AcpStore\n canStop: boolean\n onStop: () => void\n onRemove: () => Promise<void>\n}) {\n const [open, setOpen] = useState(false)\n const [confirming, setConfirming] = useState(false)\n const wrap = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (event: MouseEvent): void => {\n if (wrap.current !== null && event.target instanceof Node && wrap.current.contains(event.target)) return\n setOpen(false)\n setConfirming(false)\n }\n const onKey = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n setOpen(false)\n setConfirming(false)\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n const close = (): void => {\n setOpen(false)\n setConfirming(false)\n }\n\n return (\n <div className=\"dwa-menu-wrap\" ref={wrap}>\n <button\n type=\"button\"\n className=\"dwa-btn\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={acp.label('更多操作', 'More actions')}\n onClick={() => {\n setOpen(value => !value)\n setConfirming(false)\n }}\n >\n ⋯\n </button>\n {open && (\n <div className=\"dwa-menu\" role=\"menu\">\n {canStop && (\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"dwa-menu-item\"\n onClick={() => {\n close()\n onStop()\n }}\n >\n {acp.label('停止当前运行实例', 'Stop Running Process')}\n </button>\n )}\n {confirming ? (\n <div className=\"dwa-menu-confirm\">\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-danger\"\n onClick={() => {\n close()\n void onRemove()\n }}\n >\n {acp.label('确认移除?', 'Confirm remove?')}\n </button>\n <button type=\"button\" className=\"dwa-btn\" onClick={() => setConfirming(false)}>\n {acp.label('取消', 'Cancel')}\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"dwa-btn dwa-menu-item dwa-menu-item-danger\"\n onClick={() => setConfirming(true)}\n >\n {acp.label('移除配置', 'Remove')}\n </button>\n )}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Measure the id cell on row hover so its CSS marquee can reach the end of the\n * text. The id is the only cell that yields width, and the panel is resizable,\n * so the overflow is re-read on every entry rather than cached. The animation\n * itself lives in CSS and only runs while the row is hovered; `title` on the\n * row stays the fallback for reduced-motion users.\n */\nfunction armModelIdMarquee(row: HTMLElement): void {\n const view = row.querySelector<HTMLElement>('.dwa-model-row-value')\n const text = view?.querySelector<HTMLElement>('.dwa-model-row-value-text')\n if (view === null || view === undefined || text === null || text === undefined) return\n const fade = Number.parseFloat(getComputedStyle(view).paddingRight) || 0\n const shift = Math.max(0, text.offsetWidth - (view.clientWidth - fade))\n row.style.setProperty('--dwa-id-shift', `${String(-shift)}px`)\n row.style.setProperty('--dwa-id-duration', `${String(Math.min(4, Math.max(1.2, shift / 30)))}s`)\n}\n\n/**\n * Model / thought-level catalog display plus the native-selector visibility\n * filter, as a two-pane transfer list: every known model on the left, the\n * selection that reaches the NTR surfaces on the right. Variants are still\n * opt-in — an empty selection is a legitimate state (base entry only) — and\n * edits save themselves (debounced), so there is no apply button and no second\n * dirty state to lose track of.\n *\n * The panes are ALWAYS shown, never gated on a probe: the right side comes from\n * the agent definition (`exposedModels`) and the left from the host's persisted\n * catalog cache, so opening the card shows what is already configured instead\n * of an empty box that costs one click per visit. 「检测可用模型」 therefore\n * sits with the list and acts as a REFRESH of the left pane only — models the\n * agent newly publishes appear, models it stopped advertising drop out.\n *\n * The auto-discovery switch is a per-agent POLICY, not a one-shot action: it\n * appends models the agent NEWLY publishes to the selection (see\n * `AcpAgentDef.autoDiscoverModels`). It saves itself through the lightweight\n * RPC, so flipping it never restarts the agent process.\n */\nfunction ModelCatalogBlock({\n agentId,\n detected,\n acp,\n active,\n exposed,\n onExposedChange,\n autoDiscover,\n onAutoDiscoverChange,\n isDefault,\n}: {\n agentId: string\n detected: DetectedAgentInfo\n acp: AcpStore\n /** The card's body is visible; the display read waits for the first expand. */\n active: boolean\n exposed: string[] | undefined\n onExposedChange: (next: string[] | undefined) => void\n autoDiscover: boolean\n onAutoDiscoverChange: (next: boolean) => void\n isDefault: boolean\n}) {\n const ui = useUi(acp)\n const [saveError, setSaveError] = useState<string | undefined>(undefined)\n const [search, setSearch] = useState('')\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const pending = useRef<string[] | undefined>(undefined)\n const hasPending = useRef(false)\n // In-flight auto-discovery toggle. A ref, not state: the switch saves\n // silently, and a \"saving\" render here made the row wrap and snap back\n // (user report, 2026-10-21). The guard only prevents double-submits.\n const autoDiscoverBusy = useRef(false)\n // Latest host-confirmed filter: the rollback target when a save fails.\n const serverValue = useRef(exposed)\n serverValue.current = detected.currentConfig?.exposedModels\n\n const probing = ui.probingModels[agentId] === true\n const probeError = ui.probeErrors[agentId]\n const result = ui.modelCatalogs[agentId]\n const modelChoices = result?.catalog.model?.choices ?? []\n /** Result of the last click on 「检测可用模型」 (what the refresh changed). */\n const [detectNote, setDetectNote] = useState<string | undefined>(undefined)\n // Each list folds on its own so a long selection can be parked while the\n // other pane keeps the full column. The list stays mounted (display-toggled)\n // to keep its scroll position.\n const [allCollapsed, setAllCollapsed] = useState(false)\n const [selectedCollapsed, setSelectedCollapsed] = useState(false)\n\n // Display read, not a probe: the host answers from its persisted catalog\n // cache, so the panes already list the known models the first time the card\n // opens. Gated on `active` because every configured agent's body stays\n // mounted (display-toggled) and a collapsed card has nothing to show.\n useEffect(() => {\n if (!active) return\n void acp.loadCachedModels(agentId)\n }, [active, agentId])\n\n useEffect(() => () => {\n if (timer.current === undefined) return\n clearTimeout(timer.current)\n // Flush a pending edit instead of dropping it with the unmount (the\n // panel can be closed mid-debounce); the host keeps the last word.\n if (!hasPending.current) return\n hasPending.current = false\n void acp.setExposedModels(agentId, pending.current).catch(() => { /* host keeps its value */ })\n }, [])\n\n const rollback = (error: unknown): void => {\n onExposedChange(serverValue.current === undefined ? undefined : [...serverValue.current])\n setSaveError(error instanceof Error ? error.message : String(error))\n }\n\n /** Persist a filter now (mode switch); a failure restores the editor. */\n const commit = async (next: string[] | undefined): Promise<void> => {\n setSaveError(undefined)\n try {\n await acp.setExposedModels(agentId, next)\n onExposedChange(next)\n } catch (error) {\n rollback(error)\n }\n }\n\n /** Persist selection edits after a short idle — several clicks coalesce into one call. */\n const queueExposure = (next: string[]): void => {\n pending.current = next\n hasPending.current = true\n if (timer.current !== undefined) clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n if (!hasPending.current) return\n hasPending.current = false\n void commit(pending.current)\n }, EXPOSURE_SAVE_DEBOUNCE_MS)\n }\n\n const toggleAutoDiscover = async (next: boolean): Promise<void> => {\n if (autoDiscoverBusy.current) return\n autoDiscoverBusy.current = true\n onAutoDiscoverChange(next)\n setSaveError(undefined)\n try {\n await acp.setAutoDiscoverModels(agentId, next)\n } catch (error) {\n onAutoDiscoverChange(!next)\n setSaveError(error instanceof Error ? error.message : String(error))\n } finally {\n autoDiscoverBusy.current = false\n }\n }\n\n const query = search.trim().toLowerCase()\n const visibleChoices = query === ''\n ? modelChoices\n : modelChoices.filter(choice =>\n choice.name.toLowerCase().includes(query) || choice.value.toLowerCase().includes(query))\n\n // Unset means \"nothing selected\", not \"everything\": an unset filter and an\n // empty one are the same host-side state (base entry only).\n const basis = exposed ?? []\n const selected = new Set(basis)\n // Right pane = selection in catalog order, then any selected value the last\n // probe no longer lists (still saved and still usable — hiding it would make\n // the two panes quietly disagree about what is configured).\n const selectedChoices: AcpCatalogChoice[] = []\n for (const choice of modelChoices) if (selected.has(choice.value)) selectedChoices.push(choice)\n const listed = new Set(selectedChoices.map(choice => choice.value))\n for (const value of basis) if (!listed.has(value)) selectedChoices.push({ value, name: value })\n const selectedInCatalog = basis.filter(value => modelChoices.some(choice => choice.value === value)).length\n\n const apply = (next: string[]): void => {\n onExposedChange(next)\n queueExposure(next)\n }\n\n const toggleChoice = (value: string): void => {\n apply(selected.has(value) ? basis.filter(v => v !== value) : [...basis, value])\n }\n\n /** Add every model matching the current search that is not selected yet. */\n const addVisible = (): void => {\n const added = visibleChoices.map(choice => choice.value).filter(value => !selected.has(value))\n if (added.length === 0) return\n apply([...basis, ...added])\n }\n\n const clearAll = (): void => {\n if (basis.length === 0) return\n apply([])\n }\n\n /**\n * Refresh the left pane from the agent: re-probe, then report what moved.\n * The note is what makes the button legible — a refresh that changes nothing\n * looks identical to a dead button otherwise (the catalog `updatedAt` does\n * not even move, since the host keeps the entry when the answer is equal).\n */\n const detect = async (): Promise<void> => {\n const before = new Set(modelChoices.map(choice => choice.value))\n setDetectNote(undefined)\n await acp.probeModels(agentId)\n const state = acp.getSnapshot()\n if (state.probeErrors[agentId] !== undefined) return\n const after = state.modelCatalogs[agentId]?.catalog.model?.choices ?? []\n const afterValues = new Set(after.map(choice => choice.value))\n const added = after.filter(choice => !before.has(choice.value)).length\n const removed = [...before].filter(value => !afterValues.has(value)).length\n setDetectNote(added === 0 && removed === 0\n ? acp.label('目录无变化', 'Catalog unchanged')\n : acp.label(\n `新增 ${String(added)} · 移除 ${String(removed)}`,\n `${String(added)} added · ${String(removed)} removed`,\n ))\n }\n\n const selectionBadge = modelChoices.length > 0\n ? acp.label(\n `已选 ${String(selectedInCatalog)}/${String(modelChoices.length)}`,\n `Selected ${String(selectedInCatalog)}/${String(modelChoices.length)}`,\n )\n : acp.label(`已选 ${String(basis.length)}`, `Selected ${String(basis.length)}`)\n\n // Freshness of the cached list (a probe that finds nothing new keeps the old\n // stamp by design — see `detect`), superseded by the click's own outcome.\n const catalogStatus = detectNote ?? (\n result !== undefined && result.updatedAt > 0\n ? acp.label(`目录更新于 ${formatStamp(result.updatedAt)}`, `Catalog from ${formatStamp(result.updatedAt)}`)\n : undefined\n )\n\n const leftEmptyCopy = visibleChoices.length > 0\n ? undefined\n : modelChoices.length > 0\n ? acp.label('无匹配模型', 'No matching models')\n : result === undefined\n ? acp.label('尚未读取模型目录,点「检测可用模型」从 Agent 读取。', 'No catalog yet — click \"Detect available models\" to read it from the agent.')\n : acp.label('该 Agent 未暴露模型选择器(category: model)。', 'This agent exposes no model selector (category: model).')\n\n return (\n <div>\n {!isDefault && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>\n <button className=\"dwa-btn\" onClick={() => void acp.setDefaultAgent(agentId)}>\n {acp.label('设为默认智能体', 'Set as default agent')}\n </button>\n </div>\n )}\n {isDefault && (\n // This default belongs to the plugin, not the deployment model route.\n <div className=\"dwa-hint\" style={{ marginTop: 8 }}>\n {acp.label(\n '当前为默认智能体:新的 NTR 会话由它服务,不影响其他模式的默认模型。',\n \"This is the default agent for new NTR sessions; other modes' default models are untouched.\",\n )}\n </div>\n )}\n\n <div className=\"dwa-inline-setting\" style={{ marginTop: 12 }}>\n <label\n className=\"dwa-switch\"\n title={acp.label(\n 'Agent 新发布的模型会自动加入右侧已选列表;首次检测到的目录只作为基线,不会一次性全选。',\n 'Models the agent newly publishes are added to the selected list; the first observed catalog is only a baseline and is not selected wholesale.',\n )}\n >\n <input\n type=\"checkbox\"\n checked={autoDiscover}\n onChange={e => void toggleAutoDiscover(e.target.checked)}\n style={{ margin: 0, accentColor: 'var(--dsw-alias-state-business-primary, #2563eb)' }}\n />\n {acp.label('自动发现新模型', 'Auto-discover new models')}\n </label>\n <span className=\"dwa-setting-copy\">\n {acp.label('新增模型自动加入已选列表,现有勾选不受影响。', 'New models join the selected list automatically; existing picks are untouched.')}\n </span>\n </div>\n {saveError !== undefined && <div className=\"dwa-error\" style={{ marginTop: 4 }}>{saveError}</div>}\n {probeError !== undefined && (\n <div className=\"dwa-error\" style={{ marginTop: 8, wordBreak: 'break-word' }}>\n ⚠ {acp.label('模型检测失败:', 'Model detection failed: ')}{probeError}\n </div>\n )}\n\n {/* Always rendered, never gated on a probe: the selection is known from\n the agent definition and the left list from the host's persisted\n catalog cache. A card that only fills in after a click makes the\n configured state invisible on every visit. */}\n <div className=\"dwa-model-section\">\n <div className=\"dwa-model-toolbar\">\n <span className=\"dwa-form-label\">\n {acp.label('对话中可见的模型', 'Models offered in the conversation')}\n <span\n className=\"dwa-hint\"\n style={{ marginLeft: 6, fontWeight: 400 }}\n title={acp.label(\n 'NTR 模式下的对话只列出这里勾选的模型;未勾选的仍可用——正在生效的那个也会以「· 当前」列出来,其余不列出。',\n 'The NTR surfaces only list what is checked here; unchecked models still work, and the one currently in force is still listed with a \"· current\" row — the rest are not offered.',\n )}\n >\n {acp.label('仅影响菜单可见性', 'Menu visibility only')}\n </span>\n </span>\n <span className=\"dwa-badge dwa-badge-info\">{selectionBadge}</span>\n {/* Freshness of the list stays on the header line, right-aligned; the\n controls below it wrap as one unit. */}\n {catalogStatus !== undefined && <span className=\"dwa-hint dwa-model-status\">{catalogStatus}</span>}\n <div className=\"dwa-model-toolbar-actions\">\n {/* Sits with the list it acts on: a refresh of the LEFT pane only.\n Detection is not what reveals the models — the cache read above\n already does — so keeping it at the top made it read as the\n price of seeing anything at all. */}\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n disabled={probing}\n onClick={() => void detect()}\n title={acp.label(\n '重新读取 Agent 的模型目录:新增的加入左侧,已下架的从左侧移除;已选列表不受影响。',\n 'Re-read the agent catalog: newly published models join the left list, withdrawn ones drop out. The selection is untouched.',\n )}\n >\n {probing ? acp.label('模型检测中...', 'Probing models...') : acp.label('检测可用模型', 'Detect available models')}\n </button>\n {/* The filter lives above BOTH panes, not inside the left one: a\n search field between the pane head and its list made that pane\n structurally taller, so the two could never share one height. */}\n <input\n className=\"dwa-input dwa-model-search\"\n aria-label={acp.label('搜索模型', 'Search models')}\n value={search}\n onChange={e => setSearch(e.target.value)}\n placeholder={acp.label('搜索模型…', 'Search models…')}\n />\n </div>\n </div>\n\n <div className=\"dwa-model-transfer\">\n <div className={`dwa-model-pane${allCollapsed ? ' is-collapsed' : ''}`}>\n <div className=\"dwa-model-pane-head\">\n <span>{acp.label('全部模型', 'All models')}</span>\n {result !== undefined && <span className=\"dwa-badge\">{modelChoices.length}</span>}\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n disabled={visibleChoices.length === 0}\n onClick={addVisible}\n title={acp.label('把当前搜索范围内的模型全部加入右侧', 'Add every model matching the current search')}\n >\n {acp.label('全部加入', 'Add all')}\n </button>\n <button\n type=\"button\"\n className=\"dwa-caret-btn\"\n aria-expanded={!allCollapsed}\n onClick={() => setAllCollapsed(value => !value)}\n title={allCollapsed ? acp.label('展开全部模型', 'Expand all models') : acp.label('收起全部模型', 'Collapse all models')}\n >\n <span className=\"dwa-caret\" aria-hidden>{allCollapsed ? '▸' : '▾'}</span>\n </button>\n </div>\n <div className=\"dwa-model-list\">\n {visibleChoices.map(choice => {\n const checked = selected.has(choice.value)\n // group 前缀仅来自 agent 返回的 SessionConfigSelectGroup(见 impl.ts\n // pushCatalogChoice);供应商可见性兜底靠原样展示 value,严禁从\n // value 路径截取供应商段做标签(约束 8)。\n const title = choice.description === undefined\n ? choice.value\n : `${choice.value}\\n${choice.description}`\n return (\n <button\n type=\"button\"\n key={choice.value}\n className={`dwa-model-row${checked ? ' is-on' : ''}`}\n title={title}\n onClick={() => toggleChoice(choice.value)}\n onPointerEnter={event => { armModelIdMarquee(event.currentTarget) }}\n >\n <span className=\"dwa-model-row-mark\">{checked ? '✓' : '+'}</span>\n <span className=\"dwa-model-row-name\">\n {choice.group !== undefined && <span style={{ opacity: 0.7 }}>{choice.group} · </span>}\n {choice.name}\n </span>\n {choice.value !== choice.name && (\n <span className=\"dwa-model-row-value\">\n <span className=\"dwa-model-row-value-text\">{choice.value}</span>\n </span>\n )}\n {/* Credit / capability notes belong on the row itself: the\n list exists to compare models, and a hover-only price\n cannot be scanned. Grouped choices reuse the group label\n as their fallback description — that one is already\n printed before the name. */}\n {choice.description !== undefined && choice.description !== choice.group && (\n <span className=\"dwa-model-row-desc\">{choice.description}</span>\n )}\n </button>\n )\n })}\n {leftEmptyCopy !== undefined && <span className=\"dwa-hint dwa-model-empty\">{leftEmptyCopy}</span>}\n </div>\n </div>\n\n <div className={`dwa-model-pane${selectedCollapsed ? ' is-collapsed' : ''}`}>\n <div className=\"dwa-model-pane-head\">\n <span>{acp.label('已选模型', 'Selected models')}</span>\n <span className=\"dwa-badge dwa-badge-info\">{basis.length}</span>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n disabled={basis.length === 0}\n onClick={clearAll}\n >\n {acp.label('清空', 'Clear')}\n </button>\n <button\n type=\"button\"\n className=\"dwa-caret-btn\"\n aria-expanded={!selectedCollapsed}\n onClick={() => setSelectedCollapsed(value => !value)}\n title={selectedCollapsed ? acp.label('展开已选模型', 'Expand selected models') : acp.label('收起已选模型', 'Collapse selected models')}\n >\n <span className=\"dwa-caret\" aria-hidden>{selectedCollapsed ? '▸' : '▾'}</span>\n </button>\n </div>\n <div className=\"dwa-model-list\">\n {selectedChoices.length === 0 && (\n <span className=\"dwa-hint dwa-model-empty\">\n {acp.label('尚未选择模型,从「全部模型」中加入。', 'No models selected. Add from All models.')}\n </span>\n )}\n {selectedChoices.map(choice => (\n <button\n type=\"button\"\n key={choice.value}\n className=\"dwa-model-row is-on\"\n title={choice.description === undefined ? choice.value : `${choice.value}\\n${choice.description}`}\n onClick={() => toggleChoice(choice.value)}\n onPointerEnter={event => { armModelIdMarquee(event.currentTarget) }}\n >\n <span className=\"dwa-model-row-name\">\n {choice.group !== undefined && <span style={{ opacity: 0.7 }}>{choice.group} · </span>}\n {choice.name}\n </span>\n {choice.value !== choice.name && (\n <span className=\"dwa-model-row-value\">\n <span className=\"dwa-model-row-value-text\">{choice.value}</span>\n </span>\n )}\n {choice.description !== undefined && choice.description !== choice.group && (\n <span className=\"dwa-model-row-desc\">{choice.description}</span>\n )}\n <span className=\"dwa-model-row-remove\" aria-hidden=\"true\">×</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {result?.catalog.thought !== undefined && (\n <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', alignItems: 'center' }}>\n <span className=\"dwa-form-label\">{acp.label('思考等级', 'Thought Levels')}</span>\n {result.catalog.thought.choices.map(choice => (\n <span key={choice.value} className=\"dwa-badge\" title={choice.description}>\n {choice.name}\n </span>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n","/**\n * ACP-native mode control for the conversation composer\n * (`conversation.input.left` — the seat the slot contract reserves for a small\n * native control).\n *\n * THE TWO MODE CONTROLS NEVER SHARE A SCREEN (audit finding F27): the ACP-mode\n * gate that lets this entry render also occupies the host's Access mode seat\n * with a claim that renders nothing (`createPermissionSeatClaim` in\n * model-seat.tsx) — the host's write policy (仅可查看 / 工作区内修改 / 完全权限)\n * says nothing about an external agent process, so showing it next to an ACP\n * conversation only misleads. This select is therefore NOT \"complementary\" to\n * the Access mode: in the only conversation where it appears, the Access mode is\n * hidden and this IS the mode switch. What it switches is the agent's own\n * `session/set_mode` vocabulary (`plan`, `acceptEdits`, …). An ordinary\n * conversation renders nothing here at all — the entry is registered\n * unconditionally and gates itself (see `isAcp` below).\n *\n * The host supplies the data by chat session id (`session/chat-list`), so the\n * control renders nothing until an ACP agent has actually run in this\n * conversation, and nothing at all for an agent that declares no modes.\n */\n\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { AcpSessionHook } from './acp-ui.tsx'\nimport type { AcpStore } from './store.ts'\n\n/**\n * The props this occupant actually receives: our injected face (which carries\n * the chat session id, like every other entry in this plugin) plus the\n * framework's session standard kit, of which only `useSession` is read.\n *\n * `conversation.input.left` declares no `owner` share — the composer bar calls\n * `renderSlot('conversation.input.left', {})` — so the ONLY props that exist are\n * the standard kit (session-scope `sessionId` + the bound `use<Name>` selector\n * hooks + `useProjection`/`inputActions`) and whatever our `inject` returns. The\n * session id deliberately rides the injected face rather than the standard kit:\n * the kit's top-level `sessionId` is a newer renderer addition, while the\n * positional inject argument (`binding.key`) is the contract every version in\n * play honors. Inventing a prop here is a render crash, not a silent undefined.\n */\nexport interface ComposerModeProps {\n /** Injected face: the chat session id this control belongs to. */\n sessionId: string\n /** Framework-bound conversation snapshot hook (`SessionStandardProps.useSession`). */\n useSession: AcpSessionHook\n acp: AcpStore\n}\n\nfunction useUi(acp: AcpStore) {\n return useSyncExternalStore(cb => acp.subscribe(cb), () => acp.getSnapshot())\n}\n\n/**\n * The mode select's tooltip (audit finding F27). It must not call the host's\n * Access mode \"complementary\": the ACP-mode gate that puts this select on screen\n * also hides that control (`createPermissionSeatClaim` renders nothing in the\n * permission seat), so in the conversation where this appears the Access mode is\n * gone and this select is the mode switch the user actually has.\n */\nexport function agentModeTitle(agentId: string | undefined, acp: AcpStore): string {\n return acp.label(\n `agent 模式(${agentId ?? 'agent'}):由 agent 自己定义。NTR 会话里 DSH 的 Access mode 不显示,模式就在此切换。`,\n `agent mode (${agentId ?? 'agent'}): defined by the agent. In an NTR conversation the DSH access mode is not shown, so this is where the mode is switched.`,\n )\n}\n\nexport function AcpComposerMode({ acp, sessionId, useSession }: ComposerModeProps) {\n const ui = useUi(acp)\n const dshSessionId = String(sessionId)\n // A fresh chat session has no binding until its first turn creates one, so\n // re-read whenever the running flag settles.\n const running = useSession(snapshot => snapshot.running)\n const [switching, setSwitching] = useState(false)\n\n useEffect(() => {\n void acp.loadChatSessions(dshSessionId)\n }, [acp, dshSessionId, running])\n\n const bound = ui.chatSessionsFor === dshSessionId ? ui.chatSessions : []\n // Prefer the live binding; a released one still describes the conversation.\n const target = bound.find(session => !session.released) ?? bound[0]\n // ISOLATION — this entry is NOT inside the ACP-mode gate (`syncAcpMode` holds\n // the model/permission seats, but `conversation.input.left` is registered\n // unconditionally), so it must gate itself. The `defaultAgent` fallback below\n // would otherwise put an ACP mode select into ORDINARY conversations: the\n // agent id is plugin-global, so \"an agent is configured\" is not the same\n // question as \"this conversation is served by one\". A live session is itself\n // proof; otherwise it is the same host verdict the seat gate reads.\n const isAcp = target !== undefined || ui.acpMode[dshSessionId] === true\n // The agent this conversation is set up on, live session or not. Before the\n // first message there is no session to read a mode list from, yet the user is\n // already choosing the agent — and the mode is one of the things they may\n // want to choose with it (user report, 2026-10-18: this control vanished on a\n // conversation set up ahead of its first message).\n //\n // The `defaultAgent` fallback applies ONLY inside an ACP-mode conversation.\n // Without that gate an ordinary chat would resolve an agent id it has nothing\n // to do with and read ITS catalog — and reading a catalog can spawn a probe\n // (user report, 2026-10-18: an unrelated agent showed up \"已连接\").\n const agentId = target?.agentId\n // A locked conversation belongs to this agent even when no session is live\n // (after a restart / agent stop). It must outrank the binding read and the\n // plugin default: reading another agent's catalog here would put THAT\n // agent's modes on a locked conversation.\n ?? ui.chatLockedAgent[dshSessionId]\n ?? ui.chatBoundAgent[dshSessionId]\n ?? ui.chatChoice[dshSessionId]?.agentId\n ?? (isAcp ? ui.defaultAgent : undefined)\n const catalog = agentId === undefined ? undefined : ui.seatCatalogs[agentId]\n useEffect(() => {\n // Nothing to read for a conversation that is not served by an agent, and\n // nothing to read while a live session is already answering (its\n // `session/new` modes outrank the catalog).\n if (!isAcp || agentId === undefined || target !== undefined) return\n void acp.loadAgentCatalog(agentId)\n }, [acp, agentId, isAcp, target])\n\n const live = target?.modes?.availableModes ?? []\n // TWO sources, one list. A live session's `session/new` answer is the\n // authority on which modes exist and which is in force; the probed catalog\n // carries the same list for the window before one exists (it is what the\n // agent published on ITS throwaway session). A live session always wins.\n const modes = live.length > 0\n ? live\n : (isAcp ? catalog?.mode?.choices ?? [] : []).map(choice => ({ id: choice.id, name: choice.name, description: choice.description }))\n if (modes.length === 0) return null\n // The mode IN FORCE comes from the live session; before one exists the only\n // claims available are the agent's own reported current mode and the pick this\n // conversation recorded (a pick outranks the agent's default, exactly like the\n // model and thought level do in `describeRoute`).\n const persisted = ui.chatChoice[dshSessionId]?.agentId === agentId\n ? ui.chatChoice[dshSessionId]?.mode\n : undefined\n const current = target?.modes?.currentModeId\n ?? persisted\n ?? catalog?.mode?.currentValue\n ?? modes[0]?.id\n ?? ''\n\n return (\n <label\n style={{ display: 'inline-flex', alignItems: 'center', fontSize: 12 }}\n // No visible \"Agent mode\" caption (user decision, 2026-10-17): the option\n // labels are the agent's own mode names (Plan / Accept edits / …), which\n // read as what they are on their own — a caption beside a single select\n // only adds a second line of chrome to a control whose value already says\n // it. The explanation stays in the tooltip for whoever goes looking.\n title={agentModeTitle(agentId, acp)}\n >\n <select\n aria-label={acp.label('agent 模式', 'agent mode')}\n value={current}\n disabled={switching}\n onChange={event => {\n const modeId = event.target.value\n setSwitching(true)\n void acp.setChatSessionMode(dshSessionId, modeId, agentId).finally(() => { setSwitching(false) })\n }}\n style={{\n font: 'inherit',\n fontSize: 12,\n padding: '2px 4px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border-l2, #d1d5db)',\n background: 'transparent',\n color: 'inherit',\n maxWidth: 180,\n }}\n >\n {modes.map(mode => (\n <option key={mode.id} value={mode.id}>{mode.name ?? mode.id}</option>\n ))}\n </select>\n </label>\n )\n}\n","/**\n * The one-shot hand-off from the composer's agent base to the edge tag.\n *\n * The base and the tag live in DIFFERENT slots (`conversation.input.model` and\n * `conversation.input.overlay`), so neither can animate the other directly.\n * This module is the shared clock they both read: when the conversation locks\n * to its agent, the base plays a shatter for {@link SHATTER_MS} while the tag\n * stays hidden, then the tag mounts and condenses for {@link FORM_MS}. Both\n * surfaces derive the same phase from the same map, so the two slots cannot\n * drift apart.\n *\n * The ceremony plays at the START of the conversation's first turn: the host\n * locks (and broadcasts) it as soon as the first ACP session is ready, before\n * the prompt is sent. If that turn delivers nothing at all, the host rolls the\n * lock back (`chat/agent-unlocked`); the base returns and the tag disappears,\n * with no reverse ceremony — the important part there is that the picker is\n * usable again.\n *\n * It must NOT replay on a page load. The lock is durable, so opening a\n * conversation that locked yesterday would otherwise replay the ceremony every\n * time. `AgentLockPhase` is therefore parameterized by the store's\n * `agentLockReveal` serial, which only advances when THIS page witnesses a\n * conversation go from unlocked to locked (a `chat/agent-locked` frame, or a\n * later `session/chat-list` answer). A reveal with no animation entry yet reads\n * as `shatter` (the commit that first sees it renders the base with its shatter\n * class immediately — no frame without either surface), and once the clock\n * finishes it records that serial as consumed so the phase settles to `idle`.\n * A NEWER reveal (a retry after a rolled-back first turn) restarts the clock\n * even if the previous ceremony is still running; the old timers are fenced off\n * by their epoch.\n *\n * `prefers-reduced-motion` turns the whole ceremony off: the base disappears\n * and the tag appears in the same commit.\n */\n\nimport { useSyncExternalStore } from 'react'\n\nexport type AgentLockPhase = 'idle' | 'shatter' | 'form'\n\ninterface ActiveAnim {\n phase: 'shatter' | 'form'\n reveal: number\n /** Fences timers of a ceremony a newer reveal replaced. */\n epoch: number\n}\n\n/** Keep in step with the `ntr-acp-shatter-chip` / `ntr-acp-shard-out` CSS. */\nexport const SHATTER_MS = 460\n/** Keep in step with the `ntr-acp-form-chip` / `ntr-acp-shard-in` CSS. */\nexport const FORM_MS = 540\n\nconst phases = new Map<string, ActiveAnim>()\n/** The reveal serial whose ceremony has already played (or was skipped). */\nconst consumed = new Map<string, number>()\nconst listeners = new Set<() => void>()\nlet version = 0\nlet epochSeq = 0\n\nfunction emit(): void {\n version += 1\n for (const listener of [...listeners]) listener()\n}\n\nfunction reducedMotion(): boolean {\n return typeof window !== 'undefined'\n && typeof window.matchMedia === 'function'\n && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n}\n\n/**\n * The phase for one conversation. `reveal` is the store's lock-reveal serial\n * for it (absent for a conversation this page never saw lock). A brand-new,\n * not-yet-consumed reveal reads as `shatter` so the commit after the lock\n * already renders the shattering base rather than briefly rendering neither\n * surface. An active ceremony only answers for its OWN reveal: a retry after a\n * rolled-back first turn is a new reveal and must start from `shatter`.\n */\nexport function agentLockPhaseOf(sessionId: string, reveal?: number): AgentLockPhase {\n const active = phases.get(sessionId)\n if (active !== undefined && active.reveal === reveal) return active.phase\n if (reveal === undefined || consumed.get(sessionId) === reveal || reducedMotion()) return 'idle'\n return 'shatter'\n}\n\n/**\n * Start the ceremony for a reveal this page witnessed. Idempotent for the same\n * reveal (both slots call it from an effect); a newer reveal supersedes a\n * running older one, whose timers are fenced by the epoch check.\n */\nexport function playAgentLockAnim(sessionId: string, reveal: number): void {\n const active = phases.get(sessionId)\n if (active?.reveal === reveal || consumed.get(sessionId) === reveal) return\n if (reducedMotion()) {\n phases.delete(sessionId)\n consumed.set(sessionId, reveal)\n emit()\n return\n }\n const epoch = ++epochSeq\n phases.set(sessionId, { phase: 'shatter', reveal, epoch })\n emit()\n setTimeout(() => {\n const current = phases.get(sessionId)\n if (current === undefined || current.epoch !== epoch || current.phase !== 'shatter') return\n phases.set(sessionId, { ...current, phase: 'form' })\n emit()\n setTimeout(() => {\n const done = phases.get(sessionId)\n if (done === undefined || done.epoch !== epoch || done.phase !== 'form') return\n phases.delete(sessionId)\n consumed.set(sessionId, reveal)\n emit()\n }, FORM_MS)\n }, SHATTER_MS)\n}\n\n/** Subscribe to phase changes (one global clock shared by both slots). */\nexport function useAgentLockPhase(sessionId: string, reveal?: number): AgentLockPhase {\n useSyncExternalStore(\n cb => {\n listeners.add(cb)\n return () => { listeners.delete(cb) }\n },\n () => version,\n )\n return agentLockPhaseOf(sessionId, reveal)\n}\n","/**\n * The ACP base in the conversation composer's model seat\n * (`conversation.input.model`) and its picker (`conversation.input.overlay`).\n *\n * WHAT IT IS: the base of an ACP conversation. The first level is WHICH agent\n * serves it; hanging off that are the things the agent itself owns — its own\n * permission preset, its own model, its own thought level, and any further\n * config options it publishes (the \"advanced\" base). All of them come from the\n * agent (`agent/catalog`, the agent's own mode list), never from DSH's model\n * catalog: an ACP agent is not a DSH provider, and DSH's catalog has no\n * per-session seam to filter it with anyway. See\n * docs/acp-convergence.md §2.1.\n *\n * WHERE IT SITS: the composer's model seat — the trailing group of the bar,\n * immediately before the submit button. That is where the host's own base\n * (the model selector) sits, and it is the cell an ACP conversation must not\n * leave to that selector: it lists DSH's providers, none of which this\n * conversation can use, and clicking one writes the process-wide default\n * model. One cell, one occupant. The base is installed there by the ACP-mode\n * gate in `index.tsx` (see `createModelSeatClaim`), which holds it only while\n * the staged conversation runs the ACP preset — so an ordinary chat gets the\n * native selector back and sees no ACP chrome at all.\n *\n * WHAT IT ADDS over a bare model selector: the axes are separate. A native\n * selector would flatten them into one list of \"Agent · Model\" rows; here\n * \"which agent\", \"which of ITS models\", \"how hard it thinks\" and every extra\n * option the agent publishes are chips of their own. Before the first turn the\n * agent base is the fixed right-hand anchor; the advanced entry sits left of\n * the model, and model / thought sit between them. Once the lock retires the\n * base, model / thought inherit the right edge (user decision, 2026-10-21).\n *\n * ONE CONVERSATION, ONE AGENT (user decision, 2026-10-20): once this\n * conversation's first turn STARTS — its ACP session materializes, before the\n * prompt is sent — the agent base steps aside and the conversation is LOCKED to\n * that agent. The current agent then lives on a small, read-only tag that rides\n * the composer card's top edge (`AcpComposerAgentTag`, installed in\n * `conversation.input.overlay`). Before that first turn the base stays, so a\n * pick can still be changed before it is committed to a message. See\n * `agentBaseHidden`.\n *\n * The lock is provisional until that first turn delivers something: a turn\n * that ends without any visible block (an error before the first chunk, a\n * silent provider failure, a cancel) is rolled back by the host, the base\n * returns, and the picker works again. The composer learns both transitions\n * from `chat/agent-locked` / `chat/agent-unlocked`, not from a list read.\n *\n * The hand-off is a shatter-and-condense animation (`agent-lock-anim.ts`): the\n * base breaks into debris, then the tag forms from it. The two live in\n * different slots, so they share that module's phase clock rather than trying\n * to animate across slot boundaries.\n *\n * It is deliberately quiet: outside ACP mode the gate never mounts it, and\n * before the host has answered the agent roster it renders nothing. Once the\n * roster is KNOWN to be empty the base stays and its picker carries the\n * \"open NTR settings\" action — an NTR conversation with nothing configured\n * still has a way forward instead of an empty cell. The ACP view tab remains\n * the entry point for conversations no agent has served.\n */\n\nimport { useEffect, useState } from 'react'\nimport type { CSSProperties } from 'react'\n// Type-only: pulls the ui-conversation SlotMap merge into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport {\n AcpChip, AcpMenu, AcpShatterParts, buildAcpModelId, closeAcpMenu, ensureAcpStyle,\n openAcpMenu, openAcpSettingsDialog, setAcpMenuError, splitAcpModelId, useAcpMenu, useUi,\n} from './acp-ui.tsx'\nimport { playAgentLockAnim, useAgentLockPhase } from './agent-lock-anim.ts'\nimport type {\n AcpChatInjected, AcpMenuKind, AcpMenuItem, AcpMenuSection, AcpSessionHook,\n} from './acp-ui.tsx'\nimport type { AcpConversationChoice, AcpTagHalo, AgentCatalogResult, AgentView, ChatSessionView } from '../protocol.ts'\n\n/**\n * Props every entry in this file receives: the injected face (built in\n * `index.tsx` where the services live) plus the session id the slot frame\n * passes to `inject`.\n */\nexport interface AcpClusterProps extends AcpChatInjected {\n sessionId: string\n /**\n * The framework's session snapshot hook (`SessionStandardProps.useSession`,\n * see `AcpSessionHook`). The cluster watches `running` alone: a fresh\n * conversation has no ACP binding until its first turn settles, so the binding\n * read is repeated then.\n */\n useSession: AcpSessionHook\n}\n\nconst STATE_COLORS: Record<AgentView['state'], string> = {\n ready: 'var(--dsw-alias-state-success-primary, #22c55e)',\n starting: 'var(--dsw-alias-state-warn-primary, #eab308)',\n error: 'var(--dsw-alias-state-error-primary, #ef4444)',\n stopped: 'var(--dsw-alias-state-idle-primary, #9ca3af)',\n}\n\nfunction agentStateText(acp: AcpChatInjected['acp'], state: AgentView['state']): string {\n switch (state) {\n case 'ready': return acp.label('已连接', 'connected')\n case 'starting': return acp.label('启动中', 'starting')\n case 'error': return acp.label('错误', 'error')\n default: return acp.label('未启动', 'not running')\n }\n}\n\n/** Everything the chips need to describe the current agent binding. */\ninterface RouteInfo {\n /** The agent serving this conversation. */\n agentId?: string\n agentTitle?: string\n modelId?: string\n modelLabel?: string\n efforts: readonly { id: string; name: string; description?: string }[]\n /** `current.reasoningEffort` folded over the model's default (native seat semantics). */\n effectiveEffort?: string\n effortLabel?: string\n}\n\n/** The live (non-released) ACP session bound to this chat, if any. */\nfunction boundSession(ui: { chatSessions: ChatSessionView[]; chatSessionsFor?: string }, sessionId: string): ChatSessionView | undefined {\n if (ui.chatSessionsFor !== sessionId) return undefined\n return ui.chatSessions.find(session => !session.released) ?? ui.chatSessions[0]\n}\n\n/**\n * The agent serving this conversation: the plugin's own binding first (who\n * serves the NEXT message — the host reports it per chat session), then the\n * agent an ACP session for this conversation is already served by, then the\n * plugin's default — which is what lets a chip offer \"use ACP: <agent>\" for a\n * conversation that has never been routed, instead of going blank. The binding\n * outranks the live session because a switch applies from the next message:\n * until that turn materializes a session for the new agent, the live list still\n * names the previous one, and the chip must not read as \"the click did nothing\".\n *\n * `undefined` IS an answer, not a gap: with no binding, no live session and no\n * plugin default the guard's route rewrite has nothing to rewrite with and the\n * turn runs on the deployment default provider (see `boundModelFor` in\n * presets/acp/acp-guard.mjs). This function used to end in `agents[0]?.id`,\n * which put the first configured agent on the chip in exactly that state — a\n * name no message would ever reach (audit finding F5). The honest chip there is\n * the \"选择 Agent\" entry point, not a borrowed name.\n */\nfunction routeAgentId(\n boundAgentId: string | undefined,\n bound: ChatSessionView | undefined,\n agents: readonly AgentView[],\n defaultAgent: string | undefined,\n): string | undefined {\n if (boundAgentId !== undefined && agents.some(agent => agent.id === boundAgentId)) return boundAgentId\n if (bound?.agentId !== undefined) return bound.agentId\n if (defaultAgent !== undefined && agents.some(agent => agent.id === defaultAgent)) return defaultAgent\n return undefined\n}\n\n/**\n * Whether the agent base has anything honest to show.\n *\n * An empty roster has two meanings, and they must not be conflated: before the\n * host answers `agents/list` it means \"not read yet\" (rendering a setup entry\n * there would accuse a correctly configured deployment on every page load),\n * while after the answer it means \"nothing is configured\" — exactly the state\n * that must keep the base and offer the way into NTR settings.\n */\nexport function agentBaseHasData(\n bound: ChatSessionView | undefined,\n agents: readonly AgentView[],\n rosterLoaded: boolean,\n): boolean {\n return bound !== undefined || agents.length > 0 || rosterLoaded\n}\n\n/**\n * The agent this conversation is LOCKED to, if its first ACP turn has run.\n *\n * The host's `lockedAgentId` is the durable answer: it is written when the\n * first ACP session materializes and it survives restarts, so it is the only\n * source that can still answer after the session is gone. The live/released\n * session is the fallback for a host half from before the lock existed — the\n * moment a session exists, the conversation already has context with it.\n *\n * There is deliberately no roster check: a deleted agent still owns the\n * conversation's context, and pretending the conversation is unbound would\n * bring back a picker the turn path has to refuse. The tag names the id even\n * when no title is left.\n */\nexport function lockedAgentOf(\n hostLockedAgentId: string | undefined,\n bound: ChatSessionView | undefined,\n): string | undefined {\n return hostLockedAgentId ?? bound?.agentId\n}\n\n/**\n * Whether the composer's agent base has done its job and should step aside.\n *\n * The timing is the product decision (2026-10-20): at the START of this\n * conversation's first turn. A conversation that merely recorded a pick is\n * still being set up; the user may change their mind before sending, and\n * hiding the base there would strand the pick. Once locked, the base only\n * comes back if the host ROLLS THE LOCK BACK — a first turn that delivered\n * nothing, so there is no context to protect. It also stays retired when the\n * locked agent has been deleted from the roster: the conversation's context\n * still belongs to it.\n */\nexport function agentBaseHidden(lockedTo: string | undefined): boolean {\n return lockedTo !== undefined\n}\n\n/**\n * Whether the composer-edge `NTR · <agent>` tag should render.\n *\n * The tag is the locked agent's nameplate: it appears exactly when the base\n * has stood down. The `acpMode` gate matters because the overlay entry is\n * registered for EVERY conversation while `ui.chatSessions` can still carry an\n * ACP session record after a conversation switched back to an ordinary preset:\n * an explicit `false` verdict must win over that lingering record, or the tag\n * would keep advertising an agent the conversation no longer runs. `undefined`\n * is \"the host has not answered yet\" and falls back to the lock itself, which\n * is proof this conversation was served by an agent.\n */\nexport function agentTagVisible(\n acpMode: boolean | undefined,\n lockedTo: string | undefined,\n): boolean {\n return (acpMode ?? true) && lockedTo !== undefined\n}\n\n/**\n * Whether the tagged agent is LIVE: an ACP session serves this conversation\n * right now and it is not released (the process is up, or at least the host\n * has not marked the session suspended). This drives the tag's breathing\n * halo — released must turn it off, and the named agent must be the one the\n * live session actually serves. No session view at all reads as not alive:\n * before the first answer the state says nothing about the process.\n */\nexport function agentTagAlive(\n bound: ChatSessionView | undefined,\n lockedTo: string | undefined,\n): boolean {\n return bound !== undefined && !bound.released && bound.agentId === lockedTo\n}\n\n/** One breath cycle per halo speed, in seconds. Settings card offers these three. */\nexport const TAG_HALO_PERIODS: Record<AcpTagHalo['speed'], string> = {\n slow: '5.2s',\n normal: '3.2s',\n fast: '1.2s',\n}\n\n/**\n * The tag's halo presentation for one setting: the extra classes for the tag\n * span (`alive` — the session is live; steady mode replaces the pulse with a\n * constant glow) and the CSS variable the stylesheet reads for the period.\n * The variable is set for breath mode only: in steady mode nothing animates,\n * so a stale variable could never leak into the reduced-motion fallback.\n */\nexport function agentTagHaloPresentation(\n halo: AcpTagHalo,\n alive: boolean,\n): { classes: string; style: CSSProperties } {\n if (!alive) return { classes: '', style: {} }\n if (halo.mode === 'steady') return { classes: ' is-alive is-steady', style: {} }\n return { classes: ' is-alive', style: { '--ntr-halo-period': TAG_HALO_PERIODS[halo.speed] } as CSSProperties }\n}\n\n/**\n * Describe this conversation's ACP agent from the binding (the host's record of\n * which agent actually serves it) + the agent roster + the agent's own model\n * surface (`agent/catalog`).\n *\n * An `undefined` agentId means nothing serves this conversation's next message\n * (see `routeAgentId`); every axis then degrades to nothing rather than naming\n * an agent the request path will never use.\n *\n * The model axis reads the ACP catalog, NOT any DSH model directory: an ACP\n * agent's models are the agent's own (see the module header), so there is\n * nothing to look up in DSH's catalog — and reading `current` off a directory\n * was the retired route axis.\n */\nexport function describeRoute(\n agents: readonly AgentView[],\n bound: ChatSessionView | undefined,\n defaultAgent: string | undefined,\n catalog?: AgentCatalogResult,\n boundAgentId?: string,\n choice?: AcpConversationChoice,\n): RouteInfo {\n const agentId = routeAgentId(boundAgentId, bound, agents, defaultAgent)\n if (agentId === undefined) return { efforts: [] }\n const def = agents.find(agent => agent.id === agentId)\n const agentTitle = def?.title ?? bound?.agentTitle ?? catalog?.title ?? agentId\n // The conversation's persisted pick covers the window in which there is no\n // live ACP session yet (nothing has been sent). The live session outranks it\n // — that is what the agent is actually running right now — but without the\n // pick a conversation set up ahead of its first message could not show the\n // model and thought level it is about to use, which is the whole point of\n // choosing them early.\n const pick = choice !== undefined && choice.agentId === agentId ? choice : undefined\n const modelId = bound?.modelId ?? pick?.modelId\n const effort = bound?.reasoningEffort ?? pick?.effort\n const reasoning = catalog?.reasoning\n // The model the AGENT says it is on, for a conversation nobody has picked a\n // model for yet. Without it the chip has no label and does not render at all,\n // so an agent with a perfectly good model axis looked like it had none (user\n // report, 2026-10-18). An agent publishing no `category: 'model'` select\n // genuinely has no model axis — there `currentModel` is absent and nothing is\n // faked. Display-only: `modelId` stays in the adapter-id space, so the picker\n // keeps marking rows by what this conversation actually chose.\n const agentModel = catalog?.currentModel\n if (bound === undefined && pick === undefined && agentModel === undefined) {\n return { agentId, agentTitle, efforts: [] }\n }\n const parsed = splitAcpModelId(modelId ?? '')\n const variant = catalog?.variants.find(entry => entry.id === modelId)\n // A model the conversation picked may be outside `exposedModels` (the pick\n // outlives the visibility filter). Its NAME is still known from the catalog's\n // `currentModel` when the values agree — show that instead of the raw value.\n const currentName = catalog?.currentModel !== undefined && parsed.choice === catalog.currentModel.value\n ? catalog.currentModel.name\n : undefined\n const effectiveEffort = effort ?? reasoning?.defaultEffort\n // The chip names the MODEL, not the agent (user report, 2026-10-18): the agent\n // is already the chip to its right, so \"CodeBuddy Code\" beside \"ACP · CodeBuddy\n // Code\" said nothing about which model was running. A bare `agentId` route\n // means \"the agent's own choice\", which the catalog resolves to `currentModel`.\n const modelLabel = modelId === undefined\n ? agentModel?.name\n : parsed.choice === undefined\n // Bare route: name the model it resolves to. `base.name` already spells out\n // `<agent> · <model>`, so prefer the model half when we have it.\n ? agentModel?.name ?? catalog?.base.name ?? agentTitle\n : variant?.name ?? currentName ?? parsed.choice\n return {\n agentId,\n agentTitle,\n ...(modelId === undefined ? {} : { modelId }),\n ...(modelLabel === undefined ? {} : { modelLabel }),\n efforts: reasoning?.efforts ?? [],\n ...effectiveEffort === undefined ? {} : { effectiveEffort },\n ...effectiveEffort === undefined\n ? {}\n : { effortLabel: reasoning?.efforts.find(level => level.id === effectiveEffort)?.name ?? effectiveEffort },\n }\n}\n\n/** One extra config option as the advanced base presents it. */\nexport interface AdvancedOption {\n configId: string\n name: string\n /** `'boolean'` for an on/off toggle (`value` is `'true'` / `'false'`, no choices). */\n kind?: 'boolean'\n /** The conversation's pick, else the value the agent reports as current. */\n value?: string\n choices: readonly { id: string; name: string; description?: string }[]\n}\n\n/**\n * The agent's extra config options for one conversation, with the\n * conversation's own pick folded over the agent-reported current value.\n *\n * `choice` only applies when it belongs to the catalog's agent: a stale pick\n * from a previous agent is the previous agent's vocabulary and must not label\n * the new one's menu (same rule as `describeRoute`). Options are whatever the\n * agent publishes beyond model / thought level / mode — those have their own\n * controls, and duplicating them here would present two truths for one axis.\n */\nexport function advancedOptions(\n catalog: AgentCatalogResult | undefined,\n choice?: AcpConversationChoice,\n): AdvancedOption[] {\n const extras = catalog?.extras ?? []\n if (catalog === undefined || extras.length === 0) return []\n const picked = choice !== undefined && choice.agentId === catalog.agentId ? choice.extraConfigs : undefined\n return extras.map(option => {\n const raw = picked?.[option.configId] ?? option.currentValue\n // A toggle's catalog value is 'true' / 'false'; a conversation pick stores\n // the real boolean. Both collapse to the string form the menu speaks.\n const value = typeof raw === 'boolean' ? (raw ? 'true' : 'false') : raw\n return {\n configId: option.configId,\n name: option.name,\n ...(option.kind === 'boolean' ? { kind: 'boolean' as const } : {}),\n ...(value === undefined ? {} : { value }),\n choices: option.choices,\n }\n })\n}\n\n/**\n * The agent base chip's label and tooltip (audit findings F5 and F26).\n *\n * The label names an agent ONLY when one actually serves this conversation:\n * `route.agentId` is the binding, the live session or the plugin default, and\n * each of those is a name the request path uses (see `routeAgentId`). With none\n * of them the chip is the \"选择 Agent\" entry point and says so — the old\n * `agents[0]` fallback put a stranger's name on a conversation whose next\n * message would run on the deployment default provider.\n *\n * The tooltip claims only what the menu behind the chip does: switch the agent.\n * The agent's models and thought levels have chips of their own (user decision,\n * 2026-10-17), so the previous wording — \"switch the agent, its model and the\n * thought level\" — promised a menu that holds the roster alone. An EMPTY\n * roster is its own case: the menu cannot switch to anything, it offers the\n * way into NTR settings, so the tooltip says that instead of promising a pick.\n */\nexport function agentBaseChip(\n route: RouteInfo,\n routed: boolean,\n acp: AcpChatInjected['acp'],\n rosterEmpty = false,\n): { label: string; title: string } {\n const label = route.agentTitle === undefined\n ? (routed ? 'NTR' : acp.label('选择 Agent', 'Choose agent'))\n : `NTR · ${route.agentTitle}`\n const title = route.agentId === undefined\n ? rosterEmpty\n ? acp.label(\n '这条会话当前没有 NTR agent;点开可前往 NTR 设置添加',\n 'No NTR agent is serving this conversation — open to set one up in NTR settings',\n )\n : acp.label(\n '这条会话当前没有 NTR agent 服务;点开可选择 agent',\n 'No NTR agent is serving this conversation right now — open to choose one',\n )\n : acp.label(\n '这条会话由外接 NTR agent 服务;点开可切换 agent',\n 'This conversation is served by an external NTR agent — open to switch the agent',\n )\n return { label, title }\n}\n\n/**\n * The composer-edge tag's label and tooltip.\n *\n * This tag is where the agent identity lives once the composer base steps\n * aside (see `agentBaseHidden`), so it names the agent unconditionally — the\n * caller only mounts it when the conversation is locked. It is a nameplate,\n * not a control: the tooltip says the lock is one-way and points at the only\n * way to use another agent (a new conversation).\n */\nexport function agentTagChip(agentTitle: string, acp: AcpChatInjected['acp']): { label: string; title: string } {\n return {\n label: `NTR · ${agentTitle}`,\n title: acp.label(\n `这条会话已锁定在 ${agentTitle};首轮开始后不能切换 agent(上下文属于它)。换 agent 请新建会话。`,\n `This conversation is locked to ${agentTitle}. It cannot switch agents once the first turn has started — the context belongs to it. Start a new conversation to use another agent.`,\n ),\n }\n}\n\n/**\n * The agent tag riding the composer card's top edge\n * (`conversation.input.overlay`).\n *\n * The composer's agent base retires when the conversation's first ACP turn\n * starts, and that turn LOCKS the conversation to its agent (user decision,\n * 2026-10-20): a different agent would have none of the context. This tag is\n * therefore a NAMEPLATE, not a control — it keeps \"who serves this\n * conversation\" on screen and says why it cannot change. It deliberately has\n * no click target and no caret: the only way to use another agent is a new\n * conversation. If the turn delivered nothing, the host rolls the lock back\n * and this tag leaves with it.\n *\n * WHERE IT SITS: the overlay anchor is a zero-height absolute box at the top\n * edge of the composer card, so the tag's wrapper straddles that edge (see\n * `.ntr-acp-tag` in acp-ui.tsx) — half above the card, half inside it, like a\n * small nameplate hung on the box. This is deliberately NOT the session\n * header: the tag belongs to the input it describes, and the header is shared\n * with conversation chrome that is not about the agent.\n *\n * It gates itself: the overlay slot is registered for EVERY conversation, so\n * it must not leak `NTR · <agent>` into ordinary chats, and it may only appear\n * once the base has actually stood down (see `agentTagVisible`).\n */\nexport function AcpComposerAgentTag(props: AcpClusterProps) {\n ensureAcpStyle()\n const { acp, sessionId, useSession } = props\n const ui = useUi(acp)\n const running = useSession(snapshot => snapshot.running)\n useEffect(() => {\n void acp.loadChatSessions(sessionId)\n }, [acp, sessionId, running])\n const bound = boundSession(ui, sessionId)\n const lockedTo = lockedAgentOf(ui.chatLockedAgent[sessionId], bound)\n const reveal = ui.agentLockReveal[sessionId]\n const phase = useAgentLockPhase(sessionId, reveal)\n // Both surfaces start the same clock; whichever effect runs first wins and\n // the other is a no-op (playAgentLockAnim is idempotent per reveal). The\n // lock arrives at the START of the first turn (chat/agent-locked), so the\n // hand-off plays while the turn runs — see agent-lock-anim.ts.\n useEffect(() => {\n if (reveal !== undefined) playAgentLockAnim(sessionId, reveal)\n }, [reveal, sessionId])\n // The tag stays away while the base is still breaking apart: the hand-off is\n // base → debris → tag, never both on screen at once.\n if (phase === 'shatter') return null\n if (!agentTagVisible(ui.acpMode[sessionId], lockedTo) || lockedTo === undefined) return null\n const agentTitle = ui.agents.find(agent => agent.id === lockedTo)?.title ?? bound?.agentTitle ?? lockedTo\n // The halo rides the settled tag only: while the forming animation owns the\n // chip its `animation` shorthand must not be overridden mid-flight.\n const alive = phase !== 'form' && agentTagAlive(bound, lockedTo)\n const halo = agentTagHaloPresentation(ui.tagHalo, alive)\n return (\n <span\n className={`ntr-acp-tag${phase === 'form' ? ' is-forming' : ''}${halo.classes}`}\n style={halo.style}\n >\n <AcpChip {...agentTagChip(agentTitle, acp)}>\n {phase === 'form' && <AcpShatterParts mode=\"in\" />}\n </AcpChip>\n </span>\n )\n}\n\n/** The composer cluster: agent, model and thought level — or nothing at all. */\nexport function AcpSessionControls(props: AcpClusterProps) {\n ensureAcpStyle()\n const { acp, sessionId, useSession } = props\n const ui = useUi(acp)\n const running = useSession(snapshot => snapshot.running)\n const menu = useAcpMenu()\n useEffect(() => {\n void acp.loadChatSessions(sessionId)\n }, [acp, sessionId, running])\n const bound = boundSession(ui, sessionId)\n const agents = ui.agents\n const choice = ui.chatChoice[sessionId]\n // The lock is resolved BEFORE the agent route: a locked conversation belongs\n // to its agent even when no session is live, so it must never fall back to\n // the plugin default (that would put another agent's models and thought\n // levels on its bar).\n const lockedTo = lockedAgentOf(ui.chatLockedAgent[sessionId], bound)\n // The lock hand-off (see agent-lock-anim.ts): the tag slot in the overlay\n // starts the same clock from its own effect; either call arms it once.\n const reveal = ui.agentLockReveal[sessionId]\n const lockPhase = useAgentLockPhase(sessionId, reveal)\n useEffect(() => {\n if (reveal !== undefined) playAgentLockAnim(sessionId, reveal)\n }, [reveal, sessionId])\n const agentId = lockedTo ?? routeAgentId(ui.chatBoundAgent[sessionId], bound, agents, ui.defaultAgent)\n // A deleted locked agent has no surface to show: suppress its chips instead\n // of keeping a dead agent's last-known model menu interactive.\n const agentListed = agentId !== undefined && agents.some(agent => agent.id === agentId)\n const catalog = agentListed ? ui.seatCatalogs[agentId] : undefined\n // Read the catalog ONLY for a conversation that actually points at an agent\n // (`routeAgentId`'s answer, which includes the plugin default). The old gate\n // listed only bound / pick / optimistic binding, so a NEW conversation\n // served by the default agent showed no model/thought chips until the user\n // re-picked the agent by hand. `agent/catalog` is pure memory (it never\n // spawns), so reading the default agent's surface cannot start a process.\n // (The 2026-10-18 spawn report is about the retired probe-on-read path;\n // the host answers this RPC from memory.)\n const served = agentId !== undefined\n useEffect(() => {\n if (served && agentListed && agentId !== undefined) void acp.loadAgentCatalog(agentId)\n }, [acp, served, agentListed, agentId])\n const route = describeRoute(\n agents,\n bound,\n // A locked conversation has no default fallback: the lock is the answer.\n lockedTo === undefined ? ui.defaultAgent : undefined,\n catalog,\n lockedTo ?? ui.chatBoundAgent[sessionId],\n choice,\n )\n // An empty roster is the SETUP case, not \"no story\": the base stays and its\n // picker carries the way into NTR settings. Only the window before the host\n // has answered `agents/list` stays blank — `agents: []` there means \"not read\n // yet\", and the setup entry would accuse a configured deployment (see\n // `agentBaseHasData`).\n if (!agentBaseHasData(bound, agents, ui.agentsLoaded)) return null\n // Set up = an agent serves this conversation: a live session, a persisted\n // pick, the host's optimistic binding, OR the plugin default the guard's\n // route rewrite falls back to. The last one is what makes a brand-new\n // conversation show its model / thought chips without a manual re-pick.\n const routed = route.agentId !== undefined\n // The base's retirement and the conversation's one-way agent lock are the\n // same event (see `agentBaseHidden`): the first ACP turn. From then on the\n // base never comes back, and the tag is the only place the agent's name\n // lives.\n //\n // The picker could already be open when the first turn starts (clicking the\n // send button is inside the composer card, which AcpMenu treats as its own\n // furniture, so it does not close the menu). Once locked, that menu can only\n // offer a switch the host will refuse — close it instead of leaving a live\n // control over a locked conversation.\n useEffect(() => {\n if (lockedTo === undefined || menu === null) return\n if (menu.sessionId === sessionId && menu.kind === 'agent') closeAcpMenu()\n }, [lockedTo, menu?.sessionId, menu?.kind, sessionId])\n // Keep the base mounted for the shatter half even though the lock already\n // retired it: unmounting on the same commit the lock arrives would skip the\n // transition entirely (the tag waits for the same phase to leave 'shatter').\n const shattering = lockPhase === 'shatter' && lockedTo !== undefined\n const showAgentBase = !agentBaseHidden(lockedTo) || shattering\n const modelLabel = routed ? route.modelLabel : undefined\n const showModel = agentListed && modelLabel !== undefined\n const showThought = agentListed && routed && route.efforts.length > 0\n const extras = advancedOptions(catalog, choice)\n const showAdvanced = agentListed && routed && extras.length > 0\n // Nothing left to show: the cell stays claimed (the claim owns it), but this\n // occupant renders an empty cluster rather than a bare wrapper.\n if (!showAgentBase && !showAdvanced && !showModel && !showThought) return null\n const open = (kind: AcpMenuKind, trigger: HTMLElement): void => { openAcpMenu(sessionId, kind, trigger) }\n\n // Ordering (user decision, 2026-10-21): the AGENT base stays the fixed\n // right-hand anchor BEFORE the first turn — that is where users know it\n // from, and it must not be squeezed out by chips the agent happens to\n // publish. The advanced entry sits immediately left of the model, so the\n // agent's extra options never push the primary axes around. Once the first\n // turn locks the conversation the base retires (2026-10-20) and the\n // model / thought pair inherits the right edge.\n return (\n <span className=\"ntr-acp-cluster\">\n {/* Immediately left of the model: the advanced base collects everything\n the agent publishes that is NOT one of the primary axes. */}\n {showAdvanced && (\n <AcpChip\n label={`${acp.label('高级', 'Advanced')} · ${String(extras.length)}`}\n title={acp.label(\n `agent 的额外配置项:${extras.map(option => option.name).join('、')}`,\n `extra agent config options: ${extras.map(option => option.name).join(', ')}`,\n )}\n active={menu?.sessionId === sessionId && menu.kind === 'advanced'}\n onClick={(trigger) => { open('advanced', trigger) }}\n />\n )}\n {showModel && modelLabel !== undefined && (\n <AcpChip\n label={modelLabel}\n title={acp.label('agent 自己的模型目录', \"the agent's own model directory\")}\n active={menu?.sessionId === sessionId && menu.kind === 'model'}\n onClick={(trigger) => { open('model', trigger) }}\n />\n )}\n {showThought && (\n <AcpChip\n label={`${acp.label('思考', 'think')} · ${route.effortLabel ?? acp.label('默认', 'default')}`}\n title={acp.label('思考等级(agent 的 thought_level)', \"Thought level (the agent's thought_level)\")}\n active={menu?.sessionId === sessionId && menu.kind === 'thought'}\n onClick={(trigger) => { open('thought', trigger) }}\n />\n )}\n {/* Last on purpose while present: the fixed right-hand anchor before the\n lock. CSS gives the last chip `flex: none`, so a long model name or\n an extra chip can only squeeze what is to its left — the agent base\n never disappears. It retires after the first ACP turn (user decision,\n 2026-10-20) and the composer-edge tag replaces it; the retirement\n itself is the shatter played here (see `shattering`). */}\n {showAgentBase && (\n <AcpChip\n {...agentBaseChip(route, routed, acp, agents.length === 0)}\n active={routed}\n className={shattering ? 'ntr-acp-shatter' : undefined}\n onClick={shattering ? undefined : (trigger) => { open('agent', trigger) }}\n >\n {shattering && <AcpShatterParts mode=\"out\" />}\n </AcpChip>\n )}\n </span>\n )\n}\n\n/** Inputs one picker model is built from (kept explicit so the shape stays reviewable). */\nexport interface MenuContext {\n acp: AcpChatInjected['acp']\n agents: readonly AgentView[]\n route: RouteInfo\n /** The bound agent's own model surface (`agent/catalog`), if it has loaded. */\n catalog?: AgentCatalogResult\n /** The conversation's persisted pick (the advanced menu overlays its extras). */\n choice?: AcpConversationChoice\n /** A `agent/probe-models` request for this agent is in flight. */\n probing?: boolean\n error?: string\n}\n\n/**\n * The model picker's explicit refresh row. It is an ACTION, not a model: the\n * pick handler intercepts it and probes the agent instead of recording a pick.\n * The id cannot collide with a real row — agent ids match\n * `[a-z0-9][a-z0-9._-]{0,63}` (no underscores) and variant ids always carry\n * `agentId@value`.\n */\nexport const REFRESH_CATALOG_ITEM = '__refresh-catalog__'\n\n/**\n * The empty roster's setup action. Like the model picker's refresh row it is an\n * ACTION inside the picker, not an agent: agent ids match\n * `[a-z0-9][a-z0-9._-]{0,63}` (no leading underscore), so no real id can\n * collide with it.\n */\nexport const OPEN_SETTINGS_ITEM = '__open-settings__'\n\n/** The agent roster rows: one row per configured ACP agent, current one marked. */\nfunction agentSection(context: MenuContext): AcpMenuSection {\n const { acp, route } = context\n const items: AcpMenuItem[] = context.agents.map(agent => ({\n id: agent.id,\n label: agent.title ?? agent.id,\n detail: agentStateText(acp, agent.state),\n dot: STATE_COLORS[agent.state],\n active: route.agentId === agent.id,\n }))\n if (items.length === 0) {\n // The base stays on an empty roster (see `agentBaseHasData`), so this menu\n // is the setup surface: a status row that says what is missing, then an\n // ACTION row that opens the NTR settings. The action is a row of its own\n // because a disabled status row cannot be the way forward.\n items.push({\n id: 'none',\n label: acp.label('还没有配置 NTR agent', 'No NTR agent configured yet'),\n detail: acp.label('添加并启用一个 agent 后即可开始对话', 'Add and enable an agent to start'),\n disabled: true,\n })\n items.push({\n id: OPEN_SETTINGS_ITEM,\n label: acp.label('打开 NTR 设置', 'Open NTR settings'),\n detail: acp.label('添加或启用 agent', 'Add or enable an agent'),\n })\n }\n return {\n id: 'agent',\n items,\n }\n}\n\n/**\n * The selected agent's OWN models — `undefined` when no agent is known yet.\n *\n * The rows come from `agent/catalog`, not from any DSH model directory: ACP\n * models are not advertised in DSH's catalog at all, and the exposure filter\n * (`exposedModels`) is applied host-side, so what arrives here is exactly what\n * this agent offers in this conversation.\n *\n * ONE ROW PER MODEL, and the checked row names its model (user report,\n * 2026-10-18). The bare `agentId` route (\"let the agent choose\") is NOT a model,\n * so it is folded onto the row of the model it currently resolves to: the\n * previous shape listed `<agent> · 默认` AND `<agent> · Hy3` as two entries for\n * one model, and the checked one was the row that did not say \"Hy3\".\n */\nfunction modelSection(context: MenuContext): AcpMenuSection | undefined {\n const { acp, route, catalog } = context\n if (route.agentId === undefined) return undefined\n // An explicit gesture, not a render side effect: opening the picker stays\n // cache-first (reading must never spawn), and the user asks for a fresh\n // directory by picking this row. The host's `agent/probe-models` may start\n // or reuse that agent's process.\n const refresh: AcpMenuItem = {\n id: REFRESH_CATALOG_ITEM,\n label: context.probing === true\n ? acp.label('正在检测…', 'Detecting…')\n : acp.label('刷新目录', 'Refresh list'),\n detail: acp.label('向 agent 重新读取模型列表', 're-read the model list from the agent'),\n disabled: context.probing === true,\n }\n if (catalog === undefined) {\n // No catalog yet (a fresh conversation, or a host still probing): the\n // current model is the only row we can honestly show — plus the refresh\n // action, which is how a first catalog gets fetched on demand.\n const items: AcpMenuItem[] = route.modelId === undefined\n ? []\n : [{ id: route.modelId, label: route.modelLabel ?? route.modelId, active: true }]\n items.push(refresh)\n return {\n id: 'model',\n title: acp.label(`${route.agentTitle ?? 'agent'} 的模型`, `Models of ${route.agentTitle ?? 'the agent'}`),\n items,\n }\n }\n // Which model is IN FORCE, as a model VALUE (not a route). `route.modelId` is\n // an adapter id: the bare `agentId` means \"the agent's own choice\", which the\n // catalog resolves to `currentModel.value`. A variant is a specific value.\n const parsed = splitAcpModelId(route.modelId ?? '')\n const pickedChoice = parsed.choice\n const current = catalog.currentModel\n const inForce = pickedChoice ?? current?.value\n // What the agent offers, as model VALUES. `exposedModels` is a filter over what\n // is OFFERED, not over what works, so a model in force outside this set still\n // gets a row — otherwise the picker would imply the conversation runs another.\n const offered = new Map<string, AcpMenuItem>()\n for (const variant of catalog.variants) {\n const value = splitAcpModelId(variant.id).choice\n if (value === undefined) continue\n offered.set(value, {\n id: variant.id,\n label: variant.name,\n ...(variant.description === undefined ? {} : { detail: variant.description }),\n active: value === inForce,\n })\n }\n // THE BUG IN THE SCREENSHOT (user report, 2026-10-18): the agent is on `hy3`,\n // `hy3` is also exposed, and the list showed `<agent> · 默认 ✓` above\n // `<agent> · Hy3` — two rows for one model, the checkmark on the one that named\n // no model. So: ONE row per model, and the model in force is marked wherever it\n // appears.\n const rows: AcpMenuItem[] = []\n if (inForce !== undefined && !offered.has(inForce)) {\n // Either the agent is on a model its own exposure filter hides, or the\n // conversation picked one that has dropped out of the catalog. Named either\n // way — `route.modelLabel` already resolves the stale-variant case — so the\n // row's detail must name BOTH causes: blaming only the Settings → NTR\n // checkbox sent people looking for a box they had already ticked (audit\n // finding F25). A dropped model is NOT silently replaced: the host refuses\n // the send (`model_unavailable`), so the detail must not promise a fallback.\n rows.push({\n id: buildAcpModelId(route.agentId, inForce),\n label: `${inForce === current?.value ? current?.name ?? inForce : route.modelLabel ?? inForce} · ${acp.label('当前', 'current')}`,\n detail: acp.label(\n '不在此列表出的模型里:可能在「设置 → NTR」里取消了勾选,也可能已从 agent 的目录掉出;若 agent 已不再提供,发送会被拒绝,请改选或用「刷新目录」重新读取。',\n \"Not among the models listed here: it may be unchecked in Settings → NTR, or it may have dropped out of the agent's catalog. If the agent no longer offers it, sending is refused — pick another model or use Refresh list to re-read the directory.\",\n ),\n active: true,\n })\n }\n if (current === undefined) {\n // The bare `agentId` route could not be resolved to a model — the agent has\n // never been probed (or publishes no model selector). The bare row is then\n // the only honest description of what this conversation runs, and leaving the\n // picker empty would read as broken rather than as unknown.\n //\n // When the route DOES resolve to a model this row is deliberately absent: it\n // would be a second row for that same model, which is precisely the defect\n // being fixed.\n rows.push({\n id: catalog.base.id,\n label: `${catalog.base.name} · ${acp.label('默认', 'default')}`,\n ...(catalog.base.description === undefined ? {} : { detail: catalog.base.description }),\n active: inForce === undefined,\n })\n }\n rows.push(...offered.values())\n rows.push(refresh)\n return {\n id: 'model',\n title: acp.label(`${route.agentTitle ?? 'agent'} 的模型`, `Models of ${route.agentTitle ?? 'the agent'}`),\n note: catalog.variants.length === 0\n ? acp.label(\n 'agent 自己上报的模型目录;这里只列出它当前用的模型,其余可在「设置 → NTR」里勾选可见。',\n \"The agent's own model directory; only the model it is on is offered here — enable more in Settings → NTR.\",\n )\n : acp.label(\n 'agent 自己上报的模型目录;勾选的那条就是这条会话正在用的模型。',\n \"The agent's own model directory; the checked row is the model this conversation is running.\",\n ),\n items: rows,\n }\n}\n\n/** The agent's `thought_level` levels (its own vocabulary). */\nfunction thoughtSection(context: MenuContext): AcpMenuSection {\n const { acp, route } = context\n const items: AcpMenuItem[] = [{\n // The id keeps its historical name on purpose: it is an internal sentinel\n // the pick handler compares against, and no user ever reads it. The LABEL is\n // the user-visible claim, and \"provider default\" was false (audit finding\n // F43): this axis is the agent's own `thought_level`, with no DSH provider\n // anywhere on it (`listModels('acp')` answers an empty catalog). Clearing\n // the pick leaves the agent's own `reasoning.defaultEffort` in force, which\n // is what `effectiveEffort` reports.\n id: 'provider-default',\n label: acp.label('跟随 agent 默认', 'Agent default'),\n active: route.effectiveEffort === undefined,\n }]\n for (const level of route.efforts) {\n items.push({\n id: level.id,\n label: level.name,\n ...level.description === undefined ? {} : { detail: level.description },\n active: level.id === route.effectiveEffort,\n })\n } return {\n id: 'thought',\n title: acp.label('思考等级', 'Thought level'),\n note: acp.label(\n '由 agent 的 thought_level 配置项驱动,下一次请求生效。',\n \"Driven by the agent's own thought_level option; applies to the next request.\",\n ),\n items,\n }\n}\n\n/**\n * The advanced base's sections: one per extra config option.\n *\n * Section ids carry a `config:` prefix because `AcpMenu.onPick` hands the\n * section id back and the other pickers already own `agent` / `model` /\n * `thought` — an agent could legitimately name one of its options `model`,\n * and the pick would then be read as the primary model axis.\n */\nfunction advancedSections(context: MenuContext): AcpMenuSection[] {\n return advancedOptions(context.catalog, context.choice).map(option => {\n if (option.kind === 'boolean') {\n // A toggle is ONE checkbox row, not an On/Off pair: the row's check is\n // the current value and the pick sends the OPPOSITE, which `onPick`\n // forwards verbatim (the host normalizes 'true' / 'false' to a boolean).\n const checked = option.value === 'true'\n return {\n id: `config:${option.configId}`,\n items: [{ id: checked ? 'false' : 'true', label: option.name, active: checked, checkbox: true }],\n }\n }\n return {\n id: `config:${option.configId}`,\n title: option.name,\n items: option.choices.map(choice => ({\n id: choice.id,\n label: choice.name,\n ...(choice.description === undefined ? {} : { detail: choice.description }),\n active: choice.id === option.value,\n })),\n }\n })\n}\n\n/**\n * REMOVED on purpose (2026-10-17): the agent's own `session/set_mode` list used\n * to ride inside the agent base's menu. It has its own control\n * (`AcpComposerMode`, the native-mode select in `conversation.input.left`), so\n * duplicating it in the base made two entries for one setting and put four\n * unrelated axes behind a control whose only job is \"who serves this\n * conversation\". Do not reintroduce it here — the editor belongs to\n * composer-mode.tsx.\n */\n\n/** Build the picker model for one kind. */\nexport function menuFor(kind: AcpMenuKind, context: MenuContext): {\n title: string\n note?: string\n sections: AcpMenuSection[]\n error?: string\n loading?: boolean\n} {\n const { acp, route } = context\n\n if (kind === 'agent') {\n // The agent base is ONLY the entry point for starting/replacing the agent\n // (user decision, 2026-10-17). Everything else the agent publishes — its\n // models, its thought levels, its own modes — has a seat of its own and is\n // reached from there, so stacking them in here turned one gesture (\"who\n // serves this conversation\") into a four-section dump and made the agent\n // list harder to scan. One control, one job.\n //\n // ONE heading, no note (user decision, 2026-10-18): the menu header says it\n // and the section adds no second title. The previous caption spelled out\n // where the other axes live and how switching applies — true, but it read\n // as a manual for a list that is self-explanatory, and it pushed the rows\n // down for no information the user needed at the moment of picking.\n return {\n title: acp.label('选择你的 Agent', 'Choose your agent'),\n sections: [{ ...agentSection(context), title: undefined, note: undefined }],\n }\n }\n\n if (kind === 'model') {\n const section = modelSection(context)\n if (section === undefined) return { title: acp.label('模型', 'Models'), sections: [] }\n // The single-axis picker keeps the shape it had before: one title + note on\n // the menu header, rows under a header-less section.\n return { title: section.title ?? acp.label('模型', 'Models'), note: section.note, sections: [{ ...section, title: undefined, note: undefined }] }\n }\n\n if (kind === 'thought') {\n const section = thoughtSection(context)\n return { title: section.title ?? acp.label('思考等级', 'Thought level'), note: section.note, sections: [{ ...section, title: undefined, note: undefined }] }\n }\n\n if (kind === 'advanced') {\n return {\n title: acp.label('高级选项', 'Advanced options'),\n note: acp.label(\n 'agent 自己发布的额外配置项;选中只作用于这条会话,下一条消息生效。',\n 'Extra config options published by the agent; a pick belongs to this conversation and applies from the next message.',\n ),\n sections: advancedSections(context),\n }\n }\n\n return { title: acp.label('NTR', 'NTR'), sections: [] }\n}\n\n/**\n * The picker itself, living in the composer's overlay seat. It renders nothing\n * unless a chip of THIS session opened it, so a stale menu can never leak\n * across a session switch.\n */\nexport function AcpSessionMenu(props: AcpClusterProps) {\n ensureAcpStyle()\n const { acp, sessionId } = props\n const ui = useUi(acp)\n const menu = useAcpMenu()\n const [busy, setBusy] = useState(false)\n const bound = boundSession(ui, sessionId)\n const agents = ui.agents\n const choice = ui.chatChoice[sessionId]\n // Same lock rule as the cluster: a locked conversation belongs to its agent\n // even when the host's bound-agent mirror or the plugin default says\n // otherwise — the menu must list ITS extras, not a stranger's.\n const lockedTo = lockedAgentOf(ui.chatLockedAgent[sessionId], bound)\n const agentId = lockedTo ?? routeAgentId(ui.chatBoundAgent[sessionId], bound, agents, ui.defaultAgent)\n const catalog = agentId === undefined ? undefined : ui.seatCatalogs[agentId]\n // Same rule as the cluster: a menu that is not open, or a conversation that\n // points at no agent, must not read anybody's catalog. The default agent is\n // included: the guard's route rewrite uses it, and the read is memory-only.\n const served = agentId !== undefined\n useEffect(() => {\n if (menu !== null && served && agentId !== undefined) void acp.loadAgentCatalog(agentId)\n }, [acp, menu, served, agentId])\n\n if (menu === null || menu.sessionId !== sessionId) return null\n const route = describeRoute(\n agents,\n bound,\n lockedTo === undefined ? ui.defaultAgent : undefined,\n catalog,\n lockedTo ?? ui.chatBoundAgent[sessionId],\n choice,\n )\n // A menu-model bug must never retire the whole picker: a render throw inside\n // this slot entry makes the error boundary ABDICATE it for the rest of the\n // session (every later click silently opens nothing). Show the failure inside\n // the menu instead of dying.\n let model: ReturnType<typeof menuFor>\n try {\n const probing = menu.kind === 'model' && agentId !== undefined && ui.probingModels[agentId] === true\n const menuError = menu.error ?? (menu.kind === 'model' && agentId !== undefined ? ui.probeErrors[agentId] : undefined)\n model = menuFor(menu.kind, {\n acp,\n agents,\n route,\n ...catalog === undefined ? {} : { catalog },\n ...choice === undefined ? {} : { choice },\n ...probing ? { probing: true } : {},\n ...menuError === undefined ? {} : { error: menuError },\n })\n } catch (error) {\n model = {\n title: acp.label('NTR', 'NTR'),\n sections: [],\n error: error instanceof Error ? error.message : String(error),\n }\n }\n\n const pick = async (sectionId: string, itemId: string): Promise<void> => {\n // Everything here writes into the PLUGIN's own binding for this chat\n // session — never a DSH model route (see the module header).\n try {\n if (sectionId === 'model' && itemId === REFRESH_CATALOG_ITEM) {\n // The one explicit refresh gesture: an `agent/probe-models` request.\n // Opening the menu never probes; this row does. Keep the picker open so\n // the fresh list (or the probe error) lands where the user asked.\n if (agentId !== undefined) await acp.probeModels(agentId)\n return\n }\n if (sectionId === 'agent' && itemId === OPEN_SETTINGS_ITEM) {\n // The empty roster's way out (see `agentSection`): there is no agent to\n // bind, so this row opens the NTR settings surface instead. Close the\n // picker first — the dialog is a frame overlay and the menu must not\n // linger behind it.\n closeAcpMenu()\n openAcpSettingsDialog()\n return\n }\n if (sectionId === 'agent') {\n await acp.bindChatAgent(sessionId, itemId)\n } else if (sectionId === 'model') {\n await acp.setChatSessionModel(sessionId, itemId, agentId)\n } else if (sectionId === 'thought') {\n await acp.setChatSessionEffort(sessionId, itemId === 'provider-default' ? undefined : itemId, agentId)\n } else if (sectionId.startsWith('config:')) {\n // One section per extra option; the id after the prefix names the\n // agent's own `configId` (see advancedSections).\n await acp.setChatSessionConfig(sessionId, sectionId.slice('config:'.length), itemId, agentId)\n } else {\n // `agent-mode` used to be handled here; the agent's mode list now lives\n // only in the composer-mode control (see the removed-section note).\n return\n }\n await acp.loadChatSessions(sessionId)\n closeAcpMenu()\n } catch (error) {\n setAcpMenuError(error instanceof Error ? error.message : String(error))\n }\n }\n\n return (\n <AcpMenu\n model={model}\n anchor={menu.trigger}\n onPick={(itemId, sectionId) => {\n if (busy) return\n setAcpMenuError(undefined)\n setBusy(true)\n void pick(sectionId, itemId).finally(() => { setBusy(false) })\n }}\n onClose={() => { closeAcpMenu() }}\n />\n )\n}\n","/**\n * The conversation's ACP view tab: the agent's REAL stream, as the agent\n * produced it.\n *\n * WHY A TAB AS WELL AS THE CHAT: the native chat plane renders what the LLM\n * adapter emits, and the adapter's chunk vocabulary is text + reasoning — so\n * the agent's prose and thinking ride the native plane, while a fuller card (a\n * tool call with its arguments, a plan, a unified diff) has no chunk to ride\n * there. This view renders those as themselves.\n *\n * It is a VIEW, not a second client: it never prompts, never cancels, never\n * spawns. Everything it shows is host state.\n */\n\nimport type { ReactNode } from 'react'\nimport { memo, useEffect, useRef, useState } from 'react'\n// Type-only: pulls the ui-conversation SlotMap merge into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { ensureAcpStyle, useUi } from './acp-ui.tsx'\nimport type { AcpChatInjected } from './acp-ui.tsx'\nimport type { Block, ChatTranscript } from './store.ts'\nimport type { AgentView, ChatSessionView } from '../protocol.ts'\n\n/** Props of the ACP view entry (injected face + the frame's session id). */\nexport interface AcpViewProps extends AcpChatInjected {\n sessionId: string\n}\n\n/**\n * The store's bilingual label function (`AcpStore.label`). Exported because the\n * empty-state copy builder takes one and the bundle emits declarations.\n */\nexport type Translate = (zh: string, en: string) => string\n\n/** Glyph per ACP tool kind, so a call's nature reads before its title does. */\nconst TOOL_GLYPH: Record<string, string> = {\n read: '📖',\n edit: '✏️',\n delete: '🗑️',\n move: '📦',\n search: '🔍',\n execute: '⌨️',\n think: '🧠',\n fetch: '🌐',\n switch_mode: '⇄',\n other: '🛠️',\n}\n\n/** Tone class per ACP tool status. */\nconst STATUS_TONE: Record<string, string> = {\n pending: 'idle',\n in_progress: 'busy',\n completed: 'done',\n failed: 'fail',\n}\n\n/** Cap on the rendered diff matrix: beyond it, show the edit as remove-all/add-all. */\nconst DIFF_LCS_LIMIT = 300\n/** Tool output lines shown before the \"show all\" toggle appears. */\nconst TOOL_LINES_CLIP = 12\n\n/** One line of a rendered unified diff. */\ninterface DiffRow {\n sign: '+' | '-' | ' '\n text: string\n}\n\n/** Split a text payload into lines, tolerating an empty / null side. */\nfunction linesOf(text: string | null): string[] {\n if (text === null || text === '') return []\n return text.endsWith('\\n') ? text.slice(0, -1).split('\\n') : text.split('\\n')\n}\n\n/**\n * Line diff with a common prefix/suffix trim plus an LCS over the remaining\n * middle — the standard shape that keeps an edit reviewable without pulling a\n * diff library into a browser bundle.\n */\nexport function diffRows(oldText: string | null, newText: string): DiffRow[] {\n const oldLines = linesOf(oldText)\n const newLines = linesOf(newText)\n const rows: DiffRow[] = []\n let start = 0\n while (start < oldLines.length && start < newLines.length && oldLines[start] === newLines[start]) start++\n let tail = 0\n while (\n tail < oldLines.length - start\n && tail < newLines.length - start\n && oldLines[oldLines.length - 1 - tail] === newLines[newLines.length - 1 - tail]\n ) tail++\n for (let i = 0; i < start; i++) rows.push({ sign: ' ', text: oldLines[i] as string })\n const oldMiddle = oldLines.slice(start, oldLines.length - tail)\n const newMiddle = newLines.slice(start, newLines.length - tail)\n if (oldMiddle.length > DIFF_LCS_LIMIT || newMiddle.length > DIFF_LCS_LIMIT) {\n for (const text of oldMiddle) rows.push({ sign: '-', text })\n for (const text of newMiddle) rows.push({ sign: '+', text })\n } else {\n rows.push(...lcsRows(oldMiddle, newMiddle))\n }\n for (let i = tail; i > 0; i--) rows.push({ sign: ' ', text: oldLines[oldLines.length - i] as string })\n return rows\n}\n\n/** LCS line alignment of two short blocks (DP table, bounded by the caller). */\nfunction lcsRows(oldLines: readonly string[], newLines: readonly string[]): DiffRow[] {\n const rows = oldLines.length\n const cols = newLines.length\n const table: number[][] = Array.from({ length: rows + 1 }, () => new Array<number>(cols + 1).fill(0))\n for (let i = rows - 1; i >= 0; i--) {\n for (let j = cols - 1; j >= 0; j--) {\n const row = table[i] as number[]\n const below = table[i + 1] as number[]\n row[j] = oldLines[i] === newLines[j]\n ? (below[j + 1] as number) + 1\n : Math.max(below[j] as number, row[j + 1] as number)\n }\n }\n const out: DiffRow[] = []\n let i = 0\n let j = 0\n while (i < rows && j < cols) {\n if (oldLines[i] === newLines[j]) {\n out.push({ sign: ' ', text: oldLines[i] as string })\n i++\n j++\n } else if (((table[i + 1] as number[])[j] as number) >= ((table[i] as number[])[j + 1] as number)) {\n out.push({ sign: '-', text: oldLines[i] as string })\n i++\n } else {\n out.push({ sign: '+', text: newLines[j] as string })\n j++\n }\n }\n while (i < rows) {\n out.push({ sign: '-', text: oldLines[i] as string })\n i++\n }\n while (j < cols) {\n out.push({ sign: '+', text: newLines[j] as string })\n j++\n }\n return out\n}\n\n/** Inline `code` and **bold** spans; everything else stays literal text. */\nfunction inlineNodes(text: string): ReactNode[] {\n const out: ReactNode[] = []\n const pattern = /(`[^`\\n]+`)|(\\*\\*[^*\\n]+\\*\\*)/g\n let last = 0\n let match: RegExpExecArray | null\n let key = 0\n while ((match = pattern.exec(text)) !== null) {\n if (match.index > last) out.push(text.slice(last, match.index))\n const token = match[0]\n key++\n if (token.startsWith('`')) out.push(<code key={key} className=\"ntr-acp-inline-code\">{token.slice(1, -1)}</code>)\n else out.push(<strong key={key}>{token.slice(2, -2)}</strong>)\n last = match.index + token.length\n }\n if (last < text.length) out.push(text.slice(last))\n return out\n}\n\n/** Fenced-code-aware text body: agent prose without a markdown dependency. */\nfunction RichText({ text }: { text: string }) {\n const parts = text.split('```')\n return (\n <>\n {parts.map((part, index) => (index % 2 === 1\n ? <pre key={index} className=\"ntr-acp-code\">{part.replace(/^[a-zA-Z0-9_-]*\\n/, '')}</pre>\n : <span key={index}>{inlineNodes(part)}</span>))}\n </>\n )\n}\n\n/**\n * Whether one structured edit is a NEW FILE.\n *\n * Decided by the old side, never by the removal count: an existing file that\n * only gained lines also diffs to zero removals, and labelling that \"new file\"\n * hid a real edit behind a wrong badge (the audit's F8). The only old sides\n * that mean \"nothing was there\" are an absent one and one byte-identical to\n * the new content — the degenerate case where there is no edit at all.\n */\nexport function isNewFileDiff(oldText: string | null, newText: string): boolean {\n return oldText === null || oldText === '' || oldText === newText\n}\n\n/** One structured edit, rendered as a unified diff. */\nfunction DiffView({ diff, t }: { diff: { path: string; oldText: string | null; newText: string }; t: Translate }) {\n const rows = diffRows(diff.oldText, diff.newText)\n const added = rows.filter(row => row.sign === '+').length\n const removed = rows.filter(row => row.sign === '-').length\n const isNewFile = isNewFileDiff(diff.oldText, diff.newText)\n return (\n <div className=\"ntr-acp-diff\">\n <div className=\"ntr-acp-diff-head\">\n <span className=\"ntr-acp-diff-path\">{diff.path}</span>\n <span className=\"ntr-acp-diff-stats\">\n <span className=\"is-add\">+{added}</span> <span className=\"is-del\">-{removed}</span>\n </span>\n </div>\n <div className=\"ntr-acp-diff-body\">\n {rows.map((row, index) => (\n <div key={index} className={`ntr-acp-diff-row is-${row.sign === '+' ? 'add' : row.sign === '-' ? 'del' : 'ctx'}`}>\n <span className=\"ntr-acp-diff-sign\">{row.sign}</span>\n <span className=\"ntr-acp-diff-text\">{row.text}</span>\n </div>\n ))}\n </div>\n {isNewFile && <div className=\"ntr-acp-diff-note\">{t('新增文件', 'new file')}</div>}\n </div>\n )\n}\n\n/** One tool call: header, arguments, text output, unknown payloads, and any structured edits it carried. */\nfunction ToolCard({ block, t }: { block: Extract<Block, { kind: 'tool' }>; t: Translate }) {\n const [expanded, setExpanded] = useState(false)\n const [rawOpen, setRawOpen] = useState(false)\n const glyph = TOOL_GLYPH[block.toolKind ?? 'other'] ?? TOOL_GLYPH.other\n const tone = STATUS_TONE[block.status] ?? 'idle'\n const clipped = !expanded && block.lines.length > TOOL_LINES_CLIP\n const shown = clipped ? block.lines.slice(0, TOOL_LINES_CLIP) : block.lines\n return (\n <div className={`ntr-acp-tool is-${tone}`}>\n <div className=\"ntr-acp-tool-head\">\n <span aria-hidden>{glyph}</span>\n <span className=\"ntr-acp-tool-title\">{block.title === '' ? block.toolCallId : block.title}</span>\n <span className={`ntr-acp-badge is-${tone}`}>{block.status}</span>\n </div>\n {/*\n The arguments the agent sent. Rendered as-is: `rawInput` may be a JSON\n object the agent serialized, a plain command line, or nothing at all —\n reformatting it would be this view guessing at another agent's schema.\n */}\n {block.input !== undefined && block.input !== '' && (\n <pre className=\"ntr-acp-tool-body ntr-acp-tool-input\">{block.input}</pre>\n )}\n {block.details !== undefined && block.details.length > 0 && (\n <pre className=\"ntr-acp-tool-body ntr-acp-tool-meta\">{block.details.join('\\n')}</pre>\n )}\n {shown.length > 0 && (\n <pre className=\"ntr-acp-tool-body\">{`${shown.join('\\n')}${clipped ? '\\n…' : ''}`}</pre>\n )}\n {block.extras !== undefined && block.extras.length > 0 && (\n <pre className=\"ntr-acp-tool-body\">{block.extras.join('\\n')}</pre>\n )}\n {block.diffs?.map((diff, index) => <DiffView key={`${diff.path}#${String(index)}`} diff={diff} t={t} />)}\n {block.lines.length > TOOL_LINES_CLIP && (\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { setExpanded(!expanded) }}>\n {expanded\n ? t('收起', 'collapse')\n : t(`显示全部 ${block.lines.length} 行`, `show all ${block.lines.length} lines`)}\n </button>\n )}\n {block.raw !== undefined && (\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { setRawOpen(!rawOpen) }}>\n {rawOpen ? t('收起原始数据', 'hide raw payload') : t('原始数据', 'raw payload')}\n </button>\n )}\n {rawOpen && block.raw !== undefined && <pre className=\"ntr-acp-tool-body\">{block.raw}</pre>}\n </div>\n )\n}\n\n/** One notification the view has no card of its own for: its readable fold, plus the untouched payload. */\nfunction EventCard({ block, t }: { block: Extract<Block, { kind: 'event' }>; t: Translate }) {\n const [rawOpen, setRawOpen] = useState(false)\n return (\n <div className=\"ntr-acp-tool is-done\">\n <div className=\"ntr-acp-tool-head\">\n <span aria-hidden>ℹ️</span>\n <span className=\"ntr-acp-tool-title\">{block.event}</span>\n </div>\n {block.text !== '' && <pre className=\"ntr-acp-tool-body\">{block.text}</pre>}\n {block.raw !== undefined && (\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { setRawOpen(!rawOpen) }}>\n {rawOpen ? t('收起原始数据', 'hide raw payload') : t('原始数据', 'raw payload')}\n </button>\n )}\n {rawOpen && block.raw !== undefined && <pre className=\"ntr-acp-tool-body\">{block.raw}</pre>}\n </div>\n )\n}\n\n/** One plan snapshot (the agent's own todo list). */\nfunction PlanCard({ entries, t }: { entries: Extract<Block, { kind: 'plan' }>['entries']; t: Translate }) {\n const done = entries.filter(entry => entry.status === 'completed').length\n return (\n <div className=\"ntr-acp-plan\">\n <div className=\"ntr-acp-plan-head\">{t(`计划 ${done}/${entries.length}`, `Plan ${done}/${entries.length}`)}</div>\n {entries.map((entry, index) => (\n <div key={index} className={`ntr-acp-plan-row is-${entry.status}`}>\n <span className=\"ntr-acp-plan-glyph\" aria-hidden>{entry.status === 'completed' ? '✓' : entry.status === 'in_progress' ? '▸' : '○'}</span>\n <span>{entry.content}</span>\n {entry.priority !== undefined && <span className=\"ntr-acp-plan-prio\">{entry.priority}</span>}\n </div>\n ))}\n </div>\n )\n}\n\n/**\n * One stream block, dispatched by kind. Memoized on its props — `block`, the\n * store's instance-bound `label`, and `localeRev` (it moves on a language\n * switch, so translated copy re-renders) — so a folded batch re-renders only\n * the rows whose block object changed (`foldUpdate` replaces blocks immutably)\n * instead of the whole transcript on every store notification. `key` stays the\n * index on purpose: blocks fold append-only, and identity is the block object.\n */\nconst BlockView = memo(function BlockView({ block, label }: { block: Block; label: Translate; localeRev: number }) {\n const t = label\n switch (block.kind) {\n case 'user':\n return <div className=\"ntr-acp-bubble is-user\"><RichText text={block.text} /></div>\n case 'agent':\n return <div className=\"ntr-acp-bubble is-agent\"><RichText text={block.text} /></div>\n case 'thought':\n return (\n <details className=\"ntr-acp-thought\">\n <summary>{t('思考过程', 'reasoning')}</summary>\n <div className=\"ntr-acp-thought-body\"><RichText text={block.text} /></div>\n </details>\n )\n case 'tool':\n return <ToolCard block={block} t={t} />\n case 'plan':\n return <PlanCard entries={block.entries} t={t} />\n case 'event':\n return <EventCard block={block} t={t} />\n default:\n return <div className={`ntr-acp-notice is-${block.tone}`}>{block.text}</div>\n }\n})\n\n/** The bound ACP session for this chat, preferring the live one. */\nfunction boundSession(list: readonly ChatSessionView[]): ChatSessionView | undefined {\n return list.find(session => !session.released) ?? list[0]\n}\n\n/**\n * Everything the view can read that says \"this conversation is set up on an\n * agent\": a live or parked ACP session, the host's binding (`session/chat-list`'s\n * `boundAgentId`, which outlives a closed session), and the persisted pick.\n *\n * The transcript is deliberately NOT one of these sources: it is a record of\n * turns, and it is dropped when a session is destroyed, so reading it alone\n * answered \"has an agent ever served this conversation\" with \"no\" for\n * conversations that had just run one.\n */\nexport function boundAgentOf(\n session: ChatSessionView | undefined,\n hostBoundAgentId: string | undefined,\n pickedAgentId: string | undefined,\n agents: readonly AgentView[],\n): { id: string; title: string } | undefined {\n const id = session?.agentId ?? hostBoundAgentId ?? pickedAgentId\n if (id === undefined) return undefined\n return { id, title: session?.agentTitle ?? agents.find(agent => agent.id === id)?.title ?? id }\n}\n\n/** What the view knows about this conversation when it has no transcript. */\nexport interface AcpEmptyFacts {\n /** The agent this conversation is set up on, when there is one. */\n bound?: { id: string; title: string }\n /** Whether any agent is configured at all. */\n hasAgents: boolean\n /**\n * This conversation's NTR-mode verdict, as the seat gate reads it\n * (`UiState.acpMode`). `undefined` while the host has not answered: the view\n * must not claim either way.\n */\n acpMode?: boolean\n}\n\n/** The three lines of the empty state, already localized. */\nexport interface AcpEmptyCopy {\n title: string\n body: string\n /** The \"what to do next\" line; absent when nothing honest can be said. */\n hint?: string\n}\n\n/**\n * The empty state's copy, decided from the facts the view can actually read.\n *\n * A pure function on purpose: the bundle has no DOM test, and this decision is\n * where two real defects lived. `chatTranscriptOf` answering `undefined` covers\n * TWO different states — \"this conversation has never been served by an NTR\n * agent\" and \"its ACP session record is gone\" (a closed session is destroyed by\n * `closeSession`/`forgetDshSession`, a stopped agent drops its bridge) while the\n * binding and the composer chip still name the agent. Claiming the first state\n * in both cases made the view contradict the chip sitting one row above it.\n *\n * The hint has its own defect: it pointed at the composer's NTR base, which only\n * the ACP-mode gate installs (`syncAcpMode` in src/client/index.tsx) — in an\n * ordinary conversation that cell still holds the host's own model selector, so\n * the hint sent the user to a control that is not there.\n */\nexport function acpEmptyCopy(facts: AcpEmptyFacts, t: Translate): AcpEmptyCopy {\n const bound = facts.bound\n if (bound !== undefined) {\n return {\n title: t('这个会话当前没有可显示的 ACP 会话', 'This conversation has no ACP session to show right now'),\n body: t(\n '给它发一条消息,它就会开始服务这个会话:原生聊天里会出现它的文字回复与思考过程,而它的工具调用、计划和 diff 会完整地出现在这个视图里。',\n 'Send it a message and it starts serving this conversation: the native chat shows its prose and reasoning, while its tool calls, plans and diffs appear here in full.',\n ),\n hint: `${t('绑定的是', 'bound to')} · ${bound.title}`,\n }\n }\n const copy: AcpEmptyCopy = {\n title: t('这个会话还没有 NTR agent 参与', 'No NTR agent has run in this conversation yet'),\n body: t(\n '选中一个 NTR agent 后,本会话的对话就由它服务:原生聊天里会出现它的文字回复与思考过程,而它的工具调用、计划和 diff 会完整地出现在这个视图里。',\n \"Pick a NTR agent and it serves this conversation: the native chat shows its prose and reasoning, while its tool calls, plans and diffs appear here in full.\",\n ),\n }\n if (!facts.hasAgents) {\n return { ...copy, hint: `${t('还没有配置 agent', 'No agent configured')} · ${t('设置 → NTR', 'Settings → NTR')}` }\n }\n // The base is only on screen in NTR mode; naming it anywhere else points at\n // the host's model selector. A host that has not answered yet gets no line\n // rather than a guess (the seat diagnostics below still say what they saw).\n if (facts.acpMode === true) {\n return { ...copy, hint: t('用输入框右侧的 NTR 底座选择一个 agent', 'pick one from the NTR base in the composer') }\n }\n if (facts.acpMode === false) {\n return {\n ...copy,\n hint: `${t('这个会话不在 NTR 模式', 'This conversation is not in NTR mode')} · ${t('新建会话时可以选择 NTR 模式', 'NTR is chosen when a conversation starts')}`,\n }\n }\n return copy\n}\n\n/**\n * The ACP view tab body: who serves this conversation and how it is doing,\n * then the agent's own stream.\n */\nexport function AcpConversationView(props: AcpViewProps) {\n ensureAcpStyle()\n const { acp, sessionId } = props\n const t: Translate = (zh, en) => acp.label(zh, en)\n const ui = useUi(acp)\n const bound = ui.chatSessionsFor === sessionId ? boundSession(ui.chatSessions) : undefined\n const transcript: ChatTranscript | undefined = acp.chatTranscriptOf(sessionId)\n // Who this conversation is set up on — readable with no transcript at all,\n // which is the whole point: the transcript is a record of turns, the binding\n // is the conversation's own state.\n const boundAgent = boundAgentOf(bound, ui.chatBoundAgent[sessionId], ui.chatChoice[sessionId]?.agentId, ui.agents)\n const streamRef = useRef<HTMLDivElement>(null)\n const pinned = useRef(true)\n\n // Observe this chat session's ACP transcripts whenever the binding could\n // have changed (first turn, agent switch, crash → resume).\n useEffect(() => {\n void acp.loadChatSessions(sessionId)\n void acp.loadChatTranscript(sessionId)\n }, [acp, sessionId, bound?.sessionId, bound?.released])\n\n const blockCount = transcript?.blocks.length ?? 0\n const tail = transcript?.blocks[blockCount - 1]\n useEffect(() => {\n const node = streamRef.current\n if (node === null || !pinned.current) return\n node.scrollTop = node.scrollHeight\n }, [blockCount, tail])\n\n if (transcript === undefined) {\n // `ui.acpMode[sessionId]` is `undefined` until the host has answered for\n // this conversation; the copy builder reads that as \"unknown\" and says\n // nothing rather than guessing.\n const empty = acpEmptyCopy({\n bound: boundAgent,\n hasAgents: ui.agents.length > 0,\n acpMode: ui.acpMode[sessionId],\n }, t)\n return (\n <div className=\"ntr-acp-view\">\n <div className=\"ntr-acp-empty\">\n <div className=\"ntr-acp-empty-title\">{empty.title}</div>\n <p className=\"ntr-acp-empty-body\">{empty.body}</p>\n {empty.hint !== undefined && <div className=\"ntr-acp-empty-hint\">{empty.hint}</div>}\n {/*\n The model-seat gate's last verdict. When the ACP base does not take\n over the composer's model seat — the native model selector is still\n sitting there — the four possible reasons are otherwise\n indistinguishable, because they all end with the same visible state.\n Publishing the reason here turns \"the base still is not replaced\"\n into something readable.\n */}\n {ui.seatDiag !== 'idle' && ui.seatDiag !== 'gate disposed' && (\n <div className=\"ntr-acp-empty-hint ntr-acp-seat-diag\">\n {t('模型座位', 'model seat')} · {ui.seatDiag}\n </div>\n )}\n {ui.permissionSeatDiag !== 'idle' && ui.permissionSeatDiag !== 'gate disposed' && (\n <div className=\"ntr-acp-empty-hint ntr-acp-seat-diag\">\n {t('权限底座', 'permission seat')} · {ui.permissionSeatDiag}\n </div>\n )}\n {/*\n Slash isolation degrades open (an unrecognized host source shape\n leaves the host's commands in charge). That is the safe failure,\n but it looks exactly like the feature never having existed — the\n menu simply lists DSH's commands again. Say so.\n */}\n {ui.slashDiag !== 'idle' && (\n <div className=\"ntr-acp-empty-hint ntr-acp-seat-diag\">\n {t('斜杠菜单', 'slash menu')} · {ui.slashDiag}\n </div>\n )}\n </div>\n </div>\n )\n }\n\n const session = transcript.session\n const usage = session.usage\n const agentTitle = ui.agents.find(agent => agent.id === session.agentId)?.title ?? bound?.agentTitle ?? session.agentId\n const health = session.released\n ? { text: t('已挂起 · 下一条消息自动恢复', 'paused · resumes on your next message'), tone: 'warn' }\n : session.closed\n ? { text: t('已关闭', 'closed'), tone: 'idle' }\n : session.running\n ? { text: t('运行中', 'running'), tone: 'busy' }\n : { text: t('空闲', 'idle'), tone: 'done' }\n\n return (\n <div className=\"ntr-acp-view\">\n <div className=\"ntr-acp-view-head\">\n <span className={`ntr-acp-badge is-${health.tone}`}>{health.text}</span>\n <span className=\"ntr-acp-view-agent\">{agentTitle}</span>\n <span className=\"ntr-acp-view-meta\" title={session.cwd}>{session.cwd}</span>\n {usage !== undefined && <span className=\"ntr-acp-view-meta\">{usage.used}/{usage.size}</span>}\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { void acp.loadChatTranscript(sessionId) }}>\n {t('刷新', 'refresh')}\n </button>\n </div>\n <div\n className=\"ntr-acp-stream\"\n ref={streamRef}\n onScroll={event => {\n const node = event.currentTarget\n pinned.current = node.scrollHeight - node.scrollTop - node.clientHeight < 40\n }}\n >\n {transcript.blocks.length === 0\n ? <div className=\"ntr-acp-empty-hint\">{t('还没有消息:在下面的输入框里发给它。', 'Nothing yet — send it a message below.')}</div>\n : transcript.blocks.map((block, index) => <BlockView key={index} block={block} label={acp.label} localeRev={ui.localeRev} />)}\n </div>\n </div>\n )\n}\n","/**\n * The NTR settings dialog, opened from the agent base's empty-roster menu.\n *\n * WHY A DIALOG OF OUR OWN: the host settings shell owns its open state and\n * exposes no programmatic way in. The sidebar seat that receives `openSettings`\n * (`settings.launcher`) is a single-occupant slot already held by the account\n * menu, and `openSection` is only handed to `settings.onboarding` registrants —\n * so a conversation surface cannot drive the shell's modal. This dialog mounts\n * the SAME `AcpSettingsSection` the shell mounts, on the frame's overlay layer\n * (`shell.overlay`): one settings surface, two entrances, no duplicated\n * configuration logic.\n *\n * The trigger (composer model seat) and this dialog (root overlay) live in\n * unrelated slot trees, so the open state is the module-level store in\n * `acp-ui.tsx` (`openAcpSettingsDialog`).\n */\n\nimport { useEffect } from 'react'\nimport { AcpSettingsSection } from './panel.tsx'\nimport {\n closeAcpSettingsDialog,\n ensureAcpStyle,\n useAcpSettingsDialogOpen,\n} from './acp-ui.tsx'\nimport type { AcpStore } from './store.ts'\n\n/** The root overlay entry's inject face. */\nexport interface AcpSettingsDialogProps {\n acp: AcpStore\n}\n\n/** Frame-wide NTR settings surface; renders nothing while closed. */\nexport function AcpSettingsDialog(props: AcpSettingsDialogProps) {\n ensureAcpStyle()\n const { acp } = props\n const open = useAcpSettingsDialogOpen()\n useEffect(() => {\n if (!open) return\n const onKey = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n event.preventDefault()\n event.stopPropagation()\n closeAcpSettingsDialog()\n }\n document.addEventListener('keydown', onKey, true)\n return () => { document.removeEventListener('keydown', onKey, true) }\n }, [open])\n if (!open) return null\n const title = acp.label('NTR 设置', 'NTR settings')\n return (\n <div className=\"ntr-acp-dialog-layer\">\n <div className=\"ntr-acp-dialog-mask\" aria-hidden onClick={closeAcpSettingsDialog} />\n <div className=\"ntr-acp-dialog\" role=\"dialog\" aria-modal=\"true\" aria-label={title}>\n <div className=\"ntr-acp-dialog-head\">\n <span className=\"ntr-acp-dialog-title\">{title}</span>\n <button\n type=\"button\"\n className=\"ntr-acp-dialog-close\"\n title={acp.label('关闭', 'Close')}\n aria-label={acp.label('关闭 NTR 设置', 'Close NTR settings')}\n autoFocus\n onClick={closeAcpSettingsDialog}\n >\n ✕\n </button>\n </div>\n <div className=\"ntr-acp-dialog-body\">\n <AcpSettingsSection acp={acp} />\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Pending ACP permission asks for the staged DSH conversation, rendered as\n * composer-width entries above the composer card (`conversation.input.dock`).\n * The slot itself spans the conversation column; the dock mirrors the host's\n * InputBar width formula so the cards line up with the input box.\n *\n * The host broadcasts every ask as `permission/request`, carrying the DSH chat\n * session id when the ask belongs to a conversation. This dock is the primary\n * answer surface: it renders exactly the asks whose `dshSessionId` is the\n * conversation it was mounted for, and nothing otherwise. There is no\n * ACP-mode gate — the frame set itself is the gate, so an ordinary\n * conversation shows this card if and only if it actually has an ask.\n *\n * The ask's description and options are the agent's own: buttons appear in the\n * agent's order with the agent's names (including \"Always Allow\"), and\n * `kind` is not rendered. A separate cancel button answers with no option,\n * which the host forwards as `cancelled`. No Enter/Escape handlers: the\n * composer owns its keyboard, and these are ordinary focusable buttons.\n *\n * Multiple asks stack in ARRIVAL order, oldest first: the question ignored\n * longest stays at the top. The list is capped in height by CSS and scrolls.\n * Asks without a chat identity never appear here — they stay in the\n * settings-panel ledger, the cross-session mirror.\n */\n\nimport { useEffect, useState } from 'react'\nimport { ensureAcpStyle, useUi } from './acp-ui.tsx'\nimport {\n deadlineRemainingLabel,\n PERMISSION_DEADLINE_EXPIRED,\n permissionsForSession,\n} from './store.ts'\nimport type { AcpStore, PendingPermission } from './store.ts'\n\nexport interface PermissionDockProps {\n /** Injected face: the store plus the chat session id this dock belongs to. */\n acp: AcpStore\n sessionId: string\n}\n\n/** Tick interval for the countdown (seconds are all the label shows). */\nconst COUNTDOWN_TICK_MS = 1000\n\nexport function PermissionDock({ acp, sessionId }: PermissionDockProps) {\n ensureAcpStyle()\n const ui = useUi(acp)\n const dshSessionId = String(sessionId)\n const pending = permissionsForSession(ui.permissions, dshSessionId)\n const counting = pending.some(ask => ask.timeoutMode !== 'wait' && ask.deadlineAt !== undefined)\n const [now, setNow] = useState(() => Date.now())\n useEffect(() => {\n if (!counting) return\n setNow(Date.now())\n const timer = setInterval(() => { setNow(Date.now()) }, COUNTDOWN_TICK_MS)\n return () => { clearInterval(timer) }\n }, [counting])\n if (pending.length === 0) return null\n return (\n <div\n className=\"ntr-permission-dock\"\n role=\"region\"\n aria-label={acp.label('待处理的权限请求', 'Pending permission requests')}\n >\n {pending.map(permission => (\n <PermissionAskCard key={permission.requestId} acp={acp} permission={permission} now={now} />\n ))}\n </div>\n )\n}\n\nfunction PermissionAskCard(props: { acp: AcpStore; permission: PendingPermission; now: number }) {\n const { acp, permission, now } = props\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string>()\n const remaining = deadlineRemainingLabel(permission.deadlineAt, now, permission.timeoutMode)\n const expired = remaining === PERMISSION_DEADLINE_EXPIRED\n const respond = (optionId?: string): void => {\n if (busy) return\n setBusy(true)\n setError(undefined)\n void acp.respondPermission(permission.requestId, optionId)\n // `respondPermission` reports to the store's notice too, but that notice\n // lives on the settings page; this card pins the failure where the user\n // is looking.\n .catch((failure: unknown) => {\n setError(failure instanceof Error ? failure.message : String(failure))\n })\n .finally(() => { setBusy(false) })\n }\n return (\n <div className=\"ntr-permission-card\">\n <div className=\"ntr-permission-head\">\n <span className=\"ntr-permission-tool\">{permission.toolName}</span>\n {permission.reason !== undefined && (\n <span className=\"ntr-permission-reason\" title={permission.reason}>{permission.reason}</span>\n )}\n {remaining !== undefined && (\n <span className={`ntr-permission-deadline${expired ? ' is-expired' : ''}`}>\n {expired\n ? acp.label('已超时', 'expired')\n : acp.label(`剩余 ${remaining}`, `${remaining} left`)}\n </span>\n )}\n </div>\n <div className=\"ntr-permission-actions\">\n {permission.options.map(option => (\n <button\n key={option.optionId}\n type=\"button\"\n className=\"ntr-permission-option\"\n disabled={busy}\n title={option.name}\n onClick={() => { respond(option.optionId) }}\n >\n {option.name}\n </button>\n ))}\n <button\n type=\"button\"\n className=\"ntr-permission-cancel\"\n disabled={busy}\n title={acp.label('取消这次询问(不选择任何选项)', 'Cancel this ask without choosing an option')}\n onClick={() => { respond() }}\n >\n {acp.label('取消', 'Cancel')}\n </button>\n </div>\n {error !== undefined && (\n <div className=\"ntr-permission-error\" role=\"alert\">{error}</div>\n )}\n </div>\n )\n}\n","/**\n * The ACP conversation's claim on two cells of the host's composer bar.\n *\n * Both cells are `single`, session-scoped slots the composer bar renders\n * unconditionally, and both are held by claims this module defines — one that\n * REPLACES what is there (`conversation.input.model`, see\n * {@link createModelSeatClaim}) and one that EMPTIES it\n * (`conversation.input.permission`, see {@link createPermissionSeatClaim}).\n *\n * WHAT IT IS: `conversation.input.model` is a `single` slot the host's composer\n * bar renders unconditionally, in the TRAILING group (`margin-left: auto`),\n * immediately before the activity/stop/submit chrome — which is exactly where\n * the host's own model selector sits. Its shipped occupant is\n * `client-ui-model-selection`'s `ModelSelect`, which reads the DSH model\n * directory and submits through `session.selectModel`, which in turn writes\n * `agentDefaultModel.saveSelection` — the process-wide default. On an ACP\n * conversation none of that describes anything real: the models belong to the\n * external agent (`agent/catalog`), so the seat would list providers this\n * conversation cannot use, and clicking one would poison the default for every\n * other conversation. Third-party seat plugins land in this same cell.\n *\n * SO THE CLAIM PUTS THE ACP BASE THERE. The ACP base (`AcpSessionControls`) is\n * the surface that already describes what this conversation runs on, and the\n * seat it has to displace is the one that would otherwise misdescribe it — one\n * cell, one occupant, and the position the host's own base occupied. The base\n * is installed by the ACP-mode gate in `index.tsx`, which holds the claim only\n * while the staged conversation runs the ACP preset and releases it the moment\n * it does not, so an ordinary conversation gets the native selector back and\n * sees no ACP chrome at all. That gate is the whole isolation story: the entry\n * does not exist outside ACP mode, rather than existing and rendering nothing.\n *\n * WHY A CLAIM AND NOT A `priority: -1` REGISTRATION: the declared rank is NOT\n * what decides this cell on the desktop runtime. The client runner (the facade\n * every bundle — including this one — is loaded through) REWRITES the priority\n * of every non-chain registration with its own page-global counter, which only\n * decreases:\n *\n * // @deepseek-ai/dsh-cordis-client-runner, `guardedSlots`\n * const spec = slots.spec(slot);\n * let priority = options.priority;\n * if (spec === undefined || spec.kind !== \"chain\") {\n * priority = env.allocatePriority(); // `allocatePriority: () => --this.nextPriority`\n * options.priority = priority;\n * }\n *\n * So in a `single` cell (lowest live rank renders) the winner is simply\n * whoever registered LAST — a rival seat plugin that loads a moment after us\n * takes the cell no matter what either side declares. `dsh-thinking-effort`\n * provably does exactly that: it registers a model + reasoning slider into this\n * very cell, and it is a later registration, so it would stay on screen in ACP\n * conversations regardless of any rank we declare (and its own declared `-10`\n * is discarded just the same).\n *\n * The fix is therefore to treat the cell as a claim we VERIFY and, when a rival\n * took it, retake by registering again (a new registration is the lowest rank\n * on the page, which is what wins). That is the whole mechanism: read the\n * renderer's own winner view (`entriesOfSlot`, the same call the renderer's\n * outlet makes), compare it with our own component identities, and re-register\n * when it is not ours — bounded, and honest about giving up. Nothing else from\n * the retired seat machinery comes back: no abdicate recovery, no session-long\n * `seatBroken`, no rank arithmetic as an end in itself.\n *\n * WHAT THE CLAIM MUST CARRY: the occupant's own inject face. That is not an\n * optional extra — this cell is SESSION-scoped, so the renderer gives an entry\n * only the framework's standard kit (`sessionId`, `useSession`, …) unless the\n * entry declares an `inject`, and the ACP base reads `acp` and `faceFor`, both\n * of which arrive ONLY through it. Registering the occupant without one makes\n * it render with `acp === undefined`; the slot's error boundary catches the\n * resulting crash and reports it as an ABDICATION, which retires our entry from\n * the cell for the rest of that registration's life and hands the cell back to\n * the shipped `ModelSelect` — the native seat returns, the ACP base is gone,\n * and the gate never hears about it because `sync()` already verified the cell\n * and reported \"held by the ACP base\". So `inject` is a required option (a\n * compile error at the one call site) instead of an optional one (a silent\n * abdication at runtime). See {@link ModelSeatClaimOptions.inject}.\n *\n * The gate reads the session's own preset (`stagedIsAcpMode` in `acp-ui.tsx`)\n * rather than anything the plugin recorded, so it can never drift from the\n * roster.\n */\n\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n\n/** Exact key of the composer's model seat (a `single`, session-scoped slot). */\nexport const MODEL_SEAT_KEY = 'conversation.input.model'\n\n/**\n * Exact key of the composer's permission seat (a `single`, session-scoped\n * slot): the cell the host's Access mode control occupies. It selects the\n * file-write policy the HOST applies to its own tools, read from the\n * `permissions` projection `dsh-permission-presets` owns. An ACP conversation's\n * writes belong to the external agent's process, which never consults that\n * policy, so there is nothing to put in this cell in ACP mode — which is why\n * {@link createPermissionSeatClaim} installs a `null`-rendering occupant rather\n * than a replacement control, and why its lifetime must be exactly the ACP-mode\n * verdict's lifetime (see the claim's own note in `index.tsx`).\n */\nexport const PERMISSION_SEAT_KEY = 'conversation.input.permission'\n\n/**\n * Declared rank of the first occupant we install.\n *\n * Honoured only by a registry that keeps what we declare (a statically composed\n * client, or a future runner that stops rewriting non-chain priorities): it\n * displaces a shipped occupant registering at the default rank 0. Under the\n * desktop runner the rank is overwritten with its own counter — see the module\n * header — which is why the claim verifies ownership instead of trusting this.\n */\nexport const MODEL_SEAT_PRIORITY = -1\n\n/**\n * Rank of the first occupant the permission claim installs. Same meaning (and\n * same caveat) as {@link MODEL_SEAT_PRIORITY}; see {@link createSeatClaim} for\n * why a declared rank is never trusted as proof of ownership.\n */\nexport const PERMISSION_SEAT_PRIORITY = -1\n\n/**\n * How many times ONE claim may retake the cell before it stops and says so.\n *\n * Each retake is triggered by evidence (the cell's winner is not ours), so a\n * normal page needs at most one; the cap only bounds a rival that registers in\n * a loop, and reaching it is reported rather than hidden.\n */\nexport const MAX_SEAT_CLAIMS = 4\n\n/** One live entry in the cell, narrowed to what the claim reads. */\nexport interface SeatEntry {\n component?: unknown\n options?: { priority?: number; registrant?: string }\n}\n\n/** The slot-registry face this module uses (structural: the registry is not a dependency of this bundle). */\nexport interface SeatSlots {\n inject(key: string, callback: () => (() => void) | Iterable<() => void>): () => void\n /** Returns the entry's disposer (the registry hangs it on the caller's fiber too). */\n register(options: SeatRegisterOptions, component: unknown): () => void\n /** The renderer's winner view per cell; absent on a runtime that does not expose it. */\n entriesOfSlot?(key: string): readonly SeatEntry[]\n /** The declaration probe; absent on a runtime that does not expose it. */\n spec?(key: string): { kind?: string } | undefined\n}\n\n/**\n * One registration's options, narrowed to what the claim passes. `inject` is\n * NOT optional in practice — see {@link ModelSeatClaimOptions.inject}.\n */\nexport interface SeatRegisterOptions {\n name: string\n priority?: number\n /**\n * The session-scoped face factory the renderer calls with the scope binding's\n * key (and the store's actions) before rendering, merging its result into the\n * component's props. A session-scoped occupant that renders conversation\n * chrome without one arrives with only the standard kit — no `acp`, no\n * `faceFor` — and crashes on its first render.\n */\n inject?(sessionId: unknown, actions?: unknown): unknown\n}\n\nexport interface ModelSeatClaimOptions {\n slots: SeatSlots\n /**\n * The occupant to install into the cell — the ACP base for an ACP-mode\n * conversation. Called once per installed entry and receives whatever props\n * the seat frame hands an entry (the injected face plus the standard kit),\n * so it must be the component itself and not a bound closure over one\n * session's props.\n */\n occupant(): unknown\n /**\n * The inject face every installed entry declares — REQUIRED, and the reason\n * is a failure mode that is silent by construction.\n *\n * `conversation.input.model` is a SESSION-scoped slot. The renderer composes\n * an entry's props as `{ ...standardKit, ...entryInject(sessionId, actions),\n * ...slotInject, ...ownerProps }` (`SessionEntry` → `renderEntry`), so the\n * standard kit alone carries only the framework's own shares (`sessionId`,\n * `useSession`, …). Everything this plugin's occupant reads beyond that —\n * `acp`, `faceFor` — arrives ONLY through the entry's own `inject`.\n *\n * Register without it and the occupant renders with `acp === undefined`. The\n * crash is caught by the slot's error boundary, which reports it as an\n * ABDICATION: the registry retires our entry from its cell for the rest of\n * that registration's life, and the outlet re-projects onto the cell's next\n * survivor — the shipped `ModelSelect`. The native seat is back, the ACP base\n * is gone, and nothing on the gate's side moves: the abdication happens\n * during React's render phase, after `sync()` already verified the cell and\n * reported \"held by the ACP base\". That is exactly how the base went missing\n * with the diagnostic still claiming it was installed.\n *\n * So this is a required option rather than an optional one: the omission is\n * then a compile error at the single call site instead of a runtime\n * abdication nobody can see.\n */\n inject(sessionId: unknown, actions?: unknown): unknown\n /** Every evaluation reports its verdict here (the gate forwards it to the ACP view's diagnostic line). */\n onDiag?(line: string): void\n maxClaims?: number\n}\n\n/**\n * Options for the permission claim. Unlike {@link ModelSeatClaimOptions}, both\n * the occupant and the inject face are OPTIONAL: this claim exists to hold an\n * EMPTY cell, so the defaults express what it is — nothing rendered, nothing\n * injected — and a caller only passes them to customize (the tests do).\n *\n * The default inject still matters: the cell is session-scoped, and an entry\n * without any inject receives only the framework's standard kit. A `null`\n * occupant reads none of it, so an omitted inject is harmless here — whereas\n * omitting one on the model seat is a silent abdication. The asymmetry is\n * deliberate: required where silence hides a failure, defaulted where silence\n * is the correct answer.\n */\nexport interface PermissionSeatClaimOptions {\n slots: SeatSlots\n /** Optional custom occupant; defaults to a component rendering null. */\n occupant?(): unknown\n /** Optional inject factory; defaults to providing an empty object `{}`. */\n inject?(sessionId: unknown, actions?: unknown): unknown\n /** Every evaluation reports its verdict here (the gate forwards it to the ACP view's diagnostic line). */\n onDiag?(line: string): void\n maxClaims?: number\n}\n\nexport interface SeatClaimOptions {\n slots: SeatSlots\n slotKey: string\n basePriority: number\n occupant(): unknown\n inject?(sessionId: unknown, actions?: unknown): unknown\n label?: string\n onDiag?(line: string): void\n maxClaims?: number\n}\n\nexport interface ModelSeatClaim {\n /** Hold or retake the cell; idempotent, so it can run on every gate evaluation. */\n sync(): void\n /** Whether the last verified observation found our entry winning the cell. */\n readonly holding: boolean\n /** Release every entry this claim installed (the gate's stand-down). */\n release(): void\n}\n\n/** Where the cell stood, as far as this bundle can tell. */\ninterface CellView {\n /**\n * `false` while the composer bar has not mounted the seat. `inject` WAITS\n * for the declaration instead of failing, so an undeclared seat and a seat\n * held by someone else both read as \"the winner is not ours\" through the\n * winner view alone — and only one of them justifies a retake.\n */\n declared: boolean\n /** `false` when the runtime exposes no winner view — ownership cannot be verified there. */\n verified: boolean\n entry?: SeatEntry\n}\n\n/**\n * Core generic slot claim engine.\n *\n * Installs and holds an occupant into a single-occupant slot, verifying via\n * `entriesOfSlot` and performing bounded retakes if displaced by later registrations.\n */\nexport function createSeatClaim(options: SeatClaimOptions): ModelSeatClaim {\n const { slots, slotKey, basePriority, occupant, inject } = options\n const label = options.label ?? slotKey\n const onDiag = options.onDiag\n const maxClaims = options.maxClaims ?? MAX_SEAT_CLAIMS\n const ours = new Set<unknown>()\n const declared = new Set<number>()\n const installed: Array<() => void> = []\n let retakes = 0\n let holding = false\n\n const say = (line: string): void => onDiag?.(line)\n\n const describe = (entry: SeatEntry | undefined): string => {\n if (entry === undefined) return 'no occupant'\n const who = entry.options?.registrant\n const rank = entry.options?.priority\n if (who === undefined && rank === undefined) return 'another entry'\n return `${who ?? 'an unnamed entry'} (rank ${rank ?? 0})`\n }\n\n const cell = (): CellView => {\n let isDeclared = true\n if (typeof slots.spec === 'function') {\n try { isDeclared = slots.spec(slotKey) !== undefined } catch { isDeclared = false }\n }\n if (typeof slots.entriesOfSlot !== 'function') return { declared: isDeclared, verified: false }\n try {\n const heads = slots.entriesOfSlot(slotKey) ?? []\n return { declared: isDeclared, verified: true, ...heads[0] === undefined ? {} : { entry: heads[0] } }\n } catch {\n return { declared: isDeclared, verified: false }\n }\n }\n\n const install = (head: SeatEntry | undefined): boolean => {\n const base = installed.length === 0 ? basePriority : basePriority - 1 - retakes\n let rank = Math.min((head?.options?.priority ?? 0) - 1, base)\n while (declared.has(rank)) rank -= 1\n const component = (props: unknown): unknown => {\n const target = occupant() as (p: unknown) => unknown\n return target(props)\n }\n ours.add(component)\n declared.add(rank)\n try {\n installed.push(slots.inject(slotKey, () => slots.register({\n name: slotKey,\n priority: rank,\n ...(inject ? { inject } : {}),\n }, component)))\n return true\n } catch (error) {\n ours.delete(component)\n declared.delete(rank)\n say(`${label}: registration failed (${error instanceof Error ? error.message : String(error)})`)\n return false\n }\n }\n\n return {\n get holding(): boolean { return holding },\n sync(): void {\n if (installed.length === 0 && !install(undefined)) return\n const view = cell()\n if (!view.declared) {\n holding = false\n say(`${slotKey}: waiting for the host to declare the seat`)\n return\n }\n if (!view.verified) {\n holding = true\n say(`${slotKey}: occupied (ownership is not verifiable on this runtime)`)\n return\n }\n if (ours.has(view.entry?.component)) {\n holding = true\n say(`${slotKey}: held by the ACP base${retakes === 0 ? '' : ` (retook it ${retakes}×)`}`)\n return\n }\n const incumbent = view\n if (retakes >= maxClaims) {\n holding = false\n say(`${slotKey}: taken by ${describe(incumbent.entry)} — gave up after ${maxClaims} retakes`)\n return\n }\n retakes += 1\n if (!install(incumbent.entry)) return\n const settled = cell()\n if (settled.verified && ours.has(settled.entry?.component)) {\n holding = true\n say(`${slotKey}: retook it from ${describe(incumbent.entry)} (retake ${retakes})`)\n return\n }\n holding = false\n say(`${slotKey}: still held by ${describe(settled.entry ?? incumbent.entry)} after retake ${retakes}`)\n },\n release(): void {\n while (installed.length > 0) {\n try { installed.pop()?.() } catch {}\n }\n ours.clear()\n declared.clear()\n retakes = 0\n holding = false\n },\n }\n}\n\n/**\n * Take and hold the composer's model seat for one ACP-mode spell.\n *\n * Lifecycle: the gate creates a claim when the staged conversation enters ACP\n * mode, calls `sync()` on every evaluation (store mutation, session-list\n * notification, preset frame, and any change to the cell itself), and calls\n * `release()` when the conversation leaves ACP mode or the gate tears down.\n */\nexport function createModelSeatClaim(options: ModelSeatClaimOptions): ModelSeatClaim {\n return createSeatClaim({\n slots: options.slots,\n slotKey: MODEL_SEAT_KEY,\n basePriority: MODEL_SEAT_PRIORITY,\n occupant: options.occupant,\n inject: options.inject,\n label: 'model seat',\n onDiag: options.onDiag,\n maxClaims: options.maxClaims,\n })\n}\n\n/** Null placeholder component for blanking a slot. */\nconst BlankSlotOccupant = (): null => null\n\n/** Empty inject face provider for slots that require an inject definition. */\nconst emptyInject = (): Record<string, never> => ({})\n\n/**\n * Take and hold the composer's permission seat for one ACP-mode spell.\n *\n * Same lifecycle as {@link createModelSeatClaim} — created when the staged\n * conversation enters ACP mode, `sync()`ed on every gate evaluation,\n * `release()`d the instant it leaves — with one difference worth saying out\n * loud: this claim has NO occupant to show for itself. Its entire product is\n * the ABSENCE of the host's Access mode control, and absence is exactly what\n * cannot be inspected after the fact. A permission claim that outlives its\n * reason does not render an obvious wrong thing on an ACP conversation; it\n * silently removes a control from ordinary ones, and every path through the\n * gate that cannot confirm the verdict releases it.\n */\nexport function createPermissionSeatClaim(options: PermissionSeatClaimOptions): ModelSeatClaim {\n return createSeatClaim({\n slots: options.slots,\n slotKey: PERMISSION_SEAT_KEY,\n basePriority: PERMISSION_SEAT_PRIORITY,\n occupant: options.occupant ?? (() => BlankSlotOccupant),\n inject: options.inject ?? emptyInject,\n label: 'permission seat',\n onDiag: options.onDiag,\n maxClaims: options.maxClaims,\n })\n}\n","/**\n * Slash isolation for ACP conversations.\n *\n * In an ACP conversation the composer's `/` menu belongs to the AGENT: the\n * commands it declared over `available_commands_update`. The host's own slash\n * machinery (`dsh-client-ui-commands` plus the client skill source) describes\n * commands THIS runtime would execute — `/model`, `/permission`, `/export`,\n * the DSH skill catalog — and a name it recognizes is consumed before the\n * draft ever reaches the prompt sink, so the agent never sees the line.\n *\n * There is no supported seam for \"this session only\": `InputTriggerService`\n * adjudicates in REGISTRATION order (`order` only sorts the menu), duplicate\n * `(trigger, name)` registration throws, and sources get no per-session\n * availability hook — so a source registered later can never outrank the\n * host's `command` source for one conversation. Replacing the whole composer\n * is the other option and is not worth the price (attachments, queue, draft,\n * shortcuts).\n *\n * What remains is wrapping the LIVE source objects in memory: same\n * registration, same menu group, different answers while the session is in ACP\n * mode. Nothing on disk is patched or imported, so an upstream change cannot\n * make a replacement file go missing — the worst case is that the source no\n * longer looks like what this module knows, and then nothing is wrapped and\n * the conversation keeps the host's behaviour (fail-open; a diagnostic line is\n * reported instead of an error).\n *\n * The wrappers are per CALL, not per instance: the source objects are shared\n * by every conversation, and the session id each hook receives is what tells\n * an ACP conversation from an ordinary one.\n */\n\nimport type { AcpSessionsSnapshot } from './acp-ui.tsx'\nimport { ACP_PRESET_ID, rowAgentPreset, stagedSessionOf } from './acp-ui.tsx'\nimport type { AgentCommand } from './store.ts'\n\n/** One menu row: the ACP command name, plus whatever makes it findable. */\nexport interface SlashCandidate {\n name: string\n label?: string\n description?: string\n hint?: string\n}\n\n/** The only field these hooks receive about the owning conversation. */\nexport interface SlashSession {\n readonly sessionId: string\n}\n\nexport interface SlashCandidateRequest {\n readonly query: string\n /** Leading token vs. a `/` inside running text; hint-bearing rows are leading-only. */\n readonly position?: 'leading' | 'inline'\n readonly signal?: AbortSignal\n}\n\nexport interface SlashPick {\n readonly candidate: SlashCandidate\n readonly session: SlashSession\n}\n\n/**\n * One registered trigger source, narrowed to what isolation touches. Every\n * hook is optional because the contract makes them optional — a source that\n * never participated in Enter adjudication must not start participating just\n * because we wrapped its `candidates`.\n */\nexport interface SlashSource {\n readonly trigger: string\n readonly name: string\n candidates?: (session: SlashSession, req: SlashCandidateRequest) => Promise<readonly SlashCandidate[]>\n matchEnter?: (session: SlashSession, line: string, signal: AbortSignal, envelope: { attachments: number }) => Promise<unknown>\n matchSpace?: (session: SlashSession, token: string) => unknown\n onPick?: (pick: SlashPick) => unknown\n warm?: (session: SlashSession) => void\n lexicon?: (session: SlashSession) => readonly string[] | undefined\n header?: (session: SlashSession, req: { query: string }) => readonly unknown[] | undefined\n}\n\nexport interface SlashIsolationOptions {\n /** The live source registry (`inputTriggers.live.sources`), re-read on every sync. */\n sources(): readonly SlashSource[] | undefined\n /** The ACP verdict this page holds; `undefined` = no answer yet. */\n verdict(sessionId: string): boolean | undefined\n /** Ask the host and wait for the in-flight read, then answer again. */\n ensureVerdict(sessionId: string): Promise<boolean | undefined>\n /**\n * The agent's declared commands for this conversation (live session first,\n * agent-level cache second); `undefined` = nothing known yet. May load the\n * cached surface first — that read is pure memory on the host.\n */\n commands(sessionId: string): readonly AgentCommand[] | undefined | Promise<readonly AgentCommand[] | undefined>\n /** Kick a fresh `session/chat-list` read (fire and forget). */\n refresh(sessionId: string): void\n /** Fail-open reports (console and the caller's own sink). */\n onDiag?(line: string): void\n}\n\nexport interface SlashIsolation {\n /**\n * Find and wrap every live `/` source. Idempotent and safe to re-run.\n * Returns whether isolation is in force (at least one `/` source is wrapped)\n * — `false` is what lets the caller keep retrying until the source exists.\n */\n sync(): boolean\n /** Restore every wrapped source — only where our wrapper is still installed. */\n dispose(): void\n}\n\n/**\n * Cross-instance marker. `apply()` can run again over the same live sources (a\n * page reload of the plugin, not just a page load), and a wrapper owned by the\n * previous instance closes over that instance's store — it must be restored\n * before the new one wraps, or the new callbacks never run.\n */\nconst WRAP_MARKER = Symbol.for('dsh-ntr.slash-isolation')\n\ninterface WrapMarker {\n /** The instance that installed these wrappers. */\n owner: object\n restore(): void\n}\n\n/**\n * Whether one chat session runs the ACP preset, under the same two fact\n * sources the seat gate uses: the staged row's own preset when it speaks, and\n * the host-read verdict otherwise. `undefined` = no evidence yet, which every\n * caller reads as \"not ACP\" (fail-open: an ordinary conversation must never\n * lose its native commands to a guess).\n */\nexport function slashAcpVerdict(\n snapshot: AcpSessionsSnapshot | undefined,\n sessionId: string,\n verdict: (sessionId: string) => boolean | undefined,\n): boolean | undefined {\n const staged = stagedSessionOf(snapshot)\n if (staged !== undefined && staged.sessionId === sessionId) {\n const listed = rowAgentPreset(staged.row)\n if (listed !== undefined) return listed === ACP_PRESET_ID\n }\n return verdict(sessionId)\n}\n\nconst NO_MATCH = Number.NEGATIVE_INFINITY\n\n/** Extra weight for name starts and separator boundaries (same as the host's ranker). */\nfunction boundaryBonus(name: string, index: number): number {\n return index === 0 || name.charAt(index - 1) === '-' || name.charAt(index - 1) === '_' ? 8 : 0\n}\n\n/**\n * Score the strongest ordered-subsequence alignment of `query` in `name`, or\n * `undefined` when the query is not a subsequence.\n *\n * A hand copy of `alignmentScore` from\n * `@deepseek-ai/dsh-client-ui-primitives`'s `rankByName`: this bundle does not\n * depend on that package, and the menu should rank exactly the way the host\n * ranks its own commands (prefix hits first, then this score). Keep the two in\n * sync if the host's ranker changes.\n */\nfunction alignmentScore(name: string, query: string): number | undefined {\n if (query.length > name.length) return undefined\n let previous = new Array<number>(name.length).fill(NO_MATCH)\n for (let index = 0; index < name.length; index++) {\n if (name.charAt(index) === query.charAt(0)) previous[index] = 1 + boundaryBonus(name, index) - index\n }\n for (let queryIndex = 1; queryIndex < query.length; queryIndex++) {\n const current = new Array<number>(name.length).fill(NO_MATCH)\n let left = NO_MATCH\n let leftLeft = NO_MATCH\n let bestGapped = NO_MATCH\n for (let index = 0; index < name.length; index++) {\n const prior = previous[index] ?? NO_MATCH\n if (leftLeft !== NO_MATCH) bestGapped = Math.max(bestGapped, leftLeft + index - 2)\n if (name.charAt(index) === query.charAt(queryIndex)) {\n const bonus = 1 + boundaryBonus(name, index)\n let score = NO_MATCH\n if (left !== NO_MATCH) score = left + bonus + 4\n if (bestGapped !== NO_MATCH) score = Math.max(score, bestGapped + bonus + 1 - index)\n current[index] = score\n }\n leftLeft = left\n left = prior\n }\n previous = current\n }\n let best = NO_MATCH\n for (const score of previous) best = Math.max(best, score)\n return best === NO_MATCH ? undefined : best\n}\n\n/**\n * The agent's commands as menu rows, ranked the way the host ranks its own\n * menu: the query matches as a case-insensitive ordered subsequence of the\n * command name; prefix hits come first, then the strongest alignment score,\n * then the agent's own order (empty query = everything, in that order).\n *\n * Matching is name-only, like `rankByName`: fuzzy-matching whole descriptions\n * surfaced rows nobody would think to look for, and the native menu has never\n * searched them.\n */\nexport function commandCandidates(commands: readonly AgentCommand[], query: string): SlashCandidate[] {\n const q = query.trim().toLowerCase()\n const seen = new Set<string>()\n const ranked: Array<{ candidate: SlashCandidate; order: number; prefix: boolean; score: number }> = []\n let order = 0\n for (const command of commands) {\n if (command === null || typeof command !== 'object') continue\n const name = typeof command.name === 'string' ? command.name.trim() : ''\n if (name === '' || seen.has(name)) continue\n seen.add(name)\n // Source order counts EVERY accepted row, matched or not — the host's\n // ranker breaks score ties by the input array's index, not by a compacted\n // list of matches.\n const position = order\n order += 1\n const description = typeof command.description === 'string' && command.description !== '' ? command.description : undefined\n const input = command.input as { hint?: unknown } | undefined\n const hint = typeof input?.hint === 'string' && input.hint !== '' ? input.hint : undefined\n const candidate: SlashCandidate = {\n name,\n ...(description !== undefined ? { description } : {}),\n ...(hint !== undefined ? { hint } : {}),\n }\n if (q === '') {\n ranked.push({ candidate, order: position, prefix: true, score: 0 })\n continue\n }\n const lower = name.toLowerCase()\n const score = alignmentScore(lower, q)\n if (score === undefined) continue\n ranked.push({ candidate, order: position, prefix: lower.startsWith(q), score })\n }\n if (q === '') return ranked.map(match => match.candidate)\n ranked.sort((left, right) =>\n Number(right.prefix) - Number(left.prefix)\n || right.score - left.score\n || left.order - right.order)\n return ranked.map(match => match.candidate)\n}\n\nexport function createSlashIsolation(options: SlashIsolationOptions): SlashIsolation {\n /** This instance's identity in the wrap marker (see `dispose`). */\n const owner = {}\n /** Sources this instance wrapped, so dispose can restore them. */\n const owned = new Set<SlashSource>()\n /**\n * The ONE `/` source whose menu group serves the agent's commands. Every\n * other `/` source is silenced in ACP mode — otherwise the same list would\n * appear once per group (`command` AND `skill` both register on `/`). The\n * host's command group is the natural carrier; it is picked by name so the\n * choice does not depend on registration order.\n */\n let serving: SlashSource | undefined\n let lastDiag = 'idle'\n /** A problem line was reported during the current sync. */\n let failed = false\n\n const report = (line: string): void => {\n failed = true\n if (lastDiag === line) return\n lastDiag = line\n options.onDiag?.(line)\n }\n\n const sessionIdOf = (session: SlashSession | undefined): string | undefined =>\n typeof session?.sessionId === 'string' && session.sessionId !== '' ? session.sessionId : undefined\n\n /** The verdict held synchronously, or `undefined`. Never asks. */\n const heldVerdict = (session: SlashSession | undefined): boolean | undefined => {\n const sessionId = sessionIdOf(session)\n if (sessionId === undefined) return undefined\n try {\n return options.verdict(sessionId)\n } catch {\n return undefined\n }\n }\n\n /**\n * Last host ask per session. The menu re-polls `candidates` on every\n * keystroke, and a host that never answers the verdict query would otherwise\n * turn each one into a `session/chat-list` round-trip — in ORDINARY\n * conversations too, since the wrapped source is shared by all of them.\n */\n const askedAt = new Map<string, number>()\n const ASK_INTERVAL_MS = 1000\n /** Same idea for the \"no commands known\" refresh: one read per window, not per keystroke. */\n const refreshedAt = new Map<string, number>()\n const REFRESH_INTERVAL_MS = 1500\n\n /** The verdict, asking the host at most once per {@link ASK_INTERVAL_MS}. */\n const settledVerdict = async (session: SlashSession | undefined): Promise<boolean | undefined> => {\n const sessionId = sessionIdOf(session)\n if (sessionId === undefined) return undefined\n const held = heldVerdict(session)\n if (held !== undefined) return held\n const last = askedAt.get(sessionId) ?? 0\n if (Date.now() - last < ASK_INTERVAL_MS) return undefined\n askedAt.set(sessionId, Date.now())\n try {\n return await options.ensureVerdict(sessionId)\n } catch {\n return undefined\n }\n }\n\n const wrap = (source: SlashSource): boolean => {\n if (owned.has(source)) return false\n const existing = (source as unknown as Record<symbol, WrapMarker | undefined>)[WRAP_MARKER]\n try {\n existing?.restore()\n } catch {\n // A stale wrapper that cannot restore itself is still better replaced\n // than left in place: the new wrapper re-reads the current property.\n }\n if (typeof source.candidates !== 'function') {\n report(`slash source \"${source.name}\" has no candidates hook; left alone`)\n return false\n }\n\n const originals = {\n candidates: source.candidates,\n matchEnter: source.matchEnter,\n matchSpace: source.matchSpace,\n onPick: source.onPick,\n warm: source.warm,\n lexicon: source.lexicon,\n header: source.header,\n }\n const hadOwn = new Map<keyof typeof originals, boolean>()\n for (const key of Object.keys(originals) as Array<keyof typeof originals>) {\n hadOwn.set(key, Object.prototype.hasOwnProperty.call(source, key))\n }\n\n const patched: Array<{ key: keyof typeof originals; wrapper: unknown }> = []\n\n // The menu. In ACP mode its only rows are the agent's own commands; the\n // host directory is never read (which is also why a broken host catalog\n // cannot fail the menu open here).\n const candidates = async (session: SlashSession, req: SlashCandidateRequest): Promise<readonly SlashCandidate[]> => {\n if (await settledVerdict(session) !== true) return originals.candidates!.call(source, session, req)\n // A second `/` group would list the same commands again (and an empty\n // group auto-hides), so only the carrier source answers in ACP mode.\n if (serving !== source) return []\n const sessionId = sessionIdOf(session)!\n let commands: readonly AgentCommand[] | undefined\n try {\n commands = await options.commands(sessionId)\n } catch {\n // A failed cache read is \"nothing known\", never a failed menu.\n commands = undefined\n }\n if (commands === undefined) {\n // Nothing known for this conversation yet. Ask the host once per\n // window (the menu re-polls on every keystroke) and show nothing\n // rather than the host's catalog in the meantime.\n const last = refreshedAt.get(sessionId) ?? 0\n if (Date.now() - last >= REFRESH_INTERVAL_MS) {\n refreshedAt.set(sessionId, Date.now())\n options.refresh(sessionId)\n }\n return []\n }\n const rows = commandCandidates(commands, req?.query ?? '')\n // Same rule the host menu applies: a command that takes arguments is\n // only a completion at the leading position, not inside running text.\n return req?.position === 'inline' ? rows.filter(row => row.hint === undefined) : rows\n }\n source.candidates = candidates\n patched.push({ key: 'candidates', wrapper: candidates })\n\n // Enter/Space. `undefined` = \"no claimant\": the line falls through to the\n // default sink and reaches the agent byte-for-byte. A trailing space or\n // arguments are the agent's to parse, not ours.\n if (typeof originals.matchEnter === 'function') {\n const matchEnter: NonNullable<SlashSource['matchEnter']> = async (session, line, signal, envelope) => {\n if (await settledVerdict(session) === true) return undefined\n return originals.matchEnter!.call(source, session, line, signal, envelope)\n }\n source.matchEnter = matchEnter\n patched.push({ key: 'matchEnter', wrapper: matchEnter })\n }\n if (typeof originals.matchSpace === 'function') {\n const matchSpace: NonNullable<SlashSource['matchSpace']> = (session, token) => {\n if (heldVerdict(session) === true) return undefined\n return originals.matchSpace!.call(source, session, token)\n }\n source.matchSpace = matchSpace\n patched.push({ key: 'matchSpace', wrapper: matchSpace })\n }\n\n // A pick inserts the command into the draft; it never executes anything.\n // The user still presses Enter, and Enter is not ours to consume — the\n // same line then travels to the agent like any other message.\n if (typeof originals.onPick === 'function') {\n const onPick: NonNullable<SlashSource['onPick']> = (pick) => {\n if (heldVerdict(pick?.session) !== true) return originals.onPick!.call(source, pick)\n const name = typeof pick?.candidate?.name === 'string' ? pick.candidate.name.trim() : ''\n return name === '' ? undefined : { text: `/${name} ` }\n }\n source.onPick = onPick\n patched.push({ key: 'onPick', wrapper: onPick })\n }\n\n // Host-side warmups and decoration are harness work in an ACP\n // conversation: no catalog to fetch, and no host skill names to underline\n // in the draft (the agent's commands are not DSH references).\n if (typeof originals.warm === 'function') {\n const warm: NonNullable<SlashSource['warm']> = (session) => {\n if (heldVerdict(session) === true) return\n originals.warm!.call(source, session)\n }\n source.warm = warm\n patched.push({ key: 'warm', wrapper: warm })\n }\n if (typeof originals.lexicon === 'function') {\n const lexicon: NonNullable<SlashSource['lexicon']> = (session) => {\n if (heldVerdict(session) === true) return undefined\n return originals.lexicon!.call(source, session)\n }\n source.lexicon = lexicon\n patched.push({ key: 'lexicon', wrapper: lexicon })\n }\n if (typeof originals.header === 'function') {\n const header: NonNullable<SlashSource['header']> = (session, req) => {\n if (heldVerdict(session) === true) return undefined\n return originals.header!.call(source, session, req)\n }\n source.header = header\n patched.push({ key: 'header', wrapper: header })\n }\n\n const slots = source as unknown as Record<string, unknown>\n const restore = (): void => {\n for (const { key, wrapper } of patched) {\n // Only undo OUR assignment: a later wrapper (another instance, or an\n // upstream reassignment) owns the property now, and restoring over it\n // would resurrect code nobody is running.\n if (slots[key] !== wrapper) continue\n const original = originals[key]\n if (original !== undefined) {\n slots[key] = original\n } else if (hadOwn.get(key) === false) {\n delete slots[key]\n } else {\n slots[key] = undefined\n }\n }\n }\n\n ;(source as unknown as Record<symbol, WrapMarker>)[WRAP_MARKER] = { owner, restore }\n owned.add(source)\n return true\n }\n\n return {\n sync(): boolean {\n failed = false\n let sources: readonly SlashSource[] | undefined\n try {\n sources = options.sources()\n } catch {\n sources = undefined\n }\n if (sources === undefined) {\n report('the slash registry is unavailable; host slash commands stay in charge')\n return false\n }\n // Prefer the host's command group by name; fall back to any `/` source so\n // a live menu still exists if upstream renames it.\n serving = sources.find(source => source?.trigger === '/' && source.name === 'command')\n ?? sources.find(source => source?.trigger === '/')\n let inForce = false\n for (const source of sources) {\n if (source === null || typeof source !== 'object') continue\n if (source.trigger !== '/') continue\n try {\n if (wrap(source) || owned.has(source)) inForce = true\n } catch (error) {\n report(`could not wrap slash source \"${String(source.name)}\": ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n // Back to `idle` after a problem healed. The caller's sink is the store,\n // which no-ops when the value did not change.\n if (!failed && inForce && lastDiag !== 'idle') {\n lastDiag = 'idle'\n options.onDiag?.('idle')\n }\n return inForce\n },\n dispose(): void {\n for (const source of owned) {\n const marker = (source as unknown as Record<symbol, WrapMarker | undefined>)[WRAP_MARKER]\n // A newer instance replaced our wrappers (a plugin reload re-applies\n // over the same live sources). Restoring on top of it would resurrect\n // hooks nobody is running — its own dispose is what ends that wrap.\n if (marker?.owner !== owner) continue\n try {\n marker.restore()\n } catch {\n // Disposal must never throw: a source that cannot be restored is\n // simply left as the host finds it.\n }\n ;(source as unknown as Record<symbol, WrapMarker | undefined>)[WRAP_MARKER] = undefined\n }\n owned.clear()\n askedAt.clear()\n refreshedAt.clear()\n },\n }\n}\n","/**\n * dsh-ntr browser entry: builds the store over the Connection RPC face and\n * contributes every public seat — the settings section, the ACP conversation\n * view, the agent-native mode control, and the ACP base in the composer's\n * model seat (installed by the ACP-mode gate, which is what keeps it out of\n * every non-ACP conversation).\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SlotComponent } from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { AcpStore, type RpcCall } from './store.ts'\nimport { AcpSettingsSection } from './panel.tsx'\nimport { AcpComposerMode } from './composer-mode.tsx'\nimport { AcpComposerAgentTag, AcpSessionControls, AcpSessionMenu } from './session-controls.tsx'\nimport { AcpConversationView } from './acp-view.tsx'\nimport { AcpSettingsDialog } from './settings-dialog.tsx'\nimport { PermissionDock } from './permission-dock.tsx'\nimport {\n createModelSeatClaim,\n createPermissionSeatClaim,\n MODEL_SEAT_KEY,\n PERMISSION_SEAT_KEY,\n} from './model-seat.tsx'\nimport type { ModelSeatClaim, SeatSlots } from './model-seat.tsx'\nimport {\n acpModeFromPresetEvent, rowAgentPreset, stagedIsAcpMode, stagedSessionOf,\n} from './acp-ui.tsx'\nimport type { AcpChatInjected, AcpSessionFace, AcpSessionsSnapshot, SessionListSource } from './acp-ui.tsx'\nimport { createSlashIsolation, slashAcpVerdict } from './slash-isolation.ts'\nimport type { SlashSource } from './slash-isolation.ts'\n\nexport const name = 'dsh-ntr-client'\nexport const inject = ['slots', 'connection']\n\n/** Structural view of the browser Connection handle (official type is not merged into Context). */\ninterface BrowserConnection {\n rpc?: {\n call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<\n | { ok: true; value: unknown }\n | { ok: false; error: { code: string; message: string } }\n >\n }\n}\n\n/**\n * Optional services, resolved structurally rather than injected. Declaring\n * `sessions` in `inject` would make this plugin fail to load in a deployment\n * that composes only a subset of the official client modules — and it is read\n * through `ctx.get` at call time, so a late-arriving service still answers.\n */\ninterface StructuralServices {\n sessions: {\n binding(sessionId: string): { session?: unknown } | undefined\n /** The client runtime's session list store (see `AcpSessionsSnapshot`). */\n list?: SessionListSource\n }\n}\n\nconst browserPrefersZh = typeof navigator !== 'undefined' && /^zh\\b/i.test(navigator.language)\n\n/**\n * Structural view of the shell's locale service (`@deepseek-ai/dsh-client-locale`).\n *\n * Read through {@link ctx.get} rather than injected: a composition without the\n * service must still load, and the label pairs carry their own browser-derived\n * fallback for exactly that case.\n */\ninterface LocaleFace {\n resolveText?(text: Record<string, string>): string\n subscribe?(listener: () => void): () => void\n}\n\n/**\n * How long a host verdict may stand as the gate's ONLY fact source before the\n * gate asks the host again.\n *\n * The gate prefers the staged session row's own preset. On a host whose client\n * rows carry no `agentPreset` the row is silent, and then the\n * `session/chat-list` verdict is all there is — and it used to be asked ONCE\n * per session, for the rest of the page's life. A preset switch that no\n * `agent-preset/selected` frame announced (the `remote` face absent when this\n * effect ran, or a runtime that never forwards the frame) therefore left the\n * gate judging a conversation that had already left ACP mode: both cells stayed\n * claimed, and the permission cell's product is an ABSENCE — an ordinary\n * conversation silently lost its Access mode control with nothing on screen to\n * say why. The host reads the session log every switch is appended to, so\n * re-asking is what lets the truth arrive late; the bound keeps that at one\n * round-trip per window per staged session instead of one per store mutation\n * (the resolve-loop the one-shot ask was bought to prevent).\n */\nconst ACP_MODE_VERDICT_TTL_MS = 2000\n\n/**\n * Slash-source discovery retry (see the slash-isolation effect): a source\n * registers with no event, so a module that loads after this one is found by\n * polling the roster a bounded number of times. 20 × 500ms covers a slow page\n * load; the timer stops as soon as a source is wrapped.\n */\nconst SLASH_SYNC_ATTEMPTS = 20\nconst SLASH_SYNC_DELAY_MS = 500\n\nexport function apply(ctx: Context): void {\n const connection = ctx.get('connection') as BrowserConnection | undefined\n const rpc = connection?.rpc\n if (rpc === undefined) {\n throw new Error('dsh-ntr: browser connection.rpc is unavailable')\n }\n const call: RpcCall = (channel, endpoint, payload) => rpc.call(channel, endpoint, payload)\n\n /**\n * Resolve one optional service without injecting it. A plain `ctx.get` at\n * call time keeps the plugin loadable when the service is absent AND picks it\n * up when it arrives later — which an `inject` dependency cannot do without\n * also making its absence fatal.\n */\n const service = <T,>(name: string): T | undefined => (ctx as unknown as { get(key: string): unknown }).get(name) as T | undefined\n\n /**\n * Resolve a Chinese/English pair against the shell's active locale. Read per\n * call, so a language switch reaches every render-time `label()` call without\n * rebuilding the store. A host without the locale service keeps the\n * browser-derived choice the pairs shipped with.\n */\n const label = (zhText: string, enText: string): string => {\n const resolved = service<LocaleFace>('locale')?.resolveText?.({ zh: zhText, en: enText })\n return typeof resolved === 'string' && resolved !== '' ? resolved : browserPrefersZh ? zhText : enText\n }\n\n const store = new AcpStore(call, label)\n store.start()\n\n // A locale switch changes no data, only which side of each pair renders;\n // republishing the snapshot is what re-runs them. Attached through\n // `internal/service` so a late-arriving locale service is still hooked.\n ctx.effect(() => {\n let stop: (() => void) | undefined\n const attach = (): void => {\n if (stop !== undefined) return\n stop = service<LocaleFace>('locale')?.subscribe?.(() => { store.refreshLocale() })\n }\n attach()\n const stopService = ctx.on('internal/service', attach)\n return () => {\n stopService()\n stop?.()\n }\n }, 'dsh-ntr: locale switch re-render')\n\n /** This session's command/projection face (`ctx.sessions`); `undefined` before it materializes. */\n const faceFor = (sessionId: string): AcpSessionFace | undefined => {\n try {\n return service<StructuralServices['sessions']>('sessions')?.binding(sessionId)?.session as AcpSessionFace | undefined\n } catch {\n return undefined\n }\n }\n\n /** The injected face every ACP conversation entry receives. */\n const chatFace = (sessionId: unknown): AcpChatInjected & { sessionId: string } => ({\n acp: store,\n sessionId: String(sessionId),\n faceFor,\n })\n\n ctx.effect(() => ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'acp-agents',\n order: 120,\n label: () => 'NTR',\n inject: () => ({ acp: store }),\n }, AcpSettingsSection)), 'dsh-ntr: settings section')\n\n // The same NTR settings, opened from the composer when the agent roster is\n // empty (the agent base's picker action). It rides the frame's overlay layer\n // because the composer seat and the dialog live in unrelated slot trees; the\n // host settings shell has no programmatic open seam, so the dialog mounts the\n // same section the shell mounts (see settings-dialog.tsx). Renders nothing\n // while closed.\n ctx.effect(() => ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'acp-settings-dialog',\n order: 30,\n inject: () => ({ acp: store }),\n }, AcpSettingsDialog as unknown as SlotComponent<any>)), 'dsh-ntr: NTR settings dialog')\n\n // The agent's OWN modes sit in the composer tool row — the seat the slot\n // contract reserves for exactly this (\"entries sit beside that chrome, never\n // replace it\"). They are a different layer from everything the host controls:\n // this is HOW the agent works, not what it may touch.\n ctx.effect(() => ctx.slots.inject('conversation.input.left', () => ctx.slots.register({\n name: 'conversation.input.left',\n id: 'acp-mode',\n order: 20,\n // The id rides the injected face rather than the standard kit: the kit's\n // top-level `sessionId` only exists in newer renderers, while the positional\n // inject argument is the contract every version honors (see\n // composer-mode.tsx).\n inject: (sessionId: unknown) => chatFace(sessionId),\n // The cast is the price of the slot map's published `.d.ts` dropping its\n // `owner` field: the props contract is declared structurally in\n // composer-mode.tsx, so the composed-props constraint cannot be met by type\n // (see the note there). Runtime passes the real shares either way.\n }, AcpComposerMode as unknown as SlotComponent<any>)), 'dsh-ntr: composer agent-mode control')\n\n // Its picker rides the composer's overlay seat — the platform's home for\n // menus, so the popup floats above the card instead of being clipped by it.\n ctx.effect(() => ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register({\n name: 'conversation.input.overlay',\n id: 'acp-menu',\n order: 10,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpSessionMenu as unknown as SlotComponent<any>)), 'dsh-ntr: composer ACP picker')\n\n // The agent tag riding the composer card's top edge. The agent base steps\n // aside after the conversation's first ACP turn, which also LOCKS the\n // conversation to its agent (`agentBaseHidden` in session-controls.tsx); this\n // tag is the read-only nameplate that keeps naming it. It shares the overlay\n // seat with the picker itself; both are positioned floating entries inside\n // the card, while the tag is pinned to the card's top edge\n // (`.ntr-acp-tag`). Registered for every conversation, gated inside the\n // component.\n ctx.effect(() => ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register({\n name: 'conversation.input.overlay',\n id: 'acp-agent-tag',\n order: 5,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpComposerAgentTag as unknown as SlotComponent<any>)), 'dsh-ntr: composer agent tag')\n\n // Pending permission asks for the staged conversation, above the composer\n // card (`conversation.input.dock` — a session-scoped list slot; the host's\n // own queue/todo docks register the same way). The slot spans the whole\n // conversation column, so the dock component applies the composer's own\n // width formula to line up with the input box. Every ask is dispatched as\n // broadcast as `permission/request`; the dock renders only the ones carrying\n // this conversation's id, so it needs no ACP-mode gate. Asks without a chat\n // identity stay on the settings-panel ledger.\n ctx.effect(() => ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({\n name: 'conversation.input.dock',\n id: 'acp-permissions',\n order: 10,\n // The positional inject argument is the session binding key — the chat\n // session id this dock belongs to (same contract as every other entry).\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, PermissionDock as unknown as SlotComponent<any>)), 'dsh-ntr: conversation permission dock')\n\n /**\n * The client runtime's session list store, read structurally. Used to find\n * the session the composer is staged on and whether it runs the ACP preset —\n * which is what every ACP conversation entry keys off.\n */\n const sessionsList = (): SessionListSource | undefined => {\n const list = service<StructuralServices['sessions']>('sessions')?.list\n if (list === undefined || typeof list.getSnapshot !== 'function' || typeof list.subscribe !== 'function') return undefined\n return list\n }\n\n /**\n * Chat session ids the ACP-mode tracker has already asked the host about (the\n * `session/chat-list` fallback), mapped to WHEN the last ask went out. The\n * ask is once per id so a host whose answer carries no `acpMode` cannot turn\n * every store mutation into a fresh RPC — the classic resolve-loop this file\n * already guards against elsewhere. `agent-preset/selected` clears an id so a\n * committed preset switch re-asks.\n *\n * An id is REMOVED again when its ask comes back without a verdict: a\n * one-shot ask that cannot answer must not wedge the gate for the rest of\n * the page's life. The next store mutation or list notification retries it,\n * which is what makes the fallback recoverable rather than a single coin\n * flip (a host half that transiently fails, or answers `{}` because the\n * session's projection row is not resident yet, used to leave the native\n * model seat visible forever with nothing in the console to say why).\n *\n * A verdict that IS held is re-asked once per {@link ACP_MODE_VERDICT_TTL_MS}\n * while the row stays silent — see the constant for why a held verdict may\n * not be permanent.\n */\n const acpModeAsked = new Map<string, number>()\n\n /** The host-side verdict for the seat gate, when we hold one (`AcpStore.acpMode`). */\n const acpModeVerdict = (sessionId: string): boolean | undefined => store.getSnapshot().acpMode[sessionId]\n\n ctx.effect(() => {\n let stopWatch: (() => void) | undefined\n /**\n * The claim on the host's model seat, held while the staged conversation\n * runs the ACP preset. That cell is where the host's own base sits (the\n * trailing group, right before the submit button), so the claim installs\n * the ACP base there rather than blanking it — one cell, one occupant, and\n * the position the user expects. Ownership of the cell is NOT settled by\n * the rank a registration declares — the client runner rewrites non-chain\n * priorities with its own page-global counter, so the last registrant\n * wins — which is why the claim verifies the cell's winner and retakes it\n * when a rival (a shipped occupant, or a third-party seat plugin later in\n * the load order) is holding it. See model-seat.tsx.\n */\n let claim: ModelSeatClaim | undefined\n /**\n * Claim on the host's native permission seat (`conversation.input.permission`),\n * held while the staged conversation runs in ACP mode to blank out the\n * Access mode dropdown — the DSH read-only / workspace-write /\n * danger-full-access knob, which describes policies this runtime applies to\n * ITS OWN tools and therefore says nothing about a separate agent process.\n *\n * Unlike the model seat, the right answer here is an empty cell rather than\n * an ACP replacement: the ACP agent's own permission surface is what it\n * sends over `session/request_permission`, and there is nothing of ours to\n * put in this slot. So the occupant is a component that renders `null`.\n *\n * That makes the lifecycle discipline load-bearing in a way the model seat\n * does not rely on: the evidence for taking the cell (the staged preset) is\n * derived from a store that can go silent, while the entry itself is\n * indistinguishable from a permanent one to anyone looking at the screen.\n * A claim that outlives its reason has silently removed a control from\n * ordinary conversations, so {@link standDown} runs on every path that is\n * not a confirmed ACP verdict — including the ones where no verdict is\n * available yet.\n */\n let permissionClaim: ModelSeatClaim | undefined\n /**\n * Last diagnostic line from {@link syncAcpMode}. Every way this gate can\n * end with the native seat still on screen used to be silent — an inner\n * `catch { return [] }` and an outer `catch { … }` between them swallowed\n * both the registration failure and the inject failure, leaving \"the ACP\n * conversation still shows the native model picker\" with nothing to read\n * it from. This string is the gate's own account of what it last decided,\n * verified or not, and why; the ACP view renders it.\n */\n let seatDiag = 'idle'\n let permissionSeatDiag = 'idle'\n /**\n * Hand BOTH cells back to their shipped occupants.\n *\n * Every path that is not \"this staged conversation is CONFIRMED to run the\n * ACP preset\" ends here — including the paths that carry no verdict at all\n * (no session list, nothing staged, an addressed subagent route). Standing\n * down is the only safe default, because keeping a blanking entry installed\n * while the evidence has gone silent hides a control the user needs: the\n * cells sit in every conversation's composer bar, so an entry that outlives\n * its reason is indistinguishable from a permanent one. The next evaluation\n * re-installs if ACP mode is confirmed again, so this is not a decision\n * about the conversation — only about whose chrome is on screen while we\n * cannot answer.\n */\n const standDown = (reason: string): void => {\n seatDiag = reason\n // The permission line names the release explicitly: \"staged X is not in\n // ACP mode\" and \"staged X is ACP mode\" are the two states a reader has\n // to tell apart, and the seat this claim holds renders nothing — so\n // without the word, \"released\" and \"re-installed on the next evidence\"\n // read identically in the one place either is observable.\n permissionSeatDiag = `${reason} (released)`\n disarmVerdictReask()\n if (claim !== undefined) { claim.release(); claim = undefined }\n if (permissionClaim !== undefined) { permissionClaim.release(); permissionClaim = undefined }\n }\n /**\n * Re-ask timer for the staged session, armed while the row is silent and a\n * verdict is held (see {@link ACP_MODE_VERDICT_TTL_MS}). A re-ask needs a\n * trigger of its own: on the host shape this covers nothing else moves —\n * the row never speaks, the frame path may be dead, and a page with no\n * store traffic would otherwise never re-run the gate, which is exactly\n * how a stale verdict became permanent. One timer at a time, because the\n * gate judges exactly one staged session; cleared whenever the row speaks,\n * whenever the gate stands down, and on teardown.\n */\n let verdictReask: ReturnType<typeof setTimeout> | undefined\n const armVerdictReask = (): void => {\n if (verdictReask !== undefined) return\n verdictReask = setTimeout(() => {\n verdictReask = undefined\n runGate()\n }, ACP_MODE_VERDICT_TTL_MS)\n }\n const disarmVerdictReask = (): void => {\n if (verdictReask === undefined) return\n clearTimeout(verdictReask)\n verdictReask = undefined\n }\n const syncAcpMode = (): void => {\n const list = sessionsList()\n const snapshot = list?.getSnapshot() as AcpSessionsSnapshot | undefined\n if (list === undefined) {\n standDown('no session list service')\n return\n }\n const staged = stagedSessionOf(snapshot)\n if (staged === undefined) {\n standDown('no staged session (nothing retains the main view)')\n return\n }\n if (staged.subagent) {\n standDown(`staged ${staged.sessionId} is an addressed subagent route`)\n return\n }\n // The gate prefers the runtime's own session row; on hosts whose rows\n // carry no `agentPreset` the row is silent and only our host-side report\n // can answer. Ask while the row is silent AND we hold no verdict — the\n // store subscriber below re-runs this gate when the answer lands\n // (`loadChatSessions` dedups racing asks in flight too). An ask that came\n // back without an answer is reported unsettled and retracted here, so the\n // next tick asks again rather than parking the session forever.\n //\n // A verdict that IS held is re-asked once it ages past\n // {@link ACP_MODE_VERDICT_TTL_MS}: while the row is silent it is this\n // gate's only fact source, and the host's log read is the one place a\n // switch the runtime never framed can still be learned from.\n const verdictHeld = acpModeVerdict(staged.sessionId) !== undefined || store.hasAcpModeVerdict(staged.sessionId)\n const askedAt = acpModeAsked.get(staged.sessionId)\n const askIsDue = askedAt === undefined\n || (!verdictHeld || Date.now() - askedAt >= ACP_MODE_VERDICT_TTL_MS)\n if (rowAgentPreset(staged.row) === undefined && askIsDue) {\n acpModeAsked.set(staged.sessionId, Date.now())\n // `settleVerdict` on the TTL re-ask only: the answer of a read the gate\n // itself commissioned may settle a verdict a frame had settled (see\n // `acpModeLive`), which is what lets a switch the runtime never framed\n // still reach this gate. A composer reader's answer keeps deferring to\n // the frame, because the host's log read can lag a switch by one.\n void store.loadChatSessions(staged.sessionId, verdictHeld ? { settleVerdict: true } : undefined)\n } else if (!verdictHeld && askedAt !== undefined) {\n // The previous ask for this session came back without a verdict (or is\n // still in flight). Withdraw the one-shot mark so the round-trip can be\n // repeated: without this, one silent failure pins the native seat in\n // place for the rest of the page's life with nothing to show why.\n acpModeAsked.delete(staged.sessionId)\n }\n // An ACP conversation's models belong to the external agent, so the\n // host's native model seat must not be on screen here — it would list\n // providers this conversation cannot use, and clicking one writes the\n // process-wide default model. The seat is also exactly where the host's\n // own base sat (the trailing group, before the submit button), which is\n // where the ACP base belongs — so instead of blanking the cell we install\n // the ACP base in it. See model-seat.tsx for why occupying it is not\n // enough on its own.\n //\n // The Access mode selector beside it (`conversation.input.permission`)\n // has no ACP counterpart to be replaced with: it selects the file-write\n // policy THIS runtime applies to its own tools, which a separate agent\n // process never consults, so in ACP mode the honest thing to render is\n // nothing. Both cells follow the same evidence — the staged\n // conversation's preset — through one {@link standDown}, so neither can\n // stay claimed after the evidence for it stops answering.\n if (!stagedIsAcpMode(snapshot, acpModeVerdict)) {\n standDown(`staged ${staged.sessionId} is not in ACP mode`)\n return\n }\n // The row speaks, so the host verdict is not this gate's only fact\n // source and no re-ask timer is needed.\n if (rowAgentPreset(staged.row) !== undefined) disarmVerdictReask()\n // Stays re-runnable on purpose: the seat is not declared before the\n // composer bar mounts, a registration can be refused, and the cell can be\n // taken from us later. The claim reports each of those through the\n // diagnostic line instead of pretending the base is installed — the\n // next evaluation (store mutation, list notification, preset frame, or a\n // change to the cell itself) simply tries again.\n seatDiag = `staged ${staged.sessionId} is ACP mode`\n claim ??= createModelSeatClaim({\n slots: ctx.slots as unknown as SeatSlots,\n occupant: () => AcpSessionControls,\n // The occupant's own face — REQUIRED by the claim, and the reason is a\n // silent failure this gate could never see: `conversation.input.model`\n // is session-scoped, so without it the entry renders with the standard\n // kit alone (`acp === undefined`), the slot's error boundary abdicates\n // our entry from the cell, and the native seat comes back while the\n // diagnostic still says \"held by the ACP base\". See model-seat.tsx.\n inject: (sessionId: unknown) => chatFace(sessionId),\n onDiag: (line) => { seatDiag = `staged ${staged.sessionId} · ${line}` },\n })\n claim.sync()\n\n permissionSeatDiag = `staged ${staged.sessionId} is ACP mode`\n permissionClaim ??= createPermissionSeatClaim({\n slots: ctx.slots as unknown as SeatSlots,\n onDiag: (line) => { permissionSeatDiag = `staged ${staged.sessionId} · ${line}` },\n })\n permissionClaim.sync()\n // Confirmed ACP on a silent row: the verdict is all the evidence there\n // is, so it gets a re-ask deadline (see {@link ACP_MODE_VERDICT_TTL_MS}).\n if (rowAgentPreset(staged.row) === undefined) armVerdictReask()\n }\n // A committed preset switch rewrites the session the gate judged. The\n // runtime's frame carries the new preset, so fold it straight into the\n // verdict map — see the handler for why re-reading the log there would be a\n // step behind. Structural `remote` (not injected): an older runtime without\n // it simply never gets this refresh path, and the one-shot ask still applies.\n //\n // Resolved through {@link attach} rather than once when this effect ran: a\n // page that loads this bundle before the gateway publishes `remote` used to\n // lose the frame path for the rest of its life, and on a host whose rows\n // are silent the frame is the only live fact source there is. The\n // subscription is made once (the guard), and `internal/service` re-runs the\n // attach whenever a service appears — the same late-arrival path the\n // session list already uses.\n let stopPreset: (() => void) | undefined\n const attachPresetFrames = (): void => {\n if (stopPreset !== undefined) return\n const remote = (ctx as unknown as { get(key: string): unknown }).get('remote') as\n | { $on?(event: string, listener: (...args: unknown[]) => void): () => void }\n | undefined\n if (typeof remote?.$on !== 'function') return\n stopPreset = remote.$on('agent-preset/selected', (...args: unknown[]) => {\n // `[sessionId, agentPreset]`: the frame carries the committed preset, so\n // fold IT rather than re-asking the host. The runtime broadcasts this\n // the moment the switch commits, while the appended session-log row is\n // not yet visible to a fresh `session/chat-list` cold read — reading\n // there folds the PREVIOUS preset, which kept the surface one switch\n // behind until a page reload (the reported \"switching into or out of ACP\n // mode only moves the model seat after a refresh\" symptom). The host read\n // stays the cold path for a session this tab has no frame for.\n const sessionId = args[0]\n if (typeof sessionId !== 'string') return\n // A frame supersedes both a held verdict and a pending one-shot ask.\n acpModeAsked.delete(sessionId)\n if (args.length >= 2) {\n // The late answer to an ask that predates the frame must not win: it\n // describes the log from before the switch.\n store.abandonChatSessionRead(sessionId)\n store.noteAcpMode(sessionId, acpModeFromPresetEvent(args[1]))\n } else {\n // A frame carrying no preset can only be honoured by a fresh host\n // read — the behaviour the fold replaced.\n store.invalidateAcpMode(sessionId)\n }\n runGate()\n })\n }\n const attach = (): void => {\n const list = sessionsList()\n if (list !== undefined && stopWatch === undefined) stopWatch = list.subscribe(runGate)\n attachPresetFrames()\n runGate()\n }\n /**\n * Wrap {@link syncAcpMode} so every evaluation also publishes its verdict\n * to the store. The gate runs on every store mutation, list notification,\n * preset frame and change to the model-seat cell itself, so the published\n * line is always the latest verdict — which is the whole point: the visible\n * symptom (\"the native seat is still there\") looks identical whether the\n * seat was never declared, refused us, or was taken by a rival, and only\n * this line tells them apart.\n *\n * NOT RE-ENTRANT, and deliberately so. The host's slot registry emits\n * `slots/changed` SYNCHRONOUSLY from inside `slots.register` and from\n * inside an entry's disposer (`SlotCore.markDirty` → the registry's\n * `onMutate` bridge → `ctx.emit`), and the store re-runs this gate from\n * inside its own `mutate`. So an evaluation that installs or releases an\n * entry re-enters itself from within its own call stack — and a nested\n * evaluation reads the SAME evidence (neither the session snapshot nor the\n * store has changed), so it can only repeat the decision. Worse, a nested\n * install sees `installed.length === 0` because the outer install has not\n * pushed its disposer yet, registers a second entry, and that entry's own\n * `slots/changed` re-enters again: an unbounded registration pile whose\n * store-subscriber feedback never settles. A nested run is therefore\n * dropped — the evaluation already in flight judged this evidence and is\n * about to publish its verdict, and the next trigger tries again.\n */\n let gateRunning = false\n const runGate = (): void => {\n if (gateRunning) return\n gateRunning = true\n try {\n syncAcpMode()\n store.noteSeatDiag(seatDiag)\n store.notePermissionSeatDiag(permissionSeatDiag)\n } finally {\n gateRunning = false\n }\n }\n attach()\n // The store mutates when `session/chat-list` answers, so the fallback\n // verdict arriving is what re-runs this gate — the list subscription alone\n // cannot see it (the runtime rows are silent on these hosts).\n const stopStore = store.subscribe(runGate)\n // The cell can be taken by someone else at any moment (the runner ranks the\n // most recent registration lowest, so a seat plugin loading or re-mounting\n // after us wins). `slots/changed` carries the key, and the claim's `sync`\n // retakes the cell only when it is actually holding something that is not\n // ours — so this is evidence-driven, not a poll.\n const stopCell = ctx.on('slots/changed', (...args: unknown[]) => {\n if (args[0] !== MODEL_SEAT_KEY && args[0] !== PERMISSION_SEAT_KEY) return\n runGate()\n })\n // Late services: the runtime's session list normally exists before this\n // module applies, but a build that loads us first must not end up without\n // the gate for the rest of the page's life.\n const stopService = ctx.on('internal/service', attach)\n return () => {\n stopService()\n stopStore()\n stopCell()\n stopPreset?.()\n stopWatch?.()\n disarmVerdictReask()\n if (claim !== undefined) { claim.release(); claim = undefined }\n if (permissionClaim !== undefined) { permissionClaim.release(); permissionClaim = undefined }\n store.noteSeatDiag('gate disposed')\n store.notePermissionSeatDiag('gate disposed')\n }\n }, 'dsh-ntr: ACP mode tracking')\n\n /**\n * Slash isolation (`src/client/slash-isolation.ts`): in an ACP conversation\n * the composer's `/` menu, Enter and Space adjudication belong to the AGENT.\n * The host's slash sources describe commands THIS runtime would execute, and\n * a name they recognize never reaches the prompt sink — so `/model` in an NTR\n * conversation would run DSH's model picker instead of reaching the agent.\n *\n * The host pipeline offers no per-session seam (adjudication polls sources in\n * registration order; duplicates throw; no availability hook), so the live\n * sources are wrapped in memory. The verdict is the same two-source read the\n * seat gate uses: the staged row's own preset, else the host's answer.\n *\n * Re-runs on `internal/service` (a late `inputTriggers`) and on store traffic\n * (a source registering after us), and is idempotent. Everything is restored\n * on teardown so a plugin reload never leaves the previous instance's\n * callbacks behind.\n */\n ctx.effect(() => {\n let warned = ''\n const isolation = createSlashIsolation({\n sources: () => {\n const triggers = service<{ live?: { sources?: unknown } }>('inputTriggers')\n // A host WITHOUT the trigger service has no slash menu, so there is\n // nothing to isolate and nothing to report. A service whose shape is\n // not what this module knows is different: that returns undefined and\n // is reported (the seam degraded open, which needs a line).\n if (triggers === undefined) return []\n const sources = triggers.live?.sources\n return Array.isArray(sources) ? sources as SlashSource[] : undefined\n },\n verdict: (sessionId) => slashAcpVerdict(\n sessionsList()?.getSnapshot(),\n sessionId,\n id => store.getSnapshot().acpMode[id],\n ),\n ensureVerdict: async (sessionId) => {\n const held = (): boolean | undefined => slashAcpVerdict(\n sessionsList()?.getSnapshot(),\n sessionId,\n id => store.getSnapshot().acpMode[id],\n )\n const known = held()\n if (known !== undefined) return known\n await store.loadChatSessions(sessionId)\n return held()\n },\n // The LIVE session's declaration wins; a conversation that has not sent\n // its first message yet falls back to the agent-level cache learned from\n // earlier sessions (`agent/catalog` → `commands`). That fallback is what\n // makes `/` list the agent's commands immediately, like the native menu,\n // instead of waiting for a session that only the first message creates.\n commands: async (sessionId) => {\n const live = store.chatSessionCommands(sessionId)\n if (live !== undefined) return live\n const agentId = store.chatAgentFor(sessionId)\n if (agentId === undefined) return undefined\n const cached = store.agentCommands(agentId)\n if (cached !== undefined) return cached\n // `agent/catalog` is pure memory on the host — no probe is spawned by\n // this read (constraint 8); an agent with no cached list simply stays\n // absent, and the menu shows nothing until its first session declares.\n await store.loadAgentCatalog(agentId)\n return store.agentCommands(agentId)\n },\n refresh: (sessionId) => { void store.loadChatSessions(sessionId) },\n onDiag: (line) => {\n store.noteSlashDiag(line)\n // Console once per distinct line: the NTR view shows the same string,\n // but a shape change is exactly the kind of thing a bug report needs a\n // timestamped console record of.\n if (warned === line) return\n warned = line\n if (line !== 'idle') console.warn(`[dsh-ntr] slash isolation: ${line}`)\n },\n })\n /**\n * Sources register with no event of their own: `inputTriggers` announces\n * the SERVICE, not the roster. A client module that loads after this one\n * therefore adds the `command` source in silence. The retry below is the\n * bounded answer to that gap — stop as soon as something is wrapped, and\n * give up after {@link SLASH_SYNC_ATTEMPTS} so a host with no slash\n * pipeline does not keep a timer alive.\n */\n let retryTimer: ReturnType<typeof setTimeout> | undefined\n let attempts = 0\n const sync = (): void => {\n if (isolation.sync()) {\n if (retryTimer !== undefined) {\n clearTimeout(retryTimer)\n retryTimer = undefined\n }\n return\n }\n // The service's arrival is announced (`internal/service`), so there is\n // nothing to poll for until it exists; the gap this retry closes is a\n // roster that is still empty while the service is already there.\n if (service('inputTriggers') === undefined) return\n if (retryTimer !== undefined || attempts >= SLASH_SYNC_ATTEMPTS) return\n attempts += 1\n retryTimer = setTimeout(() => {\n retryTimer = undefined\n sync()\n }, SLASH_SYNC_DELAY_MS)\n }\n sync()\n const stopService = ctx.on('internal/service', sync)\n // Store traffic is guaranteed after mount; sync is cheap and idempotent\n // once everything is wrapped.\n const stopStore = store.subscribe(sync)\n return () => {\n if (retryTimer !== undefined) clearTimeout(retryTimer)\n stopStore()\n stopService()\n isolation.dispose()\n store.noteSlashDiag('idle')\n }\n }, 'dsh-ntr: slash isolation')\n\n // The ACP view tab: the agent's real stream (tool cards, plans, unified\n // diffs). Session-scope list entry; the view ring renders it one tab at a\n // time. Meanwhile the agent's prose and thinking ride the NATIVE chat plane\n // through the LLM adapter — see docs/acp-convergence.md §2.1.\n ctx.effect(() => ctx.slots.inject('conversation.view', () => ctx.slots.register({\n name: 'conversation.view',\n id: 'acp',\n order: 20,\n label: () => 'NTR',\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpConversationView as unknown as SlotComponent<any>)), 'dsh-ntr: ACP view tab')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useUi","useSyncExternalStore","useRef","STYLE","styleMounted","useUi","useSyncExternalStore","useState","useRef","useSyncExternalStore","useState","useUi","boundSession","useUi","useState","useState","memo","useUi","useRef","useUi","useState","useUi","useState","primitives","useUi","useRef","useState","Tooltip","useUi","useMemo","createElement"],"sources":["../src/protocol.ts","../src/client/store.ts","../src/client/acp-ui.tsx","../src/client/panel.tsx","../src/client/composer-mode.tsx","../src/client/context-meter.tsx","../src/client/agent-lock-anim.ts","../src/client/session-controls.tsx","../src/client/acp-view.tsx","../src/client/settings-dialog.tsx","../src/client/permission-dock.tsx","../src/client/released-banner.tsx","../src/client/branch-action.tsx","../src/client/model-seat.tsx","../src/client/activity-pill.tsx","../src/client/slash-isolation.ts","../src/client/index.tsx"],"sourcesContent":["/**\n * dsh-ntr shared contracts (browser-safe: types only, zero runtime code).\n *\n * Two planes:\n * - `AcpEvent`: host → browser, one JSON frame per `session/update` fold,\n * permission round-trip, agent process status, and turn lifecycle.\n * - RPC payloads: browser → host unary calls on the `/acp` Connection channel.\n *\n * Security invariants encoded here (enforced host-side in impl.ts):\n * - A SESSION never names a program to spawn: `session/start` and the\n * conversation↔agent binding only select a configured agent id and a cwd\n * that must resolve inside the agent's allowed roots. Naming a program is\n * a CONFIGURATION act instead — the browser may do it through `agent/save`\n * / `agent/test`, both of which run the command through `validateAgentDef`\n * (a command line, not a bare path) and both of which really spawn.\n * - Agent environment variables flow config → child process only.\n */\n\nimport type { SessionUpdate } from '@agentclientprotocol/sdk'\n\n// ---- Configuration (host side, from the profile layer) ----\n\n/** One pre-configured ACP agent executable. */\nexport interface AcpAgentDef {\n /** Stable id the browser selects by. `^[a-z0-9][a-z0-9._-]*$`. */\n id: string\n /** Display name (defaults to the id). */\n title?: string\n /**\n * Executable spawned over stdio, as a full command line (`codebuddy --acp`),\n * parsed by `validateAgentDef`. The browser MAY set it — that is exactly\n * what the settings panel's single command box does, through `agent/save`\n * (persist, then probe) and `agent/test` (spawn a throwaway handshake). A\n * SESSION never can: `session/start` and the conversation binding only\n * select a configured agent id (see the module header).\n */\n command: string\n /** Argument vector (no shell interpretation). */\n args?: string[]\n /**\n * npm package installed globally (`npm install -g`) when `command` cannot be\n * resolved on the host PATH, right before the spawn is attempted. Unset → a\n * missing command just fails with ENOENT. Name-only charset is enforced by\n * `validateAgentDef` (no shell metacharacters can ever reach the installer).\n */\n npmPackage?: string\n /** Child environment: the inherited host env, with these keys layered on top. */\n env?: Record<string, string>\n /** Default absolute workspace directory for new sessions. */\n cwd?: string\n /** Absolute roots a session cwd may resolve under. Defaults to `[cwd]`. */\n allowedCwdRoots?: string[]\n /**\n * Which probed `category: model` choices this agent offers as `agentId@choice`\n * variants in the plugin's own ACP seats (the chips/picker in the chat\n * composer and the ACP-mode model seat). Those menus have no search, so the\n * full catalog is a list storm nobody can navigate: a real pi agent publishes\n * 500 choices, omp 581. Variants are **opt-in** — unset or empty offers none\n * of them, and only the listed choice values are added. The bare `agentId`\n * entry (the agent's own default model) is always offered regardless, so an\n * agent is never unreachable. Filtering only affects listing — hidden\n * variants still resolve and stream (see `agent/catalog`).\n *\n * ACP models are deliberately absent from DSH's native model selector — see\n * {@link AgentCatalogResult} for why that is structural, not cosmetic.\n */\n exposedModels?: string[]\n /**\n * Auto-discovery for {@link exposedModels}, ON by default: every model the\n * agent NEWLY publishes (present in an observed catalog but absent from the\n * one known before it) is appended to `exposedModels` automatically. It never\n * touches models the user already had selected, and the very first catalog\n * observation is a baseline (nothing is \"new\" yet). `false` is the explicit\n * opt-out and the only value persisted; unset means \"on\". Flipped from the\n * settings card's lightweight `agent/set-auto-discover-models` RPC — never by\n * `agent/save`, which restarts the process for connection changes.\n */\n autoDiscoverModels?: boolean\n /**\n * How long this agent's process may sit READY with no live session and\n * nothing in flight before the host releases it (kills the child). Unset →\n * the host default (30 minutes). `0` = never release, and only agents with\n * no live session are ever candidates (see `AgentBridge.releaseIfIdle`):\n * probing / post-save spawns are what leak processes, since they hold no\n * session at all. Nothing is lost either way — a released session is\n * resumed with `session/load` on the next send.\n */\n idleTimeoutMs?: number\n /**\n * MCP servers this agent should connect to, in the plugin's NORMALIZED shape\n * (see {@link AcpMcpServerDef}) — not the ACP wire shape.\n *\n * WHY A NORMALIZED SHAPE: the ACP wire union is asymmetric — its stdio\n * variant carries NO `type` field while http/sse do, and `env`/`headers` are\n * `[{name,value}]` arrays. A hand-written config that guessed the wire\n * shape wrong is accepted by the agent and then SILENTLY DROPPED (measured:\n * a stdio entry carrying `type:'stdio'` never connects). The normalized\n * shape has one explicit discriminant, so the wire conversion happens in\n * exactly one place (bridge.ts) and TypeScript checks it against the SDK's\n * `McpServer` union.\n *\n * An agent's OWN MCP configuration (e.g. `~/.codebuddy/.mcp.json`) is read\n * by the agent itself and needs no declaration here — this field only adds\n * servers ON TOP of that.\n */\n mcpServers?: AcpMcpServerDef[]\n}\n\n/** How an MCP server is reached. `acp` (agent→client `mcp/message`) is NOT supported. */\nexport type AcpMcpTransport = 'stdio' | 'http' | 'sse'\n\n/**\n * One MCP server, in the plugin's normalized shape.\n *\n * The ACP-transport variant (agent calls back into the client over\n * `mcp/message`) is deliberately absent: the client side of that method is\n * not in the ACP SDK's stable surface, and no agent advertises the\n * capability. `validateAgentDef` rejects it with that reason.\n */\nexport type AcpMcpServerDef =\n | {\n /** Namespaces the agent's tools as `mcp__<name>__<tool>`; unique per agent. */\n name: string\n transport: 'stdio'\n /** Executable to spawn for this server. */\n command: string\n /** Arguments for the command. */\n args?: string[]\n /** Environment for the server process (layered over the agent's own env). */\n env?: Record<string, string>\n }\n | {\n /** Namespaces the agent's tools as `mcp__<name>__<tool>`; unique per agent. */\n name: string\n transport: 'http' | 'sse'\n /** The server's URL. */\n url: string\n /** Extra request headers (e.g. authorization). */\n headers?: Record<string, string>\n }\n\nexport interface AcpPluginConfig {\n agents: AcpAgentDef[]\n /** Per-session transcript ring bound (default 600 entries). */\n transcriptMaxEntries?: number\n /**\n * DEAD CONFIG — nothing in this plugin reads it. `validateConfig` still\n * accepts it and the host entry still threads it into the `BridgeManager`\n * constructor, but boot no longer probes (`bootstrapCatalogs` is\n * intentionally empty) and `agent/catalog` is pure memory, so there is no\n * automatic spawn left for it to switch off. Catalogs fill only from a\n * gesture: the agent's own `session/new` / `session/load`, the settings\n * card's `agent/probe-models` button, or an on-disk cache. Kept only so\n * that removing it can be a separate cross-file change (impl.ts, bridge.ts\n * and the host entry all touch it today).\n */\n probeModelsOnBoot?: boolean\n /**\n * Budget for one agent startup step (`initialize`, `session/new`, and the\n * `session/load` of a resume). An agent that does not answer within this\n * window is killed and the request fails with `agent_timeout` instead of\n * hanging forever. Default 60000 ms.\n */\n startupTimeoutMs?: number\n /**\n * How long a permission ask may sit unanswered before the deadline fires.\n *\n * ONE window for both places an ask can live — the conversation's approval\n * card and the settings panel's ledger. They ask the same question (\"is\n * anyone there?\"), so they get the same answer; two windows only made the\n * settings card look like two unrelated knobs.\n *\n * `0` means 不等待 — the ask is settled the moment it arrives, without asking\n * anyone. That rung belongs to the `auto` MODE: it answers the ask, so the\n * agent continues. Under `abort` the same zero would refuse every ask on\n * arrival (the agent stops at every gate), so `abort` never runs it — the\n * host normalizes it to the shortest real wait. Never expiring is the `wait`\n * MODE's business, so the two never overlap. Anything else is the wait\n * before the deadline fires. Default 300000 ms (5 minutes).\n *\n * When it fires, {@link AcpPluginConfig.permissionTimeoutMode} decides what\n * happens.\n *\n * Editable from the settings page (`ui/approval-deadlines-set`), which\n * persists an override in the plugin's own state file and applies it to the\n * running bridges without restarting anything; the value here is the\n * deployment baseline that override falls back to. See {@link AcpApprovalDeadlines}.\n */\n permissionTimeoutMs?: number\n /**\n * How long a chat process may sit idle before it is released (parked, then\n * resumed by the next message). In MILLISECONDS; default 1800000 (30 min).\n *\n * This is the HOST DEFAULT: an agent that names its own\n * {@link AcpAgentDef.idleTimeoutMs} keeps its own. Editable from the\n * settings page (「进程管理」 → `ui/idle-release-set`), which persists an\n * override in the plugin's own state file and applies it to the running\n * bridges without restarting anything; the value here is the deployment\n * baseline that override falls back to. See {@link AcpIdleRelease}.\n */\n idleTimeoutMs?: number\n /**\n * LEGACY: the old conversation-route window, kept so an existing\n * `cordis.patch.yml` still says what it meant. Folded into\n * {@link AcpPluginConfig.permissionTimeoutMs} when that key is absent, and\n * ignored when it is present.\n */\n nativeApprovalTimeoutMs?: number\n /**\n * LEGACY: the old ledger-route window, kept for the same reason as\n * {@link AcpPluginConfig.nativeApprovalTimeoutMs}.\n */\n permissionFallbackTimeoutMs?: number\n /**\n * What an expired approval deadline DOES. `wait` (default) never expires\n * at all; `abort` answers `cancelled`; `auto` answers with the option the\n * agent's own kinds imply and lets the agent continue.\n *\n * The default is `wait`: a permission ask is a question FOR A HUMAN, and\n * refusing it because the human looked away is worse than waiting — the\n * ask stays visible, so nobody is left wondering. `abort` and `auto` are\n * opt-in for deployments that would rather have a bounded turn.\n *\n * `auto` is unattended operation — the plugin may approve something no human\n * looked at — so it is opt-in and the outcome is announced. `wait` is the\n * \"hang visibly until I come back\" mode: the ask stays pending and the turn\n * stays open.\n *\n * Editable from the settings page, like {@link nativeApprovalTimeoutMs}.\n */\n permissionTimeoutMode?: PermissionTimeoutMode\n /**\n * After `session/cancel`, how long to wait for the agent to settle the turn\n * before the host force-finalizes it. An agent that ignores the cancel\n * notification must not wedge the session in `running` forever.\n * Default 15000 ms; `0` disables the fallback.\n */\n cancelFallbackMs?: number\n /**\n * How long to wait before resending a prompt the agent answered with\n * `cancelled` although no cancel was requested for that turn. Some agents\n * (observed on codebuddy 2.161.4) clean up cancel state asynchronously, and\n * a prompt landing inside that cleanup window is answered with `cancelled`\n * without generating a turn — the user's message is silently swallowed.\n * The delay must outlast the agent's cleanup window. Default 600 ms.\n */\n staleCancelRetryDelayMs?: number\n /**\n * How many times a stale-cancelled prompt is resent before the host gives\n * up and reports the `cancelled` stop as-is. Default 2.\n */\n staleCancelMaxRetries?: number\n /**\n * Inactivity watchdog for a turn the agent owes an answer: how long the turn\n * may stay SILENT before the host finalizes it as stalled.\n *\n * The clock is `LiveSession.lastAgentActivityAt`, refreshed by ANY\n * `session/update`, at every turn start, at each segment start (the host\n * handing the turn back after a card), and when a permission ask arrives or\n * is resolved. The watchdog only READS it while the turn is watched: an OPEN\n * agent segment (always true on the `/acp`-RPC path, which has no driver\n * stream) and no pending approval. The host-card execution phase and an\n * unanswered ask are exempt by that gate, not because the clock is frozen —\n * they legitimately move it too.\n *\n * Firing finalizes the TURN only (an `agent/notice` plus the usual turn\n * end); it does NOT kill the agent process. The wedged `session/prompt` is\n * abandoned, and the next message reclaims the session: parked with\n * `releaseAll` and resumed with `session/load`, or reset outright when the\n * agent declares no `loadSession` (the context is gone and the user is\n * told).\n *\n * Default 600000 ms (10 minutes); `0` disables the WHOLE watchdog, the tool\n * window included.\n */\n turnStallMs?: number\n /**\n * The longer silence window applied while ANY ACP tool call is still active\n * by `toolCallId` — parallel calls are tracked as a set, so the window lasts\n * until the LAST of them finishes. The agent is working on its own clock in\n * there, so the plain turn window would call a healthy long-running tool\n * stalled.\n *\n * Default 1800000 ms (30 minutes); `0` falls back to\n * {@link AcpPluginConfig.turnStallMs}. It matches\n * `TOOL_OUTPUT_WAIT_MS = 30 * 60 * 1000` in `presets/acp/acp-guard.mjs`, so\n * the two halves of one turn agree on how long a tool may take.\n */\n turnStallToolMs?: number\n /**\n * How often the watchdog scans for silent turns, in milliseconds. A tuning /\n * test knob only: NO settings-page control exists for it — and none for the\n * two windows above either; the whole watchdog is configured in the patch\n * file. Default 30000 ms.\n */\n turnStallTickMs?: number\n}\n\n// ---- Host → browser events (`/api/acp/stream`, SSE) ----\n\nexport type AgentProcessState = 'stopped' | 'starting' | 'ready' | 'error'\n\n/** Agent facts the browser may render (never includes env/argv secrets beyond argv itself, which is config-visible). */\n/**\n * State of the ACP-mode agent preset in the HARNESS's own preset roster.\n *\n * Nothing is installed any more: the preset ships as this package's bundle\n * patch (`presets/acp/acp.patch.yml`), which the harness composes when it\n * builds the profile, so the only question left is whether the roster carries\n * the declaration and can compose a session from it — which is exactly what a\n * `preset-<id>` row that failed to activate reports as `problem`.\n */\nexport interface AcpPresetStatus {\n /** Preset id (a session's `agentPreset` value; matched by the browser half). */\n id: string\n /** The declaration inside this package, for copy and debugging. */\n source: string\n /** Whether the roster carries the preset and it can compose a session. */\n installed: boolean\n /** The harness's own diagnostic when the declaration exists but is unusable. */\n problem?: string\n}\n\nexport interface AgentView {\n id: string\n title: string\n command: string\n args: string[]\n cwd?: string\n allowedCwdRoots: string[]\n state: AgentProcessState\n /** Last stderr lines when state is `error` (diagnostics). */\n stderrTail?: string[]\n /** `initialize` result, once the process is ready. */\n agentInfo?: { name?: string; title?: string; version?: string }\n agentCapabilities?: unknown\n liveSessions: string[]\n /**\n * The window the host actually honors for this agent (its own\n * `idleTimeoutMs`, else the host default). Resolved, not raw, so the panel\n * can show a concrete number instead of guessing at a default; `0` = never\n * released. Absent on an older host half.\n */\n idleTimeoutMs?: number\n}\n\n/**\n * Why the host released a session's agent process. The browser turns this into\n * one runtime-log line per cause; `idle` stays silent (expected lifecycle).\n */\nexport type AcpReleaseCause = 'idle' | 'workspace' | 'resume-failed' | 'stalled' | 'died'\n\nexport type AcpEvent =\n | { type: 'agent/status'; agentId: string; state: AgentProcessState; error?: string; agent?: AgentView }\n | { type: 'agent/notice'; agentId: string; level: 'info' | 'error'; text: string }\n | { type: 'session/created'; sessionId: string; session: SessionView }\n | { type: 'session/meta'; sessionId: string; session: SessionView }\n | { type: 'session/closed'; sessionId: string; reason: 'closed' | 'agent-stopped' | 'agent-died' }\n | {\n /** The agent process died: the session is kept resumable, not destroyed. */\n type: 'session/released'\n sessionId: string\n session: SessionView\n reason: string\n /** Host's release-cause classification; absent on an older host half. */\n cause?: AcpReleaseCause\n }\n | {\n /**\n * The session was resumed through `session/load` on a fresh agent\n * process; the agent replays its history as fresh `session/update`s, so\n * the browser must drop local blocks and the seq watermark first.\n */\n type: 'session/reset'\n sessionId: string\n session: SessionView\n }\n | { type: 'session/update'; sessionId: string; seq: number; update: SessionUpdate }\n /**\n * One agent's learned surface changed on the host because a real session\n * published new `configOptions` or `availableCommands` (its `session/new` /\n * `session/load`, a `config_option_update`, or an\n * `available_commands_update`). The browser must drop its cached seat\n * catalog for that agent and re-read `agent/catalog`; without this, a\n * conversation opened before the agent's first real session keeps rendering\n * yesterday's model list (user report, 2026-10-19) — and a conversation\n * opened after it would miss the agent's slash commands until a reload.\n */\n | { type: 'catalog/updated'; agentId: string; updatedAt: number }\n /**\n * One agent's model visibility changed outside `agent/save`: the settings\n * panel edited `exposedModels`, the auto-discovery switch was flipped, or an\n * observed catalog contributed newly published models. The browser patches\n * its copy of the agent definition (the settings card) and re-reads\n * `agent/catalog` (the composer's seat list). `exposedModels: []` means the\n * same as unset — no `agentId@choice` variants.\n */\n | { type: 'agent/exposure'; agentId: string; exposedModels: string[]; autoDiscoverModels: boolean }\n /**\n * One outstanding ask, broadcast ONCE when it is created and replayed as-is\n * to every freshly connected client (see `permission/synced`).\n *\n * Every ACP permission ask carries everything the conversation card needs:\n * who owns it (`dshSessionId`), what it is about (`toolCall` + the derived\n * `toolName` / `reason`), the options to render verbatim, and the deadline in\n * force. The host never answers on its own outside an expired `abort` /\n * `auto` deadline, so this frame (and its replay) is the answer entry.\n */\n | {\n type: 'permission/request'\n requestId: string\n /** ACP session id the ask belongs to. */\n sessionId: string\n /** DSH chat session id; absent for `/acp` RPC-only sessions. */\n dshSessionId?: string\n /** Raw ACP `ToolCallUpdate` the agent sent — never summarized away. */\n toolCall: unknown\n /** Host-derived human name (`permissionToolName`, `_meta` fallback included). */\n toolName: string\n /** One-line detail from the agent's declared input, when it has any. */\n reason?: string\n options: PermissionOptionView[]\n /** What an unanswered ask does when its deadline fires. */\n timeoutMode: PermissionTimeoutMode\n /** Epoch ms of the deadline; absent in `wait` mode (no deadline). */\n deadlineAt?: number\n }\n | { type: 'permission/resolved'; requestId: string; sessionId: string; outcome: PermissionOutcome }\n /**\n * An ask whose deadline fired and was settled by the plugin itself — refused\n * (`abort`), or answered with the option the agent's own kinds imply\n * (`auto`).\n *\n * This is the conversation's record of the expiry: the card it was answering\n * is gone, so without this frame the user would see it vanish with nothing\n * said about it.\n */\n | {\n type: 'permission/expired'\n sessionId: string\n toolCall: unknown\n /** How long the ask waited before the deadline fired. */\n timeoutMs: number\n /** What the plugin answered with. */\n outcome: PermissionOutcome\n /** The picked option's label, when one was picked (`auto` mode). */\n optionName?: string\n }\n /**\n * Terminator of the pending-permission REPLAY a freshly connected client\n * receives (see the `/api/acp/stream` route). A `permission/request` frame is\n * broadcast once, when the ask is created — a client that connects later (page\n * reload, a second tab, a stream outage) would otherwise never learn the ask\n * exists, and the settings-panel ledger card is the only answer entry for it.\n * The replay re-sends every outstanding ask and then this frame, whose\n * `requestIds` is the authoritative live set: the client drops any pending ask\n * of its own that is not in it (one resolved while it was disconnected).\n */\n | { type: 'permission/synced'; requestIds: string[] }\n | { type: 'turn/start'; sessionId: string; turnId: string; text: string }\n | {\n type: 'turn/metrics'\n /** ACP session id (the store keys live burst state by it). */\n sessionId: string\n /** DSH chat session id (routing/context only). */\n dshSessionId: string\n turnId: string\n /** Session aggregate INCLUDING the current turn when measurable. */\n outputTokens: number\n burstMs: number\n final: boolean\n }\n | { type: 'turn/end'; sessionId: string; turnId: string; stopReason?: string; error?: string }\n /**\n * A DSH chat conversation was locked to its agent at the start of its first\n * ACP turn. Addressed by the CHAT session id (the browser may not know the\n * ACP session yet). The composer plays its base→tag hand-off on this frame;\n * a `session/chat-list` read is only the fallback (it can arrive a turn late).\n */\n | { type: 'chat/agent-locked'; dshSessionId: string; agentId: string }\n /**\n * The provisional lock of a first turn that delivered no visible output was\n * rolled back: the conversation may switch agents again, and its base must\n * return. The failed ACP session is closed with it.\n */\n | { type: 'chat/agent-unlocked'; dshSessionId: string }\n\nexport interface PermissionOptionView {\n optionId: string\n name: string\n kind: string\n}\n\nexport type PermissionOutcome = { outcome: 'selected'; optionId: string } | { outcome: 'cancelled' }\n\n/** Plugin-side measurable burst aggregate for one ACP session (not host turn timings). */\nexport interface SessionBurst {\n /** Sum of estimated output tokens over measurable turns. */\n outputTokens: number\n /** Sum of within-burst chunk gaps over those turns; tool/approval/long pauses excluded. */\n burstMs: number\n}\n\n/** Derived session snapshot for replay/reconnect (source of truth stays in the transcript). */\nexport interface SessionView {\n sessionId: string\n agentId: string\n acpSessionId: string\n cwd: string\n title?: string\n modes?: { currentModeId?: string; availableModes?: Array<{ id: string; name?: string; description?: string }> }\n configOptions?: unknown[]\n /** The agent's slash commands, every field it declared — not just the two the picker draws. */\n availableCommands?: Array<{ name: string; description?: string; [key: string]: unknown }>\n plan?: unknown[]\n usage?: { used: number; size: number; cost?: { amount: number; currency: string } }\n /** Measurable burst aggregate over this session's turns; absent until measured. */\n burst?: SessionBurst\n running: boolean\n closed: boolean\n /**\n * The owning agent process died and the session awaits a `session/load`\n * resume. Resuming happens implicitly on the next prompt (\"send to\n * recover\") when the agent declares the `loadSession` capability.\n */\n released?: boolean\n /**\n * The agent-side session has history while the local buffer is empty: the\n * record was restored from disk after a host restart (the `session/load`\n * replay has not landed), or a resume cleared the buffer ahead of the\n * replay. The ACP view then says history replays on restore instead of\n * claiming \"nothing yet\".\n */\n hasHistory?: boolean\n /** Highest transcript seq included in this snapshot (for gap detection). */\n seq: number\n}\n\n// ---- Browser → host RPC (`/acp` channel; payloads below) ----\n\nexport type RpcResult<T> = {\n ok: true\n value: T\n} | {\n ok: false\n /** Mirrors the host `ConnectionRpcFailure` shape (details is transport-mandated). */\n error: { code: string; message: string; details: object }\n}\n\nexport interface AgentsListResult {\n agents: AgentView[]\n}\nexport type SessionPromptResult = { accepted: true; turnId: string }\nexport type TranscriptResult = { session: SessionView; entries: Array<{ seq: number; update: SessionUpdate }> }\nexport type SessionsListResult = { sessions: SessionView[] }\n\n/**\n * One ACP agent session currently backing a DSH chat session, looked up by the\n * chat session id the composer already knows. This is what lets the browser\n * expose an agent's OWN mode list, model, thought level AND slash commands in\n * the conversation (the ACP-native controls), independently of DSH's model\n * selection and command catalog.\n */\nexport interface ChatSessionView {\n agentId: string\n /** Display name of the agent definition, when it differs from the id. */\n agentTitle?: string\n /** Host-minted session id (stable across an agent restart). */\n sessionId: string\n title?: string\n /**\n * The adapter model id this session was last aligned to\n * (`agentId` / `agentId@choice`), as recorded by the host half. Absent until\n * the first turn or an explicit switch.\n */\n modelId?: string\n /** The reasoning effort in force, when the agent reports one. */\n reasoningEffort?: string\n /** The agent's declared modes, when it has any (`session/new` / `session/load`). */\n modes?: SessionView['modes']\n /**\n * The slash commands the agent declared (`available_commands_update`). The\n * composer's `/` menu shows THESE in an ACP conversation — the host's own\n * command catalog describes commands this runtime would execute, which the\n * agent can never see.\n */\n availableCommands?: SessionView['availableCommands']\n /**\n * Context occupancy the agent last reported (`usage_update`): prompt-side\n * used/size, not request tokens. Absent until the agent reports one; the\n * browser live-patches this row from the update frames (see AGENTS/ui.md).\n */\n usage?: SessionView['usage']\n /** Measurable burst aggregate over this session's turns; absent until measured. */\n burst?: SessionView['burst']\n /** The agent process died; the session awaits a `session/load` resume. */\n released?: boolean\n /** The agent is running a turn on this session right now. */\n running?: boolean\n /**\n * Whether this session's ACP agent can be forked (`session/fork`) into a\n * child conversation, as the host computed it. The browser does not derive\n * this itself — the capability is host knowledge (the agent's\n * `initialize`), and the row is the one place it is delivered.\n * `supported: false` carries the reason; no reason on `false` means the\n * host half predates the field (treat as unsupported).\n */\n fork?: ChatSessionForkInfo\n}\n/**\n * The host's per-row verdict on forking one chat session's ACP session\n * (`ChatSessionView.fork`). Computed from the agent's LAST KNOWN\n * `sessionCapabilities.fork` (persisted with the model catalog) plus the\n * session's own state — the browser never reads capability bits itself.\n */\nexport interface ChatSessionForkInfo {\n supported: boolean\n /** Why {@link supported} is false: the agent cannot fork, or the session cannot be forked right now. */\n reason?: 'unsupported' | 'released' | 'busy'\n}\nexport type ChatSessionsListResult = { sessions: ChatSessionView[] }\n\n/**\n * The `session/chat-list` answer: the backing ACP sessions, plus whether that\n * DSH chat session runs the ACP agent preset (host-read from the session log).\n * The composer surfaces fall back to the latter when the client runtime's\n * session rows carry no `agentPreset` — on such hosts it is the only fact\n * source they have.\n */\nexport interface ChatSessionListResult extends ChatSessionsListResult {\n acpMode?: boolean\n /**\n * The agent this conversation is LOCKED to, once its first ACP turn has run.\n *\n * The composer's agent base retires at that point and the composer-edge tag\n * shows this agent; the tag is display-only because a different agent would\n * have none of the conversation's context. The lock is persisted with the\n * conversation record, so it outlives the ACP session (and a host restart),\n * and it is reported even when the agent is no longer in the roster — the\n * conversation still belongs to it.\n */\n lockedAgentId?: string\n /**\n * The conversation's persisted pick (agent + model + thought level), when one\n * exists. This is what the composer reads BEFORE the conversation has an ACP\n * session, so a model chosen up front shows up as the current one instead of\n * appearing to have been forgotten.\n */\n choice?: AcpConversationChoice\n}\n\n/**\n * One ACP session's folded transcript, addressed by its DSH chat session.\n *\n * This is the data behind the conversation's ACP view tab. The native chat\n * plane renders what the LLM adapter emits, so the agent's prose and thinking\n * ride there — but a fuller card (a tool call with its arguments, a plan, a\n * unified diff) has no chunk vocabulary in the adapter's output, so it is\n * rendered here instead. This is how the browser reaches it without knowing an\n * ACP session id up front. See docs/acp-convergence.md §1.2.\n */\nexport interface ChatTranscriptView {\n session: SessionView\n /**\n * Raw ACP `session/update` payloads in seq order (the fold happens\n * client-side). `unknown` is deliberate, matching `TranscriptResult`: the\n * host's buffer is transport-agnostic and must not import the SDK.\n */\n entries: Array<{ seq: number; update: unknown }>\n}\nexport type ChatTranscriptResult = { views: ChatTranscriptView[] }\nexport interface ChatTranscriptPayload { dshSessionId: string; afterSeq?: number }\n\nexport interface SessionStartPayload { agentId: string; cwd?: string }\nexport interface SessionPromptPayload { sessionId: string; text: string }\nexport interface SessionIdPayload { sessionId: string }\nexport interface PermissionRespondPayload { requestId: string; optionId?: string }\nexport interface SessionModeSetPayload { sessionId: string; modeId: string }\nexport interface SessionConfigSetPayload { sessionId: string; configId: string; value: string | boolean }\nexport interface TranscriptGetPayload { sessionId: string; afterSeq?: number }\nexport interface AgentIdPayload { agentId: string }\n/**\n * The plugin's OWN default ACP agent (the panel's ★ gesture): which agent an\n * ACP-mode conversation starts on. Plugin-private state — the host never sees a\n * model route here, and nothing in this plugin writes DSH's model selection\n * (see docs/acp-convergence.md §2.2).\n */\nexport interface DefaultAgentSetPayload { agentId?: string }\n/** `default/agent`: the plugin's own default agent id, or `undefined` when unset. */\nexport type DefaultAgentResult = { agentId?: string }\n/** `plugin/version`: the installed host package's version, for the settings badge. */\nexport type PluginVersionResult = { version?: string }\n\n/**\n * Where the plugin's own update check stands. The host half only ever reports\n * facts it can read from the harness plugin manager and the npm registry — it\n * never downloads, swaps files, or restarts itself.\n */\nexport type PluginUpdateStatus =\n | 'unsupported' | 'not-installed' | 'source-install' | 'managed'\n | 'current' | 'available' | 'applying' | 'busy'\n | 'applied' | 'restart-required' | 'failed'\n\n/**\n * One update-check answer, shared by `plugin/update-check` and\n * `plugin/update-apply`.\n */\nexport interface PluginUpdateState {\n status: PluginUpdateStatus\n /** 运行中(内存里)的插件版本 */\n current: string\n /** 磁盘上装着的版本(listBundles().version),可读时才有 */\n installed?: string\n /** profile registry 链上最新的稳定版 */\n latest?: string\n /** latest 的 npm 发布时间 ISO */\n publishedAt?: string\n /** 实际按顺序问过的 registry */\n registries?: string[]\n /** 失败/额外说明(英文技术信息,UI 自己负责本地化) */\n message?: string\n}\n\n/**\n * How the composer-edge agent tag shows liveness (the blue halo). `breath`\n * pulses the halo at the speed below; `steady` shows a constant glow.\n * Plugin-private display preference, persisted with the agent registry.\n */\nexport interface AcpTagHalo {\n mode: 'breath' | 'steady'\n speed: 'slow' | 'normal' | 'fast'\n}\n/** Default when nothing was ever saved: the breathing halo at normal speed. */\nexport const DEFAULT_TAG_HALO: AcpTagHalo = { mode: 'breath', speed: 'normal' }\n/** `ui/tag-halo`: read the halo preference. */\nexport type TagHaloResult = { halo: AcpTagHalo }\n/** `ui/tag-halo-set`: write the halo preference. */\nexport interface TagHaloSetPayload { halo: AcpTagHalo }\n\n/** The approval deadline the settings page can edit, by field name. ONE window. */\nexport type ApprovalDeadlineKey = 'permissionTimeoutMs'\n\n/**\n * The idle-release windows the settings page offers, in MILLISECONDS.\n *\n * ONE ladder, shared by both halves: the browser snaps its slider to these\n * values and the host accepts only these values, so a window the slider can\n * produce is always a window the host runs. The first rung is 1 minute (an\n * aggressive release), the rest are 5-minute steps up to an hour.\n */\nexport const IDLE_RELEASE_MS: readonly number[] = [\n 60_000, 300_000, 600_000, 900_000, 1_200_000, 1_500_000, 1_800_000, 2_100_000, 2_400_000, 2_700_000, 3_000_000, 3_300_000, 3_600_000,\n]\n\n/**\n * The built-in idle-release window for chat processes: 30 minutes.\n *\n * Lives beside the ladder it is a rung of, so the two cannot drift: a\n * deployment that names no window of its own runs this, and the settings\n * page's reset returns to it.\n */\nexport const DEFAULT_IDLE_TIMEOUT_MS = 1_800_000\n\n/**\n * The idle-release window for chat processes, as the settings page edits it.\n *\n * The window is the HOST default: an agent that names its own\n * {@link AcpAgentDef.idleTimeoutMs} keeps it, and a probe-only process (one\n * that never hosted a session) still honors the short probe window — see\n * `AGENTS/protocol.md` §5.\n */\nexport interface AcpIdleRelease {\n /** The window in force, in ms. */\n idleTimeoutMs: number\n /**\n * The window a RESET returns to: the deployment baseline when the config\n * file names one, else the built-in default (30 minutes).\n */\n defaultIdleTimeoutMs: number\n /**\n * The window `cordis.patch.yml` names, when it names one. Reported only\n * when the file actually names one: the settings card must not say \"the\n * file asks for 30 minutes\" about a file that says nothing.\n */\n configuredIdleTimeoutMs?: number\n}\n/** `ui/idle-release`: read the idle-release window in force. */\nexport type IdleReleaseResult = { idleRelease?: AcpIdleRelease }\n/**\n * `ui/idle-release-set`: write the idle-release window, or drop the persisted\n * override with `reset` so the built-in default applies again.\n */\nexport interface IdleReleaseSetPayload {\n /** The window to run, in ms; omitted when `reset` is true. */\n idleTimeoutMs?: number\n /** Drop the persisted override so the built-in default applies again. */\n reset?: boolean\n}\n\n/**\n * What an EXPIRED approval deadline does. Three modes, and only three — an\n * unanswered ask either gets answered for you, keeps waiting, or is refused.\n *\n * - `abort`: answer `cancelled`. The operation is refused and the agent\n * continues with that answer.\n * - `auto`: answer the ask with the option the AGENT's own kinds imply\n * (`allow_always` → `allow_once` → `reject_once` → `reject_always` → the\n * first option), and the agent CONTINUES executing. This is unattended\n * operation: the plugin may end up approving something nobody looked at,\n * which is why it is opt-in and why the outcome is announced.\n * - `wait` (default): never expire. The ask stays pending, visibly, until a\n * human answers it or the session is cancelled.\n *\n * Same three modes as OmniTerm dev's `acp_perm_timeout_mode`, under the same\n * names. The plugin's own default is `wait`, not `abort` and not `auto`: a\n * permission ask is a question for a human, so the safe default is to keep\n * waiting rather than to refuse (or worse, approve) on their behalf.\n */\nexport type PermissionTimeoutMode = 'abort' | 'auto' | 'wait'\n\n/**\n * What the settings page's approval-deadline card shows: the values in force,\n * plus the facts that explain why a value the user did not pick is winning.\n *\n * `configured` is present only when `cordis.patch.yml` names a key — a settings\n * override outranks it, and the card must say so rather than silently showing\n * the override. `configuredMode` is the same fact about the mode.\n */\nexport interface AcpApprovalDeadlines {\n /**\n * The one wait window, in ms. `0` means 不等待 — the ask is settled on\n * arrival, without asking anyone, and only the `auto` mode runs it.\n * Never expiring is the `mode`'s business.\n *\n * It covers BOTH places an ask can live (the conversation's approval card\n * and the settings panel's ledger): one question, one answer, one number.\n */\n permissionTimeoutMs: number\n /** What an expired deadline does. Always present: `wait` is the default. */\n mode: PermissionTimeoutMode\n /** The config-file value for the window, when the file names one. */\n configured?: Partial<Record<ApprovalDeadlineKey, number>>\n /** The config-file baseline for `mode`, when the file names one. */\n configuredMode?: PermissionTimeoutMode\n}\n/**\n * `ui/approval-deadlines`: read the deadlines in force. `deadlines` is absent\n * on a host half from before the card existed, which hides the card rather than\n * showing a number the browser invented.\n */\nexport type ApprovalDeadlinesResult = { deadlines?: AcpApprovalDeadlines }\n/**\n * One write to the approval-deadline settings: the window, and the mode.\n * Omitted keys keep their current value — a card that only moved the mode\n * must not reset the number.\n */\nexport interface ApprovalDeadlinePatch {\n permissionTimeoutMs?: number\n mode?: PermissionTimeoutMode\n}\n/**\n * `ui/approval-deadlines-set`: write the deadline, or the mode.\n * `reset: true` drops the persisted override so the `cordis.patch.yml`\n * baseline (or the built-in default) applies again.\n */\nexport interface ApprovalDeadlinesSetPayload extends ApprovalDeadlinePatch {\n reset?: boolean\n}\n\n/**\n * `session/chat-bind`: point one DSH chat conversation at an ACP agent. The\n * plugin's own conversation↔agent mapping — deliberately NOT a DSH model\n * selection, which is what polluting the deployment default and getting\n * refused by the GUI's catalog gate both came from.\n */\nexport interface ChatBindPayload { dshSessionId: string; agentId: string }\n/** `session/chat-model-set`: switch the bound agent's model (`agentId@choice`). */\nexport interface ChatModelSetPayload { dshSessionId: string; modelId: string }\n/** `session/chat-effort-set`: switch (or clear, by omitting) the agent's effort; `agentId` names the agent a not-yet-bound conversation is served by (the plugin default). */\nexport interface ChatEffortSetPayload { dshSessionId: string; effort?: string; agentId?: string }\n/** `session/chat-config-set`: set one of the agent's extra config options. */\nexport interface ChatConfigSetPayload { dshSessionId: string; configId: string; value: string | boolean; agentId?: string }\n\n/**\n * `session/chat-fork-adopt`: bind the DSH chat session a host `sessions.fork`\n * just created to the SAME agent process, through the agent's own\n * `session/fork` of the parent conversation's ACP session — the second half of\n * the two-stage branch (host builds the DSH tree first, the plugin then adopts\n * the ACP side; see the NTR message-actions plan, §D5/D7).\n *\n * On success the child conversation's next message reaches an agent that\n * actually remembers the parent context. On failure NOTHING is bound: the\n * child stays an ordinary copied-history conversation and its first message\n * starts a fresh `session/new` (today's behavior).\n */\nexport interface ChatForkAdoptPayload { dshSessionId: string; parentDshSessionId: string }\n/** `session/chat-fork-adopt` success: the adopted ACP session's ids. */\nexport type ChatForkAdoptResult = { sessionId: string; acpSessionId: string }\n\n/** Host-detected agent presence and tested defaults. */\nexport interface DetectedAgentInfo {\n id: string\n name: string\n description?: string\n detected: boolean\n executablePath?: string\n defaultCommand: string\n defaultArgs: string[]\n currentConfig?: AcpAgentDef\n}\n\nexport type AgentDetectResult = { agents: DetectedAgentInfo[] }\n\nexport interface AgentSavePayload {\n id: string\n title?: string\n command: string\n args?: string[]\n npmPackage?: string\n cwd?: string\n allowedCwdRoots?: string[]\n env?: Record<string, string>\n exposedModels?: string[]\n /** Opt out of auto-discovery with `false`; absent keeps the default (on). See {@link AcpAgentDef.autoDiscoverModels}. */\n autoDiscoverModels?: boolean\n /** Absent → keep the host default (30 minutes); `0` = never release. */\n idleTimeoutMs?: number\n /** MCP servers to connect to, in the normalized shape. See {@link AcpAgentDef.mcpServers}. */\n mcpServers?: AcpMcpServerDef[]\n}\n\n/** Result of `agent/test`: spawn + initialize a throwaway process from form values, then tear it down. */\nexport interface AgentTestResult {\n ok: boolean\n /** Failure reason when `ok` is false (spawn ENOENT, install failure, initialize timeout, …). */\n error?: string\n /** The agent's `initialize` identity, when the handshake completed. */\n agentInfo?: { name?: string; title?: string; version?: string }\n /** Wall-clock cost of the whole test, including a triggered npm install. */\n durationMs: number\n /** Tail of the child's stderr (and installer output), newest last. */\n stderr: string[]\n}\n\nexport interface AgentExposedModelsPayload {\n agentId: string\n /** Absent → clear the filter (expose no variants); array (even empty) → expose exactly these. */\n exposedModels?: string[]\n}\n\n// ---- Model catalog (host-side cache of ACP `configOptions`) ----\n\n/** One selectable value of a `model` / `thought_level` config option. */\nexport interface AcpCatalogChoice {\n value: string\n name: string\n description?: string\n /**\n * Label of the `SessionConfigSelectGroup` this choice arrived under\n * (commonly the vendor); absent when the agent ships a flat option list.\n * Deliberately never guessed from name/value — vendors cross-list models,\n * so only the agent's own grouping is trustworthy.\n */\n group?: string\n}\n\n/** A parsed `category: model | thought_level` select option for one agent. */\nexport interface AcpCatalogOption {\n configId: string\n name: string\n /**\n * `'boolean'` for an on/off toggle: `currentValue` is the string `'true'` /\n * `'false'` (the catalog stays string-typed so every existing reader keeps\n * working) and `choices` is empty. Absent means the ordinary select.\n */\n kind?: 'boolean'\n /** Currently selected value at parse time (may be absent from `choices`). */\n currentValue: string | null\n choices: AcpCatalogChoice[]\n}\n\n/**\n * The per-category catalog derived from one agent's `configOptions`.\n *\n * `mode` is the agent's own permission/working mode published as a\n * `category: 'mode'` select. It duplicates the `modes` field of `session/new`\n * on the agents observed so far (omp, codebuddy), but it is the only copy a\n * conversation can read BEFORE it has a live session — which is exactly the\n * window in which the composer must still show the control (user report,\n * 2026-10-18: the mode select vanished on a conversation set up ahead of its\n * first message).\n *\n * `extras` are the agent's REMAINING select options (temperature, vendor\n * switches, `model_config` entries, anything unknown). They have no dedicated\n * control in the composer, so they ride the \"advanced\" base to the left of the\n * model chip. Keeping them here — rather than in the browser's raw\n * `configOptions` — is what lets a conversation with no live session show and\n * set them.\n */\nexport interface AcpAgentCatalog {\n model?: AcpCatalogOption\n thought?: AcpCatalogOption\n mode?: AcpCatalogOption\n extras?: AcpCatalogOption[]\n}\n\n/**\n * Everything one DSH chat conversation has chosen for its ACP agent — the\n * plugin's own per-conversation record, persisted with the agent registry.\n *\n * Every field is a PICK, not a live-session fact: a conversation can be set up\n * before it has ever run a turn (that is the whole point — choosing the agent,\n * model and thought level BEFORE the first message rather than discovering you\n * cannot). Nothing here is a DSH model route (see the module header): these\n * values are applied to the agent's `session/new` / `session/set_config_option`\n * on the next turn, and re-applied when a session is recreated or resumed.\n */\nexport interface AcpConversationChoice {\n /** The ACP agent serving this conversation. */\n agentId: string\n /**\n * Whether this conversation has LOCKED its agent: its first ACP turn has\n * STARTED. From then on the agent cannot be switched — a different agent has\n * none of the context — so the composer hides its picker and the host\n * refuses `session/chat-bind` to another agent (`agent_locked`). The lock is\n * provisional until that turn produces a visible block: a first turn that\n * delivers nothing is rolled back and the conversation is free again. Once\n * delivered, the lock is permanent; start a new conversation to use another\n * agent.\n */\n locked?: boolean\n /**\n * The agent's model as an adapter model id (`agentId` = the agent's default,\n * `agentId@choice` = an explicit variant). Absent means \"the agent's default\".\n */\n modelId?: string\n /** The agent's own `thought_level` value. Absent means \"the agent's default\". */\n effort?: string\n /**\n * The agent's own working mode (`session/set_mode`: `plan` / `acceptEdits` /\n * …). Absent means \"whatever the agent starts on\".\n *\n * This is the agent's vocabulary for HOW it works, distinct from the model and\n * the thought level, but it sits in the same composer bar — so it belongs to\n * the same \"remember what the user last chose here\" contract rather than being\n * the one control that resets every time the process restarts.\n */\n mode?: string\n /**\n * The agent's extra `configOptions`, by `configId` (the \"advanced\" base).\n * Absent means \"none chosen\"; individual keys absent mean \"the agent's own\n * current value\". These are the same values `session/set_config_option`\n * accepts, persisted per conversation so a pick survives a restart and is\n * re-applied when its ACP session is recreated or resumed.\n */\n extraConfigs?: Record<string, string | boolean>\n}\n\n/** Result of `agent/probe-models`: the freshly probed (or cached) catalog. */\nexport interface AgentProbeModelsResult {\n agentId: string\n catalog: AcpAgentCatalog\n updatedAt: number\n}\n\n/** One of the agent's extra select options, shaped for the composer's advanced menu. */\nexport interface AgentCatalogExtraOption {\n configId: string\n /** The agent's own option name (falls back to the config id host-side). */\n name: string\n /**\n * `'boolean'` for an on/off toggle (`currentValue` is `'true'` / `'false'`,\n * `choices` is empty and the composer renders a checkbox row). Absent means\n * the ordinary select. Kept optional so cached catalogs written before\n * toggles existed still read.\n */\n kind?: 'boolean'\n /** The value the agent reports as current, when it reports one. */\n currentValue?: string\n choices: Array<{ id: string; name: string; description?: string }>\n}\n\n/**\n * Result of `agent/catalog`: the ACP model surface for ONE agent.\n *\n * THIS IS THE ONLY PLACE ACP MODELS EXIST. DSH builds its provider/model\n * catalog per *request*, not per session — `buildModelCatalog(ctx)` takes no\n * session id, so every session's model directory is that same global list. A\n * provider that advertises models therefore advertises them to every\n * conversation, including the ones where the external agent has no tool\n * registration, no runtime context and is not the model talking at all. The\n * `acp` route consequently advertises nothing (`LlmAdapter.listModels` is\n * advisory — exact ids still resolve and stream), and the plugin's own seats\n * read this per-agent view instead. There is no per-session seam on the host\n * to filter the global catalog with, so \"advertise nothing\" is the only\n * structurally honest isolation.\n *\n * Read-only and memory-only: it never spawns and never starts a probe, not\n * even for an agent with no cached catalog — that case is reported honestly\n * (`probed: false`, `base` entry only). A catalog fills only from a gesture:\n * the agent's own `session/new` / `session/load`, the settings card's\n * `agent/probe-models` button, or an on-disk cache. (Boot-time probing used\n * to exist and no longer does — see `BridgeManager.bootstrapCatalogs`.)\n */\nexport interface AgentCatalogResult {\n agentId: string\n /** Agent display name (`title` ?? id). */\n title: string\n /**\n * The bare-`agentId` entry: the route that means \"let the agent choose its own\n * model\". Its `name` spells out the model that route CURRENTLY resolves to\n * (`<agent> · <model>`) — the bare route is not a model, so naming it after the\n * agent alone left the checked row in the picker saying nothing about which\n * model was in force.\n */\n base: { id: string; name: string; description?: string }\n /** The `agentId@choice` variants this agent exposes (`exposedModels` applied). */\n variants: Array<{ id: string; name: string; description?: string }>\n /**\n * Reasoning efforts from the agent's own `thought_level` option, when it\n * advertises one. Agent-wide by construction (a thought level is not a\n * property of one model), which is why it rides here and not on `variants`.\n */\n reasoning?: LlmModelReasoningInfo\n /**\n * The model the agent itself reports as selected (`model.currentValue`).\n *\n * This is what the model chip shows for a conversation that has not picked\n * anything yet: without it the chip has no label and therefore does not\n * render at all, so an agent the user simply has not tuned yet looks like an\n * agent with no model axis (user report, 2026-10-18). Its absence is\n * meaningful — an agent that publishes no `category: 'model'` select has no\n * model axis, and the chip stays away.\n *\n * NOTE the difference from `base`: `base` is a ROUTE (bare `agentId`), this is\n * a MODEL VALUE (a choice id). The picker needs both — one to name the route,\n * one to mark the row the conversation is actually on.\n */\n currentModel?: { value: string; name: string; description?: string }\n /**\n * The agent's own MODE options (`category: 'mode'`, e.g. codebuddy's\n * `Always Ask` … `Bypass Permissions`).\n *\n * The live composer select reads the `modes` field of `session/new` /\n * `session/load`; this copy exists for the window before one exists. It is\n * the same list on every agent observed so far (omp, codebuddy), and it is\n * the only source a conversation set up ahead of its first message has.\n */\n mode?: {\n /** The agent's current mode id, when it reports one. */\n currentValue?: string\n choices: Array<{ id: string; name: string; description?: string }>\n }\n /**\n * The agent's remaining select options — everything that is not the model,\n * the thought level, or the mode. Empty/absent means the composer shows no\n * advanced base.\n */\n extras?: AgentCatalogExtraOption[]\n /**\n * The slash commands this agent declared in its most recent session\n * (`available_commands_update`), cached per agent.\n *\n * A live session reports its own list through `session/chat-list`, and that\n * one always wins. This copy exists for the window BEFORE a conversation has\n * an ACP session: typing `/` in a fresh NTR conversation must list the\n * agent's commands, the way the native menu lists DSH's — waiting for the\n * first message to discover them is what makes the menu look empty.\n */\n commands?: SessionView['availableCommands']\n /** When those commands were last observed. */\n commandsUpdatedAt?: number\n /** A probe has produced a catalog for this agent (`false` → base entry only). */\n probed: boolean\n /** When the cached catalog was last refreshed. */\n updatedAt?: number\n}\n\n// ---- LLM Streaming & Adapter types (shared with DSH core LLM service) ----\n\nexport interface LlmModelInfo {\n provider: string\n id: string\n name: string\n description?: string\n inputModalities?: readonly string[]\n}\n\n/** One reasoning effort advertised by a model (mirrors DSH `LlmReasoningEffortInfo`). */\nexport interface LlmReasoningEffortInfo {\n id: string\n name: string\n description?: string\n}\n\n/** Reasoning metadata (mirrors DSH `LlmModelReasoningInfo`): `defaultEffort` must be one of `efforts`. */\nexport interface LlmModelReasoningInfo {\n efforts: readonly LlmReasoningEffortInfo[]\n defaultEffort?: string\n}\n\nexport interface LlmResolvedModelInfo extends LlmModelInfo {\n context?: { contextWindow: number }\n defaultMaxTokens?: number\n reasoning?: LlmModelReasoningInfo\n}\n\nexport interface LlmProviderInfo {\n id: string\n name: string\n}\n\nexport type StreamChunk =\n | { type: 'block-start'; index: number; blockType: 'text' | 'reasoning' | 'tool-call' | 'tool-result' }\n | { type: 'text-delta'; index: number; text: string }\n | { type: 'reasoning-delta'; index: number; text: string }\n | { type: 'tool-call-delta'; index: number; id: string; name?: string; argumentsDelta: string }\n | { type: 'block-end'; index: number; block: unknown }\n | { type: 'usage'; usage: { inputTokens: number; outputTokens: number } }\n | {\n type: 'finish'\n reason:\n | { kind: 'stop' }\n | { kind: 'aborted'; failure?: { message: string; code: string } }\n | { kind: 'error'; failure?: { message: string; code: string } }\n }\n\n/**\n * Provenance of one message in a host-built request. The host tags every\n * message, and the tag is the only way to tell the user's own words apart from\n * the context sections the harness injects around them (`agent-instructions`,\n * `skill-catalog`, the `@deepseek-ai/dsh-system-prompt` runtime snapshot, tool\n * results) — see AGENTS.md rule 16.\n */\nexport interface GenerateMessageSource {\n kind?: string\n plugin?: string\n form?: string\n [k: string]: unknown\n}\n\nexport interface GenerateMessage {\n role: 'system' | 'user' | 'assistant'\n content: Array<{ type: string; text?: string; [k: string]: unknown }>\n /** Host provenance; absent sources are treated as real user input (hand-built callers). */\n source?: GenerateMessageSource\n /** Stable identity of the durable message, when the host built the request from a session log. */\n id?: string\n}\n\nexport interface GenerateOptions {\n provider: string\n model: string\n /** Adapter-owned reasoning effort selected for this exact model. */\n reasoningEffort?: string\n messages: GenerateMessage[]\n system?: string\n signal?: AbortSignal\n sessionId?: string\n /**\n * Auxiliary (non-conversation) call classification, e.g. `session-title` or\n * `compaction`. Ordinary turns leave it unset; an ACP bridge must never turn\n * one of these into a remote agent turn.\n */\n purpose?: string\n}\n\nexport interface PreparedAdapterCall {\n readonly model: LlmResolvedModelInfo\n stream(options: GenerateOptions): AsyncIterable<StreamChunk>\n}\n\nexport interface LlmAdapterLike {\n providerInfo?(provider: string): LlmProviderInfo\n providerRetryPolicy?(provider: string): unknown\n imageRequestPricing?(provider: string, model: string): unknown\n listModels?(provider: string): Promise<readonly LlmModelInfo[]>\n resolveModel?(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>\n prepareCall?(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>\n stream(options: GenerateOptions): AsyncIterable<StreamChunk>\n}\n\n","/**\n * Browser-side state store for the ACP panel. Owns: the `/acp` RPC calls, the\n * `/api/acp/stream` EventSource, per-session block folding (raw ACP\n * `session/update` → render blocks), and the pending permission set.\n *\n * Deliberately framework-free: React binds through `useSyncExternalStore`.\n */\n\nimport type { AcpApprovalDeadlines, AcpConversationChoice, AcpEvent, AcpIdleRelease, AcpPresetStatus, AcpTagHalo, AgentCatalogResult, AgentProbeModelsResult, AgentSavePayload, AgentTestResult, AgentView, ChatSessionView, ChatTranscriptView, ApprovalDeadlinesSetPayload, ApprovalDeadlinesResult, DetectedAgentInfo, IdleReleaseResult, IdleReleaseSetPayload, PermissionTimeoutMode, PluginUpdateState, PluginVersionResult, SessionView } from '../protocol.ts'\nimport { DEFAULT_TAG_HALO } from '../protocol.ts'\n\n/** One slash command the ACP agent declared, whole row (the menu draws name, description and `input.hint`). */\nexport type AgentCommand = NonNullable<ChatSessionView['availableCommands']>[number]\n\n/** The unary RPC face (narrowed `ctx.connection.rpc.call`). */\nexport type RpcCall = (channel: string, endpoint: string, payload: unknown) => Promise<\n | { ok: true; value: unknown }\n | { ok: false; error: { code: string; message: string } }\n>\n\nexport type Block =\n | { kind: 'user'; text: string }\n | { kind: 'agent'; messageId?: string; text: string }\n | { kind: 'thought'; text: string }\n | {\n kind: 'tool'\n toolCallId: string\n title: string\n status: string\n toolKind?: string\n lines: string[]\n /**\n * Structured ACP diffs (`content: [{type:'diff'}]`). Kept apart from\n * {@link lines} because a diff is the one tool payload worth rendering as\n * a diff: flattening it to `diff <path>` throws away everything that makes\n * an edit reviewable.\n */\n diffs?: Array<{ path: string; oldText: string | null; newText: string }>\n /** Text items the card cannot render (diff output, an unknown content kind's JSON). */\n extras?: string[]\n /**\n * The call's arguments, as the agent sent them (`rawInput` / `arguments`).\n * Rendered read-only under the header: the real model's view of \"it ran\n * `ls -la`\" is that line, and the old fold dropped it entirely.\n */\n input?: string\n /** The call's other named fields (`kind`, `status`, `locations`, `terminalId`, `meta`), one `key: value` per line. */\n details?: string[]\n /** The whole notification this block was last written from, pretty-printed. */\n raw?: string\n }\n | { kind: 'plan'; entries: Array<{ content: string; status: string; priority?: string }> }\n /**\n * Any other ACP notification, kept whole: an available-commands list, a mode\n * change, or a kind newer than this build. `text` is the readable fold and\n * `raw` the untouched payload — an agent's word the ACP view cannot format is\n * still an agent's word.\n */\n | { kind: 'event'; event: string; text: string; raw?: string; status?: string }\n | { kind: 'notice'; tone: 'info' | 'error'; text: string }\n\n/**\n * One permission ask waiting for a human. Every field is copied VERBATIM from\n * the host's `permission/request` frame: the host owns what the ask is called\n * (`toolName` / `reason`), which options the agent offered (order included),\n * and whether the ask carries a deadline. The browser does not derive a title\n * from `toolCall` and does not second-guess the agent's option labels.\n */\nexport interface PendingPermission {\n requestId: string\n /** ACP session id. */\n sessionId: string\n /** DSH chat session id; absent for `/acp` RPC-only sessions (settings-only ask). */\n dshSessionId?: string\n toolName: string\n reason?: string\n options: Array<{ optionId: string; name: string; kind: string }>\n timeoutMode: PermissionTimeoutMode\n /** Epoch ms; absent in `wait` mode. */\n deadlineAt?: number\n}\n\n/** Returned by {@link deadlineRemainingLabel} once the deadline has passed. */\nexport const PERMISSION_DEADLINE_EXPIRED = 'expired'\n\n/**\n * The asks that belong to one DSH chat conversation. An ask without a\n * `dshSessionId` belongs to no conversation (an `/acp` RPC-only session), so it\n * is deliberately absent from every conversation's dock and stays in the\n * settings-panel ledger.\n */\nexport function permissionsForSession(\n permissions: readonly PendingPermission[],\n dshSessionId: string,\n): PendingPermission[] {\n return permissions.filter(permission => permission.dshSessionId === dshSessionId)\n}\n\n/** The branch button's six states (the NTR message-actions plan, §D4). */\nexport type ChatForkState =\n | 'ready'\n | 'unsupported'\n | 'released'\n | 'busy'\n | 'notTail'\n | 'hostUnavailable'\n\n/**\n * The branch click's outcome: the child DSH session id on success, or one\n * actionable failure line (also flashed into the notice banner — the line is\n * the conversation's local copy of the same failure).\n */\nexport type ChatForkOutcome = { childId: string } | { failure: string }\n\n/**\n * The branch button's state for one conversation, in the precedence it renders:\n * the host service missing beats everything (nothing can work), then the\n * agent's fork capability, then the session's own state (a parked session\n * before a running one), then the DOM's tail verdict, and only then ready.\n *\n * Every input is a fact someone else owns — the row's `fork` verdict is the\n * host's, `hostFork` is the structural probe of the runtime's `sessions.fork`,\n * `isTail` is read from the host's own `[data-turn-tail]` marker, and\n * `liveRunning` is the SSE-driven running flag (the row's own `running` only\n * refreshes on a chat-list read, so it would answer last turn). `undefined`\n * facts degrade CLOSED: no row and no `fork` verdict both read as not-ready,\n * never as a click-through.\n */\nexport function forkStateOf(\n row: ChatSessionView | undefined,\n hostFork: boolean,\n isTail: boolean,\n liveRunning?: boolean,\n): ChatForkState {\n if (!hostFork) return 'hostUnavailable'\n if (row === undefined) return 'released'\n if (row.fork === undefined) return 'unsupported'\n if (row.fork.supported !== true) return row.fork.reason ?? 'unsupported'\n if (liveRunning === true) return 'busy'\n if (!isTail) return 'notTail'\n return 'ready'\n}\n\n/**\n * Compact countdown for one ask, or `undefined` when there is nothing to count\n * down (`wait` mode, or a frame without a deadline). Pure so the formatting is\n * testable without a clock: the dock passes `Date.now()` on its tick.\n *\n * Past the deadline the answer is {@link PERMISSION_DEADLINE_EXPIRED} rather\n * than a negative number — the host has already armed the expiry, and a card\n * counting backwards would claim a deadline that no longer exists.\n */\nexport function deadlineRemainingLabel(\n deadlineAt: number | undefined,\n now: number,\n mode: PermissionTimeoutMode,\n): string | undefined {\n if (mode === 'wait' || deadlineAt === undefined) return undefined\n const remainingMs = deadlineAt - now\n if (remainingMs <= 0) return PERMISSION_DEADLINE_EXPIRED\n const seconds = Math.ceil(remainingMs / 1000)\n if (seconds < 60) return `${String(seconds)}s`\n const minutes = Math.floor(seconds / 60)\n const rest = seconds % 60\n return rest === 0 ? `${String(minutes)}m` : `${String(minutes)}m ${String(rest)}s`\n}\n\n/**\n * One observed ACP transcript, keyed by ACP session id. This is the ACP view\n * tab's data source: the native chat plane can only render text/reasoning\n * (that is the LLM adapter's entire chunk vocabulary), so the real tool calls,\n * plans and diffs live here and nowhere else.\n */\nexport interface ChatTranscript {\n /** The DSH chat session this ACP session belongs to (its refetch address). */\n owner: string\n session: SessionView\n blocks: Block[]\n /** Highest folded transcript seq (gap detection for the live stream). */\n seq: number\n}\n\n/**\n * One line of the settings page's ACP runtime log. Agent-side notices and\n * session-release reasons land here (bounded, append order oldest→newest);\n * failures of the settings page's OWN actions stay in {@link UiState.notice}.\n */\nexport interface AcpLogEntry {\n id: number\n at: number\n level: 'info' | 'error'\n text: string\n agentId?: string\n}\n\n/** Cap on retained runtime-log lines; older entries fall off the front. */\nexport const LOG_MAX_ENTRIES = 200\n\nexport interface UiState {\n open: boolean\n /**\n * Bumped by {@link AcpStore.refreshLocale}. Labels resolve while rendering,\n * but memoized transcript rows compare props — this counter is what tells\n * them the language switched.\n */\n localeRev: number\n stream: 'init' | 'live' | 'down'\n agents: AgentView[]\n /**\n * Whether the host has answered `agents/list` at least once in this page.\n * `agents: []` alone cannot tell \"no agent configured\" from \"not read yet\";\n * a surface that offers a setup entry on an empty roster must wait for this.\n */\n agentsLoaded: boolean\n sessions: SessionView[]\n activeSessionId?: string\n blocks: Block[]\n permissions: PendingPermission[]\n busy: boolean\n /**\n * The settings page's one-line banner for its OWN action failures. Agent-side\n * runtime messages do not land here; they append to {@link logs} instead.\n */\n notice?: string\n /** Agent-side runtime log, append order oldest→newest, capped at {@link LOG_MAX_ENTRIES}. */\n logs: AcpLogEntry[]\n /** Lines appended since the user last opened the log; reset by {@link AcpStore.markLogsRead}. */\n logUnread: number\n /**\n * The plugin's OWN default ACP agent (the panel's ★ gesture) — which agent a\n * new ACP-mode conversation starts on. NOT the deployment default model: that\n * one is global to every mode, and nothing in this bundle writes it.\n */\n defaultAgent?: string\n detectedAgents: DetectedAgentInfo[]\n detecting: boolean\n /**\n * Outcome of the last explicit re-detect (`重新检测与刷新`). Detection\n * usually finds exactly what the panel already renders — without this line\n * the button reads as dead. Set only by an explicit click, not the ambient\n * resync, and cleared again when that click's detection fails (a stale success\n * line next to the failure notice reads as if nothing happened).\n */\n detectSummary?: string\n /** Probed model catalogs per agent id (from `agent/probe-models`). */\n modelCatalogs: Record<string, AgentProbeModelsResult>\n /**\n * Per-agent ACP model surfaces (from `agent/catalog`) — the data source for\n * the conversation-side chips and the ACP-mode model seat. Kept apart from\n * {@link modelCatalogs} on purpose: that one is the panel's full probe\n * result (every choice an agent published), while this is the filtered,\n * ready-to-render list the seats offer.\n */\n seatCatalogs: Record<string, AgentCatalogResult>\n probingModels: Record<string, boolean>\n /**\n * Last probe failure per agent id, rendered next to the button that\n * triggered it. `notice` alone is not enough: it lives at the top of the\n * settings panel, so a probe failing below the fold (missing cwd, an agent\n * that never answers, …) reads as a dead button.\n */\n probeErrors: Record<string, string>\n /**\n * ACP agent sessions backing the chat session the composer control sits in.\n * Only meaningful together with {@link chatSessionsFor}: the control reads it\n * by chat session id, so a stale list from another conversation never renders.\n */\n chatSessions: ChatSessionView[]\n /** The DSH chat session id {@link chatSessions} was loaded for. */\n chatSessionsFor?: string\n /**\n * Live burst aggregate per ACP session id, from `turn/metrics`. Absent means\n * no measured burst yet; a row's persisted `burst` is the reload seed.\n */\n burstStats: Record<string, { outputTokens: number; burstMs: number; turnId: string; final: boolean }>\n /**\n * The agent tag's halo preference (settings-editable display choice). The\n * tag reads it per render; the host answer in `resync` is the authority and\n * a write lands optimistically first.\n */\n tagHalo: AcpTagHalo\n /**\n * The approval deadlines in force, as the settings card edits them. Absent on\n * a host half from before the card existed, which hides the card rather than\n * showing a number this browser invented. Writes land optimistically and the\n * host answer (returned by the same route) is the authority.\n */\n approvalDeadlines?: AcpApprovalDeadlines\n /**\n * The idle-release window in force, as the settings page edits it.\n * Absent on a host half from before the tab existed, which hides the\n * tab rather than showing a number this browser invented.\n */\n idleRelease?: AcpIdleRelease\n /**\n * Per-chat-session \"does this conversation run the ACP preset\" verdicts, as\n * reported by `session/chat-list` (host-read from the session log). The\n * composer surfaces fall back to this when the client runtime's session rows\n * carry no `agentPreset` (older hosts); a session never asked about is simply\n * absent.\n */\n acpMode: Record<string, boolean>\n /**\n * Per-chat-session ACP agent binding, as reported by `session/chat-list`'s\n * `boundAgentId` (the host's intent map plus its live-session fallback): who\n * serves this conversation's NEXT message. `session/chat-bind` writes the\n * optimistic entry here the moment the click succeeds — the live session\n * list cannot reflect a switch until a turn materializes a session for the\n * new agent, and the chip would otherwise keep naming the previous agent.\n */\n chatBoundAgent: Record<string, string>\n /**\n * Per-chat-session agent lock, as reported by `chat/agent-locked` (the live\n * path) or `session/chat-list`'s `lockedAgentId` (the fallback): the first\n * ACP turn has STARTED, so this conversation belongs to that agent and\n * cannot be switched. The lock is provisional until that turn shows\n * something; `chat/agent-unlocked` removes it when it delivers nothing. The\n * composer hides its agent base and shows a read-only tag for this agent.\n * The id is kept even when the agent has been deleted from the roster — the\n * conversation is still locked to it.\n */\n chatLockedAgent: Record<string, string>\n /**\n * Per-chat-session serial for a lock THIS page saw arrive — bumped by the\n * `chat/agent-locked` frame (the live path, sent when the first turn starts)\n * or by a later `session/chat-list` answer that first reports `lockedAgentId`\n * (the fallback when the frame was missed). A rolled-back first turn followed\n * by a retry therefore gets a fresh serial, and its ceremony plays again.\n *\n * This is the animation's trigger, and the reason it cannot replay on a page\n * load: a conversation that is already locked when its list is first read\n * has no entry here. The composer's base and its edge tag\n * (`agent-lock-anim.ts`) play the shatter/condense hand-off only for a serial\n * they have not consumed.\n */\n agentLockReveal: Record<string, number>\n /**\n * The conversation's persisted pick — model and thought level — as reported by\n * `session/chat-list`'s `choice`, and written optimistically the moment a\n * picker commits one.\n *\n * This is the record that makes a conversation usable BEFORE it has ever run\n * a turn: without it the model / thought-level chips can only reflect a live\n * ACP session, which does not exist until the first message has already gone\n * out on whatever the agent's defaults were.\n */\n chatChoice: Record<string, AcpConversationChoice>\n /**\n * The model-seat shadow gate's own account of what it last decided, and why\n * (`src/client/index.tsx`'s `syncAcpMode`). Read-only, human-facing, and\n * rendered by the ACP view: every branch of that gate ends with the native\n * model seat either shadowed or not, and without this line the four ways it\n * can end \"not shadowed\" are indistinguishable from the outside — which is\n * exactly how \"the ACP base still has not been replaced\" became unreadable.\n */\n seatDiag: string\n /**\n * Diagnostic summary from the permission-seat blanking gate (`conversation.input.permission`).\n */\n permissionSeatDiag: string\n /**\n * Why slash isolation is not in force, when it is not (`idle` while it is).\n * Isolation degrades OPEN — an unrecognized source shape leaves the host's\n * commands in charge — so without this line the only symptom would be a menu\n * that quietly lists the wrong commands.\n */\n slashDiag: string\n /**\n * Observed ACP transcripts keyed by ACP session id. Only sessions the\n * conversation's ACP view asked for are watched (live `session/update`\n * frames fold straight into them), and the map is capped so a long browsing\n * session cannot accumulate every transcript it ever looked at.\n */\n chatTranscripts: Record<string, ChatTranscript>\n /**\n * State of the host's ACP-mode agent preset IN THE HARNESS ROSTER (the entry\n * the new-session picker shows). `undefined` when the host serves no roster\n * (`preset/status` answering `null`), in which case the panel hides the block.\n */\n preset?: AcpPresetStatus\n /**\n * The installed host package's version (`plugin/version`), rendered as the\n * settings page badge. Absent on a host half without the endpoint, which\n * hides the badge rather than naming a version this browser invented.\n */\n pluginVersion?: string\n /**\n * The running version and its update status (`plugin/update-check`). Absent\n * on a host half without the endpoint, which hides the update entry instead\n * of offering an install this browser cannot perform.\n */\n pluginUpdate?: PluginUpdateState\n}\n\ninterface Listener { (): void }\n\nconst MAX_BLOCKS = 400\n/** How many observed ACP transcripts stay live at once (insertion-ordered eviction). */\nconst MAX_WATCHED = 6\n\n/** Raw JSON budget for one event block; the cut is marked. */\nconst RAW_MAX = 256_000\n\n/**\n * How long a fetched update state stays fresh. The settings panel asks on every\n * mount; without this, every open would hit the registry. A forced check (the\n * card's own button) always goes out.\n */\nconst PLUGIN_UPDATE_TTL_MS = 10 * 60 * 1000\n\n/** Pretty-printed JSON of an agent message (never throws, cuts only at {@link RAW_MAX} with a marker). */\nexport function jsonText(value: unknown): string {\n let text: string\n try {\n text = JSON.stringify(value, null, 2) ?? String(value)\n } catch {\n text = String(value)\n }\n return text.length > RAW_MAX\n ? `${text.slice(0, RAW_MAX)}\\n… [truncated ${String(text.length - RAW_MAX)} chars]`\n : text\n}\n\n/**\n * The call's arguments as the agent sent them. Accepts every spelling seen in\n * the wild (`rawInput` / `arguments`, string / object / array) and never\n * invents one: a call with no input gets `undefined`.\n */\nexport function toolInputOf(update: Record<string, unknown>): string | undefined {\n for (const key of ['rawInput', 'arguments'] as const) {\n const value = update[key]\n if (typeof value === 'string') {\n if (value !== '') return value\n continue\n }\n if (Array.isArray(value)) {\n if (value.length > 0) return JSON.stringify(value, null, 2)\n continue\n }\n if (typeof value === 'object' && value !== null) return JSON.stringify(value, null, 2)\n }\n return undefined\n}\n\n/** The call's other named fields, one `key: value` per line. */\nexport function toolDetailsOf(update: Record<string, unknown>): string[] {\n const lines: string[] = []\n for (const key of ['kind', 'status', 'locations', 'terminalId', 'meta'] as const) {\n const value = update[key]\n if (value === undefined || value === null || value === '') continue\n if (key === 'locations' && Array.isArray(value) && value.length === 0) continue\n lines.push(`${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`)\n }\n return lines\n}\n\n/** Text a `text` / `thinking` chunk's `content` carries (string, object, or array of blocks). */\nfunction chunkText(content: unknown): string {\n if (typeof content === 'string') return content\n if (Array.isArray(content)) {\n return content.map(item => {\n if (typeof item === 'string') return item\n if (typeof item === 'object' && item !== null) {\n const row = item as Record<string, unknown>\n return typeof row.text === 'string' ? row.text : JSON.stringify(item)\n }\n return ''\n }).join('')\n }\n if (typeof content === 'object' && content !== null) {\n const row = content as Record<string, unknown>\n if (typeof row.text === 'string') return row.text\n return JSON.stringify(content)\n }\n return ''\n}\n\n/**\n * One tool call's `content`, split into what the card renders as text\n * ({@link lines}), the structured edits it renders as diffs, and everything\n * else as its own readable text ({@link extras}). Nothing is dropped: a diff is\n * ALSO shown as text (header plus `-`/`+` lines) because the model's view of a\n * tool result is text, and a kind this build cannot render becomes its full\n * JSON.\n */\nfunction splitToolContent(content: unknown): { lines: string[]; diffs?: Array<{ path: string; oldText: string | null; newText: string }>; extras: string[] } {\n const lines: string[] = []\n const extras: string[] = []\n const diffs: Array<{ path: string; oldText: string | null; newText: string }> = []\n if (!Array.isArray(content)) {\n if (typeof content === 'string' && content !== '') lines.push(content)\n return { lines, extras }\n }\n for (const item of content) {\n if (typeof item === 'string') {\n if (item !== '') lines.push(item)\n continue\n }\n if (typeof item !== 'object' || item === null) continue\n const row = item as Record<string, unknown>\n if (row.type === 'content') {\n const inner = row.content as Record<string, unknown> | undefined\n if (typeof inner === 'string') {\n lines.push(inner)\n } else if (inner?.type === 'text') {\n lines.push(typeof inner.text === 'string' ? inner.text : JSON.stringify(item))\n } else if (inner?.type === 'diff' && typeof inner.path === 'string') {\n const diff = { path: inner.path, oldText: typeof inner.oldText === 'string' ? inner.oldText : null, newText: typeof inner.newText === 'string' ? inner.newText : '' }\n diffs.push(diff)\n lines.push(diffText(diff))\n } else {\n extras.push(`${String(inner?.type ?? 'content')} ${JSON.stringify(item)}`)\n }\n continue\n }\n if (row.type === 'diff' && typeof row.path === 'string' && typeof row.newText === 'string') {\n const diff = { path: row.path, oldText: typeof row.oldText === 'string' ? row.oldText : null, newText: row.newText }\n diffs.push(diff)\n lines.push(diffText(diff))\n continue\n }\n if (row.type === 'text' && typeof row.text === 'string') {\n lines.push(row.text)\n continue\n }\n extras.push(`${String(row.type ?? 'content')} ${JSON.stringify(item)}`)\n }\n return { lines, extras, ...(diffs.length === 0 ? {} : { diffs }) }\n}\n\n/** A structured edit as plain text: its path, then `-`/`+` lines with three lines of context. */\nfunction diffText(diff: { path: string; oldText: string | null; newText: string }): string {\n const split = (text: string | null): string[] => text === null || text === '' ? [] : (text.endsWith('\\n') ? text.slice(0, -1) : text).split('\\n')\n const oldLines = split(diff.oldText)\n const newLines = split(diff.newText)\n let head = 0\n while (head < oldLines.length && head < newLines.length && oldLines[head] === newLines[head]) head++\n let tail = 0\n while (\n tail < oldLines.length - head\n && tail < newLines.length - head\n && oldLines[oldLines.length - 1 - tail] === newLines[newLines.length - 1 - tail]\n ) tail++\n const out: string[] = [diff.path]\n for (let i = Math.max(0, head - 3); i < head; i++) out.push(` ${oldLines[i] ?? ''}`)\n for (let i = head; i < oldLines.length - tail; i++) out.push(`- ${oldLines[i] ?? ''}`)\n for (let i = head; i < newLines.length - tail; i++) out.push(`+ ${newLines[i] ?? ''}`)\n for (let i = oldLines.length - tail; i < oldLines.length; i++) out.push(` ${oldLines[i] ?? ''}`)\n return out.join('\\n')\n}\n\n/** The readable fold of a notification the view has no card of its own for. */\nexport function describeEvent(kind: string, update: Record<string, unknown>): string {\n if (kind === 'available_commands_update') {\n const commands = Array.isArray(update.availableCommands) ? update.availableCommands : []\n return commands.map(command => {\n const row = (typeof command === 'object' && command !== null ? command : {}) as Record<string, unknown>\n const name = typeof row.name === 'string' ? row.name : ''\n const description = typeof row.description === 'string' && row.description !== '' ? ` — ${row.description}` : ''\n return `${name}${description}`\n }).join('\\n')\n }\n if (kind === 'plan') {\n const entries = Array.isArray(update.entries) ? update.entries : []\n return entries.map(entry => {\n const row = (typeof entry === 'object' && entry !== null ? entry : {}) as Record<string, unknown>\n return `[${String(row.status ?? 'pending')}] ${String(row.content ?? '')}`\n }).join('\\n')\n }\n if (kind === 'current_mode_update' && typeof update.currentModeId === 'string') return update.currentModeId\n return ''\n}\n\nexport const DEFAULT_KNOWN_PRESETS: DetectedAgentInfo[] = [\n {\n id: 'pi',\n name: 'Pi Coding Agent',\n description: 'Automata / Pi Coding Agent with native ACP bridge support',\n detected: false,\n defaultCommand: 'pi-acp',\n defaultArgs: [],\n },\n {\n id: 'omp',\n name: 'Oh My Pi',\n description: 'Oh My Pi (pi-based coding agent) — run as `omp acp` over stdio',\n detected: false,\n defaultCommand: 'omp',\n defaultArgs: ['acp'],\n },\n // Hand copy of the host's `AGENT_PRESETS` entry (src/bridge.ts): the panel\n // falls back to this list before the first `agents/detect` round-trip.\n {\n id: 'codebuddy',\n name: 'CodeBuddy Code',\n description: 'Tencent CodeBuddy Code — run as `codebuddy --acp` over stdio',\n detected: false,\n defaultCommand: 'codebuddy',\n defaultArgs: ['--acp'],\n },\n {\n id: 'claude',\n name: 'Claude Code',\n description: 'Claude Agent SDK adapter — run as `npx -y @agentclientprotocol/claude-agent-acp` over stdio',\n detected: false,\n defaultCommand: 'npx',\n defaultArgs: ['-y', '@agentclientprotocol/claude-agent-acp'],\n },\n {\n id: 'codex',\n name: 'Codex',\n description: 'OpenAI Codex adapter — run as `npx -y @agentclientprotocol/codex-acp` over stdio',\n detected: false,\n defaultCommand: 'npx',\n defaultArgs: ['-y', '@agentclientprotocol/codex-acp'],\n },\n {\n id: 'gemini',\n name: 'Gemini CLI',\n description: 'Google Gemini CLI — run as `gemini --experimental-acp` over stdio',\n detected: false,\n defaultCommand: 'gemini',\n defaultArgs: ['--experimental-acp'],\n },\n {\n id: 'opencode',\n name: 'OpenCode',\n description: 'OpenCode — run as `opencode acp` over stdio',\n detected: false,\n defaultCommand: 'opencode',\n defaultArgs: ['acp'],\n },\n {\n id: 'cursor',\n name: 'Cursor CLI',\n description: 'Cursor CLI — run as `agent acp` over stdio (legacy alias `cursor-agent acp`)',\n detected: false,\n defaultCommand: 'agent',\n defaultArgs: ['acp'],\n },\n {\n id: 'qwen',\n name: 'Qwen Code',\n description: 'Qwen Code — run as `qwen --experimental-acp` over stdio',\n detected: false,\n defaultCommand: 'qwen',\n defaultArgs: ['--experimental-acp'],\n },\n {\n id: 'kiro',\n name: 'Kiro',\n description: 'Kiro CLI — run as `kiro-cli acp` over stdio',\n detected: false,\n defaultCommand: 'kiro-cli',\n defaultArgs: ['acp'],\n },\n {\n id: 'qoder',\n name: 'Qoder',\n description: 'Qoder CLI — run as `qodercli --acp` over stdio',\n detected: false,\n defaultCommand: 'qodercli',\n defaultArgs: ['--acp'],\n },\n {\n id: 'qoder-cn',\n name: 'Qoder CN',\n description: 'Qoder CLI (China) — run as `qoderclicn --acp` over stdio',\n detected: false,\n defaultCommand: 'qoderclicn',\n defaultArgs: ['--acp'],\n },\n]\n\nexport class AcpStore {\n private state: UiState = {\n open: false,\n localeRev: 0,\n stream: 'init',\n agents: [],\n agentsLoaded: false,\n sessions: [],\n blocks: [],\n permissions: [],\n busy: false,\n logs: [],\n logUnread: 0,\n detectedAgents: [...DEFAULT_KNOWN_PRESETS],\n detecting: false,\n modelCatalogs: {},\n seatCatalogs: {},\n probingModels: {},\n probeErrors: {},\n chatSessions: [],\n burstStats: {},\n tagHalo: { ...DEFAULT_TAG_HALO },\n approvalDeadlines: undefined,\n idleRelease: undefined,\n chatTranscripts: {},\n acpMode: {},\n chatBoundAgent: {},\n chatLockedAgent: {},\n agentLockReveal: {},\n chatChoice: {},\n seatDiag: 'idle',\n permissionSeatDiag: 'idle',\n slashDiag: 'idle',\n }\n\n private readonly listeners = new Set<Listener>()\n private source?: EventSource\n /** Monotonic id source for {@link AcpLogEntry}s (never reset by `clearLogs`). */\n private logSeq = 0\n private readonly lastSeq = new Map<string, number>()\n /** Sessions with a finalized turn (dedups the cancel-fallback + late real turn/end pair). */\n private readonly openTurns = new Set<string>()\n /** In-flight `session/chat-list` reads, keyed by chat session id. */\n private readonly chatSessionReads = new Map<string, { dropped: boolean; done: Promise<void> }>()\n /**\n * ACP session ids whose chat-list row was requested mid-turn (one read each).\n * A conversation's first ACP session does not exist when the composer's\n * mount/running read fires, so its first `usage_update` would find no row to\n * patch; `turn/start` asks again once the host has materialized the session.\n */\n private readonly chatSessionRowReads = new Set<string>()\n /**\n * Chat sessions whose list has answered at least once in this page. The\n * FIRST answer only seeds the lock; a lock appearing on a later answer is a\n * transition this page actually witnessed, and only that one is announced via\n * {@link UiState.agentLockReveal}.\n */\n private readonly chatListLoaded = new Set<string>()\n /**\n * Conversations whose provisional first-turn lock the host rolled back\n * (`chat/agent-unlocked`, no visible output in the first turn). A\n * `session/chat-list` read that started BEFORE the rollback can still name\n * `lockedAgentId`; it must not revive the lock. A new `chat/agent-locked`\n * frame clears the entry (the user retried and the turn really started).\n */\n private readonly chatUnlocked = new Set<string>()\n /**\n * Sessions whose ACP-mode verdict a committed `agent-preset/selected` frame\n * settled. A frame settles the verdict; the host's log read can lag a switch\n * by one (the appended row is not yet visible to a fresh read, and the seat\n * taking the model cell re-mounts readers), so a read may only settle a\n * session no frame has spoken for — otherwise a stale answer would regress a\n * verdict the runtime already told us the truth about.\n *\n * The ONE exception is the gate's own re-ask ({@link loadChatSessions} with\n * `settleVerdict`): a verdict the gate has held as its only fact source for\n * longer than its TTL is exactly the case where the frame path has gone\n * quiet, and without an authorized re-read a switch the runtime never framed\n * could never be corrected.\n */\n private readonly acpModeLive = new Set<string>()\n /**\n * Chat sessions whose host read came back WITHOUT a verdict (no `acpMode`\n * field, or the read failed). The seat gate consults this to retract its\n * one-shot ask ledger entry, so a read that could not answer costs one retry\n * instead of leaving the native model seat visible for the rest of the page.\n */\n private readonly acpModeUnsettled = new Set<string>()\n /** In-flight `agent/catalog` reads, keyed by agent id. */\n private readonly seatCatalogReads = new Set<string>()\n /** In-flight `agent/cached-models` reads, keyed by agent id. */\n private readonly cachedModelReads = new Set<string>()\n /** Counts behind the last successful explicit re-detect, so a locale switch can re-render its summary. */\n private detectCounts: { total: number; installed: number } | undefined\n /** When {@link UiState.pluginUpdate} was last read from the host (epoch ms). */\n private pluginUpdateFetchedAt = 0\n /**\n * The shared in-flight `plugin/update-check`. The settings section mounts\n * twice (both entries) and the update popover asks too; without this, two\n * mounts in the same tick each fire their own registry read.\n */\n private pluginUpdateInflight?: Promise<PluginUpdateState | undefined>\n /** Re-entry guard for `plugin/update-apply` (a double click is one install). */\n private pluginUpdateApplying = false\n /**\n * Update read generation. An apply bumps it so a check that was already in\n * flight (started before the install) cannot land afterwards and overwrite\n * the install's verdict with the pre-install `available`.\n */\n private pluginUpdateGeneration = 0\n\n constructor(private readonly rpc: RpcCall, private readonly locale: (zh: string, en: string) => string) {}\n\n // ---- React bridge ----\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n getSnapshot(): UiState {\n return this.state\n }\n\n private mutate(patch: Partial<UiState>, blocksChanged = false): void {\n this.state = {\n ...this.state,\n ...patch,\n blocks: blocksChanged ? [...this.state.blocks] : patch.blocks ?? this.state.blocks,\n }\n for (const listener of [...this.listeners]) listener()\n }\n\n /**\n * Build the log patch for one line. Does NOT mutate: the caller merges it\n * into the single `mutate` call it was already making for the same event.\n */\n private logPatch(level: 'info' | 'error', text: string, agentId?: string): Pick<UiState, 'logs' | 'logUnread'> {\n const entry: AcpLogEntry = {\n id: ++this.logSeq,\n at: Date.now(),\n level,\n text,\n ...(agentId === undefined ? {} : { agentId }),\n }\n return {\n logs: [...this.state.logs, entry].slice(-LOG_MAX_ENTRIES),\n logUnread: this.state.logUnread + 1,\n }\n }\n\n // ---- Streaming update coalescing ----\n\n /**\n * Live `session/update` frames waiting to be folded. Agents stream at token\n * granularity — hundreds of frames a second with usage ticks interleaved —\n * and folding plus notifying listeners per frame re-rendered every store\n * consumer per chunk. Frames are coalesced into ONE fold + ONE mutate per\n * animation frame instead; the order inside a batch is preserved, and the\n * transcript watermark (`lastSeq`) advances per batch.\n */\n private readonly pendingUpdates: Array<{ sessionId: string; seq: number; update: unknown }> = []\n private updateFlushArmed = false\n\n /** Fold everything pending now (idempotent; the scheduler clears its flag). */\n flushUpdates(): void {\n const batch = this.pendingUpdates.splice(0, this.pendingUpdates.length)\n this.updateFlushArmed = false\n if (batch.length === 0) return\n const bySession = new Map<string, Array<{ seq: number; update: unknown }>>()\n for (const item of batch) {\n const list = bySession.get(item.sessionId)\n if (list === undefined) bySession.set(item.sessionId, [item])\n else list.push(item)\n }\n let chatTranscripts = this.state.chatTranscripts\n let chatSessions = this.state.chatSessions\n let blocksChanged = false\n for (const [sessionId, items] of bySession) {\n const last = items[items.length - 1]\n // `usage_update` is state, not stream content: it never becomes a block,\n // but both observed snapshots that carry usage must move with it —\n // otherwise the ACP view header and the composer's occupancy ring only\n // refresh when some other RPC re-reads the session.\n const usage = usageOfBatch(items)\n if (usage !== undefined) chatSessions = withSessionUsage(chatSessions, sessionId, usage)\n // The observed transcript folds every frame (a watched session's cards\n // and prose); blocks are copied only when something actually changed, so\n // a usage-only tick costs one record object, not a re-render payload.\n const record = chatTranscripts[sessionId]\n if (record !== undefined) {\n let changed = false\n for (const item of items) {\n if (foldUpdate(record.blocks, item.update as Record<string, unknown>)) changed = true\n }\n const session = usage !== undefined && !sameUsage(record.session.usage, usage)\n ? { ...record.session, usage }\n : record.session\n chatTranscripts = {\n ...chatTranscripts,\n [sessionId]: { ...record, session, seq: last.seq, ...(changed ? { blocks: [...record.blocks] } : {}) },\n }\n }\n if (sessionId !== this.state.activeSessionId) {\n this.lastSeq.set(sessionId, last.seq)\n continue\n }\n const seen = this.lastSeq.get(sessionId)\n const contiguous = seen === undefined\n || (items[0].seq === seen + 1 && items.every((item, index) => index === 0 || item.seq === items[index - 1].seq + 1))\n this.lastSeq.set(sessionId, last.seq)\n if (!contiguous) {\n void this.loadTranscript(sessionId) // gap detected → replay\n continue\n }\n for (const item of items) {\n if (foldUpdate(this.state.blocks, item.update as Record<string, unknown>)) blocksChanged = true\n }\n }\n const patch: Partial<UiState> = {}\n if (chatTranscripts !== this.state.chatTranscripts) patch.chatTranscripts = chatTranscripts\n if (chatSessions !== this.state.chatSessions) patch.chatSessions = chatSessions\n if (blocksChanged) patch.blocks = [...this.state.blocks]\n if (Object.keys(patch).length > 0) this.mutate(patch)\n }\n\n private armUpdateFlush(): void {\n if (this.updateFlushArmed) return\n this.updateFlushArmed = true\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => { this.updateFlushArmed = false; this.flushUpdates() })\n return\n }\n // No frame scheduler (a plain Node runtime): fold at once, keeping the\n // same one-mutate-per-batch contract for non-browser consumers.\n this.flushUpdates()\n }\n\n // ---- Transport ----\n\n /** Open the shared event stream once; schedule first snapshot fetch. */\n start(): void {\n if (this.source !== undefined) return\n this.connectStream()\n }\n\n private connectStream(): void {\n const source = new EventSource('/api/acp/stream')\n source.onopen = () => {\n void this.resync()\n this.mutate({ stream: 'live' })\n }\n source.onerror = () => {\n this.mutate({ stream: 'down' })\n }\n source.onmessage = raw => {\n let event: AcpEvent\n try {\n event = JSON.parse(String(raw.data)) as AcpEvent\n } catch {\n return\n }\n void this.applyEvent(event)\n }\n this.source = source\n }\n\n private async call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n const result = await this.rpc('/acp', endpoint, payload)\n if (!result.ok) throw new Error(result.error.message)\n return result.value as T\n }\n\n /** Snapshot refresh used on panel open and after a stream outage. */\n async resync(): Promise<void> {\n try {\n const [{ agents }, { sessions }, defaultAgentInfo, detectResult, presetResult, tagHaloResult, deadlinesResult, idleReleaseResult, versionResult] = await Promise.all([\n this.call<{ agents: AgentView[] }>('agents/list'),\n this.call<{ sessions: SessionView[] }>('sessions/list'),\n // The plugin's OWN default ACP agent — a plugin-private id, not a DSH\n // model route (see `defaultAgent` on the host half).\n this.call<{ agentId?: string }>('default/agent').catch(() => ({} as { agentId?: string })),\n this.call<{ agents: DetectedAgentInfo[] }>('agents/detect').catch(() => null),\n // `null` (host without a preset roster) hides the panel block.\n this.call<{ preset: AcpPresetStatus | null }>('preset/status').catch(() => null),\n // Host without the endpoint (older half) keeps the local default.\n this.call<{ halo: AcpTagHalo }>('ui/tag-halo').catch(() => ({ halo: { ...DEFAULT_TAG_HALO } })),\n // `undefined` (older half) hides the deadline card instead of showing a\n // number this browser made up.\n this.call<ApprovalDeadlinesResult>('ui/approval-deadlines').catch(() => ({ deadlines: undefined })),\n // `undefined` (older half) hides the tab instead of showing a\n // number this browser made up.\n this.call<IdleReleaseResult>('ui/idle-release').catch(() => ({ idleRelease: undefined })),\n // The settings badge's data source; older halves answer `unknown\n // endpoint`, which hides the badge.\n this.call<PluginVersionResult>('plugin/version').catch(() => null),\n ])\n const detectedAgents = (detectResult && detectResult.agents.length > 0)\n ? detectResult.agents\n : (this.state.detectedAgents.length > 0 ? this.state.detectedAgents : [...DEFAULT_KNOWN_PRESETS])\n // Absent on an older host: keep the settings-panel card (degraded path).\n this.mutate({\n agents,\n agentsLoaded: true,\n sessions,\n defaultAgent: defaultAgentInfo?.agentId,\n detectedAgents,\n preset: presetResult?.preset ?? undefined,\n tagHalo: tagHaloResult?.halo ?? { ...DEFAULT_TAG_HALO },\n approvalDeadlines: deadlinesResult.deadlines,\n idleRelease: idleReleaseResult.idleRelease,\n pluginVersion: versionResult?.version !== undefined && versionResult.version !== '' ? versionResult.version : undefined,\n })\n // `sessions/list` is the `/acp` RPC face; the composer's chat rows\n // (agent modes, occupancy) are a separate list. A reconnect must refresh\n // that list too or the ring keeps a pre-outage sample until a turn ends.\n if (this.state.chatSessionsFor !== undefined) void this.loadChatSessions(this.state.chatSessionsFor)\n const active = this.state.activeSessionId ?? sessions[0]?.sessionId\n if (active !== undefined) await this.loadTranscript(active)\n } catch (error) {\n this.mutate({ notice: String(error instanceof Error ? error.message : error) })\n }\n }\n\n /**\n * Read the plugin's update state (`plugin/update-check`). Throttled to ten\n * minutes unless `force`: the settings panel asks on mount, and every\n * remount must not hit the registry.\n *\n * An older host serves no such endpoint (`unknown endpoint`). That is a\n * normal degradation, not an error worth a notice: the answer is `undefined`\n * and the panel simply keeps its update entry hidden.\n */\n async checkPluginUpdate(force = false): Promise<PluginUpdateState | undefined> {\n // One read for every caller while it is in flight, force included: the\n // settings page and the popover mounting together must not hit the\n // registry twice.\n if (this.pluginUpdateInflight !== undefined) return this.pluginUpdateInflight\n const current = this.state.pluginUpdate\n if (!force && current !== undefined && Date.now() - this.pluginUpdateFetchedAt < PLUGIN_UPDATE_TTL_MS) {\n return current\n }\n const generation = this.pluginUpdateGeneration\n const inflight = this.readPluginUpdate(generation)\n this.pluginUpdateInflight = inflight\n try {\n return await inflight\n } finally {\n this.pluginUpdateInflight = undefined\n }\n }\n\n private async readPluginUpdate(generation: number): Promise<PluginUpdateState | undefined> {\n try {\n const pluginUpdate = await this.call<PluginUpdateState>('plugin/update-check')\n // An install started while this read was in flight: its answer is\n // already stale, and the install's verdict owns the state now.\n if (generation !== this.pluginUpdateGeneration) return undefined\n this.pluginUpdateFetchedAt = Date.now()\n this.mutate({ pluginUpdate })\n return pluginUpdate\n } catch {\n return undefined\n }\n }\n\n /**\n * Install one exact version (`plugin/update-apply`). The status flips to\n * `applying` first so the badge answers the click; the host's answer then\n * becomes the state.\n *\n * A failed call does NOT prove the install did not happen: on an HMR host\n * the update uninstalls the plugin before reinstalling it, which can cut the\n * response. Whatever the error, the disk may already carry the new version,\n * so the state becomes `restart-required` (the one neutral verdict for \"the\n * running code is not the disk's\") with `installed` dropped (unknown, not a\n * fact to display) and the notice asks for a reload rather than restoring\n * `available` and pretending nothing changed.\n */\n async applyPluginUpdate(version: string): Promise<PluginUpdateState | undefined> {\n if (this.pluginUpdateApplying) return undefined\n this.pluginUpdateApplying = true\n // Any check already in flight predates the install; its answer must not\n // land on top of the install's verdict.\n this.pluginUpdateGeneration += 1\n const previous = this.state.pluginUpdate\n if (previous !== undefined) {\n this.mutate({ pluginUpdate: { ...previous, status: 'applying' } })\n }\n try {\n const pluginUpdate = await this.call<PluginUpdateState>('plugin/update-apply', { version })\n this.pluginUpdateFetchedAt = Date.now()\n this.mutate({ pluginUpdate })\n return pluginUpdate\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n // The disk state is UNKNOWN here: the install may have completed before\n // the response was cut. Keep `latest` for display, drop `installed` so no\n // surface can claim a disk version we never read, and reset the throttle\n // so the next check actually asks the host instead of serving this\n // unknown verdict for ten minutes.\n const interrupted: PluginUpdateState = previous !== undefined\n ? { ...previous, status: 'restart-required', installed: undefined, message }\n : { status: 'restart-required', current: this.state.pluginVersion ?? '?', message }\n this.pluginUpdateFetchedAt = 0\n this.mutate({\n pluginUpdate: interrupted,\n notice: this.locale(\n '更新请求已中断;请刷新页面确认当前版本。',\n 'The update request was interrupted. Reload the page to confirm the installed version.',\n ),\n })\n return undefined\n } finally {\n // Invalidate reads that STARTED during the install: their \"applying\"\n // answer would otherwise land on top of this call's final verdict.\n this.pluginUpdateGeneration += 1\n this.pluginUpdateApplying = false\n }\n }\n\n async detectAgents(): Promise<void> {\n this.mutate({ detecting: true })\n try {\n const result = await this.call<{ agents: DetectedAgentInfo[] }>('agents/detect')\n const agents = result.agents.length > 0 ? result.agents : [...DEFAULT_KNOWN_PRESETS]\n const installed = agents.filter(agent => agent.detected).length\n this.detectCounts = { total: agents.length, installed }\n this.mutate({\n detectedAgents: agents,\n detecting: false,\n detectSummary: this.detectSummaryOf(agents.length, installed),\n })\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agents/detect')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以激活主机检测功能。', 'Host running older version. Please restart dsh web to enable agent detection.')\n : msg\n this.detectCounts = undefined\n this.mutate({\n detecting: false,\n notice,\n detectedAgents: this.state.detectedAgents.length > 0 ? this.state.detectedAgents : [...DEFAULT_KNOWN_PRESETS],\n // A failed re-detect must not keep showing the previous run's success line.\n detectSummary: undefined,\n })\n }\n }\n\n private detectSummaryOf(total: number, installed: number): string {\n return this.locale(\n `已检测 ${String(total)} 个预设:${String(installed)} 个已安装,其余可从「添加智能体」启用。`,\n `Detected ${String(total)} presets: ${String(installed)} installed, enable the rest via \"Add agent\".`,\n )\n }\n\n async saveAgent(payload: AgentSavePayload): Promise<void> {\n try {\n await this.call('agent/save', payload)\n await this.resync()\n // The host probes the catalog right after a save; pull the fresh result\n // so the agent card fills in without a second click.\n void this.probeModels(payload.id)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /**\n * Dry-run the connection form: the host spawns a throwaway process from\n * these exact values, handshakes, and tears it down. A reachable-but-broken\n * agent arrives as `{ ok: false }`, not a thrown error; only transport or\n * validation failures reject.\n */\n async testAgent(payload: AgentSavePayload): Promise<AgentTestResult> {\n try {\n return await this.call<AgentTestResult>('agent/test', payload)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/test')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以启用连接测试功能。', 'Host running older version. Please restart dsh web to enable connection testing.')\n : msg\n this.mutate({ notice })\n throw error\n }\n }\n\n /**\n * Seed one agent's model panes from the host's PERSISTENT catalog cache\n * (`agent/cached-models`, memory-only: it never spawns).\n *\n * The settings card used to learn a catalog only from `agent/probe-models`,\n * so its panes stayed blank until the user clicked 「检测可用模型」 on every\n * visit — including the models already configured, which are known from the\n * agent definition and the on-disk catalog. This read fills both panes on\n * mount; the probe stays the explicit REFRESH (add what the agent newly\n * publishes, drop what it stopped advertising).\n */\n async loadCachedModels(agentId: string): Promise<void> {\n if (this.state.modelCatalogs[agentId] !== undefined || this.cachedModelReads.has(agentId)) return\n this.cachedModelReads.add(agentId)\n try {\n const result = await this.call<AgentProbeModelsResult | null>('agent/cached-models', { agentId })\n // `null` = this agent never published a catalog. A probe may have landed\n // while this read was in flight; a fresh probe always wins.\n if (result === null || result === undefined || this.state.modelCatalogs[agentId] !== undefined) return\n this.mutate({ modelCatalogs: { ...this.state.modelCatalogs, [agentId]: result } })\n } catch {\n // Older host without the endpoint, or an agent deleted meanwhile: the\n // panes fall back to the selection alone, exactly as before a probe.\n } finally {\n this.cachedModelReads.delete(agentId)\n }\n }\n\n /** Ask the host to probe one agent's model catalog (spawn + throwaway session). */\n async probeModels(agentId: string): Promise<void> {\n const previousError = this.state.probeErrors[agentId]\n this.mutate({\n probingModels: { ...this.state.probingModels, [agentId]: true },\n probeErrors: this.clearProbeError(agentId),\n })\n try {\n const result = await this.call<AgentProbeModelsResult>('agent/probe-models', { agentId })\n // The seat list is a filtered view of the same catalog: drop the cached\n // one (in the same patch) and re-read below, so a fresh probe lands in an\n // open conversation too.\n const seatCatalogs = { ...this.state.seatCatalogs }\n delete seatCatalogs[agentId]\n this.mutate({\n modelCatalogs: { ...this.state.modelCatalogs, [agentId]: result },\n seatCatalogs,\n probingModels: { ...this.state.probingModels, [agentId]: false },\n // A retry after the user fixed the problem must not leave the old\n // failure pinned at the top of the panel. Only drop the notice when it\n // is still ours — anything newer belongs to someone else.\n ...(previousError !== undefined && this.state.notice === previousError ? { notice: undefined } : {}),\n })\n void this.loadAgentCatalog(agentId)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/probe-models')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以激活模型检测功能。', 'Host running older version. Please restart dsh web to enable model detection.')\n : msg\n this.mutate({\n notice,\n probingModels: { ...this.state.probingModels, [agentId]: false },\n probeErrors: { ...this.state.probeErrors, [agentId]: notice },\n })\n }\n }\n\n private clearProbeError(agentId: string): Record<string, string> {\n if (this.state.probeErrors[agentId] === undefined) return this.state.probeErrors\n const next = { ...this.state.probeErrors }\n delete next[agentId]\n return next\n }\n\n /**\n * Read one agent's ACP model surface (`agent/catalog`) — what the composer\n * chips and the ACP-mode model seat render. Cached per agent, because the\n * list only changes when the agent's catalog is re-probed or its exposure\n * filter is edited (both of which drop the cache below), and deduped because\n * every ACP surface asks for it on mount.\n *\n * The host answers from memory, so this is safe to call from a render effect;\n * a probe it may trigger runs in the background on the host side.\n */\n async loadAgentCatalog(agentId: string): Promise<void> {\n if (this.state.seatCatalogs[agentId] !== undefined || this.seatCatalogReads.has(agentId)) return\n this.seatCatalogReads.add(agentId)\n try {\n const result = await this.call<AgentCatalogResult>('agent/catalog', { agentId })\n this.mutate({ seatCatalogs: { ...this.state.seatCatalogs, [agentId]: result } })\n } catch {\n // An older host half, or an agent that was deleted meanwhile: the seats\n // fall back to the roster's own title and the bare agent route, which is\n // exactly what they show before a catalog exists.\n } finally {\n this.seatCatalogReads.delete(agentId)\n }\n }\n\n /** Drop one agent's cached seat catalog (its model surface just changed). */\n private invalidateSeatCatalog(agentId: string): void {\n if (this.state.seatCatalogs[agentId] === undefined) return\n const next = { ...this.state.seatCatalogs }\n delete next[agentId]\n this.mutate({ seatCatalogs: next })\n }\n\n /**\n * Persist the ACP-seat model filter for one agent. `undefined` clears the\n * filter — same as an empty list, i.e. no `agentId@choice` variants, only\n * the agent's base entry. Uses the dedicated lightweight RPC so the agent\n * process and its live sessions are never restarted.\n */\n async setExposedModels(agentId: string, exposedModels: string[] | undefined): Promise<void> {\n try {\n await this.call('agent/set-exposed-models', {\n agentId,\n ...(exposedModels === undefined ? {} : { exposedModels }),\n })\n await this.resync()\n // The seats render the filtered list, so an edited filter has to be\n // re-read or the open conversation keeps offering the old set.\n this.invalidateSeatCatalog(agentId)\n void this.loadAgentCatalog(agentId)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/set-exposed-models')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以启用模型筛选功能。', 'Host running older version. Please restart dsh web to enable model filtering.')\n : msg\n this.mutate({ notice })\n throw error\n }\n }\n\n async deleteAgent(agentId: string): Promise<void> {\n try {\n await this.call('agent/delete', { agentId })\n await this.resync()\n // Nothing should keep offering a deleted agent's models (a conversation\n // that was routed to it falls back to its roster entry).\n this.invalidateSeatCatalog(agentId)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /**\n * Persist the auto-discovery switch for one agent. Lightweight like\n * {@link setExposedModels}: no process restart, and the current selection is\n * left exactly as it is.\n */\n async setAutoDiscoverModels(agentId: string, autoDiscoverModels: boolean): Promise<void> {\n try {\n await this.call('agent/set-auto-discover-models', { agentId, autoDiscoverModels })\n await this.resync()\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n const notice = msg.includes('unknown endpoint agent/set-auto-discover-models')\n ? this.locale('宿主服务运行着旧版本代码,请在终端重启 dsh web 服务以启用自动发现功能。', 'Host running older version. Please restart dsh web to enable auto-discovery.')\n : msg\n this.mutate({ notice })\n throw error\n }\n }\n\n // ---- Panel lifecycle ----\n\n toggle(): void {\n const nextOpen = !this.state.open\n this.mutate({ open: nextOpen })\n if (nextOpen) {\n this.start()\n void this.resync()\n }\n }\n\n close(): void {\n this.mutate({ open: false })\n }\n\n selectSession(sessionId: string): void {\n this.mutate({ activeSessionId: sessionId })\n void this.loadTranscript(sessionId)\n }\n\n private async loadTranscript(sessionId: string): Promise<void> {\n try {\n const { session, entries } = await this.call<{ session: SessionView; entries: Array<{ seq: number; update: Record<string, unknown> }> }>(\n 'transcript/get',\n { sessionId },\n )\n // An answer BEHIND what this page already folded must not replace it.\n //\n // The host fills its transcript from the notification pipeline, which the\n // SDK paces independently of the `session/prompt` response, so a read\n // taken right after a turn can still be missing the agent's last chunk.\n // `lastSeq` already refuses to go backwards; the blocks must not either,\n // or switching away and back inside that window truncates a turn the user\n // just watched finish. A resumed session replays as fresh updates, but\n // only after `session/reset` zeroes the watermark — so this cannot swallow\n // a legitimate replay.\n const seen = this.lastSeq.get(sessionId) ?? 0\n if ((entries.at(-1)?.seq ?? 0) < seen) return\n const blocks: Block[] = []\n for (const entry of entries) {\n foldUpdate(blocks, entry.update)\n this.lastSeq.set(sessionId, Math.max(this.lastSeq.get(sessionId) ?? 0, entry.seq))\n }\n const sessions = this.state.sessions.some(s => s.sessionId === sessionId)\n ? this.state.sessions.map(s => (s.sessionId === sessionId ? session : s))\n : [...this.state.sessions, session]\n this.mutate({ sessions, blocks, activeSessionId: sessionId })\n } catch {\n this.mutate({ blocks: [] })\n }\n }\n\n // ---- Actions ----\n\n async startSession(agentId: string, cwd?: string): Promise<void> {\n try {\n const { sessionId } = await this.call<{ sessionId: string }>('session/start', { agentId, ...(cwd === undefined || cwd === '' ? {} : { cwd }) })\n await this.resync()\n this.selectSession(sessionId)\n } catch (error) {\n this.flashError(error)\n }\n }\n\n async sendPrompt(text: string): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined || text.trim() === '') return\n this.mutate({ busy: true })\n try {\n await this.call('session/prompt', { sessionId, text })\n } catch (error) {\n this.flashError(error)\n } finally {\n this.mutate({ busy: false })\n }\n }\n\n async cancelActive(): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/cancel', { sessionId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n async closeActive(): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/close', { sessionId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n async setMode(modeId: string): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/mode-set', { sessionId, modeId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Load the ACP agent sessions backing one DSH chat session. The composer\n * control calls this per chat session (and again whenever a turn settles, so\n * the binding a fresh conversation just created shows up).\n */\n /**\n * Load the ACP agent sessions backing one DSH chat session. The composer\n * control calls this per chat session (and again whenever a turn settles, so\n * the binding a fresh conversation just created shows up).\n *\n * `settleVerdict` is the seat gate's own re-ask and the ONLY read allowed to\n * settle a verdict a frame had already settled (see {@link acpModeLive}): the\n * gate holds such a verdict as its only fact source while the session row\n * stays silent, and a host read is the one place a switch the runtime never\n * framed can still be learned from. Composer readers pass nothing and keep\n * the frame's authority.\n */\n loadChatSessions(dshSessionId: string, options?: { settleVerdict?: boolean }): Promise<void> {\n // The composer entries (agent-mode control, ACP cluster, occupancy ring)\n // read this and re-read when a turn settles; one round-trip answers for\n // all of them instead of racing reads. A caller arriving mid-read waits\n // for THAT read rather than returning immediately: the slash source's\n // one-shot \"do I have a verdict yet?\" ask is only useful if it can\n // receive the answer of a read already in flight.\n const inFlight = this.chatSessionReads.get(dshSessionId)\n if (inFlight !== undefined) return inFlight.done\n const read: { dropped: boolean; done: Promise<void> } = { dropped: false, done: Promise.resolve() }\n this.chatSessionReads.set(dshSessionId, read)\n read.done = this.readChatSessionList(dshSessionId, options, read)\n return read.done\n }\n\n private async readChatSessionList(\n dshSessionId: string,\n options: { settleVerdict?: boolean } | undefined,\n read: { dropped: boolean },\n ): Promise<void> {\n const settle = options?.settleVerdict === true || !this.acpModeLive.has(dshSessionId)\n try {\n const { sessions = [], acpMode, boundAgentId, lockedAgentId, choice } = await this.call<{ sessions: ChatSessionView[]; acpMode?: boolean; boundAgentId?: string; lockedAgentId?: string; choice?: AcpConversationChoice }>('session/chat-list', { dshSessionId, refresh: true })\n // A committed `agent-preset/selected` frame may have settled this\n // session's verdict while the read was in flight; the host answer then\n // describes the log as of BEFORE that switch and must not overwrite it.\n if (read.dropped) return\n // The first answer for a conversation only seeds what is already true: a\n // lock reported there belongs to an earlier page. A lock that appears on\n // a LATER answer is the first-turn transition unfolding in front of the\n // user, and only that one is announced to the hand-off animation.\n const firstAnswer = !this.chatListLoaded.has(dshSessionId)\n this.chatListLoaded.add(dshSessionId)\n // The lock is one-way, so a read that no longer reports one is an older\n // host half, not an unlock: keep what we already know in that case. The\n // one explicit unlock is the host's rollback frame, tracked separately.\n // A read that AGREES there is no lock clears the rollback marker, so a\n // later legitimate lock (even with its frame missed) is accepted again.\n if (lockedAgentId === undefined) this.chatUnlocked.delete(dshSessionId)\n const reportedLock = lockedAgentId !== undefined && this.chatUnlocked.has(dshSessionId)\n ? undefined\n : lockedAgentId\n const chatLockedAgent = reportedLock === undefined\n ? this.state.chatLockedAgent\n : { ...this.state.chatLockedAgent, [dshSessionId]: reportedLock }\n const witnessedLock = !firstAnswer\n && reportedLock !== undefined\n && this.state.chatLockedAgent[dshSessionId] === undefined\n const agentLockReveal = witnessedLock\n ? { ...this.state.agentLockReveal, [dshSessionId]: (this.state.agentLockReveal[dshSessionId] ?? 0) + 1 }\n : this.state.agentLockReveal\n this.mutate({\n chatSessions: sessions,\n chatSessionsFor: dshSessionId,\n chatLockedAgent,\n agentLockReveal,\n // The host's binding (intent + live-session fallback) outranks the\n // optimistic entry this store wrote on the click; a host without the\n // field (older half) simply leaves the optimistic value standing.\n ...(boundAgentId === undefined\n ? {}\n : { chatBoundAgent: { ...this.state.chatBoundAgent, [dshSessionId]: boundAgentId } }),\n // The persisted pick (model + thought level) — the host's record,\n // present before any ACP session exists. Merged over the optimistic\n // entry so a value a click just wrote is not erased by a read that\n // started before the click landed.\n ...(choice === undefined\n ? {}\n : { chatChoice: { ...this.state.chatChoice, [dshSessionId]: choice } }),\n // The seat gate's fallback fact source: absent on an older host half,\n // which then simply never takes the seat over (the allowed failure).\n // A frame-settled verdict outranks any read, whenever it lands: the\n // host's log read can lag a switch by one, and taking the model seat\n // re-mounts readers, so a stale answer would otherwise regress it.\n ...(acpMode === undefined || !settle\n ? {}\n : { acpMode: { ...this.state.acpMode, [dshSessionId]: acpMode === true } }),\n })\n // A read that answered but carried no verdict has NOT settled this\n // session. The gate's ask ledger is one-shot per id, so unless the id is\n // retracted here it stays marked \"asked\" forever and the gate never asks\n // again — one silent round-trip and the native model seat is permanent\n // for that session. The gate re-derives this from `hasAcpModeVerdict`\n // on its next evaluation (every store mutation re-runs it), which is\n // what makes the fallback recoverable instead of a single coin flip.\n if (acpMode === undefined && settle) {\n this.acpModeUnsettled.add(dshSessionId)\n }\n } catch {\n // A host without this surface (older build) has nothing to show; the\n // control simply renders nothing rather than an error nobody can act on.\n if (read.dropped) return\n this.mutate({ chatSessions: [], chatSessionsFor: dshSessionId })\n if (settle) {\n this.acpModeUnsettled.add(dshSessionId)\n }\n } finally {\n if (this.chatSessionReads.get(dshSessionId) === read) this.chatSessionReads.delete(dshSessionId)\n }\n }\n\n /**\n * Abandon the in-flight `session/chat-list` read for one session (or all of\n * them): a committed `agent-preset/selected` frame carries the authoritative\n * verdict, and the late answer to an ask that predates the frame describes\n * the log from before the switch — it must not land in the store.\n */\n abandonChatSessionRead(dshSessionId?: string): void {\n if (dshSessionId === undefined) {\n for (const read of this.chatSessionReads.values()) read.dropped = true\n this.chatSessionReads.clear()\n return\n }\n const read = this.chatSessionReads.get(dshSessionId)\n if (read === undefined) return\n read.dropped = true\n this.chatSessionReads.delete(dshSessionId)\n }\n\n /**\n * The slash commands the LIVE ACP session declared for one chat session, as\n * read by {@link loadChatSessions} (`available_commands_update` →\n * `session/chat-list`).\n *\n * `undefined` covers both \"no answer has landed yet\" and \"there is no live\n * session (or it has not declared anything)\" — in every one of those cases\n * the caller falls back to the agent-level cache ({@link agentCommands}),\n * which is what makes `/` list commands before the first message exists.\n */\n chatSessionCommands(dshSessionId: string): readonly AgentCommand[] | undefined {\n if (this.state.chatSessionsFor !== dshSessionId) return undefined\n return this.state.chatSessions[0]?.availableCommands\n }\n\n /**\n * The slash commands the agent declared in its most recent session, from\n * `agent/catalog` (host-cached per agent, see `AgentCatalogResult.commands`).\n * The pre-first-turn source for the `/` menu.\n */\n agentCommands(agentId: string | undefined): readonly AgentCommand[] | undefined {\n if (agentId === undefined) return undefined\n return this.state.seatCatalogs[agentId]?.commands\n }\n\n /**\n * Which agent serves one conversation as far as this page can tell, without\n * reading any catalog: live ACP session → agent lock → host/optimistic\n * binding → persisted pick → plugin default. Used to find the agent whose\n * cached commands the `/` menu should offer before a session exists.\n */\n chatAgentFor(dshSessionId: string): string | undefined {\n if (this.state.chatSessionsFor === dshSessionId) {\n const live = this.state.chatSessions[0]\n if (live !== undefined) return live.agentId\n }\n // The lock outlives the session: after the first turn it is the durable\n // answer even when the ACP session is gone.\n return this.state.chatLockedAgent[dshSessionId]\n ?? this.state.chatBoundAgent[dshSessionId]\n ?? this.state.chatChoice[dshSessionId]?.agentId\n ?? this.state.defaultAgent\n }\n\n /**\n * Record the verdict a committed `agent-preset/selected` frame carries, folded\n * from the frame's own preset field ({@link acpModeFromPresetEvent}). The\n * frame is authoritative: it marks the session live-settled, so no later host\n * read may regress it (see {@link acpModeLive}) — the gate's own TTL re-ask\n * being the one authorized exception. Same no-op-when-unchanged discipline as\n * the other accessors, so a frame restating the held verdict costs no\n * listener churn.\n *\n * The seat gate (`syncAcpMode`) is the SECOND caller: it folds the runtime\n * row's own `agentPreset` — the same session-log source the frames announce —\n * through here, in both directions. That hands the row the frame's authority\n * on purpose (one provenance, one rank): a stale projection is corrected by\n * the gate's next evaluation with fresher row data, and only HOST READS are\n * barred from regressing what the runtime itself already said.\n */\n noteAcpMode(dshSessionId: string, acpMode: boolean): void {\n this.acpModeLive.add(dshSessionId)\n if (this.state.acpMode[dshSessionId] === acpMode) return\n this.mutate({ acpMode: { ...this.state.acpMode, [dshSessionId]: acpMode } })\n }\n\n /**\n * Record the model-seat gate's last decision (see the `seatDiag` field). The\n * gate owns the string; the store only carries it so the ACP view can render\n * *why* the native seat is or is not shadowed. No-op when unchanged, so the\n * gate can call this on every evaluation without churning listeners.\n */\n noteSeatDiag(text: string): void {\n if (this.state.seatDiag === text) return\n this.mutate({ seatDiag: text })\n }\n\n /**\n * Record the permission-seat gate's last decision (`conversation.input.permission`).\n */\n notePermissionSeatDiag(text: string): void {\n if (this.state.permissionSeatDiag === text) return\n this.mutate({ permissionSeatDiag: text })\n }\n\n /**\n * Record the slash-isolation seam's last verdict (`idle` = in force, or no\n * problem to report). No-op when unchanged, like the seat lines.\n */\n noteSlashDiag(text: string): void {\n if (this.state.slashDiag === text) return\n this.mutate({ slashDiag: text })\n }\n\n /**\n * Whether a chat session's host read produced a verdict the seat gate can\n * rely on. `false` means \"asked but not answered\" — the read returned no\n * `acpMode` field or failed outright — and is the signal for the gate to\n * retract its one-shot ask ledger entry for this id so it can ask again.\n *\n * A verdict handed down by a committed `agent-preset/selected` frame is\n * authoritative and outranks everything (see {@link acpModeLive}); such a\n * session always answers `true` here, settled or not.\n */\n hasAcpModeVerdict(dshSessionId: string): boolean {\n if (this.acpModeLive.has(dshSessionId)) return true\n if (this.acpModeUnsettled.has(dshSessionId)) {\n this.acpModeUnsettled.delete(dshSessionId)\n return false\n }\n return this.state.acpMode[dshSessionId] !== undefined\n }\n\n /**\n * Drop one chat session's ACP-mode verdict — or the whole map. The preset\n * switch a session committed is announced by the host (`agent-preset/selected`)\n * and folded by the runtime into its own rows, but NOT into this map, so the\n * gate re-asks instead of trusting a verdict the session log has moved past.\n */\n invalidateAcpMode(dshSessionId?: string): void {\n if (dshSessionId === undefined) {\n // Dropping the whole map also re-opens every session to host reads.\n this.acpModeLive.clear()\n if (Object.keys(this.state.acpMode).length === 0) return\n this.mutate({ acpMode: {} })\n return\n }\n this.acpModeLive.delete(dshSessionId)\n if (this.state.acpMode[dshSessionId] === undefined) return\n const acpMode = { ...this.state.acpMode }\n delete acpMode[dshSessionId]\n this.mutate({ acpMode })\n }\n\n /**\n * Switch the ACP agent's own mode for one chat session (`session/set_mode`),\n * then re-read the binding so the control reflects what the agent actually\n * reports (a rejected switch must not leave an optimistic lie on screen).\n */\n async setChatSessionMode(dshSessionId: string, modeId: string, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-mode-set', { dshSessionId, modeId, ...(agentId === undefined ? {} : { agentId }) })\n const safeAgent = (agentId && agentId.trim()) || undefined\n this.writeChoice(dshSessionId, choice => ({ ...choice, mode: modeId }), safeAgent)\n if (this.state.chatSessionsFor === dshSessionId) {\n const owner = safeAgent ?? this.state.chatChoice[dshSessionId]?.agentId ?? this.state.chatBoundAgent[dshSessionId] ?? this.state.defaultAgent\n let changed = false\n const chatSessions = this.state.chatSessions.map(s => {\n if (s.agentId !== owner || !s.modes || s.modes.currentModeId === modeId) return s\n changed = true\n return { ...s, modes: { ...s.modes, currentModeId: modeId } }\n })\n if (changed) this.mutate({ chatSessions })\n }\n } catch (error) {\n this.flashError(error)\n }\n await this.loadChatSessions(dshSessionId)\n }\n\n /**\n * Bring the conversation's released ACP session back on a fresh process —\n * the released banner's explicit form of the \"next message resumes it\"\n * promise (`session/chat-resume`). A failure is reported through the notice\n * AND rethrown (the `respondPermission` contract), so the banner can pin\n * the message beside the button the user just pressed; the binding is\n * re-read either way so the row follows the host's real answer.\n */\n async resumeChatSession(dshSessionId: string): Promise<void> {\n let failure: unknown\n try {\n await this.call('session/chat-resume', { dshSessionId })\n } catch (error) {\n this.flashError(error)\n failure = error\n }\n await this.loadChatSessions(dshSessionId)\n if (failure !== undefined) throw failure\n }\n\n /**\n * Branch one conversation (the NTR branch button's click): the fixed\n * two-stage order — the HOST forks the DSH session tree first (copied\n * history, `(1)` title, workspace), then the plugin adopts the ACP side via\n * `session/chat-fork-adopt`, which runs the agent's own `session/fork` so\n * the child actually remembers the parent context. Host first is structural:\n * ACP fork has no cut-point parameter and nothing to return to if the host\n * half failed second (see the plan's §D7).\n *\n * Resolves `{ childId }` on success, or `{ failure }` with one actionable\n * line — the reason is ALSO flashed into the notice banner (a user-action\n * failure, so the banner is its surface), and the local line is what the\n * conversation renders beside the button (the user is not on the settings\n * page). A failed adopt leaves the child an ordinary copied-history\n * conversation (its first message starts a fresh `session/new` — today's\n * behavior), never a half-bound one, and NOTHING is opened.\n *\n * `forkHost` is the runtime's `sessions.fork` face, resolved structurally by\n * the entry and injected here: the store stays transport-free.\n */\n async forkChatSession(\n dshSessionId: string,\n forkHost: (sessionId: string) => Promise<unknown>,\n ): Promise<ChatForkOutcome> {\n let childId: unknown\n try {\n childId = await forkHost(dshSessionId)\n } catch (error) {\n const failure = error instanceof Error ? error.message : String(error)\n this.flashError(failure)\n return { failure }\n }\n if (typeof childId !== 'string' || childId === '') {\n const failure = this.locale(\n '宿主没有返回分支会话,分支未完成。',\n 'The host returned no branched session; the branch did not complete.',\n )\n this.flashError(new Error(failure))\n return { failure }\n }\n try {\n await this.call('session/chat-fork-adopt', { dshSessionId: childId, parentDshSessionId: dshSessionId })\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n const failure = this.locale(\n `分支会话已创建,但没有接上 agent 的上下文(${reason});在子会话里发送的第一条消息会开启全新会话。`,\n `The branched session was created but the agent context was not attached (${reason}); its first message will start a fresh session.`,\n )\n this.flashError(new Error(failure))\n return { failure }\n }\n return { childId }\n }\n\n // ---- ACP transcript observation (the conversation's ACP view) ----\n\n /**\n * Start (or refresh) observation of every ACP transcript behind one DSH chat\n * session. Blocks are rebuilt from scratch — the host buffer is the record,\n * and this view is the only consumer that needs the raw agent stream.\n */\n async loadChatTranscript(dshSessionId: string): Promise<void> {\n let views: ChatTranscriptView[]\n try {\n ({ views } = await this.call<{ views: ChatTranscriptView[] }>(\n 'session/chat-transcript',\n { dshSessionId },\n ))\n } catch {\n // An older host half (or a session with no ACP binding): nothing to\n // observe, and no error worth flashing for a read-only side view.\n return\n }\n // Everything this chat session used to have, minus what it still has: a\n // closed or replaced ACP session must not linger as a ghost tab body.\n const merged: Record<string, ChatTranscript> = {}\n for (const [id, record] of Object.entries(this.state.chatTranscripts)) {\n if (record.owner !== dshSessionId) merged[id] = record\n }\n for (const view of views) {\n const blocks: Block[] = []\n for (const entry of view.entries) foldUpdate(blocks, entry.update as Record<string, unknown>)\n merged[view.session.sessionId] = {\n owner: dshSessionId,\n session: view.session,\n blocks,\n seq: view.entries.length > 0 ? view.entries[view.entries.length - 1]?.seq ?? 0 : 0,\n }\n }\n this.mutate({ chatTranscripts: this.evictTranscripts(merged) })\n }\n\n /** Observed transcript for one ACP session id (undefined → nobody is watching it). */\n transcriptOf(acpSessionId: string): ChatTranscript | undefined {\n return this.state.chatTranscripts[acpSessionId]\n }\n\n /** The observed transcript of one chat session (the live binding wins over a released one). */\n chatTranscriptOf(dshSessionId: string): ChatTranscript | undefined {\n const owned = Object.values(this.state.chatTranscripts).filter(record => record.owner === dshSessionId)\n return owned.find(record => !record.session.released) ?? owned[0]\n }\n\n /** Keep the resident transcript map at its bound, newest insertion first. */\n private evictTranscripts(map: Record<string, ChatTranscript>): Record<string, ChatTranscript> {\n const ids = Object.keys(map)\n if (ids.length <= MAX_WATCHED) return map\n const out: Record<string, ChatTranscript> = {}\n for (const id of ids.slice(ids.length - MAX_WATCHED)) {\n const value = map[id]\n if (value !== undefined) out[id] = value\n }\n return out\n }\n\n /** Re-publish an observed transcript's session snapshot (mode / released flips). */\n private watchedSessionPatch(sessionId: string, session: SessionView): { chatTranscripts?: Record<string, ChatTranscript> } {\n const current = this.state.chatTranscripts[sessionId]\n if (current === undefined) return {}\n return { chatTranscripts: { ...this.state.chatTranscripts, [sessionId]: { ...current, session } } }\n }\n\n /** Mirror the running flag onto a transcript the ACP view is showing. */\n private markWatchedRunning(sessionId: string, running: boolean): void {\n const current = this.state.chatTranscripts[sessionId]\n if (current === undefined || current.session.running === running) return\n this.mutate({\n chatTranscripts: {\n ...this.state.chatTranscripts,\n [sessionId]: { ...current, session: { ...current.session, running } },\n },\n })\n }\n\n async setConfigOption(configId: string, value: string): Promise<void> {\n const sessionId = this.state.activeSessionId\n if (sessionId === undefined) return\n try {\n await this.call('session/config-set', { sessionId, configId, value })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Answer one ask (`optionId` absent = cancel it). A failure is reported\n * through the store's notice — the settings panel renders that — AND\n * rethrown, so the conversation dock can pin the message next to the card\n * that failed instead of leaving it on a settings page the user may not\n * have open. Callers that cannot show it (the settings panel) swallow it\n * after the notice.\n */\n async respondPermission(requestId: string, optionId?: string): Promise<void> {\n try {\n await this.call('permission/respond', { requestId, ...(optionId === undefined ? {} : { optionId }) })\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n async stopAgent(agentId: string): Promise<void> {\n try {\n await this.call('agent/stop', { agentId })\n await this.resync()\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Set the plugin's OWN default ACP agent (the panel's ★ gesture): which agent\n * an ACP-mode conversation starts on. Plugin-private state — the host keeps it\n * in its own agent registry file, and nothing here touches DSH's model\n * selection (see docs/acp-convergence.md §2.2).\n */\n async setDefaultAgent(agentId: string | undefined): Promise<void> {\n try {\n await this.call('default/agent-set', { ...(agentId === undefined ? {} : { agentId }) })\n this.mutate({ defaultAgent: agentId })\n } catch (error) {\n this.flashError(error)\n }\n }\n\n /**\n * Write the agent tag's halo preference (settings card). The value is\n * validated host-side; this side writes optimistically first so the tag's\n * look changes with the click, and reverts to the host answer on a failure.\n */\n async setTagHalo(halo: AcpTagHalo): Promise<void> {\n const previous = this.state.tagHalo\n this.mutate({ tagHalo: halo })\n try {\n await this.call('ui/tag-halo-set', { halo })\n } catch (error) {\n this.mutate({ tagHalo: previous })\n this.flashError(error)\n }\n }\n\n /**\n * Write the approval deadline (settings card), or drop the persisted override\n * with `reset` so the `cordis.patch.yml` baseline applies again.\n *\n * The host answer — the same route returns the deadlines now in force — is the\n * authority, so an optimistic write that the host trims (out of range, or\n * merged with a value this browser did not know about) self-corrects instead\n * of drifting from what runs.\n */\n async setApprovalDeadline(ms: number): Promise<void> {\n await this.writeApprovalDeadlines({ permissionTimeoutMs: ms })\n }\n\n /**\n * Write the mode: what an expired deadline DOES (`abort` refuses, `auto`\n * answers with the option the agent's own kinds imply, `wait` never\n * expires). Same write path as the window, so the host answer stays\n * the authority.\n */\n async setApprovalTimeoutMode(mode: PermissionTimeoutMode): Promise<void> {\n await this.writeApprovalDeadlines({ mode })\n }\n\n /** Drop the persisted override: the config file (or the built-in default) decides again. */\n async resetApprovalDeadlines(): Promise<void> {\n await this.writeApprovalDeadlines({ reset: true })\n }\n\n /**\n * Write the idle-release window (in ms).\n *\n * The host answer — the same route returns the window now in force — is\n * the authority, so an optimistic write that the host trims self-corrects\n * instead of drifting from what runs.\n */\n async setIdleRelease(idleTimeoutMs: number): Promise<void> {\n await this.writeIdleRelease({ idleTimeoutMs })\n }\n\n /**\n * Drop the persisted override: the config file (or the built-in default)\n * decides again.\n */\n async resetIdleRelease(): Promise<void> {\n await this.writeIdleRelease({ reset: true })\n }\n\n private async writeIdleRelease(payload: IdleReleaseSetPayload): Promise<void> {\n const previous = this.state.idleRelease\n try {\n const result = await this.call<IdleReleaseResult>('ui/idle-release-set', payload)\n this.mutate({ idleRelease: result.idleRelease })\n } catch (error) {\n this.mutate({ idleRelease: previous })\n this.flashError(error)\n }\n }\n\n private async writeApprovalDeadlines(payload: ApprovalDeadlinesSetPayload): Promise<void> {\n const previous = this.state.approvalDeadlines\n // Optimistic only where it is safe: the effective numbers and the mode move\n // now, and the override facts (`configured`) stay as the host last reported\n // them. A failure restores the whole view.\n if (previous !== undefined) {\n this.mutate({\n approvalDeadlines: {\n ...previous,\n ...(payload.permissionTimeoutMs === undefined ? {} : { permissionTimeoutMs: payload.permissionTimeoutMs }),\n ...(payload.mode === undefined ? {} : { mode: payload.mode }),\n },\n })\n }\n try {\n const result = await this.call<ApprovalDeadlinesResult>('ui/approval-deadlines-set', payload)\n this.mutate({ approvalDeadlines: result.deadlines })\n } catch (error) {\n this.mutate({ approvalDeadlines: previous })\n this.flashError(error)\n }\n }\n\n /**\n * Bind one DSH chat session to an ACP agent. This is the plugin's own\n * conversation↔agent mapping: it does NOT write a DSH model route, so it\n * cannot pollute the deployment default model and cannot be refused by the\n * GUI's catalog gate (the retired `session/model-unavailable` failure).\n *\n * The mapping is host-side and keyed by chat session id; the browser learns\n * the binding back through `session/chat-list`.\n */\n async bindChatAgent(dshSessionId: string, agentId: string): Promise<void> {\n try {\n await this.call('session/chat-bind', { dshSessionId, agentId })\n // Optimistic: the host recorded the intent, so the NEXT message runs the\n // picked agent — but the live-session list (`chatSessions`) cannot show\n // that until the turn materializes its session. Recording here is what\n // makes the chip and the picker's checkmark move on the click itself.\n this.mutate({ chatBoundAgent: { ...this.state.chatBoundAgent, [dshSessionId]: agentId } })\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /** Switch the bound agent's model (`agentId@choice`) for one chat session. */\n async setChatSessionModel(dshSessionId: string, modelId: string, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-model-set', { dshSessionId, modelId })\n // Optimistic for the same reason the binding is: the host recorded the\n // pick, and a conversation with no ACP session yet would otherwise show\n // the agent's default until its first turn created one.\n const safeAgent = (agentId && agentId.trim()) || undefined\n const targetAgent = safeAgent ?? (modelId ? splitModelAgentId(modelId) : undefined)\n this.writeChoice(dshSessionId, choice => ({ ...choice, modelId }), targetAgent)\n if (this.state.chatSessionsFor === dshSessionId) {\n const owner = targetAgent ?? this.state.chatChoice[dshSessionId]?.agentId ?? this.state.chatBoundAgent[dshSessionId] ?? this.state.defaultAgent\n let changed = false\n const chatSessions = this.state.chatSessions.map(s => {\n if (s.agentId !== owner || s.modelId === modelId) return s\n changed = true\n return { ...s, modelId }\n })\n if (changed) this.mutate({ chatSessions })\n }\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /** Switch the bound agent's reasoning effort for one chat session. */\n async setChatSessionEffort(dshSessionId: string, effort: string | undefined, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-effort-set', {\n dshSessionId,\n ...(effort === undefined ? {} : { effort }),\n ...(agentId === undefined ? {} : { agentId }),\n })\n const targetAgent = (agentId && agentId.trim()) || undefined\n this.writeChoice(dshSessionId, choice => {\n const { effort: _dropped, ...rest } = choice\n return effort === undefined ? rest : { ...rest, effort }\n }, targetAgent)\n if (this.state.chatSessionsFor === dshSessionId) {\n const owner = targetAgent ?? this.state.chatChoice[dshSessionId]?.agentId ?? this.state.chatBoundAgent[dshSessionId] ?? this.state.defaultAgent\n let changed = false\n const chatSessions = this.state.chatSessions.map(s => {\n if (s.agentId !== owner || s.reasoningEffort === effort) return s\n changed = true\n return { ...s, reasoningEffort: effort }\n })\n if (changed) this.mutate({ chatSessions })\n }\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /** Set one of the bound agent's EXTRA config options for one chat session. */\n async setChatSessionConfig(dshSessionId: string, configId: string, value: string | boolean, agentId?: string): Promise<void> {\n try {\n await this.call('session/chat-config-set', {\n dshSessionId,\n configId,\n value,\n ...(agentId === undefined ? {} : { agentId }),\n })\n this.writeChoice(dshSessionId, choice => ({\n ...choice,\n extraConfigs: { ...choice.extraConfigs, [configId]: value },\n }), agentId)\n } catch (error) {\n this.flashError(error)\n throw error\n }\n }\n\n /**\n * Patch one conversation's optimistic pick. The agent half is required by the\n * record (a model or thought level belongs to an agent). Its owner is the\n * route the picker used, else the recorded pick, the optimistic binding, or\n * the plugin default — that last one really serves a never-bound conversation\n * (the guard routes to it), so waiting for `session/chat-list` would leave the\n * chip behind the click. Only a conversation with none of those is left alone.\n */\n private writeChoice(dshSessionId: string, update: (choice: AcpConversationChoice) => AcpConversationChoice, agentId?: string): void {\n const current = this.state.chatChoice[dshSessionId]\n // The plugin default is a real route (the host applies it to a conversation\n // nobody bound), so an optimistic pick in a fresh conversation belongs to\n // it — without this the host recorded the pick but the chip waited for the\n // next `session/chat-list`.\n const owner = agentId ?? current?.agentId ?? this.state.chatBoundAgent[dshSessionId] ?? this.state.defaultAgent\n if (owner === undefined) return\n const next = update({ ...current, agentId: owner })\n this.mutate({ chatChoice: { ...this.state.chatChoice, [dshSessionId]: { ...next, agentId: owner } } })\n }\n\n /**\n * Localize one string. BOUND to the instance in the constructor: the ACP\n * view hands this reference to memoized rows, so it must stay stable across\n * renders or every row re-renders on every store notification.\n */\n label: (zh: string, en: string) => string = (zh: string, en: string): string => this.locale(zh, en)\n\n /**\n * Republish the snapshot after a locale switch. Every `label()` resolves\n * against the current language while rendering, so a notification is all\n * that is needed — except the detect summary, which the store owns as an\n * already-resolved string and rebuilds from its counts here.\n */\n refreshLocale(): void {\n const counts = this.detectCounts\n this.mutate({\n localeRev: this.state.localeRev + 1,\n ...(counts === undefined ? {} : { detectSummary: this.detectSummaryOf(counts.total, counts.installed) }),\n })\n }\n\n clearNotice(): void {\n this.mutate({ notice: undefined })\n }\n\n /** Drop every retained runtime-log line (the unread counter restarts too). */\n clearLogs(): void {\n this.mutate({ logs: [], logUnread: 0 })\n }\n\n /** The user looked at the log: clear the unread badge. */\n markLogsRead(): void {\n if (this.state.logUnread === 0) return\n this.mutate({ logUnread: 0 })\n }\n\n private flashError(error: unknown): void {\n this.mutate({ notice: error instanceof Error ? error.message : String(error) })\n }\n\n // ---- Event application ----\n\n private applyEvent(event: AcpEvent): Promise<void> {\n switch (event.type) {\n case 'agent/notice': {\n this.mutate(this.logPatch(event.level, event.text, event.agentId))\n return Promise.resolve()\n }\n case 'agent/status': {\n if (event.agent !== undefined) {\n const agents = this.state.agents.some(a => a.id === event.agent?.id)\n ? this.state.agents.map(a => (a.id === event.agent?.id ? event.agent : a))\n : [...this.state.agents, event.agent]\n this.mutate({ agents })\n } else {\n this.mutate({})\n }\n return Promise.resolve()\n }\n case 'session/created': {\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : [...this.state.sessions, event.session]\n this.mutate({ sessions, activeSessionId: this.state.activeSessionId ?? event.sessionId })\n return Promise.resolve()\n }\n case 'session/meta': {\n // Host re-published the session view after mode/config changes or a\n // resume. The composer's occupancy ring reads the chat-list row, so\n // that row must follow the snapshot's usage too — including an absent\n // one, which must clear an older reading rather than outlive it.\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : this.state.sessions\n let chatSessions = withSessionUsage(this.state.chatSessions, event.sessionId, event.session.usage)\n chatSessions = withSessionModes(chatSessions, event.sessionId, event.session.modes)\n this.mutate({\n sessions,\n ...(chatSessions === this.state.chatSessions ? {} : { chatSessions }),\n ...this.watchedSessionPatch(event.sessionId, event.session),\n })\n return Promise.resolve()\n }\n case 'session/closed': {\n const closingSession = this.state.sessions.find(s => s.sessionId === event.sessionId)\n const sessions = this.state.sessions.filter(s => s.sessionId !== event.sessionId)\n const active = this.state.activeSessionId === event.sessionId ? sessions[0]?.sessionId : this.state.activeSessionId\n this.openTurns.delete(event.sessionId)\n const closedTranscripts = { ...this.state.chatTranscripts }\n delete closedTranscripts[event.sessionId]\n // The composer's chat-list view must lose the closed row too: a stale\n // entry would keep reading as a live session (the agent tag's halo\n // breathes on `!released`) until some future turn re-reads the list.\n const chatSessions = this.state.chatSessions.filter(s => s.sessionId !== event.sessionId)\n // The burst aggregate belongs to this ACP session only; another\n // session's entry must survive. Only patch when there was an entry.\n const hadBurst = this.state.burstStats[event.sessionId] !== undefined\n let burstStats = this.state.burstStats\n if (hadBurst) {\n burstStats = { ...burstStats }\n delete burstStats[event.sessionId]\n }\n this.mutate({\n sessions,\n activeSessionId: active,\n chatTranscripts: closedTranscripts,\n ...(chatSessions.length !== this.state.chatSessions.length ? { chatSessions } : {}),\n ...(hadBurst ? { burstStats } : {}),\n permissions: this.state.permissions.filter(p => p.sessionId !== event.sessionId),\n ...(event.reason === 'agent-died'\n ? this.logPatch('error', this.locale('会话因 agent 进程退出而终止', 'session ended: agent process died'), closingSession?.agentId)\n : {}),\n })\n return Promise.resolve()\n }\n case 'session/released': {\n // The agent process died: keep the session visible (marked released)\n // instead of dropping it — the next prompt resumes it via session/load.\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : [...this.state.sessions, event.session]\n this.openTurns.delete(event.sessionId)\n // The composer's own list (`session/chat-list` views) must flip too, or\n // the agent tag's live halo keeps breathing for a process that is gone:\n // nothing else re-reads this list until the next turn settles.\n const chatSessions = withSessionReleased(this.state.chatSessions, event.sessionId, true)\n // One runtime-log line per release cause; `idle` stays silent on\n // purpose (an expected lifecycle transition, not a fault).\n const cause = event.cause ?? 'died'\n let log: Partial<Pick<UiState, 'logs' | 'logUnread'>> = {}\n if (cause === 'workspace') {\n log = this.logPatch('info', this.locale(\n 'agent 为切换工作区重启,会话已挂起;再次发送消息将自动恢复。',\n 'agent restarted for its workspace; the session is paused and will resume on your next message.',\n ), event.session.agentId)\n } else if (cause === 'resume-failed') {\n log = this.logPatch('error', this.locale(\n '会话恢复失败;再次发送消息将重试恢复。',\n 'session resume failed; your next message will retry it.',\n ), event.session.agentId)\n } else if (cause === 'stalled') {\n log = this.logPatch('error', this.locale(\n 'agent 未响应卡死的回合,会话已挂起;再次发送消息将自动恢复。',\n 'agent did not acknowledge the stalled turn; the session is paused and will resume on your next message.',\n ), event.session.agentId)\n } else if (cause === 'died') {\n log = this.logPatch('error', this.locale(\n 'agent 进程已退出,会话已挂起;再次发送消息将自动恢复。',\n 'agent process died; the session is paused and will resume on your next message.',\n ), event.session.agentId)\n }\n this.mutate({\n sessions,\n ...(chatSessions !== this.state.chatSessions ? { chatSessions } : {}),\n permissions: this.state.permissions.filter(p => p.sessionId !== event.sessionId),\n // An observed transcript keeps its buffer but flips to released, so\n // the ACP view can say \"会恢复\" instead of silently looking dead.\n ...this.watchedSessionPatch(event.sessionId, event.session),\n ...log,\n })\n return Promise.resolve()\n }\n case 'session/reset': {\n // The session was resumed on a fresh process and the agent is about to\n // replay its history as fresh updates: drop local blocks and the seq\n // watermark so the replay folds in from a clean slate.\n this.lastSeq.set(event.sessionId, 0)\n this.openTurns.delete(event.sessionId)\n const isActive = event.sessionId === this.state.activeSessionId\n const sessions = this.state.sessions.some(s => s.sessionId === event.sessionId)\n ? this.state.sessions.map(s => (s.sessionId === event.sessionId ? event.session : s))\n : [...this.state.sessions, event.session]\n // The agent is about to replay its whole history as fresh updates:\n // an observed transcript must drop both its blocks and its watermark\n // or the replay would be read as a gap and refetched forever.\n const watched = this.state.chatTranscripts[event.sessionId]\n // ...and the composer's own list must flip back to live on the same\n // event (mirror of the `session/released` flip): the released banner\n // and the tag's halo read that row, and nothing else re-reads it until\n // a turn settles — a resume would leave the banner claiming a parked\n // process that is already back.\n const chatSessions = withSessionReleased(this.state.chatSessions, event.sessionId, false)\n this.mutate({\n sessions,\n ...(chatSessions === this.state.chatSessions ? {} : { chatSessions }),\n ...(isActive ? { blocks: [] } : {}),\n ...(watched === undefined ? {} : {\n chatTranscripts: {\n ...this.state.chatTranscripts,\n [event.sessionId]: { ...watched, session: event.session, blocks: [], seq: 0 },\n },\n }),\n })\n return Promise.resolve()\n }\n case 'session/update': {\n // Coalesced (see `pendingUpdates`): one fold + one mutate per frame.\n this.pendingUpdates.push({ sessionId: event.sessionId, seq: event.seq, update: event.update })\n this.armUpdateFlush()\n return Promise.resolve()\n }\n case 'catalog/updated': {\n // The host learned a new catalog, or a new slash-command list, from a\n // real session (its session/new, session/load, a config_option_update,\n // or an available_commands_update). The seat list is a cached\n // projection of both, so drop and re-read it — deduped by\n // `loadAgentCatalog`, and memory-only on the host, so this never starts\n // a probe.\n this.invalidateSeatCatalog(event.agentId)\n return this.loadAgentCatalog(event.agentId)\n }\n case 'agent/exposure': {\n // The host changed an agent's model visibility outside `agent/save`:\n // the settings card edited it, the auto-discovery switch was flipped,\n // or newly published models were appended automatically. Patch the\n // settings card's copy of the definition AND drop the seat cache — the\n // variants `agent/catalog` answers are a projection of this filter.\n const exposedModels = event.exposedModels.length === 0 ? undefined : [...event.exposedModels]\n const detectedAgents = this.state.detectedAgents.map(info =>\n info.id === event.agentId && info.currentConfig !== undefined\n ? {\n ...info,\n currentConfig: {\n ...info.currentConfig,\n exposedModels: exposedModels === undefined ? undefined : exposedModels,\n autoDiscoverModels: event.autoDiscoverModels,\n },\n }\n : info)\n this.invalidateSeatCatalog(event.agentId)\n this.mutate({ detectedAgents })\n return this.loadAgentCatalog(event.agentId)\n }\n case 'permission/request': {\n // Verbatim copy of the host's frame. The host decides what the ask is\n // called (`toolName` / `reason`) and which options exist; deriving a\n // title from `toolCall` here used to make the card say something the\n // host never said.\n const permission: PendingPermission = {\n requestId: event.requestId,\n sessionId: event.sessionId,\n ...(event.dshSessionId === undefined ? {} : { dshSessionId: event.dshSessionId }),\n toolName: event.toolName,\n ...(event.reason === undefined ? {} : { reason: event.reason }),\n options: event.options,\n timeoutMode: event.timeoutMode,\n ...(event.deadlineAt === undefined ? {} : { deadlineAt: event.deadlineAt }),\n }\n this.mutate({\n permissions: [...this.state.permissions.filter(p => p.requestId !== event.requestId), permission],\n })\n return Promise.resolve()\n }\n case 'permission/resolved': {\n this.mutate({ permissions: this.state.permissions.filter(p => p.requestId !== event.requestId) })\n return Promise.resolve()\n }\n case 'permission/synced': {\n // Terminator of the reconnect replay: the host just re-sent every\n // outstanding ask, and this set is the authoritative live list. Drop\n // anything this client still holds from before the disconnect — an ask\n // resolved while it was away is gone host-side, and answering it would\n // only earn `unknown_request`.\n const live = new Set(event.requestIds)\n this.mutate({ permissions: this.state.permissions.filter(p => live.has(p.requestId)) })\n return Promise.resolve()\n }\n case 'chat/agent-locked': {\n // The host locks a conversation at the START of its first ACP turn and\n // says so at once; this frame is what makes the hand-off animation run\n // at the start rather than on the turn-settled list read. It is emitted\n // only on the transition, so it always announces a new ceremony — even\n // if a racing list read already seeded the lock itself.\n this.chatUnlocked.delete(event.dshSessionId)\n this.mutate({\n chatLockedAgent: { ...this.state.chatLockedAgent, [event.dshSessionId]: event.agentId },\n chatBoundAgent: { ...this.state.chatBoundAgent, [event.dshSessionId]: event.agentId },\n agentLockReveal: { ...this.state.agentLockReveal, [event.dshSessionId]: (this.state.agentLockReveal[event.dshSessionId] ?? 0) + 1 },\n })\n return Promise.resolve()\n }\n case 'chat/agent-unlocked': {\n // The first turn delivered nothing and the host rolled the lock back.\n // The base must return; the failed ACP session is closed host-side, so\n // drop a cached list that still names it and let the next read confirm.\n this.chatUnlocked.add(event.dshSessionId)\n const chatLockedAgent = { ...this.state.chatLockedAgent }\n delete chatLockedAgent[event.dshSessionId]\n this.mutate({\n chatLockedAgent,\n ...(this.state.chatSessionsFor === event.dshSessionId ? { chatSessions: [] } : {}),\n })\n return Promise.resolve()\n }\n case 'turn/metrics': {\n // Plugin-side burst aggregate for one ACP session. A late LIVE frame\n // must not regress a turn this page already saw finalized (frames can\n // land out of order across a stream reconnect); any other frame writes\n // the entry. Blocks / sessions stay untouched — this is a metric.\n const prior = this.state.burstStats[event.sessionId]\n if (event.final === false && prior !== undefined && prior.final === true && prior.turnId === event.turnId) {\n return Promise.resolve()\n }\n this.mutate({\n burstStats: {\n ...this.state.burstStats,\n [event.sessionId]: {\n outputTokens: event.outputTokens,\n burstMs: event.burstMs,\n turnId: event.turnId,\n final: event.final,\n },\n },\n })\n return Promise.resolve()\n }\n case 'turn/start': {\n this.openTurns.add(event.sessionId)\n this.markWatchedRunning(event.sessionId, true)\n // The session materializes inside this turn; if the composer's list\n // has not seen its row yet (the mount/running read beat session/new),\n // read once now so the occupancy ring has a row for the live usage\n // ticks. The per-session set bounds this to one extra read.\n const chatSessionId = this.state.chatSessionsFor\n if (chatSessionId !== undefined\n && !this.state.chatSessions.some(row => row.sessionId === event.sessionId)\n && !this.chatSessionRowReads.has(event.sessionId)) {\n this.chatSessionRowReads.add(event.sessionId)\n void this.loadChatSessions(chatSessionId)\n }\n if (event.sessionId === this.state.activeSessionId) {\n this.state.blocks.push({ kind: 'user', text: event.text })\n trimBlocks(this.state.blocks)\n this.mutate({ sessions: this.state.sessions.map(s => (s.sessionId === event.sessionId ? { ...s, running: true } : s)) })\n }\n return Promise.resolve()\n }\n case 'turn/end': {\n // One turn may end twice (force-finalized after an ignored cancel, or\n // settled by the host after a crash, then the real response arrives);\n // only the first turn/end per open turn is applied.\n if (!this.openTurns.has(event.sessionId)) return Promise.resolve()\n this.openTurns.delete(event.sessionId)\n this.markWatchedRunning(event.sessionId, false)\n if (event.sessionId === this.state.activeSessionId) {\n if (event.error !== undefined) {\n this.state.blocks.push({ kind: 'notice', tone: 'error', text: event.error })\n } else if (event.stopReason !== undefined && event.stopReason !== 'end_turn') {\n this.state.blocks.push({ kind: 'notice', tone: 'info', text: `stop: ${event.stopReason}` })\n }\n trimBlocks(this.state.blocks)\n this.mutate({ sessions: this.state.sessions.map(s => (s.sessionId === event.sessionId ? { ...s, running: false } : s)) })\n }\n return Promise.resolve()\n }\n default:\n return Promise.resolve()\n }\n }\n}\n\n/** Merge a raw ACP update into the block list (mutates in place). */\n/**\n * Fold one transcript update into a block list. IMMUTABLE per block: the ACP\n * view memoizes rows on the block object, so an appended chunk replaces the\n * tail block instead of mutating it (array copies stay the caller's job, one\n * per folded batch). Returns whether any block changed — a usage-only tick\n * folds to `false` and the batch skips copying the array.\n *\n * `raw` is a lazy getter: pretty-printing a tool payload costs up to\n * {@link RAW_MAX} characters of JSON per call, and progress ticks restate\n * whole calls at high frequency — the cost is paid once, by the view that\n * actually shows the card, never by the fold loop.\n */\nexport function foldUpdate(blocks: Block[], update: Record<string, unknown>): boolean {\n const kind = String(update.sessionUpdate ?? '')\n const content = update.content as Record<string, unknown> | undefined\n switch (kind) {\n case 'user_message_chunk':\n case 'agent_message_chunk':\n case 'agent_thought_chunk': {\n const targetKind = kind === 'user_message_chunk' ? 'user' : kind === 'agent_message_chunk' ? 'agent' : 'thought'\n const text = chunkText(content)\n if (text === '') return false\n const messageId = typeof update.messageId === 'string' ? update.messageId : undefined\n const last = blocks[blocks.length - 1]\n if (targetKind === 'agent' && last?.kind === 'agent' && ((messageId !== undefined && last.messageId === messageId) || (messageId === undefined && last.messageId === undefined))) {\n blocks[blocks.length - 1] = { ...last, text: last.text + text }\n } else if (targetKind !== 'agent' && last?.kind === targetKind) {\n blocks[blocks.length - 1] = { ...last, text: last.text + text }\n } else if (targetKind === 'agent') {\n blocks.push({ kind: 'agent', ...(messageId === undefined ? {} : { messageId }), text })\n } else {\n blocks.push({ kind: targetKind, text })\n }\n return true\n }\n case 'tool_call': {\n let rawOf: string | undefined\n blocks.push({\n kind: 'tool',\n toolCallId: String(update.toolCallId ?? ''),\n title: String(update.title ?? ''),\n status: String(update.status ?? 'pending'),\n ...(typeof update.kind === 'string' ? { toolKind: update.kind } : {}),\n ...splitToolContent(update.content),\n ...toolInputFields(update),\n get raw() { return rawOf ??= jsonText(update) },\n })\n return true\n }\n case 'tool_call_update': {\n const id = String(update.toolCallId ?? '')\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i]\n if (block?.kind !== 'tool' || block.toolCallId !== id) continue\n // An update restates the call in whole, but every field it does NOT\n // carry keeps the block's previous value (the restatement that only\n // brings a result must not wipe the arguments the card shows).\n const parsed = splitToolContent(update.content)\n const input = toolInputOf(update)\n const details = toolDetailsOf(update)\n let rawOf: string | undefined\n blocks[i] = {\n kind: 'tool',\n toolCallId: id,\n title: typeof update.title === 'string' && update.title !== '' ? update.title : block.title,\n status: typeof update.status === 'string' ? update.status : block.status,\n ...(typeof update.kind === 'string' ? { toolKind: update.kind } : block.toolKind === undefined ? {} : { toolKind: block.toolKind }),\n lines: parsed.lines.length > 0 ? parsed.lines : block.lines,\n ...(parsed.diffs === undefined\n ? block.diffs === undefined ? {} : { diffs: block.diffs }\n : { diffs: parsed.diffs }),\n ...(parsed.extras.length > 0 ? { extras: parsed.extras } : block.extras === undefined ? {} : { extras: block.extras }),\n ...(input === undefined ? block.input === undefined ? {} : { input: block.input } : { input }),\n ...(details.length > 0 ? { details } : block.details === undefined ? {} : { details: block.details }),\n get raw() { return rawOf ??= jsonText(update) },\n }\n return true\n }\n // Update for a tool call that rolled out of the transcript window.\n let rawOf: string | undefined\n blocks.push({\n kind: 'tool',\n toolCallId: id,\n title: id,\n status: String(update.status ?? 'completed'),\n ...splitToolContent(update.content),\n ...toolInputFields(update),\n get raw() { return rawOf ??= jsonText(update) },\n })\n return true\n }\n case 'plan': {\n const entries = Array.isArray(update.entries)\n ? (update.entries as Array<Record<string, unknown>>).map(e => ({\n content: String(e.content ?? ''),\n status: String(e.status ?? 'pending'),\n ...(typeof e.priority === 'string' ? { priority: e.priority } : {}),\n }))\n : []\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i]\n if (block?.kind !== 'plan') continue\n blocks[i] = { ...block, entries }\n return true\n }\n blocks.push({ kind: 'plan', entries })\n return true\n }\n default: {\n // Every remaining notification kind is kept, not dropped: a command list,\n // a mode change, or a kind newer than this build. Bookkeeping frames that\n // say nothing new (`config_option_update` / `usage_update` /\n // `session_info_update`) ride the SessionView snapshot — they are state,\n // not stream content, and a row per tick would be noise.\n if (kind === '' || STATE_ONLY_UPDATES.has(kind)) return false\n let rawOf: string | undefined\n blocks.push({ kind: 'event', event: kind, text: describeEvent(kind, update), get raw() { return rawOf ??= jsonText(update) } })\n return true\n }\n }\n}\n\n/**\n * The input / detail fields a tool block carries from an update, spread-ready.\n * Kept apart from the content split so `tool_call` and `tool_call_update` share\n * one spelling of \"when does a field appear\".\n */\nfunction toolInputFields(update: Record<string, unknown>): Record<string, unknown> {\n const fields: Record<string, unknown> = {}\n const input = toolInputOf(update)\n if (input !== undefined) fields.input = input\n const details = toolDetailsOf(update)\n if (details.length > 0) fields.details = details\n return fields\n}\n\n/** Notifications that update the SessionView snapshot instead of the block stream. */\nexport const STATE_ONLY_UPDATES: ReadonlySet<string> = new Set([\n 'config_option_update',\n 'usage_update',\n 'session_info_update',\n])\n\n/**\n * Context occupancy carried by one raw `usage_update` frame, in the shape the\n * session snapshots already use. A malformed frame is ignored (the last good\n * sample stays on screen) instead of zeroing the meter.\n */\nexport function usageFromUpdate(update: Record<string, unknown>): SessionView['usage'] | undefined {\n const used = update.used\n const size = update.size\n if (typeof used !== 'number' || !Number.isFinite(used)) return undefined\n if (typeof size !== 'number' || !Number.isFinite(size)) return undefined\n const cost = update.cost\n if (cost !== null && typeof cost === 'object' && !Array.isArray(cost)) {\n const row = cost as Record<string, unknown>\n if (typeof row.amount === 'number' && Number.isFinite(row.amount)\n && typeof row.currency === 'string' && row.currency !== '') {\n return { used, size, cost: { amount: row.amount, currency: row.currency } }\n }\n }\n return { used, size }\n}\n\nfunction sameUsage(left: SessionView['usage'], right: SessionView['usage']): boolean {\n if (left === right) return true\n if (left === undefined || right === undefined) return false\n if (left.used !== right.used || left.size !== right.size) return false\n const leftCost = left.cost\n const rightCost = right.cost\n if (leftCost === undefined || rightCost === undefined) return leftCost === rightCost\n return leftCost.amount === rightCost.amount && leftCost.currency === rightCost.currency\n}\n\n/** Patch one session row's usage; other rows and an unchanged value keep their identities. */\nfunction withSessionUsage<T extends { sessionId: string; usage?: SessionView['usage'] }>(\n rows: T[],\n sessionId: string,\n usage: SessionView['usage'],\n): T[] {\n let changed = false\n const next = rows.map(row => {\n if (row.sessionId !== sessionId || sameUsage(row.usage, usage)) return row\n changed = true\n return { ...row, usage }\n })\n return changed ? next : rows\n}\n\n/**\n * Flip one chat row's `released` flag (release/resume bookkeeping). The\n * composer's own list (`session/chat-list` rows) has no other live writer —\n * `session/released` must mark it, `session/reset` must un-mark it, or the\n * agent tag's halo and the released banner read yesterday's process state.\n */\nfunction withSessionReleased<T extends { sessionId: string; released?: boolean }>(\n rows: T[],\n sessionId: string,\n released: boolean,\n): T[] {\n let changed = false\n const next = rows.map(row => {\n if (row.sessionId !== sessionId || (row.released === true) === released) return row\n changed = true\n return { ...row, released }\n })\n return changed ? next : rows\n}\n\nfunction sameModes(a?: SessionView['modes'], b?: SessionView['modes']): boolean {\n if (a === b) return true\n if (!a || !b) return false\n if (a.currentModeId !== b.currentModeId) return false\n const aModes = a.availableModes ?? []\n const bModes = b.availableModes ?? []\n if (aModes.length !== bModes.length) return false\n return aModes.every((m, i) => {\n const o = bModes[i]\n return m != null && o != null && m.id === o.id && m.name === o.name && m.description === o.description\n })\n}\n\nfunction withSessionModes<T extends { sessionId: string; modes?: SessionView['modes'] }>(\n rows: T[],\n sessionId: string,\n modes?: SessionView['modes'],\n): T[] {\n if (!modes) return rows\n let changed = false\n const next = rows.map(row => {\n if (row.sessionId !== sessionId || sameModes(row.modes, modes)) return row\n changed = true\n return { ...row, modes }\n })\n return changed ? next : rows\n}\n\nfunction splitModelAgentId(modelId: string): string | undefined {\n const at = modelId.indexOf('@')\n const agent = at === -1 ? modelId.trim() : (at === 0 ? '' : modelId.slice(0, at).trim())\n return agent || undefined\n}\n\n/** Last well-formed `usage_update` in one coalesced batch (later ticks win). */\nfunction usageOfBatch(items: ReadonlyArray<{ update: unknown }>): SessionView['usage'] | undefined {\n let usage: SessionView['usage'] | undefined\n for (const item of items) {\n const update = item.update as Record<string, unknown>\n if (update.sessionUpdate !== 'usage_update') continue\n const parsed = usageFromUpdate(update)\n if (parsed !== undefined) usage = parsed\n }\n return usage\n}\n\nfunction trimBlocks(blocks: Block[]): void {\n if (blocks.length > MAX_BLOCKS) blocks.splice(0, blocks.length - MAX_BLOCKS)\n}\n","/**\n * Shared browser-side kit for the ACP *conversation* surfaces: the composer\n * base (agent / model / thought level), its picker and the ACP view tab.\n *\n * Everything here is deliberately structural. The ACP plugin's client half may\n * not import the host runtime or any client package at runtime — the seats it\n * takes (`conversation.input.model`, `conversation.input.overlay`,\n * `conversation.view`) are declared by `dsh-client-ui-conversation`, and the\n * services it reads (`sessions`) are resolved by the entry in `index.tsx` and\n * handed over as plain functions. That is what keeps this bundle loadable in a\n * deployment that composes only a subset of the official client modules: an\n * absent service yields `undefined` and the surface hides itself instead of\n * breaking the plugin.\n *\n * NO MODEL DIRECTORY. This bundle used to read and write the host's per-session\n * model directory (`ctx.modelDirectories`) so an ACP agent could be expressed as\n * a route. That axis is retired: ACP is not a DSH provider, the route write\n * polluted the deployment default, and the GUI's catalog gate then refused the\n * plugin's own route with `session/model-unavailable`. The agent identity now\n * lives in this plugin's own state (`session/chat-list`), reached by its own\n * RPC — see docs/acp-convergence.md §2.2.\n *\n * THE ONE DUPLICATED CODEC: `splitAcpModelId` mirrors the host's\n * `splitAdapterModelId` (src/impl.ts). It cannot be imported from there — the\n * browser half must not pull host-plane modules into the bundle — and the\n * shared contract file is types-only by rule. Keep the two in step: they are\n * the only place that knows how `agentId` and `agentId@choice` are spelled.\n */\n\nimport type { CSSProperties, ReactNode } from 'react'\nimport { useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport type { AcpStore, UiState } from './store.ts'\n\n// ---- Host-facing structural types ----\n\n/** One adapter-provided reasoning effort (DSH `ModelReasoningEffort`). */\nexport interface AcpReasoningEffort {\n id: string\n name: string\n description?: string\n}\n\n/**\n * One model inside a provider group of the agent's OWN catalog (from\n * `agent/catalog`). Deliberately NOT DSH's model catalog: an ACP agent's models\n * are the agent's business, and DSH's directory has no per-session seam to\n * filter them with anyway (see the module header).\n */\nexport interface AcpModelEntry {\n id: string\n name: string\n description?: string\n reasoning?: { efforts: readonly AcpReasoningEffort[]; defaultEffort?: string }\n}\n\n/** One provider group inside an agent's own model catalog. */\nexport interface AcpModelGroup {\n id: string\n name: string\n models: readonly AcpModelEntry[]\n}\n\n/** The session's command + projection face (`SessionFace`), narrowed to what the cluster reads. */\nexport interface AcpSessionFace {\n command(line: string): Promise<\n { ok: true; value: { matched: boolean } } | { ok: false; error: { code: string; message: string } }\n >\n projections: {\n faceOf(key: string): {\n getSnapshot(): unknown\n subscribe(listener: () => void): () => void\n }\n }\n}\n\n/** What `index.tsx` hands every ACP conversation entry. */\nexport interface AcpChatInjected {\n acp: AcpStore\n /** Resolve the session's command/projection face; `undefined` before the session materializes. */\n faceFor(sessionId: string): AcpSessionFace | undefined\n /**\n * The runtime's `sessions.fork` face (the host's own branching primitive,\n * resolved structurally per call); `undefined` when the host half lacks it —\n * the branch button then renders disabled (`hostUnavailable`), never hidden.\n */\n hostFork?: () => AcpHostForkFace | undefined\n /** The workspace's `openSession` face (the branch click's final step). */\n openSession?: (sessionId: string) => void\n}\n\n/** The slice of the client runtime's session service the branch button needs. */\nexport interface AcpHostForkFace {\n /**\n * Fork the DSH session tree at the latest completed turn. Resolves the CHILD\n * session id; the host owns the cut-point snapping, history copy, `(1)`\n * title and workspace attribution — the plugin rewrites none of it.\n */\n fork(options: { sessionId: string; increaseTitle?: boolean }): Promise<unknown>\n}\n\n// ---- The session standard kit (framework-provided, declared structurally) ----\n\n/**\n * The slice of the client runtime's `ConversationSnapshot` our composer entries\n * read: `running` is the only field we watch, and the only re-read trigger we\n * have for the host's ACP chat-session binding (a fresh conversation gets its\n * binding when its first turn creates one, and no SSE frame announces that).\n */\nexport interface AcpSessionSnapshot {\n running: boolean\n}\n\n/**\n * `SessionStandardProps.useSession`: the selector hook the renderer binds per\n * session binding (see `standardKit` in `dsh-client-ui-renderer` —\n * `standard['sessionId'] = info.sessionId` and every `hooks` source becomes a\n * `use<Name>` selector hook).\n *\n * Declared structurally, like every other host-facing type here, because the\n * client runtime that merges `SessionStandardProps` is not part of a plugin's\n * dependency closure: `PropsRuntime<K>` resolves to `object` in a plugin's own\n * build, so the type cannot name these members even though the runtime hands\n * them over. THE MEMBERS ARE `sessionId` + `useSession` — owners pass neither,\n * and there is no `session` prop on any of these seats (reading one was a\n * shipped crash: `Cannot read properties of undefined (reading 'sessionId')`).\n */\nexport interface AcpSessionHook {\n <S>(selector: (snapshot: AcpSessionSnapshot) => S): S\n}\n\n// ---- The shared adapter-model codec (see the module header) ----\n\n/** Split `agentId` / `agentId@choice`; mirrors the host's `splitAdapterModelId`. */\nexport function splitAcpModelId(model: string): { agentId: string; choice?: string } {\n const at = model.indexOf('@')\n if (at <= 0) return { agentId: model }\n return { agentId: model.slice(0, at), choice: model.slice(at + 1) }\n}\n\n/**\n * Build `agentId` / `agentId@choice`; mirrors the host's `buildAdapterModelId`.\n *\n * The inverse of {@link splitAcpModelId}, and a hand copy for the same reason:\n * the browser half cannot import the host plane. Needed where the browser has a\n * bare choice value (the agent's own current model, as `agent/catalog` reports\n * it) and must compare it against adapter model ids.\n */\nexport function buildAcpModelId(agentId: string, choice?: string): string {\n return choice === undefined || choice === '' ? agentId : `${agentId}@${choice}`\n}\n\n/**\n * Whether a form value looks like an absolute path on some platform the plugin\n * runs on: POSIX (`/…`), a Windows drive path (`C:\\…`, `C:/…`), or a UNC path\n * (`\\\\host\\share…`, `//host/share…`). Mirrors the host's `isAbsolutePath`\n * (src/impl.ts) — the browser must pre-reject exactly what `validateAgentDef`\n * would reject, or a save the form enables dies on the host\n * (tests/acp-view.spec.ts asserts equivalence on a shared sample set).\n *\n * The extended-length `\\\\?\\…` / `//?/…` family is rejected here too: the host's\n * workspace guard cannot interpret it, so a config written that way could never\n * resolve a cwd.\n */\nexport function isAbsolutePathLike(p: string): boolean {\n if (p.startsWith('\\\\\\\\?\\\\') || p.startsWith('//?/')) return false\n return p.startsWith('/') || /^[A-Za-z]:[\\\\/]/.test(p) || p.startsWith('\\\\\\\\')\n}\n\n// ---- The connection form's command line (hand copy of src/impl.ts) ----\n\n/**\n * Hand copies of the host's `tokenizeCommandLine` / `splitCommandLine` /\n * `quoteCommandToken` (src/impl.ts). The 命令 field is ONE command line — the\n * whole thing a user would paste into a terminal — and the panel has to parse\n * it exactly the way `validateAgentDef` will, or the \"程序 / 参数\" preview under\n * the field would lie about what the host is about to run. Same discipline as\n * `splitAcpModelId`: browser half cannot import the host plane, so\n * tests/acp-view.spec.ts asserts equivalence on a shared sample set.\n */\nexport function tokenizeCommandLine(raw: string): string[] {\n const tokens: string[] = []\n let current = ''\n let quoted = false\n let touched = false\n let quote: '\"' | \"'\" | undefined\n for (let index = 0; index < raw.length; index += 1) {\n const char = raw[index] as string\n if (quote !== undefined) {\n if (char === '\\\\' && quote === '\"' && raw[index + 1] === '\"') {\n current += '\"'\n index += 1\n continue\n }\n if (char === quote) {\n quote = undefined\n continue\n }\n current += char\n continue\n }\n if (char === '\"' || char === \"'\") {\n quote = char\n touched = true\n continue\n }\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') {\n if (touched || current !== '') tokens.push(current)\n current = ''\n touched = false\n continue\n }\n current += char\n touched = true\n }\n if (touched || current !== '') tokens.push(current)\n return tokens\n}\n\n/** First token = program, rest = arguments. Mirrors the host's `splitCommandLine`. */\nexport function splitCommandLine(raw: string): { command: string; args: string[] } {\n const tokens = tokenizeCommandLine(raw)\n return { command: tokens[0] ?? '', args: tokens.slice(1) }\n}\n\n/** Quote one token for a command line — only when it needs it. */\nexport function quoteCommandToken(token: string): string {\n if (token !== '' && !/[\\s\"']/.test(token)) return token\n return `\"${token.replaceAll('\"', '\\\\\"')}\"`\n}\n\n/** Join a parsed command back into the single editable field (round-trips through `splitCommandLine`). */\nexport function formatCommandLine(command: string, args: readonly string[] = []): string {\n return [command, ...args].map(quoteCommandToken).join(' ')\n}\n\n// ---- React bindings ----\n\n/** Subscribe to the ACP store (the panel and every chat surface share this). */\nexport function useUi(acp: AcpStore): UiState {\n return useSyncExternalStore(cb => acp.subscribe(cb), () => acp.getSnapshot())\n}\n\n\n// ---- The ACP-mode model seat (shadowing the native one) ----\n\n/**\n * The ACP agent preset's id. THREE places must agree on it (no type system\n * spans them): the declaration's `config.id` in `presets/acp/acp.patch.yml`\n * (this bundle's own patch, carried by package.json `dsh.bundle.patch` — no\n * install step), the host half's `ACP_PRESET_ID` (`src/bridge.ts`), and this\n * constant (the browser cannot read the host plane). `acp` is the value the\n * session list reports for an ACP-mode session and the same string the host's\n * own `sessionAgentPreset` resolver reads back from the session log.\n */\nexport const ACP_PRESET_ID = 'acp'\n\n/**\n * The bits of the client session list this plugin reads.\n *\n * Structural on purpose: the client runtime package is not a dependency of\n * this bundle, so the list is resolved as a plain observable at call time and\n * an unexpected shape simply reads as \"no ACP session\" — which leaves every ACP\n * surface hidden rather than broken.\n *\n * TWO SHAPES ship in the wild. DSH 0.1.7-rc.2's session controller\n * (`dsh-api-session-controller`) builds `{ ids, byId, phase,\n * projectionsBySession }` with NO `current` field at all — the staged session\n * is the row whose `retainedBy.mainView` count is positive (the same rule\n * `dsh-client-ui-session` uses to pick its main view), and the session's\n * preset rides `projectionValues.agentPreset` (folded from the\n * `agent-preset/selected` log event; the value is the preset's `id`).\n * Older runtimes carried a literal `current` plus a top-level `agentPreset`\n * on the row and a `currentAddress` marker for addressed subagent routes.\n */\nexport interface AcpSessionsSnapshot {\n /** Old runtimes only: the currently selected session id. */\n current?: string\n /** Old runtimes only: set when the current route is an addressed subagent session. */\n currentAddress?: unknown\n /** List rows by session id (both runtime shapes). */\n byId?: Record<string, AcpSessionsRow | undefined>\n /**\n * rc.2 shape: row ids in display order. A TIE-BREAK hint, never the\n * candidate set: `byId` is a superset of `ids` (the session controller adds\n * catalog-only and retained-scope rows to `byId` without giving them an\n * `ids` entry), so walking `ids` alone can miss the row the main view\n * actually retains. See {@link stagedSessionOf}.\n */\n ids?: readonly string[]\n phase?: string\n}\n\n/** One session-list row, narrowed to what the gate reads (both shapes). */\nexport interface AcpSessionsRow {\n id?: string\n /** Old runtimes: the preset at the top level. */\n agentPreset?: string\n /** rc.2: session projections, `agentPreset` among them. */\n projectionValues?: { agentPreset?: string } | undefined\n /** rc.2: per-surface retain counts; `mainView > 0` marks the staged session. */\n retainedBy?: { mainView?: number } | undefined\n /** rc.2: `\"subagent\"` marks addressed subagent rows. */\n origin?: string\n}\n\n/**\n * The preset the row records, under either runtime spelling. `undefined` means\n * the row is silent (projections not loaded yet, or a runtime without presets)\n * — never \"definitely not ACP\".\n */\nexport function rowAgentPreset(row: AcpSessionsRow | undefined): string | undefined {\n return row?.agentPreset ?? row?.projectionValues?.agentPreset\n}\n\n/** The session the composer is staged on, derived per runtime shape. */\nexport interface AcpStagedSession {\n sessionId: string\n row?: AcpSessionsRow\n /** Addressed subagent route: it is not a conversation this plugin may drive. */\n subagent: boolean\n}\n\n/**\n * Which session the composer is currently staged on.\n *\n * The renderer's own current scope binding is the first answer when the caller\n * passes it (`currentSessionId`, read from `uiSession.adapter.current` in the\n * browser entry): that is the exact session the host has selected, while the\n * list can retain more than one row in a multi-view layout. Falling back to\n * the list is still required — a host without that scope adapter, and the old\n * snapshot shape below, have no better source.\n *\n * Old runtimes answer from `snapshot.current` (with `currentAddress` as the\n * subagent marker). rc.2 has no `current`: the staged session is the row the\n * main view retains (`retainedBy.mainView > 0`) — the exact rule\n * `dsh-client-ui-session` applies, walked in the list's own `ids` order — and a\n * row with `origin === \"subagent\"` is an addressed subagent route. `undefined`\n * when nothing is staged yet (the list has not loaded, or no row holds the main\n * view): callers must stand down rather than guess.\n *\n * A current id the list has no row for is ALSO `undefined`: the selection may\n * be a subagent address, and guessing \"not a subagent\" there would let the\n * gate install ACP chrome on a route the plugin must not drive. The next list\n * notification retries.\n */\nexport function stagedSessionOf(\n snapshot: AcpSessionsSnapshot | undefined,\n currentSessionId?: string,\n): AcpStagedSession | undefined {\n if (snapshot === undefined) return undefined\n if (snapshot.current !== undefined) {\n return {\n sessionId: snapshot.current,\n row: snapshot.byId?.[snapshot.current],\n subagent: snapshot.currentAddress !== undefined,\n }\n }\n const byId = snapshot.byId ?? {}\n if (currentSessionId !== undefined) {\n const row = byId[currentSessionId]\n if (row === undefined) return undefined\n return { sessionId: currentSessionId, row, subagent: row.origin === 'subagent' }\n }\n // Candidate set is `byId`, NOT `ids`. In rc.2 the controller projects\n // `ids` from the host list only, then appends two fallback families to\n // `byId` without an `ids` entry: subagent-catalog children of a projected\n // parent, and live retained scopes carrying a subagent address. A retained\n // scope of either family can be the row holding the main view, so an\n // `ids`-only walk would report \"nothing is staged\" and every ACP surface\n // (this gate included) would stand down on a real ACP conversation.\n //\n // `ids` still matters: it is the display order, so when several rows retain\n // the main view (multi-view layouts) the one the list shows first wins —\n // the same row `dsh-client-ui-session` puts on screen. Everything outside\n // `ids` sorts last, in `byId` key order.\n const order = new Map<string, number>()\n for (const [index, id] of (snapshot.ids ?? []).entries()) order.set(id, index)\n const rank = (id: string): number => order.get(id) ?? Number.MAX_SAFE_INTEGER\n const candidates = Object.keys(byId).sort((a, b) => rank(a) - rank(b))\n const sessionId = candidates.find(id => (byId[id]?.retainedBy?.mainView ?? 0) > 0)\n if (sessionId === undefined) return undefined\n const row = byId[sessionId]\n return { sessionId, row, subagent: row?.origin === 'subagent' }\n}\n\n/** Whether the staged session runs the ACP agent preset. */\nexport function stagedIsAcpMode(\n snapshot: AcpSessionsSnapshot | undefined,\n acpMode?: (sessionId: string) => boolean | undefined,\n currentSessionId?: string,\n): boolean {\n const staged = stagedSessionOf(snapshot, currentSessionId)\n if (staged === undefined || staged.subagent) return false\n const listed = rowAgentPreset(staged.row)\n if (listed !== undefined) return listed === ACP_PRESET_ID\n return acpMode?.(staged.sessionId) === true\n}\n\n/**\n * The ACP-mode verdict a committed `agent-preset/selected` frame carries. The\n * frame is `[sessionId, agentPreset]` and the runtime broadcasts it the moment\n * a switch commits — before the appended session-log row is visible to a fresh\n * `session/chat-list` cold read, so re-reading the log from that frame folds the\n * PREVIOUS preset and the surface would lag one switch behind until a reload.\n * The frame's own preset is therefore the live path's fact source; the host read\n * only seeds a session this tab has never seen a frame for.\n */\nexport function acpModeFromPresetEvent(agentPreset: unknown): boolean {\n return agentPreset === ACP_PRESET_ID\n}\n\n/** The session list store as this bundle reads it (structural, see {@link AcpSessionsSnapshot}). */\nexport interface SessionListSource {\n getSnapshot(): AcpSessionsSnapshot | undefined\n subscribe(listener: () => void): () => void\n}\n\n// ---- The one open menu (composer overlay slot) ----\n\n/** Which picker the composer overlay is showing. */\nexport type AcpMenuKind = 'agent' | 'model' | 'thought' | 'advanced'\n\ninterface MenuState {\n sessionId: string\n kind: AcpMenuKind\n /** The control that opened the picker; the popup docks to it. */\n trigger?: HTMLElement\n /** Last pick failure, rendered inside the open picker (its own retry surface). */\n error?: string\n}\n\nlet menuState: MenuState | null = null\nconst menuListeners = new Set<() => void>()\n\nfunction emitMenu(): void {\n for (const listener of [...menuListeners]) listener()\n}\n\n/** Toggle-style open: pressing the same chip twice closes its menu. */\nexport function openAcpMenu(sessionId: string, kind: AcpMenuKind, trigger?: HTMLElement): void {\n menuState = menuState !== null && menuState.sessionId === sessionId && menuState.kind === kind\n ? null\n : { sessionId, kind, ...trigger === undefined ? {} : { trigger } }\n emitMenu()\n}\n\n/** Close whatever picker is open (Escape, pick, outside pointer). */\nexport function closeAcpMenu(): void {\n if (menuState === null) return\n menuState = null\n emitMenu()\n}\n\n/**\n * Report a pick failure inside the open picker. The menu stays open on\n * purpose: a failed switch must not leave the user staring at a closed chip\n * with no idea whether it took effect.\n */\nexport function setAcpMenuError(error?: string): void {\n if (menuState === null) return\n menuState = { ...menuState, ...(error === undefined ? { error: undefined } : { error }) }\n emitMenu()\n}\n\n/** Subscribe to the open-picker state (both the chips and the overlay read it). */\nexport function useAcpMenu(): MenuState | null {\n return useSyncExternalStore(\n cb => {\n menuListeners.add(cb)\n return () => { menuListeners.delete(cb) }\n },\n () => menuState,\n )\n}\n\n// ---- The NTR settings dialog ----\n\n/**\n * Open state of the frame-wide NTR settings dialog (`settings-dialog.tsx`).\n *\n * Module-level because the two ends live in unrelated slot trees: the trigger\n * is the composer's agent base (a session-scoped model-seat entry) and the\n * dialog itself rides the root `shell.overlay` layer. A React context cannot\n * span that boundary, so this is the same tiny external-store seam the menu\n * uses above.\n */\nlet settingsDialogOpen = false\nconst settingsDialogListeners = new Set<() => void>()\n\nfunction emitSettingsDialog(): void {\n for (const listener of [...settingsDialogListeners]) listener()\n}\n\n/** Open the NTR settings dialog (the empty agent roster's way forward). */\nexport function openAcpSettingsDialog(): void {\n if (settingsDialogOpen) return\n settingsDialogOpen = true\n emitSettingsDialog()\n}\n\n/** Close the NTR settings dialog (close button, mask click, Escape). */\nexport function closeAcpSettingsDialog(): void {\n if (!settingsDialogOpen) return\n settingsDialogOpen = false\n emitSettingsDialog()\n}\n\n/** Current dialog state, for tests and non-React readers. */\nexport function acpSettingsDialogIsOpen(): boolean {\n return settingsDialogOpen\n}\n\n/** Subscribe to the dialog state (the root overlay entry reads it). */\nexport function useAcpSettingsDialogOpen(): boolean {\n return useSyncExternalStore(\n cb => {\n settingsDialogListeners.add(cb)\n return () => { settingsDialogListeners.delete(cb) }\n },\n acpSettingsDialogIsOpen,\n )\n}\n\n// ---- Menu presentation ----\n\n/** One selectable row of a picker. */\nexport interface AcpMenuItem {\n id: string\n label: string\n detail?: string\n /** Current value (rendered with the accent + a check). */\n active?: boolean\n disabled?: boolean\n /** Leading status dot color (agent process state). */\n dot?: string\n /**\n * Render as a checkbox row (`menuitemcheckbox`) rather than a radio row.\n * The advanced base uses it for the agent's boolean config options: one row\n * whose check IS the current value, whose `id` is the value a click sets.\n */\n checkbox?: boolean\n}\n\n/** One titled block of rows. */\nexport interface AcpMenuSection {\n id: string\n title?: string\n note?: string\n items: readonly AcpMenuItem[]\n}\n\n/** Props of the picker shell rendered into `conversation.input.overlay`. */\nexport interface AcpMenuProps {\n /** Menu model; `undefined` renders nothing (closed). */\n model?: {\n title: string\n note?: string\n sections: readonly AcpMenuSection[]\n error?: string\n loading?: boolean\n }\n /** The control that opened the menu; the popup docks above it (see `placeMenu`). */\n anchor?: HTMLElement | null\n onPick(itemId: string, sectionId: string): void\n onClose(): void\n}\n\n/**\n * Pure popup placement, viewport coordinates: the menu docks with its right\n * edge under the trigger's, `gap`px above it — and only then clamps to the\n * viewport (flip below when the top has no room, slide left at the right edge).\n * Separated from the DOM measurement so the geometry stays testable.\n */\nexport function placeMenu(\n trigger: { left: number; right: number; top: number; bottom: number },\n menu: { width: number; height: number },\n viewport: { width: number; height: number },\n gap = 8,\n): { x: number; y: number } {\n const x = Math.min(\n Math.max(gap, trigger.right - menu.width),\n Math.max(gap, viewport.width - menu.width - gap),\n )\n const above = trigger.top - menu.height - gap\n let y = above >= gap ? above : trigger.bottom + gap\n if (y + menu.height > viewport.height - gap) {\n y = Math.max(gap, viewport.height - menu.height - gap)\n }\n return { x, y }\n}\n\n/**\n * The picker shell. It rides the composer's overlay anchor (the card's top\n * edge) — the seat the platform reserves for menus, so the popup is never\n * clipped by the card's own scrollport — and then docks itself to the control\n * that opened it (`anchor`): the seat or a chip is a dropdown trigger, and a\n * menu that materializes elsewhere on the card reads as \"nothing happened\".\n */\nexport function AcpMenu({ model, anchor, onPick, onClose }: AcpMenuProps) {\n const rootRef = useRef<HTMLDivElement>(null)\n useLayoutEffect(() => {\n const node = rootRef.current\n if (node === null || model === undefined) return\n const place = (): void => {\n try {\n if (anchor === undefined || anchor === null || !anchor.isConnected) {\n node.style.transform = ''\n return\n }\n const menuRect = node.getBoundingClientRect()\n const triggerRect = anchor.getBoundingClientRect()\n const { x, y } = placeMenu(\n triggerRect,\n menuRect,\n { width: window.innerWidth, height: window.innerHeight },\n )\n // Translate from wherever CSS parked the popup: the offset parent is\n // the platform's zero-height overlay anchor, so plain left/top would\n // need its exact geometry — measuring the delta needs none.\n node.style.transform = 'none'\n const base = node.getBoundingClientRect()\n node.style.transform = `translate(${x - base.left}px, ${y - base.top}px)`\n } catch { /* placement must never break the menu */ }\n }\n place()\n window.addEventListener('resize', place)\n return () => { window.removeEventListener('resize', place) }\n }, [model, anchor])\n useEffect(() => {\n if (model === undefined) return\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.stopPropagation()\n onClose()\n }\n }\n const onPointerDown = (event: PointerEvent): void => {\n if (!(event.target instanceof Node)) return\n if (rootRef.current?.contains(event.target)) return\n // Clicking the composer card itself must not close the menu (the chips\n // live there); everything else outside does.\n if (rootRef.current?.closest('[data-composer-card]')?.contains(event.target) === true) return\n onClose()\n }\n document.addEventListener('keydown', onKey, true)\n document.addEventListener('pointerdown', onPointerDown, true)\n return () => {\n document.removeEventListener('keydown', onKey, true)\n document.removeEventListener('pointerdown', onPointerDown, true)\n }\n }, [model, onClose])\n\n if (model === undefined) return null\n return (\n <div ref={rootRef} className=\"ntr-acp-menu\" role=\"menu\" aria-label={model.title}>\n <div className=\"ntr-acp-menu-head\">\n <span className=\"ntr-acp-menu-title\">{model.title}</span>\n {model.loading && <span className=\"ntr-acp-menu-spin\" aria-hidden>…</span>}\n </div>\n {model.note !== undefined && <div className=\"ntr-acp-menu-note\">{model.note}</div>}\n {model.error !== undefined && <div className=\"ntr-acp-menu-error\">{model.error}</div>}\n <div className=\"ntr-acp-menu-body\">\n {model.sections.map(section => (\n <div key={section.id} className=\"ntr-acp-menu-section\">\n {section.title !== undefined && <div className=\"ntr-acp-menu-section-title\">{section.title}</div>}\n {section.note !== undefined && <div className=\"ntr-acp-menu-note\">{section.note}</div>}\n {section.items.map(item => (\n <button\n key={`${section.id}/${item.id}`}\n type=\"button\"\n role={item.checkbox === true ? 'menuitemcheckbox' : 'menuitemradio'}\n aria-checked={item.active === true}\n className={`ntr-acp-menu-item${item.active === true ? ' is-active' : ''}`}\n disabled={item.disabled === true}\n onClick={() => { onPick(item.id, section.id) }}\n >\n {item.dot !== undefined && <span className=\"ntr-acp-dot\" style={{ background: item.dot }} aria-hidden />}\n {item.checkbox === true && (\n <span className={`ntr-acp-check${item.active === true ? ' is-on' : ''}`} aria-hidden>{item.active === true ? '✓' : ''}</span>\n )}\n <span className=\"ntr-acp-menu-item-label\">{item.label}</span>\n {item.active === true && item.checkbox !== true && <span className=\"ntr-acp-tick\" aria-hidden>✓</span>}\n {item.detail !== undefined && <span className=\"ntr-acp-menu-item-detail\">{item.detail}</span>}\n </button>\n ))}\n </div>\n ))}\n {model.sections.every(section => section.items.length === 0) && (\n <div className=\"ntr-acp-menu-empty\">—</div>\n )}\n </div>\n </div>\n )\n}\n\n// ---- Chips ----\n\n/**\n * One composer chip. With `onClick` it is the trigger of a picker; without it\n * it is a static nameplate (the locked agent on the composer edge) — same\n * shape and no button semantics, so it does not read as a control that does\n * nothing. Neither variant draws a caret: the chips are a compact status row,\n * and the disclosure arrow added visual noise without carrying information.\n *\n * `className` exists for the lock hand-off: the base needs the shatter\n * keyframes (`ntr-acp-shatter`) while it is still mounted, and that class must\n * ride the chip itself to keep the cluster's `:last-child` layout rule intact.\n */\nexport function AcpChip(props: {\n label: string\n title?: string\n active?: boolean\n onClick?: (trigger: HTMLElement) => void\n children?: ReactNode\n style?: CSSProperties\n className?: string\n}) {\n const onClick = props.onClick\n const className = `ntr-acp-chip${onClick === undefined ? ' is-static' : ''}${props.active === true ? ' is-active' : ''}${props.className === undefined ? '' : ` ${props.className}`}`\n if (onClick === undefined) {\n return (\n <span className={className} title={props.title} style={props.style}>\n {props.children}\n <span className=\"ntr-acp-chip-label\">{props.label}</span>\n </span>\n )\n }\n return (\n <button\n type=\"button\"\n className={className}\n title={props.title}\n aria-haspopup=\"menu\"\n onClick={(event) => { onClick(event.currentTarget) }}\n style={props.style}\n >\n {props.children}\n <span className=\"ntr-acp-chip-label\">{props.label}</span>\n </button>\n )\n}\n\n/**\n * The debris layer of the lock hand-off (see `agent-lock-anim.ts`).\n *\n * `out` scatters the base's fragments; `in` plays the same particles backwards\n * under the arriving tag, so the plate reads as condensed from what the base\n * broke into. Purely decorative: `aria-hidden`, no pointer events, and the\n * whole layer is gone once the phase ends.\n */\ninterface AcpShard {\n /** Resting position inside the chip, in percent. */\n x: number\n y: number\n width: number\n height: number\n /**\n * Scatter offset and spin for the outbound half. The base sits at the RIGHT\n * of the composer bar while the tag hangs near its left edge, so the debris\n * drifts leftward: it reads as the same cloud that later streams in from the\n * right to re-form the tag (see `inX` / `inY`).\n */\n dx: number\n dy: number\n /** Where the inbound half starts: to the right of the tag, off toward the base. */\n inX: number\n inY: number\n rotate: number\n scale: number\n delay: number\n}\n\nconst ACP_SHARDS: readonly AcpShard[] = [\n { x: 8, y: 30, width: 5, height: 7, dx: -42, dy: -22, inX: 150, inY: -18, rotate: -120, scale: 0.35, delay: 0 },\n { x: 18, y: 55, width: 4, height: 5, dx: -28, dy: 24, inX: 118, inY: 24, rotate: 90, scale: 0.55, delay: 20 },\n { x: 28, y: 20, width: 6, height: 4, dx: -20, dy: -30, inX: 196, inY: -10, rotate: -45, scale: 0.45, delay: 10 },\n { x: 38, y: 65, width: 5, height: 6, dx: -14, dy: 28, inX: 132, inY: 30, rotate: 140, scale: 0.4, delay: 30 },\n { x: 48, y: 35, width: 4, height: 8, dx: -36, dy: -26, inX: 210, inY: -4, rotate: 75, scale: 0.5, delay: 0 },\n { x: 56, y: 60, width: 6, height: 5, dx: -24, dy: 24, inX: 164, inY: 20, rotate: -90, scale: 0.55, delay: 25 },\n { x: 66, y: 25, width: 4, height: 4, dx: -30, dy: -20, inX: 128, inY: -24, rotate: 120, scale: 0.4, delay: 5 },\n { x: 74, y: 58, width: 5, height: 7, dx: -18, dy: 20, inX: 182, inY: 14, rotate: -140, scale: 0.5, delay: 15 },\n { x: 84, y: 32, width: 4, height: 5, dx: -10, dy: -14, inX: 112, inY: -32, rotate: 60, scale: 0.4, delay: 20 },\n { x: 13, y: 45, width: 3, height: 4, dx: -34, dy: 6, inX: 206, inY: 8, rotate: 180, scale: 0.3, delay: 35 },\n { x: 43, y: 48, width: 3, height: 3, dx: -26, dy: 16, inX: 176, inY: 4, rotate: -60, scale: 0.3, delay: 40 },\n { x: 88, y: 50, width: 3, height: 4, dx: -8, dy: 8, inX: 142, inY: 26, rotate: 160, scale: 0.3, delay: 35 },\n]\n\nexport function AcpShatterParts(props: { mode: 'out' | 'in' }) {\n return (\n <span className={`ntr-acp-shards is-${props.mode}`} aria-hidden>\n {ACP_SHARDS.map((shard, index) => (\n <span\n key={index}\n className=\"ntr-acp-shard\"\n style={{\n left: `${shard.x}%`,\n top: `${shard.y}%`,\n width: shard.width,\n height: shard.height,\n animationDelay: `${shard.delay}ms`,\n '--ntr-shard-dx': `${shard.dx}px`,\n '--ntr-shard-dy': `${shard.dy}px`,\n '--ntr-shard-in-x': `${shard.inX}px`,\n '--ntr-shard-in-y': `${shard.inY}px`,\n '--ntr-shard-rotate': `${shard.rotate}deg`,\n '--ntr-shard-scale': shard.scale,\n } as CSSProperties}\n />\n ))}\n </span>\n )\n}\n\n// ---- Styles ----\n\nconst STYLE = `\n.ntr-acp-cluster { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; flex-wrap: nowrap; }\n/* The last chip is the cluster's fixed right-hand anchor: it never shrinks\n below its label, while the chips to its left absorb the squeeze. Before the\n lock that is the agent base; after it retires, the model / thought pair\n inherits the edge. Without this a long model name could compress the agent\n base into invisibility — the \"selector disappeared\" symptom. */\n.ntr-acp-cluster > .ntr-acp-chip:last-child { flex: none; }\n/* The agent tag after the base retires. It rides the composer card's top edge:\n the overlay anchor is a zero-height absolute box at that edge, so translateY\n (-50%) puts half the tag above the card and half inside it — a nameplate\n hanging on the box. Above the menu's siblings but below the picker (z 100). */\n.ntr-acp-tag { position: absolute; top: 0; left: 24px; z-index: 90; transform: translateY(-50%); display: inline-flex; max-width: calc(100% - 48px); }\n.ntr-acp-tag > .ntr-acp-chip { font-size: 11px; line-height: 1.35; padding: 1px 8px; background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-1, #fff)); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); }\n.ntr-acp-tag > .ntr-acp-chip.is-static:hover { background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-1, #fff)); }\n.ntr-acp-chip { position: relative; display: inline-flex; align-items: center; gap: 5px; max-width: 220px; font: inherit; font-size: 12px; line-height: 1.4; padding: 3px 8px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }\n.ntr-acp-chip:hover { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-chip.is-static { cursor: default; }\n.ntr-acp-chip.is-static:hover { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.ntr-acp-chip.is-active { border-color: var(--dsw-alias-state-business-primary, #2563eb); color: var(--dsw-alias-state-business-primary, #2563eb); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 10%, transparent); }\n.ntr-acp-chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n/* Composer context-occupancy ring (conversation.composer.dock,\n context-meter.tsx): the agent's own prompt-side used/size in the host\n ContextMeter's ring geometry, pill and tokens, so it reads as that meter's\n sibling in the bottom info row. Gated inside the component; absent until the\n agent reports a sample. */\n.ntr-acp-meter { display: inline-flex; align-items: center; gap: 6px; padding: 1px 8px; border-radius: 24px; color: var(--dsw-alias-label-tertiary, #9ca3af); font-family: inherit; font-size: var(--dsh-content-font-size-secondary, 13px); font-variant-numeric: tabular-nums; line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px)); white-space: nowrap; flex: none; }\n.ntr-acp-meter-track { fill: none; stroke: var(--dsw-alias-border-l3, #e5e7eb); stroke-width: 2px; }\n.ntr-acp-meter-fill { fill: none; stroke: var(--dsw-alias-label-tertiary, #9ca3af); stroke-width: 2px; stroke-linecap: round; }\n.ntr-acp-meter-percent { color: inherit; }\n/* The lock hand-off (src/client/agent-lock-anim.ts): the outgoing agent base\n breaks into debris for 460ms, then the edge tag condenses from the same\n particles. Both halves read one shared clock in that module; these durations\n must stay equal to SHATTER_MS / FORM_MS there. */\n.ntr-acp-shards { position: absolute; inset: 0; display: block; pointer-events: none; overflow: visible; }\n.ntr-acp-shard { position: absolute; border-radius: 1px; background: currentColor; opacity: 0; }\n.ntr-acp-shards.is-out .ntr-acp-shard { animation: ntr-acp-shard-out 460ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }\n.ntr-acp-shards.is-in .ntr-acp-shard { animation: ntr-acp-shard-in 540ms cubic-bezier(0.16, 0.84, 0.28, 1) both; }\n.ntr-acp-chip.ntr-acp-shatter { animation: ntr-acp-shatter-chip 460ms cubic-bezier(0.4, 0, 0.8, 0.4) both; pointer-events: none; }\n.ntr-acp-tag.is-forming > .ntr-acp-chip { animation: ntr-acp-form-chip 540ms cubic-bezier(0.16, 0.84, 0.28, 1) both; }\n/* A live agent's nameplate breathes: a soft blue halo pulses while the ACP\n session serving this conversation is up and not released (agentTagAlive in\n session-controls.tsx). The period comes from the settings card through the\n --ntr-halo-period variable on the tag span; the base shadow from the plain\n tag chip rides inside every keyframe so elevation never flickers. */\n.ntr-acp-tag.is-alive > .ntr-acp-chip {\n border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 40%, transparent);\n animation: ntr-acp-tag-breath var(--ntr-halo-period, 3.2s) ease-in-out infinite;\n}\n/* Steady mode: the same halo, constant. Same rule as reduced-motion below. */\n.ntr-acp-tag.is-alive.is-steady > .ntr-acp-chip {\n animation: none;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 6px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 35%, transparent), 0 0 14px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 20%, transparent);\n}\n@keyframes ntr-acp-tag-breath {\n 0%, 100% { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 4px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 18%, transparent); }\n 50% { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 9px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 50%, transparent), 0 0 18px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 28%, transparent); }\n}\n@keyframes ntr-acp-shatter-chip {\n 0% { opacity: 1; transform: none; filter: blur(0); }\n 22% { opacity: 1; transform: scale(1.05); filter: blur(0); }\n 55% { opacity: 0.55; filter: blur(1.5px); }\n 100% { opacity: 0; transform: scale(0.84); filter: blur(4px); }\n}\n@keyframes ntr-acp-form-chip {\n 0% { opacity: 0; transform: scale(0.55) translateY(2px); filter: blur(6px); }\n 60% { opacity: 1; filter: blur(0); }\n 100% { opacity: 1; transform: none; filter: blur(0); }\n}\n@keyframes ntr-acp-shard-out {\n 0% { opacity: 0; transform: translate(0, 0) scale(1) rotate(0deg); }\n 18% { opacity: 0.95; }\n 100% { opacity: 0; transform: translate(var(--ntr-shard-dx), var(--ntr-shard-dy)) scale(var(--ntr-shard-scale)) rotate(var(--ntr-shard-rotate)); }\n}\n@keyframes ntr-acp-shard-in {\n 0% { opacity: 0; transform: translate(var(--ntr-shard-in-x), var(--ntr-shard-in-y)) scale(var(--ntr-shard-scale)) rotate(var(--ntr-shard-rotate)); }\n 22% { opacity: 0.9; }\n 70% { opacity: 0.5; }\n 100% { opacity: 0; transform: translate(0, 0) scale(0.35) rotate(0deg); }\n}\n@media (prefers-reduced-motion: reduce) {\n .ntr-acp-shards { display: none; }\n .ntr-acp-chip.ntr-acp-shatter, .ntr-acp-tag.is-forming > .ntr-acp-chip { animation: none; }\n /* No pulsing, but the alive state stays visible as a static halo. */\n .ntr-acp-tag.is-alive > .ntr-acp-chip {\n animation: none;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12), 0 0 6px color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 35%, transparent);\n }}\n.ntr-acp-seat { display: inline-flex; align-items: center; gap: 6px; max-width: 260px; font: inherit; font-size: 12px; line-height: 1.4; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }\n.ntr-acp-seat:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-seat:disabled { opacity: 0.5; cursor: default; }\n.ntr-acp-seat.is-open { border-color: var(--dsw-alias-state-business-primary, #2563eb); color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-seat.is-detached { border-color: var(--dsw-alias-state-warn-label, #d97706); color: var(--dsw-alias-state-warn-label, #d97706); }\n.ntr-acp-seat-main { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-seat-sub { color: var(--dsw-alias-label-secondary, #6b7280); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }\n.ntr-acp-tick { margin-left: auto; color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-check { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 13px; height: 13px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: 3px; font-size: 10px; line-height: 1; color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-check.is-on { border-color: var(--dsw-alias-state-business-primary, #2563eb); }\n.ntr-acp-menu { position: absolute; bottom: calc(100% + 8px); left: 8px; z-index: 100; width: min(360px, 100%); max-height: min(420px, 60vh); display: flex; flex-direction: column; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16); font-size: 12px; overflow: hidden; }\n.ntr-acp-menu-head { padding: 8px 10px 6px; font-weight: 600; display: flex; align-items: center; gap: 6px; }\n.ntr-acp-menu-title { flex: 1; }\n.ntr-acp-menu-spin { opacity: 0.5; }\n.ntr-acp-menu-note { padding: 0 10px 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); line-height: 1.5; }\n.ntr-acp-menu-error { margin: 0 10px 6px; padding: 6px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 11px; }\n.ntr-acp-menu-body { overflow: auto; padding: 0 4px 6px; }\n.ntr-acp-menu-section { display: flex; flex-direction: column; padding-top: 4px; }\n.ntr-acp-menu-section-title { padding: 6px 8px 4px; font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-menu-item { display: flex; align-items: center; gap: 6px; font: inherit; font-size: 12px; text-align: left; padding: 6px 8px; border: none; border-radius: 6px; background: none; color: inherit; cursor: pointer; width: 100%; }\n.ntr-acp-menu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-menu-item:disabled { opacity: 0.45; cursor: default; }\n.ntr-acp-menu-item.is-active { font-weight: 600; }\n.ntr-acp-menu-item-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-menu-item-detail { color: var(--dsw-alias-label-secondary, #6b7280); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; text-align: right; }\n.ntr-acp-menu-empty { padding: 10px; text-align: center; color: var(--dsw-alias-label-secondary, #6b7280); }\n/* ---- ACP view tab ---- */\n.ntr-acp-view { display: flex; flex-direction: column; height: 100%; min-height: 0; font-size: 13px; line-height: 1.6; }\n.ntr-acp-view-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2, #e5e7eb); font-size: 12px; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-view-agent { font-weight: 600; color: inherit; }\n.ntr-acp-view-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; direction: rtl; text-align: left; }\n.ntr-acp-view-head .ntr-acp-more { margin-left: auto; }\n.ntr-acp-stream { flex: 1; min-height: 0; overflow: auto; padding: 12px 14px 24px; display: flex; flex-direction: column; gap: 10px; }\n.ntr-acp-badge { font-size: 11px; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; }\n.ntr-acp-badge.is-done { color: color-mix(in srgb, var(--dsw-alias-state-success-primary, #22c55e) 72%, var(--dsw-alias-label-primary, #111827)); background: var(--dsw-alias-state-success-tertiary, #dcfce7); }\n.ntr-acp-badge.is-busy { color: var(--dsw-alias-state-business-primary, #1d4ed8); background: var(--dsw-alias-state-business-tertiary, #dbeafe); }\n.ntr-acp-badge.is-warn { color: var(--dsw-alias-state-warn-label, #b45309); background: var(--dsw-alias-state-warn-tertiary, #fef3c7); }\n.ntr-acp-badge.is-fail { color: var(--dsw-alias-state-error-primary, #b91c1c); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); }\n.ntr-acp-badge.is-idle { color: var(--dsw-alias-label-secondary, #6b7280); background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.ntr-acp-bubble { max-width: 88%; padding: 8px 12px; border-radius: 12px; white-space: pre-wrap; word-break: break-word; }\n.ntr-acp-bubble.is-user { align-self: flex-end; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 12%, transparent); }\n.ntr-acp-bubble.is-agent { align-self: flex-start; background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.ntr-acp-inline-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 1px 4px; border-radius: 4px; background: color-mix(in srgb, currentColor 10%, transparent); }\n.ntr-acp-code { margin: 6px 0; padding: 8px 10px; border-radius: 8px; background: #0f172a; color: #e2e8f0; overflow: auto; font-size: 12px; line-height: 1.5; }\n.ntr-acp-thought { border-left: 3px solid var(--dsw-alias-border-l2, #d1d5db); padding-left: 8px; color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12px; }\n.ntr-acp-thought > summary { cursor: pointer; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; }\n.ntr-acp-thought-body { white-space: pre-wrap; padding: 4px 0 2px; }\n.ntr-acp-tool { border: 1px solid var(--dsw-alias-border-l2, #e5e7eb); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }\n.ntr-acp-tool.is-fail { border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }\n.ntr-acp-tool-head { display: flex; align-items: center; gap: 8px; padding: 7px 10px; font-size: 12px; }\n.ntr-acp-tool-title { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-tool-body { margin: 0; padding: 8px 10px; border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); background: var(--dsw-alias-bg-layer-2, #f9fafb); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 340px; overflow: auto; }\n.ntr-acp-tool-input { background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #111827); max-height: 240px; }\n.ntr-acp-tool-meta { font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-diff { border-top: 1px solid var(--dsw-alias-border-l2, #e5e7eb); }\n.ntr-acp-diff-head { display: flex; align-items: center; gap: 8px; padding: 5px 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; background: var(--dsw-alias-bg-layer-2, #f9fafb); }\n.ntr-acp-diff-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ntr-acp-diff-stats .is-add { color: var(--dsw-alias-state-success-primary, #15803d); }\n.ntr-acp-diff-stats .is-del { color: var(--dsw-alias-state-error-primary, #b91c1c); }\n.ntr-acp-diff-body { max-height: 360px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; }\n.ntr-acp-diff-row { display: flex; gap: 8px; padding: 0 10px; white-space: pre; }\n.ntr-acp-diff-row.is-add { background: var(--dsw-alias-file-diff-added-bg, #dcfce7); }\n.ntr-acp-diff-row.is-del { background: var(--dsw-alias-file-diff-deleted-bg, #fee2e2); }\n.ntr-acp-diff-row.is-ctx { color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-diff-sign { width: 1ch; flex: none; opacity: 0.7; }\n.ntr-acp-diff-text { overflow-x: auto; }\n.ntr-acp-diff-note { padding: 4px 10px 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-more { font: inherit; font-size: 11.5px; padding: 3px 10px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; }\n.ntr-acp-more:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-more:disabled { opacity: 0.5; cursor: default; }\n.ntr-acp-more.is-primary { border-color: transparent; background: var(--dsw-alias-button-primary-fill, #2563eb); color: var(--dsw-alias-label-primary-foreground, #fff); }\n.ntr-acp-tool > .ntr-acp-more, .ntr-acp-empty-actions .ntr-acp-more { margin: 6px 10px; }\n.ntr-acp-plan { border: 1px solid var(--dsw-alias-border-l2, #e5e7eb); border-radius: 10px; padding: 8px 10px; background: var(--dsw-alias-bg-layer-1, #fff); }\n.ntr-acp-plan-head { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); margin-bottom: 4px; }\n.ntr-acp-plan-row { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; padding: 1px 0; }\n.ntr-acp-plan-row.is-completed { color: var(--dsw-alias-label-secondary, #6b7280); text-decoration: line-through; }\n.ntr-acp-plan-row.is-in_progress { font-weight: 600; }\n.ntr-acp-plan-glyph { width: 1ch; flex: none; }\n.ntr-acp-plan-prio { margin-left: auto; font-size: 10px; text-transform: uppercase; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-notice { padding: 6px 10px; border-radius: 8px; font-size: 12px; }\n.ntr-acp-notice.is-error { background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); }\n.ntr-acp-notice.is-info { background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-acp-empty { margin: auto; max-width: 460px; text-align: center; display: flex; flex-direction: column; gap: 10px; padding: 24px; }\n.ntr-acp-empty-title { font-size: 14px; font-weight: 600; }\n.ntr-acp-empty-body { margin: 0; color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12.5px; line-height: 1.7; }\n.ntr-acp-empty-hint { color: var(--dsw-alias-label-secondary, #6b7280); font-size: 12px; }\n.ntr-acp-seat-diag { font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace); opacity: 0.72; word-break: break-word; }\n.ntr-acp-empty-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }\n/* ---- Pending permission asks (conversation.input.dock) ---- */\n/* The dock slot is the full conversation column, but the composer card inside\n it is narrower (InputBar: side clearance + card max width). Mirror that\n formula so each ask lines up with the input box instead of spanning the\n column. The list owns a bounded height and scrolls so a burst of asks cannot\n push the composer off screen. */\n.ntr-permission-dock {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px));\n max-width: var(--dsh-composer-card-max-width, 920px);\n margin: 0 auto;\n display: flex; flex-direction: column; gap: 6px;\n max-height: min(38vh, 320px); overflow-y: auto;\n font-size: 12px; line-height: 1.5;\n}\n.ntr-permission-card { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); }\n.ntr-permission-head { display: flex; align-items: baseline; gap: 8px; min-width: 0; }\n.ntr-permission-tool { flex: none; font-weight: 600; }\n.ntr-permission-reason { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-permission-deadline { flex: none; font-size: 11px; color: var(--dsw-alias-label-secondary, #6b7280); font-variant-numeric: tabular-nums; }\n.ntr-permission-deadline.is-expired { color: var(--dsw-alias-state-warn-label, #b45309); font-weight: 600; }\n.ntr-permission-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }\n.ntr-permission-option, .ntr-permission-cancel { font: inherit; font-size: 12px; line-height: 1.4; padding: 4px 10px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: inherit; cursor: pointer; white-space: normal; text-align: left; }\n.ntr-permission-option:hover:not(:disabled), .ntr-permission-cancel:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-permission-option:disabled, .ntr-permission-cancel:disabled { opacity: 0.5; cursor: default; }\n.ntr-permission-option:focus-visible, .ntr-permission-cancel:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #2563eb); outline-offset: 2px; }\n.ntr-permission-cancel { margin-left: auto; color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-permission-error { padding: 4px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #b91c1c) 12%, transparent); color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 11px; word-break: break-word; }\n/* ---- Released-session banner (conversation.input.dock, released-banner.tsx) ---- */\n/* Same composer-width formula as the permission dock above: the slot spans\n the column, the card lines up with the input box. One row of state + one\n action; the error pins under it, beside the button that failed. */\n.ntr-released-banner {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance, 16px) - var(--dsh-composer-side-clearance, 16px));\n max-width: var(--dsh-composer-card-max-width, 920px);\n margin: 0 auto;\n display: flex; flex-direction: column; gap: 6px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l2, #d1d5db);\n border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1, #fff);\n font-size: 12px; line-height: 1.5;\n}\n.ntr-released-banner-row { display: flex; align-items: center; gap: 10px; min-width: 0; }\n.ntr-released-banner-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }\n.ntr-released-banner-title { font-weight: 600; }\n.ntr-released-banner-body { color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-released-banner .ntr-acp-more { flex: none; white-space: nowrap; }\n/* ---- NTR message actions (conversation.chat.assistant-actions) ---- */\n/* In an NTR conversation the host's like / dislike / branch icons are the\n plugin's business (the agent decides its own feedback surface, and the\n host's branch would fork a conversation the agent does not remember). The\n three icons hide under the html[data-ntr-acp] scope marker, by their\n LOCALIZED aria-labels — the only stable handles the host leaves (its button\n classes are CSS-module hashes and there is no data-* hook). Hiding the\n button is the WHOLE job: the host's Tooltip clones its child without a\n wrapper element, so there is no container left behind to plug the row gap\n — and any :has(> button…) rule would match the actions row itself (and the\n list slot's shared display:contents anchor), hiding the entire row. The\n :not([data-ntr-branch]) exemption is the pass our OWN button carries —\n same label, same row. Known fragility: a host copy change or a third\n language un-hides the icons; the entry probes the labels once per page and\n warns in the console. */\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"在新对话中分支\"]:not([data-ntr-branch]),\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"Branch into a new conversation\"]:not([data-ntr-branch]),\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"好的回答\"],\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"有问题的回答\"],\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"取消标记\"],\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"Good response\"],\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"Bad response\"],\nhtml[data-ntr-acp] [data-turn-tail] button[aria-label=\"Remove rating\"] {\n display: none !important;\n}\n/* The plugin's own branch button, dressed exactly like the host's .action\n class (the values are copied from the host's compiled CSS, token colors\n only). It rides the same actions row, so the host's row gap, font-delta\n sizing and the [data-actions-reveal] hover rules already govern it. */\n.ntr-branch-action {\n width: calc(28px + var(--dsh-content-font-delta, 0px));\n height: calc(28px + var(--dsh-content-font-delta, 0px));\n color: var(--dsw-alias-label-tertiary, #9ca3af);\n cursor: pointer;\n background: none;\n border: none;\n border-radius: 28px;\n justify-content: center;\n align-items: center;\n padding: 6px;\n display: inline-flex;\n}\n.ntr-branch-action svg { width: calc(15px + var(--dsh-content-font-delta, 0px)); height: calc(15px + var(--dsh-content-font-delta, 0px)); }\n.ntr-branch-action:hover { background: var(--dsw-alias-interactive-bg-hover, #0000000d); color: var(--dsw-alias-label-secondary, #6b7280); }\n.ntr-branch-action[data-unavailable] { cursor: default; opacity: 0.4; }\n.ntr-branch-action[data-unavailable]:hover { color: var(--dsw-alias-label-tertiary, #9ca3af); background: none; }\n.ntr-branch-action:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #2563eb); outline-offset: 2px; }\n/* A failed branch click, pinned in the row beside the button (the conversation\n surface of the settings banner the store also writes). One line, truncated;\n the full text is the title tooltip and the banner. */\n.ntr-branch-error { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; line-height: calc(28px + var(--dsh-content-font-delta, 0px)); color: var(--dsw-alias-state-error-primary, #b91c1c); }\n/* ---- NTR settings dialog (shell.overlay) ---- */\n/* Frame-wide surface opened from the agent base's empty-roster menu. The\n overlay layer itself is click-through, so the layer opts back in and blocks\n the app while it is up. */\n.ntr-acp-dialog-layer { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; pointer-events: auto; }\n.ntr-acp-dialog-mask { position: absolute; inset: 0; background: var(--dsw-alias-bg-mask-1, rgba(0, 0, 0, 0.4)); }\n.ntr-acp-dialog {\n position: relative; display: flex; flex-direction: column; overflow: hidden;\n width: min(920px, calc(100vw - 48px)); height: min(820px, calc(100vh - 48px));\n background: var(--dsw-alias-bg-layer-2, #fff); color: inherit;\n border-radius: 24px; box-shadow: var(--dsw-elevation-prominent, 0 24px 60px rgba(0, 0, 0, 0.25));\n}\n.ntr-acp-dialog-head { flex: none; display: flex; align-items: center; gap: 8px; padding: 16px 16px 8px 24px; }\n.ntr-acp-dialog-title { flex: 1; font-size: 16px; font-weight: 600; }\n.ntr-acp-dialog-close { font: inherit; font-size: 14px; line-height: 1; width: 30px; height: 30px; padding: 0; border: none; border-radius: 999px; background: none; color: inherit; cursor: pointer; }\n.ntr-acp-dialog-close:hover { background: var(--dsw-alias-interactive-bg-active, #0000000d); }\n.ntr-acp-dialog-body { flex: 1; min-height: 0; overflow: auto; }\n/* The section brings its own page scroll for the settings shell's content\n column; the dialog body is the scroll owner here. */\n.ntr-acp-dialog-body > .dwa-root { max-width: none; overflow: visible; }\n`\n\nlet styleMounted = false\n\n/** Mount the shared stylesheet once (same pattern as the settings panel's). */\nexport function ensureAcpStyle(): void {\n if (styleMounted || typeof document === 'undefined') return\n styleMounted = true\n const node = document.createElement('style')\n node.textContent = STYLE\n document.head.appendChild(node)\n}\n","/**\n * React surface for ACP: the settings configuration section.\n *\n * Scope is deliberately narrow — this is a CONFIGURATION surface. Runtime\n * interaction lives in the conversation: every permission ask with a chat\n * identity is answered on a card above that conversation's composer\n * (`permission-dock.tsx`). The ledger card here is the cross-session mirror —\n * asks whose conversation is not open, or which have no chat identity at all —\n * and is where those are answered (see `permissionLedgerCard`).\n *\n * Each configured agent is one compact row (status + current model + badges)\n * that expands into the full connection form and model catalog. The row stays\n * mounted and the body toggles `display`, so editing a form and collapsing it\n * never loses the in-progress values.\n * All business state lives in the injected AcpStore.\n */\n\nimport { useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport { formatCommandLine, isAbsolutePathLike, splitCommandLine } from './acp-ui.tsx'\nimport { DEFAULT_KNOWN_PRESETS, LOG_MAX_ENTRIES } from './store.ts'\nimport type { AcpStore, UiState } from './store.ts'\nimport { IDLE_RELEASE_MS } from '../protocol.ts'\nimport type { AcpAgentDef, AcpApprovalDeadlines, AcpCatalogChoice, AcpMcpServerDef, AgentSavePayload, AgentView, DetectedAgentInfo, PermissionTimeoutMode, PluginUpdateState } from '../protocol.ts'\n\ninterface WithStore { acp: AcpStore }\n\n/**\n * Mirrors `AGENT_ID_PATTERN` in impl.ts — the client cannot import host-side\n * modules, so this copy must stay in sync (same deal as the preset copies,\n * constraint 7).\n */\nconst AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/\n\n/** Preset ids (drives the「自定义」badge); mirrors AGENT_PRESETS via store copies. */\nconst PRESET_IDS = new Set(DEFAULT_KNOWN_PRESETS.map(preset => preset.id))\n\n/** Seed for the from-scratch creation card: no preset, no tested default. */\nconst NEW_AGENT_SEED: DetectedAgentInfo = {\n id: '',\n name: '',\n detected: false,\n defaultCommand: '',\n defaultArgs: [],\n}\n\n/** How long a chip edit is coalesced before the lightweight exposure RPC fires. */\nconst EXPOSURE_SAVE_DEBOUNCE_MS = 400\n\nconst STYLE = `\n.dwa-root {\n --ntr-gap-1: 4px; --ntr-gap-2: 8px; --ntr-gap-3: 12px; --ntr-gap-4: 16px;\n --ntr-radius: 10px; --ntr-radius-sm: 7px;\n --ntr-font: 13px; --ntr-font-sm: 12px; --ntr-font-xs: 11px;\n --ntr-line: 1px solid var(--dsw-alias-border-l2, #e5e7eb);\n --ntr-muted: var(--dsw-alias-label-secondary, #6b7280);\n --ntr-danger: var(--dsw-alias-state-error-primary, #dc2626);\n --ntr-warn: var(--dsw-alias-state-warn-label, #b45309);\n --ntr-success: color-mix(in srgb, var(--dsw-alias-state-success-primary, #16a34a) 72%, var(--dsw-alias-label-primary, #111827));\n --ntr-accent: var(--dsw-alias-button-primary-fill, #2563eb);\n width: 100%; max-width: 960px; box-sizing: border-box; margin: 0 auto;\n padding: 28px 32px; min-width: 0; container-type: inline-size; font-size: var(--ntr-font); line-height: 1.55;\n}\n.dwa-root [hidden] { display: none !important; }\n.dwa-root button, .dwa-root input, .dwa-root select, .dwa-root textarea { font-family: inherit; }\n.dwa-root input, .dwa-root textarea { min-width: 0; }\n.dwa-tabs { display: flex; gap: 24px; flex-wrap: wrap; border-bottom: var(--ntr-line); margin-bottom: 24px; }\n.dwa-tabs > button { font: inherit; color: var(--ntr-muted); background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 0 0 12px; min-height: 40px; white-space: nowrap; cursor: pointer; }\n.dwa-tabs > button:hover { color: var(--dsw-alias-label-primary, #111827); }\n.dwa-tabs > button[aria-pressed=\"true\"] { color: var(--dsw-alias-label-primary, #111827); border-bottom-color: currentColor; font-weight: 550; }\n.dwa-detail-tabs { margin: 4px 0 20px; gap: 20px; }\n.dwa-detail-tabs > button { font-size: var(--ntr-font-sm); }\n.dwa-row-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.dwa-notice { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }\n.dwa-notice > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.dwa-empty { text-align: center; padding: 40px 16px; color: var(--ntr-muted); }\n.dwa-empty h3 { font-size: 16px; font-weight: 550; color: var(--dsw-alias-label-primary, #111827); margin: 0 0 8px; }\n.dwa-empty .dwa-btn { margin-top: 20px; }\n.dwa-health { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 24px; color: var(--ntr-muted); font-size: var(--ntr-font-sm); flex-wrap: wrap; }\n.dwa-health-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.dwa-health-status > span:last-child:not(:nth-child(2)) { padding-left: 12px; border-left: var(--ntr-line); }\n.dwa-back { margin-bottom: 16px; }\n.dwa-root button:focus-visible, .dwa-root input:focus-visible, .dwa-root select:focus-visible {\n outline: 2px solid var(--ntr-accent); outline-offset: 2px;\n}\n.dwa-page-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 28px; }\n.dwa-page-heading { min-width: 0; }\n.dwa-page-title { margin: 0; font-size: 20px; line-height: 1.35; font-weight: 550; letter-spacing: -0.02em; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.dwa-page-description { margin: 5px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-sm); }\n.dwa-btn {\n min-height: 32px; box-sizing: border-box; font: inherit; border-radius: var(--ntr-radius-sm);\n border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-layer-1, #fff);\n padding: 5px 12px; cursor: pointer; color: inherit; font-size: var(--ntr-font-sm);\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n white-space: nowrap; transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;\n}\n.dwa-btn:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); border-color: var(--dsw-alias-border-l1, #9ca3af); }\n.dwa-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n.dwa-btn-primary { background: var(--ntr-accent); color: var(--dsw-alias-label-primary-foreground, #fff); border-color: transparent; font-weight: 550; }\n.dwa-btn-primary:hover { background: var(--ntr-accent); border-color: transparent; opacity: 0.88; }\n.dwa-btn-secondary { background: var(--dsw-alias-bg-layer-2, #f3f4f6); }\n.dwa-btn-quiet { background: transparent; border-color: transparent; color: var(--ntr-muted); }\n.dwa-btn-quiet:hover { border-color: transparent; color: var(--dsw-alias-label-primary, #111827); }\n.dwa-btn-danger { color: var(--ntr-danger); border-color: var(--dsw-alias-state-error-secondary, #fca5a5); }\n.dwa-btn-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, #fee2e2); }\n.dwa-section { margin-bottom: 24px; }\n.dwa-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }\n.dwa-section-title { font-size: 13px; font-weight: 550; margin: 0; display: flex; align-items: center; gap: 8px; }\n.dwa-section-copy { margin: 3px 0 0; font-size: var(--ntr-font-xs); color: var(--ntr-muted); }\n.dwa-card { border: var(--ntr-line); border-radius: var(--ntr-radius); padding: 16px; background: var(--dsw-alias-bg-layer-1, #fff); font-size: var(--ntr-font); line-height: 1.55; }\n.dwa-card-warn { border-color: color-mix(in srgb, var(--ntr-warn) 30%, var(--dsw-alias-border-l2, #e5e7eb)); background: var(--dsw-alias-state-warn-tertiary, #fffbeb); }\n.dwa-mode-banner { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 16px; align-items: start; margin-bottom: 24px; padding: 14px 16px; }\n.dwa-mode-title { display: flex; align-items: center; gap: 8px; font-weight: 650; }\n.dwa-mode-copy { grid-column: 1 / -1; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-mode-source { display: block; margin-top: 5px; overflow-wrap: anywhere; opacity: 0.85; }\n.dwa-badge { font-size: var(--ntr-font-xs); line-height: 1.4; border-radius: 999px; padding: 2px 8px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font-weight: 550; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }\n.dwa-badge-success { background: var(--dsw-alias-state-success-tertiary, #dcfce7); color: var(--ntr-success); }\n.dwa-badge-warn { background: var(--dsw-alias-state-warn-tertiary, #fef3c7); color: var(--ntr-warn); }\n.dwa-badge-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }\n/* The update badge is a button (it expands the update card): strip the\n browser's default button chrome, keep the badge look. */\n.dwa-update-badge { border: 0; font-family: inherit; cursor: pointer; }\n.dwa-update-badge:hover { opacity: 0.85; }\n/* The version badge doubles as the update entry: same badge look, no chrome. */\n.dwa-version-btn { border: 0; font-family: inherit; cursor: pointer; }\n.dwa-version-btn:hover { opacity: 0.85; }\n.dwa-version { font-family: var(--ds-font-family-code, monospace); }\n/* The update card sits between the page header and the permission mirror. */\n.dwa-update-card { margin-bottom: 16px; }\n.dwa-update-summary { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-weight: 550; }\n.dwa-update-copy { margin: 6px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-update-message { margin: 6px 0 0; color: var(--ntr-muted); font-size: var(--ntr-font-xs); white-space: pre-wrap; overflow-wrap: anywhere; }\n.dwa-update-actions { margin-top: 10px; }\n.dwa-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex-shrink: 0; }\n.dwa-agent-list { display: flex; flex-direction: column; border-top: var(--ntr-line); }\n.dwa-agent-card { border-bottom: var(--ntr-line); min-width: 0; }\n.dwa-agent-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; align-items: center; gap: 14px; padding: 16px 0; }\n.dwa-agent-avatar { display: grid; place-items: center; width: 38px; height: 38px; border: var(--ntr-line); border-radius: 9px; background: var(--dsw-alias-bg-layer-2, #f3f4f6); color: var(--ntr-muted); font: 12px var(--ds-font-family-code, monospace); }\n.dwa-caret { font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; width: 1em; }\n.dwa-caret-btn { font: inherit; border: none; background: var(--dsw-alias-bg-layer-2, #f3f4f6); width: 28px; height: 28px; padding: 0; border-radius: var(--ntr-radius-sm); display: inline-flex; align-items: center; justify-content: center; color: var(--ntr-muted); cursor: pointer; }\n.dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); color: inherit; }\n.dwa-agent-summary { min-width: 0; }\n.dwa-agent-titleline { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }\n.dwa-agent-name { font-size: 13px; font-weight: 550; overflow-wrap: anywhere; }\n.dwa-agent-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 4px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-agent-meta-separator { opacity: 0.45; }\n.dwa-agent-body { padding: 0 0 24px 52px; }\n.dwa-subsection { min-width: 0; }\n.dwa-form-row { display: flex; gap: var(--ntr-gap-3); margin-bottom: 14px; align-items: stretch; }\n.dwa-form-col { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }\n.dwa-form-label { font-size: var(--ntr-font-sm); font-weight: 600; color: var(--dsw-alias-label-primary, #374151); }\n.dwa-hint { font-size: var(--ntr-font-xs); line-height: 1.55; color: var(--ntr-muted); }\n.dwa-error { font-size: var(--ntr-font-xs); color: var(--ntr-danger); }\n.dwa-input { min-height: 36px; font: inherit; font-size: var(--ntr-font-sm); padding: 7px 10px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; width: 100%; box-sizing: border-box; }\n.dwa-select { min-height: 32px; font: inherit; font-size: var(--ntr-font-sm); padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); color: inherit; }\n.dwa-input:hover, .dwa-select:hover { border-color: var(--dsw-alias-border-l1, #9ca3af); }\n.dwa-input:focus, .dwa-select:focus { border-color: var(--ntr-accent); }\n.dwa-input-mono, .dwa-mono { font-family: var(--ds-font-family-code, ui-monospace), SFMono-Regular, Menlo, Monaco, Consolas, monospace; }\n.dwa-stderr { max-height: 120px; overflow: auto; font-family: monospace; font-size: var(--ntr-font-xs); white-space: pre-wrap; color: var(--ntr-warn); margin: 12px 0 0; }\n.dwa-default-hint { background: var(--dsw-alias-bg-layer-2, #f9fafb); border: var(--ntr-line); border-radius: var(--ntr-radius-sm); padding: 10px 12px; font-size: var(--ntr-font-sm); display: flex; align-items: center; justify-content: space-between; margin: 12px 0 14px; gap: 12px; }\n.dwa-form-actions { display: flex; align-items: center; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: var(--ntr-line); flex-wrap: wrap; }\n.dwa-form-actions .dwa-btn-primary { min-width: 88px; }\n.dwa-form-actions > .dwa-hint:first-child { margin-right: auto; }\n.dwa-form-actions > .dwa-hint:last-child:not(:first-child) { width: 100%; }\n.dwa-menu-wrap { position: relative; }\n.dwa-menu-wrap > .dwa-btn { width: 32px; padding: 0; }\n.dwa-menu { position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 190px; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); padding: 4px; display: flex; flex-direction: column; }\n.dwa-menu-item { font: inherit; font-size: var(--ntr-font-sm); text-align: left; border: none; background: none; padding: 7px 10px; border-radius: 5px; cursor: pointer; color: inherit; }\n.dwa-menu-item:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-menu-item-danger { color: var(--ntr-danger); }\n.dwa-menu-confirm { display: flex; align-items: center; gap: 4px; padding: 4px; }\n.dwa-add-panel .dwa-agent-row { display: none; }\n.dwa-add-panel .dwa-agent-body { padding: 0 0 24px; }\n.dwa-add-panel .dwa-agent-card { border: 0; }\n.dwa-add-content { padding-top: 10px; }\n.dwa-chip { min-height: 30px; font: inherit; font-size: var(--ntr-font-sm); border: var(--ntr-line); border-radius: 999px; padding: 4px 12px; background: var(--dsw-alias-bg-layer-1, #fff); cursor: pointer; color: inherit; }\n.dwa-chip:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-chip-active { border-color: var(--ntr-accent); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); font-weight: 550; }\n.dwa-chip-list { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }\n.dwa-switch { display: inline-flex; align-items: center; gap: 7px; font-size: var(--ntr-font-sm); cursor: pointer; user-select: none; }\n.dwa-inline-setting { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-2, #f9fafb); }\n.dwa-inline-setting .dwa-switch { flex-shrink: 0; margin-top: 1px; }\n.dwa-setting-copy { color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n/* Stacked, not side by side: the rows carry name, id and credit, and a\n half-width pane truncated them at every realistic settings width. Each list\n gets the whole column instead. */\n.dwa-model-transfer { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }\n/* Tall enough for a comfortable selection (about ten rows) before scrolling;\n the cap keeps the two stacked panes from pushing the rest of the card away. */\n.dwa-model-pane { min-width: 0; max-height: 360px; display: flex; flex-direction: column; border: var(--ntr-line); border-radius: var(--ntr-radius-sm); background: var(--dsw-alias-bg-layer-1, #fff); overflow: hidden; }\n.dwa-model-pane-head { display: flex; align-items: center; gap: 6px; padding: 7px 9px; border-bottom: var(--ntr-line); font-size: var(--ntr-font-xs); font-weight: 600; color: var(--dsw-alias-label-secondary, #4b5563); }\n.dwa-model-pane-head .dwa-btn { margin-left: auto; min-height: 26px; padding: 2px 8px; font-size: var(--ntr-font-xs); }\n.dwa-model-pane-head .dwa-caret-btn { flex: none; width: 22px; height: 22px; background: transparent; }\n.dwa-model-pane-head .dwa-caret-btn:hover { background: var(--dsw-alias-interactive-bg-active, #e5e7eb); }\n.dwa-model-pane.is-collapsed .dwa-model-list { display: none; }\n.dwa-model-list { display: flex; flex-direction: column; gap: 2px; padding: 5px; flex: 1 1 auto; min-height: 0; overflow-y: auto; }\n.dwa-model-section { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; font-size: 12px; }\n.dwa-model-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n/* The actions group keeps 「检测可用模型」 and the filter together and wraps as\n one unit; the status line takes the leftover space on the first row, so a\n narrow panel puts [detect][search] on its own row starting under the label\n instead of floating them to the right edge. */\n.dwa-model-toolbar-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.dwa-model-status { margin-left: auto; white-space: nowrap; }\n.dwa-model-search { width: 200px; min-height: 30px; }\n.dwa-model-empty { padding: 6px; }\n.dwa-model-row { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 38px; border: none; background: none; padding: 7px 8px; border-radius: 5px; cursor: pointer; color: inherit; font: inherit; font-size: var(--ntr-font-sm); text-align: left; }\n.dwa-model-row:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-model-row.is-on { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #2563eb) 8%, transparent); color: var(--dsw-alias-state-business-primary, #2563eb); }\n.dwa-model-row-mark { width: 12px; flex: none; text-align: center; opacity: 0.75; }\n.dwa-model-row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n/* Name and credit keep their room; the id is the only cell that collapses. */\n.dwa-model-row-desc { flex: 0 0 auto; max-width: 45%; opacity: 0.75; font-size: var(--ntr-font-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n/* The trailing 14px is reserved for the fade mask: while the id fits, the\n gradient only covers empty padding; once it overflows, its tail dissolves\n instead of ending in a hard ellipsis. armModelIdMarquee reads the same\n padding to scroll the full text under the pointer. */\n.dwa-model-row-value { flex: 0 1 auto; flex-shrink: 1000; box-sizing: border-box; min-width: 0; max-width: 45%; padding-right: 14px; opacity: 0.55; font-size: 10px; overflow: hidden; white-space: nowrap; mask-image: linear-gradient(to right, #000 calc(100% - 14px), transparent); -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 14px), transparent); }\n.dwa-model-row-value-text { display: inline-block; white-space: nowrap; }\n.dwa-model-row:hover .dwa-model-row-value-text { animation: dwa-model-id-marquee var(--dwa-id-duration, 1.4s) ease-in-out 0.2s infinite alternate; }\n@keyframes dwa-model-id-marquee {\n from { transform: translateX(0); }\n to { transform: translateX(var(--dwa-id-shift, 0px)); }\n}\n.dwa-model-row-remove { flex: none; opacity: 0.5; padding: 0 2px; }\n.dwa-model-row:hover .dwa-model-row-remove { opacity: 1; }\n.dwa-seg { display: inline-flex; gap: 4px; padding: 4px; box-sizing: border-box; border-radius: var(--ntr-radius-sm); border: var(--ntr-line); background: var(--dsw-alias-bg-layer-2, #f3f4f6); vertical-align: middle; }\n.dwa-seg > button { min-height: 32px; font: inherit; font-size: var(--ntr-font-sm); color: var(--ntr-muted); border: 1px solid transparent; border-radius: 4px; background: transparent; padding: 4px 12px; cursor: pointer; white-space: nowrap; }\n.dwa-seg > button:hover { background: var(--dsw-alias-interactive-bg-active, #f3f4f6); }\n.dwa-seg > button.is-on { background: var(--dsw-alias-bg-layer-1, #fff); border: var(--ntr-line); color: var(--dsw-alias-label-primary, #111827); font-weight: 550; }\n.dwa-preferences { border-top: var(--ntr-line); }\n.dwa-preference-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 0; }\n.dwa-preference-row + .dwa-preference-row { border-top: var(--ntr-line); }\n/* A control that needs the whole card: the label sits above it, not beside\n it. A right-aligned segmented control squeezed its labels into two lines\n each (中/止, 自动推/进), which read as broken rather than as choices. */\n.dwa-preference-block { padding: 12px 0; }\n.dwa-preference-block + .dwa-preference-block { border-top: var(--ntr-line); }\n.dwa-preference-label { font-size: var(--ntr-font-sm); font-weight: 600; }\n.dwa-preference-copy { margin-top: 2px; color: var(--ntr-muted); font-size: var(--ntr-font-xs); }\n.dwa-seg-wide { display: flex; width: 100%; margin-top: 10px; }\n.dwa-seg-wide > button { flex: 1; }\n/* The wait window, as a slider: the number is the headline, the rungs are the\n scale under the track. */\n.dwa-deadline-value { margin-top: 8px; font-size: 20px; font-weight: 600; }\n.dwa-deadline-range { width: 100%; margin-top: 6px; accent-color: var(--ntr-accent); }\n.dwa-deadline-range:disabled { opacity: 0.4; }\n.dwa-deadline-ticks { display: flex; justify-content: space-between; margin-top: 2px; color: var(--ntr-muted); font-size: 10px; }\n.dwa-deadline-ticks > span { white-space: nowrap; }\n/* The facts that make a number here meaningless, in warning ink rather than\n the muted gray every other hint uses: the value the config file is\n outranking, on the row it belongs to. */\n.dwa-deadline-flag { margin-top: 4px; font-size: var(--ntr-font-xs); font-weight: 550; color: var(--ntr-warn); }\n.dwa-deadline-warn { margin-top: 10px; padding: 9px 12px; border-left: 3px solid var(--dsw-alias-state-warn-primary, #d97706); background: var(--dsw-alias-state-warn-secondary, #fffbeb); border-radius: var(--ntr-radius-sm); font-size: var(--ntr-font-xs); line-height: 1.55; }\n.dwa-deadline-reset { display: flex; justify-content: flex-end; margin-top: 8px; }\n/* Runtime log: one bounded list, newest first. The time column never wraps;\n the message is the only cell that may break anywhere. */\n.dwa-log-list { list-style: none; margin: 0; padding: 0; border: var(--ntr-line); border-radius: var(--ntr-radius); overflow: hidden; }\n.dwa-log-row { display: flex; align-items: baseline; gap: 8px; padding: 6px 10px; font-size: var(--ntr-font-sm); border-top: var(--ntr-line); min-width: 0; }\n.dwa-log-row:first-child { border-top: 0; }\n.dwa-log-row.is-error { background: var(--dsw-alias-state-error-tertiary, #fef2f2); }\n.dwa-log-time { color: var(--ntr-muted); white-space: nowrap; }\n.dwa-log-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.dwa-log-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ntr-danger); display: inline-block; margin-left: 6px; }\n.dwa-log-level.is-info { background: var(--dsw-alias-state-business-tertiary, #dbeafe); color: var(--dsw-alias-state-business-primary, #2563eb); }\n.dwa-log-level.is-error { background: var(--dsw-alias-state-error-tertiary, #fef2f2); color: var(--ntr-danger); }\n@media (max-width: 640px) {\n .dwa-root { padding: 20px 16px; }\n}\n@container (max-width: 640px) {\n .dwa-page-header { align-items: flex-start; flex-wrap: wrap; }\n .dwa-agent-body { padding-left: 0; }\n .dwa-agent-row { grid-template-columns: 32px minmax(0, 1fr) auto; gap: 10px; }\n .dwa-agent-avatar { width: 32px; height: 32px; }\n .dwa-section-heading { flex-wrap: wrap; }\n .dwa-tabs { gap: 20px; }\n .dwa-notice { flex-wrap: wrap; }\n .dwa-seg-wide > button { padding-inline: 5px; }\n .dwa-model-toolbar-actions { width: 100%; }\n .dwa-model-search { flex: 1 1 auto; width: auto; }\n .dwa-preference-row { align-items: flex-start; flex-direction: column; }\n .dwa-default-hint { align-items: flex-start; flex-direction: column; }\n /* The slider's scale: up to eight rungs with full unit words (\"15 分钟\")\n need a smaller face before they can collide on a narrow panel. The rung\n count follows the mode (自动推进 adds 不等待), and the row is a flex\n space-between, so a shorter scale just spreads wider. */\n .dwa-deadline-ticks { font-size: 9px; }\n}\n@media (prefers-reduced-motion: reduce) {\n .dwa-root *, .dwa-root *::before, .dwa-root *::after { transition: none !important; }\n .dwa-model-row:hover .dwa-model-row-value-text { animation: none !important; }\n}\n`\n\nlet styleMounted = false\nfunction ensureStyle(): void {\n if (styleMounted || typeof document === 'undefined') return\n styleMounted = true\n const node = document.createElement('style')\n node.textContent = STYLE\n document.head.appendChild(node)\n}\n\nfunction useUi(acp: AcpStore) {\n return useSyncExternalStore(cb => acp.subscribe(cb), () => acp.getSnapshot())\n}\n\nfunction stateVisual(state: AgentView['state'], acp: AcpStore): { color: string; text: string; title?: string } {\n switch (state) {\n case 'ready': return { color: 'var(--dsw-alias-state-success-primary, #22c55e)', text: acp.label('已连接', 'Connected') }\n case 'starting': return { color: 'var(--dsw-alias-state-warn-primary, #eab308)', text: acp.label('启动中', 'Starting') }\n case 'error': return { color: 'var(--dsw-alias-state-error-primary, #ef4444)', text: acp.label('错误', 'Error') }\n // A configured-but-resting agent is the normal state, not a fault: the\n // process is not kept resident and connects on demand the first time a DSH\n // chat turn uses it. Surfacing a runtime lifecycle here (\"stopped\"/\"idle\")\n // read as broken; the user's real question at a glance is \"is it set up?\",\n // which the config-time 测试连接 (agent/test) already answers. So the\n // resting state just says 已配置.\n default: return {\n color: 'var(--dsw-alias-state-idle-primary, #9ca3af)',\n text: acp.label('已配置', 'Configured'),\n title: acp.label(\n '已配置,进程不常驻。在 DSH 聊天中首次使用时自动连接;展开区可用「测试连接」验证命令能否正常起来。',\n 'Configured; the process is not kept resident. It connects automatically the first time a DSH chat turn uses it — use “Test connection” in the expanded panel to verify the command starts.',\n ),\n }\n }\n}\n\n/** Bilingual label for the event-stream state shown in the toolbar. */\nfunction streamText(stream: 'init' | 'live' | 'down', acp: AcpStore): string {\n if (stream === 'live') return acp.label('事件流已连接', 'Event stream live')\n if (stream === 'down') return acp.label('事件流已断开', 'Event stream down')\n return acp.label('事件流连接中', 'Event stream connecting')\n}\n\n/**\n * Canonical comparable form of an exposure filter. Unset and empty mean the\n * same thing host-side (no `agentId@choice` variants), so they share a key —\n * treating them as different would leave a row permanently dirty.\n */\nfunction exposureKey(exposed: readonly string[] | undefined): string {\n return exposed === undefined || exposed.length === 0 ? 'none' : `set:${exposed.join('\\u0000')}`\n}\n\nfunction sameStringArray(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index])\n}\n\nfunction sameStringMap(a: Record<string, string>, b: Record<string, string>): boolean {\n const keys = Object.keys(a)\n if (keys.length !== Object.keys(b).length) return false\n return keys.every(key => a[key] === b[key])\n}\n\n/**\n * Canonical comparable form of the MCP server list. Unset and empty mean the\n * same thing host-side (no extra MCP servers), so they share a key — treating\n * them as different would leave a card permanently dirty.\n *\n * The list is compared by CANONICAL JSON (object keys sorted, the servers\n * themselves sorted by name): reformatting the text — reordering keys,\n * re-indenting, listing the servers in another order — is not a change. The\n * ARGV array inside a server keeps its order, because there it is the command\n * line. The browser half needs no copy of the host's per-server validation: a\n * list the host would reject simply never matches, and the host's own error is\n * what the user sees.\n */\nfunction mcpKey(servers: readonly AcpMcpServerDef[] | undefined): string {\n if (servers === undefined || servers.length === 0) return 'none'\n const canonical = servers\n .map(canonicalJson)\n .sort((a, b) => {\n const nameA = String((a as { name?: unknown }).name ?? '')\n const nameB = String((b as { name?: unknown }).name ?? '')\n return nameA < nameB ? -1 : nameA > nameB ? 1 : 0\n })\n return JSON.stringify(canonical)\n}\n\n/** One value in canonical JSON form: object keys sorted, arrays kept in order. */\nfunction canonicalJson(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(canonicalJson)\n if (value === null || typeof value !== 'object') return value\n const entries = Object.entries(value as Record<string, unknown>)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([key, entry]) => [key, canonicalJson(entry)])\n return Object.fromEntries(entries)\n}\n\n/**\n * The MCP server list as the form holds it: the raw JSON text the user typed.\n *\n * Parsing happens on the way OUT (`buildPayload`), never on the way in, so a\n * half-typed value never throws inside a render. An unparseable text yields\n * `undefined`, which the form reports as invalid (`mcpInvalid`) and refuses to\n * save — better than sending a truncated list.\n */\nfunction parseMcpServers(text: string): AcpMcpServerDef[] | undefined {\n const trimmed = text.trim()\n if (trimmed === '') return undefined\n try {\n const parsed: unknown = JSON.parse(trimmed)\n if (!Array.isArray(parsed)) return undefined\n return parsed as AcpMcpServerDef[]\n } catch {\n return undefined\n }\n}\n\n/** Whether the MCP server text parses as a list at all (the host validates the rest). */\nfunction mcpTextValid(text: string): boolean {\n const trimmed = text.trim()\n if (trimmed === '') return true\n try {\n return Array.isArray(JSON.parse(trimmed))\n } catch {\n return false\n }\n}\n\n/**\n * Detected presets split by configured-ness, plus configured agents missing\n * from detection (custom / legacy persisted defs) synthesized into card\n * entries so they stay manageable.\n */\nfunction partitionAgents(\n detectedAgents: readonly DetectedAgentInfo[],\n agents: readonly AgentView[],\n): { configured: Array<{ info: DetectedAgentInfo; synthesized: boolean }>; available: DetectedAgentInfo[] } {\n const configured: Array<{ info: DetectedAgentInfo; synthesized: boolean }> = []\n const available: DetectedAgentInfo[] = []\n const agentIds = new Set(agents.map(a => a.id))\n const detectedIds = new Set(detectedAgents.map(d => d.id))\n for (const info of detectedAgents) {\n if (agentIds.has(info.id) || info.currentConfig !== undefined) configured.push({ info, synthesized: false })\n else available.push(info)\n }\n for (const agent of agents) {\n if (!detectedIds.has(agent.id)) {\n configured.push({\n info: {\n id: agent.id,\n name: agent.title,\n detected: false,\n defaultCommand: agent.command,\n defaultArgs: [...agent.args],\n },\n synthesized: true,\n })\n }\n }\n return { configured, available }\n}\n\n/**\n * The persisted definition a save would replace. `currentConfig` is the full\n * def (env / npmPackage / exposedModels included); the synthesized fallback\n * covers only the fields an `AgentView` carries, so anything else compares as\n * \"changed\" — the honest answer when the host never reported it.\n *\n * The idle-release field is compared AS STORED: an absent per-agent field\n * means \"follow the global window\", and the host's resolved value must not be\n * written back into it — that would freeze the global window into this agent's\n * definition and the global slider would then never reach it.\n */\nfunction persistedDef(detected: DetectedAgentInfo, activeAgent: AgentView | undefined): AcpAgentDef | undefined {\n if (detected.currentConfig !== undefined) return detected.currentConfig\n if (activeAgent === undefined) return undefined\n return {\n id: activeAgent.id,\n title: activeAgent.title,\n command: activeAgent.command,\n args: [...activeAgent.args],\n ...(activeAgent.cwd === undefined ? {} : { cwd: activeAgent.cwd }),\n allowedCwdRoots: [...activeAgent.allowedCwdRoots],\n env: {},\n }\n}\n\n/**\n * Equivalence test between the form payload and what is already persisted.\n * Order-insensitive on purpose: `env` is a map and `args`/`allowedCwdRoots`\n * keep their order, but a reordered allow-list is not a change worth warning\n * about; `exposedModels === undefined` means \"offer no variants\", which is the\n * same state as an empty list.\n */\nexport function payloadEquals(payload: AgentSavePayload, current: AcpAgentDef): boolean {\n if (payload.command !== current.command) return false\n if ((payload.title ?? payload.id) !== (current.title ?? current.id)) return false\n if (!sameStringArray(payload.args ?? [], current.args ?? [])) return false\n if ((payload.npmPackage ?? '') !== (current.npmPackage ?? '')) return false\n if ((payload.cwd ?? '') !== (current.cwd ?? '')) return false\n if (!sameStringArray(payload.allowedCwdRoots ?? [], current.allowedCwdRoots ?? [])) return false\n if (!sameStringMap(payload.env ?? {}, current.env ?? {})) return false\n if (exposureKey(payload.exposedModels) !== exposureKey(current.exposedModels)) return false\n if (mcpKey(payload.mcpServers) !== mcpKey(current.mcpServers)) return false\n if ((payload.autoDiscoverModels !== false) !== (current.autoDiscoverModels !== false)) return false\n // The per-agent OVERRIDE, compared as stored: an absent field on both sides\n // means \"follow the global window\" and is not a change; a field on one\n // side only means the user is adding or dropping an override.\n if (payload.idleTimeoutMs !== current.idleTimeoutMs) return false\n return true\n}\n\n/**\n * The settings panel's permission-ledger rule.\n *\n * Pending asks are shown in the conversation card while that conversation is\n * open; this card is the cross-session mirror for the ones it cannot show —\n * asks belonging to another conversation, and `/acp` RPC-only asks with no\n * chat identity at all. It therefore appears whenever ANY ask is pending: a\n * pending entry with no conversation card would otherwise have no answer\n * surface anywhere.\n */\nexport function permissionLedgerCard(ui: Pick<UiState, 'permissions'>): { visible: boolean } {\n return { visible: ui.permissions.length > 0 }\n}\n\n/**\n * The ONE wait window, as the rungs the slider snaps to.\n *\n * Shortest wait first, and `0` (不等待) FIRST: the slider reads left to right\n * as \"wait longer\", and \"do not wait at all\" is the shortest wait there is.\n * Never expiring is NOT on this scale — that is the `wait` MODE's business,\n * and having 「一直等待」 in both places made the card say the same thing twice\n * in two ways.\n *\n * A window the config file names that is not on this ladder (45 seconds, say)\n * still runs as configured: the card prints the exact number and parks the\n * thumb on the nearest rung, so the slider never pretends a number the host\n * is not using.\n */\nconst DEADLINE_LADDER: readonly number[] = [\n 0,\n 30_000,\n 60_000,\n 300_000,\n 900_000,\n 1_800_000,\n 3_600_000,\n 21_600_000,\n]\n\n/**\n * The shortest REAL wait — the first rung above 不等待.\n *\n * Hand-copied from the host half (`APPROVAL_DEADLINE_MIN_WAIT_MS` in\n * `src/impl.ts`): the browser half may not import the host module, so the\n * equivalence is pinned by a test instead (see `tests/acp-view.spec.ts`).\n */\nexport const DEADLINE_MIN_WAIT_MS = 30_000\n\n/**\n * The window the card shows under a mode.\n *\n * 不等待 (`0`) is the **auto** mode's rung: it answers with the option the\n * agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —\n * refusing every ask the instant it arrives stops the agent at every gate it\n * meets, which is not a timeout but a wall. So under `abort` the window floors\n * to the shortest real wait, and the card never shows a zero it cannot run.\n *\n * `undefined` (the `wait` mode) stays undefined: there is no window at all.\n */\nexport function deadlineValueFor(mode: PermissionTimeoutMode, ms: number | undefined): number | undefined {\n if (ms === undefined) return undefined\n return mode === 'abort' ? Math.max(ms, DEADLINE_MIN_WAIT_MS) : ms\n}\n\n/**\n * The rungs a mode offers.\n *\n * 不等待 (`0`) is the **auto** mode's rung: it answers with the option the\n * agent's own kinds imply, so the agent CONTINUES. `abort` has no such rung —\n * refusing every ask the instant it arrives stops the agent at every gate it\n * meets, which is not a timeout but a wall. So `abort` starts at the shortest\n * real wait.\n */\nfunction deadlineLadder(mode: PermissionTimeoutMode): readonly number[] {\n return mode === 'auto' ? DEADLINE_LADDER : DEADLINE_LADDER.filter(ms => ms > 0)\n}\n\n/**\n * The rung the slider shows for a window. Exact hits keep their rung; anything\n * else takes the nearest one. A tie goes to the SHORTER wait: the thumb must\n * never promise more patience than the window has.\n */\nexport function deadlineSliderIndex(ms: number, mode: PermissionTimeoutMode = 'auto'): number {\n const ladder = deadlineLadder(mode)\n const exact = ladder.indexOf(ms)\n if (exact >= 0) return exact\n let best = 0\n let bestDistance = Number.POSITIVE_INFINITY\n for (let i = 0; i < ladder.length; i++) {\n const distance = Math.abs(ms - ladder[i])\n if (distance < bestDistance) {\n bestDistance = distance\n best = i\n }\n }\n return best\n}\n\n/** The three modes, in the order a user reads them: refuse, answer, wait. */\nconst DEADLINE_MODES: readonly PermissionTimeoutMode[] = ['abort', 'auto', 'wait']\n\n/** `5 分钟` / `30 秒` / `不等待` — the readout and the tick label, one formatter. */\nfunction deadlineText(ms: number, acp: AcpStore): string {\n if (ms <= 0) return acp.label('不等待', 'No wait')\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return acp.label(`${String(seconds)} 秒`, `${String(seconds)}s`)\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return acp.label(`${String(minutes)} 分钟`, `${String(minutes)}m`)\n const hours = Math.round(minutes / 60)\n return acp.label(`${String(hours)} 小时`, `${String(hours)}h`)\n}\n\n/** The mode's label — one formatter, so the picker and the override note agree. */\nfunction modeText(mode: PermissionTimeoutMode, acp: AcpStore): string {\n if (mode === 'abort') return acp.label('超时中止', 'Abort')\n if (mode === 'auto') return acp.label('自动推进', 'Auto')\n return acp.label('一直等待', 'Wait')\n}\n\n/**\n * The mode block: what happens when nobody answers.\n *\n * Three values and no more — refuse, answer, wait — so the picker is a\n * segmented control, not a dropdown. The buttons stretch across the whole\n * card: a right-aligned segmented control squeezed the labels into two lines\n * each (中/止, 自动推/进), which read as broken rather than as choices.\n *\n * The copy under the buttons says what the SELECTED mode does, in one\n * sentence — the labels alone (\"超时中止\") cannot. `auto` carries a warning of\n * its own: it approves things nobody looked at.\n */\nfunction DeadlineModeBlock(props: {\n acp: AcpStore\n mode: PermissionTimeoutMode\n /** Set when the deployment baseline is outranked here. */\n override?: string\n}) {\n const { acp, mode, override } = props\n const copy = mode === 'wait'\n ? acp.label(\n '一直等待应答,不设超时时间。',\n 'Wait until you answer — no deadline.',\n )\n : mode === 'auto'\n ? acp.label(\n '超过设定时间未应答,自动代替你选择一个选项让 agent 继续执行。',\n 'Past the deadline, an option is chosen for you and the agent continues.',\n )\n : acp.label(\n '超过设定时间未应答,自动拒绝该操作,agent 继续执行。',\n 'Past the deadline, the operation is refused and the agent continues.',\n )\n return (\n <div className=\"dwa-preference-block\">\n <div className=\"dwa-preference-label\">{acp.label('超时处理方式', 'When nobody answers')}</div>\n <div className=\"dwa-seg dwa-seg-wide\">\n {DEADLINE_MODES.map(option => (\n <button\n key={option}\n type=\"button\"\n className={mode === option ? 'is-on' : ''}\n onClick={() => void acp.setApprovalTimeoutMode(option)}\n >\n {modeText(option, acp)}\n </button>\n ))}\n </div>\n <div className=\"dwa-preference-copy\">{copy}</div>\n {mode === 'auto' && (\n <div className=\"dwa-deadline-warn\">\n {acp.label(\n '请确保你的 agent 值得信任,请谨慎选择。',\n 'Only pick this if you trust your agent — choose it carefully.',\n )}\n </div>\n )}\n {override !== undefined && (\n <div className=\"dwa-deadline-flag\">⚠ {override}</div>\n )}\n </div>\n )\n}\n\n/**\n * The duration block: how long to wait before the deadline fires.\n *\n * ONE slider, because there is ONE window: an ask in the conversation and an\n * ask in the settings ledger are the same question, so they get the same\n * answer. The slider's rungs are the windows people actually pick; a window\n * the config file names outside them still runs as configured (the readout\n * prints the exact number and the thumb parks on the nearest rung).\n *\n * `value === undefined` is the `wait` mode: there is no deadline at all, so\n * the scale has nothing to say — the slider is not drawn, and the readout\n * names the state that is actually in force. The stored window is NOT touched\n * by that (the mode write carries only the mode), so switching back restores\n * it.\n */\nfunction DeadlineSliderBlock(props: {\n acp: AcpStore\n /** undefined in `wait` mode: no window, so no scale. */\n value: number | undefined\n /** The mode the window runs under — it decides which rungs exist. */\n mode: PermissionTimeoutMode\n /** Set when the deployment baseline is outranked here. */\n override?: string\n}) {\n const { acp, value, mode, override } = props\n // 不等待 belongs to `auto`. Under `abort` the window floors to the shortest\n // real wait, so the card never shows a zero it cannot run.\n const effective = deadlineValueFor(mode, value)\n const ladder = deadlineLadder(mode)\n const index = effective === undefined ? 0 : deadlineSliderIndex(effective, mode)\n const copy = effective === undefined\n ? acp.label(\n '选择「一直等待」时不设超时时长。',\n '“Wait” sets no deadline.',\n )\n : effective <= 0\n ? acp.label(\n '不设等待时间,请求到达即按上方所选的方式处理。',\n 'No wait: the ask is settled on arrival, by whatever you picked above.',\n )\n : acp.label(\n '超过该时长仍未应答,将按上方所选的方式处理。',\n 'Past this, whatever you picked above is what happens.',\n )\n return (\n <div className=\"dwa-preference-block\">\n <div className=\"dwa-preference-label\">{acp.label('等待时长', 'How long to wait')}</div>\n <div className=\"dwa-deadline-value\">\n {effective === undefined ? acp.label('一直等待', 'Always') : deadlineText(effective, acp)}\n </div>\n {effective !== undefined && (\n <>\n <input\n className=\"dwa-deadline-range\"\n type=\"range\"\n min={0}\n max={ladder.length - 1}\n step={1}\n value={index}\n aria-label={acp.label('等待时长', 'How long to wait')}\n onChange={event => {\n const next = ladder[Number(event.target.value)]\n if (next !== undefined && next !== effective) void acp.setApprovalDeadline(next)\n }}\n />\n <div className=\"dwa-deadline-ticks\">\n {ladder.map(rung => (\n <span key={rung}>{deadlineText(rung, acp)}</span>\n ))}\n </div>\n </>\n )}\n <div className=\"dwa-preference-copy\">{copy}</div>\n {override !== undefined && (\n <div className=\"dwa-deadline-flag\">⚠ {override}</div>\n )}\n </div>\n )\n}\n\n/**\n * The facts the card has to show, decided from what the host reported.\n *\n * The card carries TWO controls, and they are orthogonal: one window (how\n * long) and one mode (what happens). An override can sit on either of them,\n * so each is decided on its own.\n *\n * `overridden` is what gates the reset button. A baseline that AGREES with the\n * running value is not an override, and offering a reset for it would be a\n * button that does nothing (observed live).\n */\nexport function approvalDeadlineFacts(deadlines: AcpApprovalDeadlines): {\n /** The config-file window the card's window outranks — undefined when\n * nothing is being outranked. */\n baseline: number | undefined\n /** The config-file mode the card's mode outranks, when it differs. */\n modeBaseline: PermissionTimeoutMode | undefined\n /** A reset is only honest when something is actually being outranked. */\n overridden: boolean\n} {\n const configured = deadlines.configured?.permissionTimeoutMs\n const baseline = configured === undefined || configured === deadlines.permissionTimeoutMs\n ? undefined\n : configured\n const modeBaseline = deadlines.configuredMode !== undefined && deadlines.configuredMode !== deadlines.mode\n ? deadlines.configuredMode\n : undefined\n return {\n baseline,\n modeBaseline,\n overridden: baseline !== undefined || modeBaseline !== undefined,\n }\n}\n\n/**\n * The approval-deadline card.\n *\n * TWO controls, and they are orthogonal: one window (how long) and one mode\n * (what happens). One window covers both places an ask can wait — the\n * conversation's approval card and the settings panel's ledger — because they\n * ask the same question and the answer should not differ by where the card\n * happens to be drawn.\n *\n * The two controls are ONE system, and the card says so in a sentence: the\n * window says how long to wait, the mode says what happens when nobody\n * answered. That sentence is the thing the previous three-row layout kept\n * missing.\n *\n * The facts that make a number here meaningless are shown, not swallowed: the\n * deployment baseline this card outranks. Each rides on the control it belongs\n * to, and the reset button appears only when something is actually being\n * outranked.\n */\nfunction ApprovalDeadlinesCard(props: { acp: AcpStore; ui: UiState }) {\n const { acp, ui } = props\n const deadlines = ui.approvalDeadlines\n // Older host half: no card rather than a number this browser invented.\n if (deadlines === undefined) return null\n const facts = approvalDeadlineFacts(deadlines)\n const baselineNote = facts.baseline === undefined\n ? undefined\n : acp.label(\n `配置文件(cordis.patch.yml)设为 ${deadlineText(facts.baseline, acp)},此处设置优先。`,\n `The config file (cordis.patch.yml) asks for ${deadlineText(facts.baseline, acp)}; the value set here wins.`,\n )\n const modeNote = facts.modeBaseline === undefined\n ? undefined\n : acp.label(\n `配置文件(cordis.patch.yml)设为「${modeText(facts.modeBaseline, acp)}」,此处设置优先。`,\n `The config file (cordis.patch.yml) asks for \"${modeText(facts.modeBaseline, acp)}\"; the value set here wins.`,\n )\n // In `wait` mode there is no deadline at all, so the slider shows the one\n // thing that is true: no deadline.\n const waitMode = deadlines.mode === 'wait'\n return (\n <section className=\"dwa-section\">\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('审批时限', 'Approval deadlines')}</h3>\n <p className=\"dwa-section-copy\">\n {acp.label(\n '权限请求的等待时长与超时处理方式。',\n 'How long to wait for a permission request, and what happens when nobody answers.',\n )}\n </p>\n </div>\n </div>\n <div className=\"dwa-preferences\">\n <DeadlineModeBlock acp={acp} mode={deadlines.mode} override={modeNote} />\n <DeadlineSliderBlock\n acp={acp}\n value={waitMode ? undefined : deadlines.permissionTimeoutMs}\n mode={deadlines.mode}\n override={baselineNote}\n />\n {facts.overridden && (\n <div className=\"dwa-deadline-reset\">\n <button type=\"button\" className=\"dwa-btn dwa-btn-secondary\" onClick={() => void acp.resetApprovalDeadlines()}>\n {acp.label('恢复配置文件的值', 'Use config file value')}\n </button>\n </div>\n )}\n </div>\n </section>\n )\n}\n\n/**\n * The idle-release window, as the settings page edits it.\n *\n * ONE control, because there is ONE window: how long a chat process may sit\n * idle before it is parked. The slider snaps to the shared ladder\n * ({@link IDLE_RELEASE_MS}), so every rung it offers is a window the host can\n * run.\n *\n * The card reports the deployment baseline as the fact being overridden, and\n * offers a reset ONLY when something is really overridden — the same rule the\n * approval deadline card follows.\n */\nfunction IdleReleaseCard(props: { acp: AcpStore; ui: UiState }) {\n const { acp, ui } = props\n const release = ui.idleRelease\n // Older host half: no card rather than a number this browser invented.\n if (release === undefined) return null\n // The value a RESET returns to: the window `cordis.patch.yml` names, else\n // the built-in default. The same split the approval card keeps — the\n // reset must restore what the deployment asked for, not a constant.\n const baseline = release.defaultIdleTimeoutMs\n // A reset is only honest when something is actually being overridden.\n const overridden = release.idleTimeoutMs !== baseline\n // A window the deployment named that is not on the ladder still runs as\n // configured: the thumb parks on the nearest rung and the readout prints the\n // exact number, so the card never lies about what is in force.\n const index = IDLE_RELEASE_MS.indexOf(release.idleTimeoutMs)\n const exact = index >= 0\n const shown = exact ? release.idleTimeoutMs : nearestIdleRung(release.idleTimeoutMs)\n // The config file's window is worth saying only when it is being\n // outranked — otherwise it IS what runs, and saying \"the value set here\n // wins\" would name a win nobody claimed.\n const note = release.configuredIdleTimeoutMs === undefined\n || release.configuredIdleTimeoutMs === release.idleTimeoutMs\n ? undefined\n : acp.label(\n `配置文件(cordis.patch.yml)设为 ${idleWindowText(release.configuredIdleTimeoutMs, acp)},此处设置优先。`,\n `The config file (cordis.patch.yml) asks for ${idleWindowText(release.configuredIdleTimeoutMs, acp)}; the value set here wins.`,\n )\n const resetLabel = release.configuredIdleTimeoutMs === undefined\n ? acp.label('恢复默认值', 'Restore default')\n : acp.label('恢复配置文件的值', 'Use config file value')\n return (\n <section className=\"dwa-section\">\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('空闲释放', 'Idle release')}</h3>\n <p className=\"dwa-section-copy\">\n {acp.label('空闲多久后挂起聊天进程。', 'How long an idle chat process is kept before it is parked.')}\n </p>\n </div>\n </div>\n <div className=\"dwa-preferences\">\n <div className=\"dwa-preference-block\">\n <div className=\"dwa-preference-label\">{acp.label('释放窗口', 'Release window')}</div>\n <div className=\"dwa-deadline-value\">{idleWindowText(release.idleTimeoutMs, acp)}</div>\n <input\n className=\"dwa-deadline-range\"\n type=\"range\"\n min={0}\n max={IDLE_RELEASE_MS.length - 1}\n step={1}\n value={exact ? index : IDLE_RELEASE_MS.indexOf(shown)}\n aria-label={acp.label('释放窗口', 'Release window')}\n onChange={event => {\n const next = IDLE_RELEASE_MS[Number(event.target.value)]\n if (next !== undefined && next !== release.idleTimeoutMs) void acp.setIdleRelease(next)\n }}\n />\n <div className=\"dwa-deadline-ticks\">\n {IDLE_RELEASE_MS.filter((_, i) => i % 2 === 0).map(ms => (\n <span key={ms}>{idleWindowText(ms, acp)}</span>\n ))}\n </div>\n <div className=\"dwa-preference-copy\">\n {acp.label(\n '挂起后会话仍保留,下一条消息自动恢复。',\n 'A parked session is kept; the next message resumes it.',\n )}\n </div>\n {note !== undefined && <div className=\"dwa-deadline-flag\">⚠ {note}</div>}\n </div>\n {overridden && (\n <div className=\"dwa-deadline-reset\">\n <button type=\"button\" className=\"dwa-btn dwa-btn-secondary\" onClick={() => void acp.resetIdleRelease()}>\n {resetLabel}\n </button>\n </div>\n )}\n </div>\n </section>\n )\n}\n\n/**\n * The rung nearest a window that is not on the ladder — used only to park the\n * thumb; the readout always prints the exact value.\n */\nfunction nearestIdleRung(ms: number): number {\n let best = IDLE_RELEASE_MS[0]\n let bestDistance = Number.POSITIVE_INFINITY\n for (const rung of IDLE_RELEASE_MS) {\n const distance = Math.abs(rung - ms)\n if (distance < bestDistance) {\n bestDistance = distance\n best = rung\n }\n }\n return best\n}\n\n/**\n * `30 分钟` — the readout and the tick label, one formatter.\n *\n * ALWAYS minutes, even at the top of the ladder: a scale that runs \"1 分钟\"\n * to \"50 分钟\" and then \"1 小时\" reads as two different scales.\n */\nfunction idleWindowText(ms: number, acp: AcpStore): string {\n // 0 is the deployment's way of saying \"never release\", and reading it as\n // \"0 分钟\" would say the opposite.\n if (ms <= 0) return acp.label('永不释放', 'Never')\n const minutes = Math.round(ms / 60_000)\n return acp.label(`${String(minutes)} 分钟`, `${String(minutes)}m`)\n}\n\ntype SettingsPage = 'agents' | 'add' | 'approvals' | 'processes' | 'appearance' | 'logs' | 'diagnostics'\n\n/**\n * Local wall-clock `HH:MM:SS` for one runtime-log line. Hand-built on purpose:\n * `toLocaleTimeString` may drop seconds or pick a 12-hour clock depending on\n * the browser locale, which makes the column ragged and ambiguous.\n */\nfunction logClock(at: number): string {\n const date = new Date(at)\n return [date.getHours(), date.getMinutes(), date.getSeconds()]\n .map(value => String(value).padStart(2, '0'))\n .join(':')\n}\n\n/** The states that carry an update badge next to the version badge. */\nfunction updateBadgeText(update: PluginUpdateState, acp: AcpStore): string | undefined {\n if (update.status === 'available') {\n return acp.label(`可更新 v${update.latest ?? update.current}`, `Update v${update.latest ?? update.current}`)\n }\n if (update.status === 'applying') return acp.label('更新中…', 'Updating…')\n if (update.status === 'applied') {\n const installed = update.installed ?? update.latest ?? update.current\n return acp.label(`已更新 v${installed}`, `Updated v${installed}`)\n }\n if (update.status === 'restart-required') {\n // Only a KNOWN disk version can be named. An interrupted install leaves\n // `installed` unset, and a badge would have to invent a version.\n if (update.installed === undefined) return undefined\n // Neutral: the disk version can be newer OR older than the running one.\n return acp.label(`待重启 v${update.installed}`, `Restart for v${update.installed}`)\n }\n return undefined\n}\n\n/**\n * The version badge's update companion. Every badge is a button, `applying`\n * included: the card is where the install's outcome is read, so the running\n * install must be able to open it too. States without anything to act on get\n * no badge at all — the version badge itself still opens the card.\n */\nfunction PluginUpdateBadge(props: { acp: AcpStore; update: PluginUpdateState; open: boolean; onToggle: () => void }) {\n const { acp, update, open, onToggle } = props\n const text = updateBadgeText(update, acp)\n if (text === undefined) return null\n return (\n <button\n type=\"button\"\n className=\"dwa-badge dwa-badge-info dwa-update-badge\"\n aria-expanded={open}\n aria-controls=\"ntr-plugin-update-card\"\n onClick={onToggle}\n >\n {text}\n </button>\n )\n}\n\n/**\n * The update card. In-page, no dialog: the settings page already owns the one\n * action (install the exact version the host reported), and a modal on top of\n * a settings section would hide the version it is updating.\n *\n * The copy stays to one line per state (constraint 33); the host's raw error\n * (`message`) is the only long text, and it wraps on its own line so a stack\n * trace or a registry error is readable without stretching the card.\n */\nfunction PluginUpdateCard(props: { acp: AcpStore; update: PluginUpdateState; onClose: () => void }) {\n const { acp, update, onClose } = props\n const applying = update.status === 'applying'\n const latest = update.latest ?? update.current\n const onDisk = update.installed !== undefined && update.installed !== update.current\n ? update.installed\n : undefined\n const published = update.publishedAt !== undefined && !Number.isNaN(new Date(update.publishedAt).getTime())\n ? new Date(update.publishedAt).toLocaleDateString()\n : undefined\n const copy = update.status === 'available'\n ? acp.label('更新会卸载并重装插件,之后可能需要重启 DSH。', 'Updating uninstalls and reinstalls the plugin, and may require restarting DSH.')\n : update.status === 'applied'\n ? acp.label('已应用,刷新页面即可使用新版本。', 'Applied. Reload the page to use the new version.')\n : update.status === 'restart-required'\n ? update.installed !== undefined\n ? acp.label(\n `磁盘已是 v${update.installed},重启 DSH 后生效(桌面 App 退出重开;命令行重启 dsh web)。`,\n `On disk v${update.installed}. Restart DSH to apply (quit and reopen the app, or restart dsh web).`,\n )\n : acp.label(\n '更新可能已完成,请刷新页面确认当前版本。',\n 'The update may already be complete. Refresh the page to confirm the current version.',\n )\n : update.status === 'busy'\n ? acp.label('有进行中的对话,结束后才能更新。', 'Finish the running conversation before updating.')\n : update.status === 'failed'\n ? acp.label('更新失败。', 'Update failed.')\n : update.status === 'current'\n ? acp.label('已是最新版本。', 'Up to date.')\n : update.status === 'source-install'\n ? acp.label(\n '源码或本地安装,无法通过 registry 更新。',\n 'Source or local install; update it through its own checkout.',\n )\n : update.status === 'managed'\n ? acp.label('此安装由宿主管理,不能在这里更新。', 'This install is managed by the host and cannot be updated here.')\n : undefined\n const showMessage = (update.status === 'failed' || update.status === 'busy' || update.status === 'managed')\n && update.message !== undefined\n const apply = async (): Promise<void> => {\n if (update.latest === undefined) return\n try {\n await acp.applyPluginUpdate(update.latest)\n } catch {\n // The store keeps the \"disk may have changed\" state and writes its own notice.\n }\n }\n return (\n <div\n id=\"ntr-plugin-update-card\"\n className=\"dwa-card dwa-update-card\"\n role=\"group\"\n aria-label={acp.label('插件更新', 'Plugin update')}\n >\n <div className=\"dwa-update-summary\">\n <span>{acp.label(`当前 v${update.current}`, `Current v${update.current}`)}</span>\n {onDisk !== undefined && <span className=\"dwa-hint\">{acp.label(`磁盘 v${onDisk}`, `On disk v${onDisk}`)}</span>}\n {update.latest !== undefined && (\n <>\n <span aria-hidden=\"true\">→</span>\n <span>{acp.label(`最新 v${latest}`, `Latest v${latest}`)}</span>\n {published !== undefined && <span className=\"dwa-hint\">{published}</span>}\n </>\n )}\n </div>\n {copy !== undefined && <p className=\"dwa-update-copy\">{copy}</p>}\n {showMessage && <div className=\"dwa-update-message\">{update.message}</div>}\n <div className=\"dwa-row-actions dwa-update-actions\">\n {update.status === 'available' && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" onClick={() => { void apply() }}>\n {acp.label('更新', 'Update')}\n </button>\n )}\n {applying && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" disabled>\n {acp.label('正在更新…', 'Updating…')}\n </button>\n )}\n {update.status !== 'applying' && (\n <button type=\"button\" className=\"dwa-btn\" onClick={onClose}>{acp.label('关闭', 'Close')}</button>\n )}\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" disabled={applying} onClick={() => void acp.checkPluginUpdate(true)}>\n {acp.label('重新检查', 'Check again')}\n </button>\n </div>\n </div>\n )\n}\n\n/**\n * The card is drawn for every state a host can report about this install,\n * except the two where there is no install to talk about at all.\n */\nfunction updateCardVisible(update: PluginUpdateState | undefined, open: boolean): boolean {\n if (update === undefined || !open) return false\n return update.status !== 'unsupported' && update.status !== 'not-installed'\n}\n\nexport function AcpSettingsSection(props: WithStore) {\n const acp = props.acp\n ensureStyle()\n const ui = useUi(acp)\n const ledger = permissionLedgerCard(ui)\n const [page, setPage] = useState<SettingsPage>('agents')\n const [addOpen, setAddOpen] = useState(false)\n const [addTarget, setAddTarget] = useState<DetectedAgentInfo | undefined>(undefined)\n const [updateOpen, setUpdateOpen] = useState(false)\n // The store throttles this to one registry read per ten minutes, so every\n // remount (both settings entries mount this section) is cheap.\n useEffect(() => {\n void acp.checkPluginUpdate()\n }, [acp])\n // Opening the log is the read receipt. The store does not mutate at zero,\n // so this cannot loop.\n useEffect(() => {\n if (page === 'logs' && ui.logUnread > 0) acp.markLogsRead()\n }, [acp, page, ui.logUnread])\n const { configured, available } = partitionAgents(ui.detectedAgents, ui.agents)\n const selected = addTarget ?? available[0] ?? NEW_AGENT_SEED\n const openAdd = (): void => {\n setAddOpen(true)\n setPage('add')\n }\n const presetProblem = ui.preset !== undefined && (!ui.preset.installed || ui.preset.problem !== undefined)\n const update = ui.pluginUpdate\n // A source install cannot be replaced by this browser; the version badge\n // says so on hover. Neutral wording: a checkout may be git or not.\n const versionTitle = update?.status === 'source-install'\n ? acp.label('源码或本地安装,无法通过 registry 更新。', 'Source or local install; update it through its own checkout.')\n : undefined\n // The running version is the update entry: every state with a real install\n // report can open the card, including \"up to date\" (which offers a re-check).\n const versionOpensUpdate = update !== undefined\n && update.status !== 'unsupported'\n && update.status !== 'not-installed'\n\n return (\n <div className=\"dwa-root\">\n <header className=\"dwa-page-header\">\n <div className=\"dwa-page-heading\">\n <h2 className=\"dwa-page-title\" aria-label={acp.label('NTR 设置', 'NTR settings')}>\n {acp.label('NTR 设置', 'NTR settings')}\n {ui.pluginVersion !== undefined && (\n versionOpensUpdate\n ? (\n <button\n type=\"button\"\n className=\"dwa-badge dwa-version dwa-version-btn\"\n title={versionTitle}\n aria-expanded={updateOpen}\n aria-controls=\"ntr-plugin-update-card\"\n onClick={() => setUpdateOpen(open => !open)}\n >\n {`v${ui.pluginVersion}`}\n </button>\n )\n : <span className=\"dwa-badge dwa-version\" title={versionTitle}>{`v${ui.pluginVersion}`}</span>\n )}\n {update !== undefined && (\n <PluginUpdateBadge\n acp={acp}\n update={update}\n open={updateOpen}\n onToggle={() => setUpdateOpen(open => !open)}\n />\n )}\n </h2>\n <p className=\"dwa-page-description\">\n {acp.label('连接智能体,设置你的对话偏好。', 'Connect agents and set your conversation preferences.')}\n </p>\n </div>\n {page === 'agents' && configured.length > 0 && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" onClick={openAdd}>\n {acp.label('添加智能体', 'Add agent')}\n </button>\n )}\n </header>\n\n {update !== undefined && updateCardVisible(update, updateOpen) && (\n <PluginUpdateCard acp={acp} update={update} onClose={() => setUpdateOpen(false)} />\n )}\n\n {/* The cross-session mirror: the conversation dock answers the asks\n belonging to the open conversation; this card answers the rest. */}\n {ledger.visible && (\n <div className=\"dwa-card dwa-card-warn\" style={{ marginBottom: 16 }}>\n <div style={{ fontWeight: 600, marginBottom: 4 }}>\n {acp.label('待处理权限请求', 'Pending Permission Requests')}\n </div>\n <div className=\"dwa-hint\" style={{ marginBottom: 8 }}>\n {acp.label(\n '对话打开时在那边的卡片上回答;这里兜底显示其他会话的询问。',\n 'Answer in the conversation card while that conversation is open; this list covers the asks whose conversation is not.',\n )}\n </div>\n {ui.permissions.map(permission => (\n <div key={permission.requestId} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, flexWrap: 'wrap' }}>\n <span style={{ flex: 1, minWidth: 160 }}>\n <span style={{ fontWeight: 600 }}>{permission.toolName}</span>\n {permission.reason !== undefined && (\n <span style={{ color: 'var(--ntr-muted)' }}> · {permission.reason}</span>\n )}\n </span>\n {permission.options.map(option => (\n <button\n key={option.optionId}\n type=\"button\"\n className=\"dwa-btn\"\n onClick={() => { void acp.respondPermission(permission.requestId, option.optionId).catch(() => undefined) }}\n >\n {option.name}\n </button>\n ))}\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" onClick={() => { void acp.respondPermission(permission.requestId).catch(() => undefined) }}>\n {acp.label('取消', 'Cancel')}\n </button>\n </div>\n ))}\n </div>\n )}\n\n {ui.notice !== undefined && (\n <div className=\"dwa-card dwa-card-warn\" style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ flex: 1 }}>{ui.notice}</span>\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" aria-label={acp.label('关闭通知', 'Dismiss notice')} onClick={() => acp.clearNotice()}>✕</button>\n </div>\n )}\n\n {presetProblem && (\n <div className=\"dwa-card dwa-card-warn dwa-notice\" role=\"status\">\n <span>{acp.label('NTR 模式需要检查,当前可能无法开始对话。', 'NTR mode needs attention; new conversations may be unavailable.')}</span>\n <button type=\"button\" className=\"dwa-btn\" onClick={() => setPage('diagnostics')}>{acp.label('查看诊断', 'View diagnostics')}</button>\n </div>\n )}\n <nav className=\"dwa-tabs\" aria-label={acp.label('NTR 设置分类', 'NTR settings categories')}>\n <button type=\"button\" aria-pressed={page === 'agents'} onClick={() => setPage('agents')}>{acp.label('智能体', 'Agents')}</button>\n {ui.approvalDeadlines !== undefined && <button type=\"button\" aria-pressed={page === 'approvals'} onClick={() => setPage('approvals')}>{acp.label('审批时限', 'Approval deadlines')}</button>}\n {ui.idleRelease !== undefined && <button type=\"button\" aria-pressed={page === 'processes'} onClick={() => setPage('processes')}>{acp.label('进程管理', 'Processes')}</button>}\n <button type=\"button\" aria-pressed={page === 'appearance'} onClick={() => setPage('appearance')}>{acp.label('对话外观', 'Appearance')}</button>\n <button type=\"button\" aria-pressed={page === 'logs'} onClick={() => setPage('logs')}>\n {acp.label('日志', 'Logs')}\n {ui.logUnread > 0 && <span className=\"dwa-log-dot\" aria-hidden=\"true\" />}\n </button>\n </nav>\n {page === 'diagnostics' && (\n <section className=\"dwa-section\">\n <h3 className=\"dwa-section-title\">{acp.label('连接诊断', 'Connection diagnostics')}</h3>\n <div className=\"dwa-preference-row\"><span>{acp.label('宿主事件流', 'Host event stream')}</span><span className=\"dwa-hint\">{streamText(ui.stream, acp)}</span></div>\n <div className=\"dwa-row-actions\">\n <button type=\"button\" className=\"dwa-btn\" onClick={() => { void acp.resync(); void acp.detectAgents() }} disabled={ui.detecting}>\n {ui.detecting ? acp.label('检测中...', 'Detecting...') : acp.label('重新检测与刷新', 'Re-detect & Refresh')}\n </button>\n {ui.detectSummary !== undefined && <span className=\"dwa-hint\" role=\"status\">{ui.detectSummary}</span>}\n </div>\n </section>\n )}\n {page === 'diagnostics' && ui.preset !== undefined && (\n <div className=\"dwa-card dwa-mode-banner\">\n <div className=\"dwa-mode-title\">\n {acp.label('NTR 模式', 'NTR mode')}\n <span className=\"dwa-badge\">{ui.preset.id}</span>\n </div>\n <span className={`dwa-badge ${ui.preset.installed ? 'dwa-badge-success' : 'dwa-badge-warn'}`}>\n {ui.preset.installed ? acp.label('已就绪', 'Ready') : acp.label('需检查', 'Needs attention')}\n </span>\n <div className=\"dwa-mode-copy\">\n {ui.preset.installed\n ? acp.label('已加入宿主模式列表,新建会话时选择「NTR 模式」即可使用。', 'Added to the harness roster. Pick “NTR mode” when creating a session.')\n : acp.label('宿主模式列表中没有可用的「NTR 模式」,请检查预设声明。', 'The harness roster has no usable “NTR mode”. Check the preset declaration.')}\n <code className=\"dwa-mode-source dwa-mono\">{ui.preset.source}</code>\n </div>\n {ui.preset.problem !== undefined && (\n <div className=\"dwa-hint\" style={{ gridColumn: '1 / -1', color: 'var(--ntr-warn)' }}>\n {acp.label(`宿主报告:${ui.preset.problem}`, `Harness reported: ${ui.preset.problem}`)}\n </div>\n )}\n </div>\n )}\n\n {/* Keep editors mounted so navigation preserves unsaved values. */}\n <section className=\"dwa-section\" hidden={page !== 'agents'} aria-label={acp.label('已接入的智能体', 'Connected agents')}>\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">\n {acp.label('已接入的智能体', 'Connected agents')}\n {ui.agentsLoaded && <span className=\"dwa-badge\">{configured.length}</span>}\n </h3>\n </div>\n {configured.length > 0 && <span className=\"dwa-hint\">{acp.label('默认项用于新的 NTR 会话', 'The default serves new NTR conversations')}</span>}\n </div>\n\n {!ui.agentsLoaded && <div className=\"dwa-empty\" role=\"status\">{acp.label('正在读取智能体…', 'Loading agents…')}</div>}\n {ui.agentsLoaded && configured.length === 0 && (\n <div className=\"dwa-empty\">\n <h3>{acp.label('接入第一个智能体', 'Connect your first agent')}</h3>\n <p className=\"dwa-section-copy\">{acp.label('选择已有预设,或填写自己的 ACP 启动命令。', 'Choose a preset or enter your own ACP launch command.')}</p>\n <button type=\"button\" className=\"dwa-btn dwa-btn-primary\" onClick={openAdd}>{acp.label('添加智能体', 'Add agent')}</button>\n </div>\n )}\n\n <div className=\"dwa-agent-list\">\n {configured.map(({ info, synthesized }) => {\n const activeAgent = ui.agents.find(a => a.id === info.id)\n return (\n <AgentRow\n key={info.id}\n detected={info}\n activeAgent={activeAgent}\n acp={acp}\n isDefault={ui.defaultAgent === info.id}\n synthesized={synthesized}\n active={page === 'agents'}\n />\n )\n })}\n </div>\n </section>\n\n <section className=\"dwa-section\" hidden={page !== 'add'} aria-label={acp.label('添加智能体', 'Add agent')}>\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet dwa-back\" onClick={() => setPage('agents')}>{acp.label('返回智能体列表', 'Back to agents')}</button>\n <div className=\"dwa-add-panel\">\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('添加智能体', 'Add agent')}</h3>\n <p className=\"dwa-section-copy\">{acp.label('从预设快速启用,或创建自定义连接。', 'Enable a preset or create a custom connection.')}</p>\n </div>\n </div>\n {addOpen && (\n <div className=\"dwa-add-content\">\n <div className=\"dwa-chip-list\">\n {available.map(info => (\n <button\n key={info.id}\n type=\"button\"\n className={`dwa-chip${selected.id === info.id ? ' dwa-chip-active' : ''}`}\n onClick={() => setAddTarget(info)}\n title={info.description}\n >\n {info.name}\n </button>\n ))}\n <button\n type=\"button\"\n className={`dwa-chip${selected.id === '' ? ' dwa-chip-active' : ''}`}\n onClick={() => setAddTarget(NEW_AGENT_SEED)}\n >\n {acp.label('自定义', 'Custom')}\n </button>\n </div>\n {available.length === 0 && selected.id === '' && (\n <div className=\"dwa-hint\" style={{ marginBottom: 8 }}>\n {acp.label('没有更多可启用的预设 Agent。', 'No more detected presets to enable.')}\n </div>\n )}\n <AgentRow\n key={selected.id}\n detected={selected}\n acp={acp}\n isDefault={false}\n synthesized={false}\n newCustom={selected.id === ''}\n active={page === 'add'}\n onCreated={() => {\n setAddTarget(undefined)\n setAddOpen(false)\n setPage('agents')\n }}\n />\n </div>\n )}\n </div>\n </section>\n\n <div hidden={page !== 'approvals'}><ApprovalDeadlinesCard acp={acp} ui={ui} /></div>\n <div hidden={page !== 'processes'}><IdleReleaseCard acp={acp} ui={ui} /></div>\n\n <section className=\"dwa-section\" hidden={page !== 'appearance'} aria-label={acp.label('对话外观', 'Appearance')}>\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('对话外观', 'Conversation appearance')}</h3>\n <p className=\"dwa-section-copy\">{acp.label('设置对话中 NTR 挂牌的进程存活提示。', 'Choose how the NTR tag indicates a running agent process.')}</p>\n </div>\n </div>\n <div className=\"dwa-preferences\">\n <div className=\"dwa-preference-row\">\n <div>\n <div className=\"dwa-preference-label\">{acp.label('挂牌光晕', 'Tag halo')}</div>\n <div className=\"dwa-preference-copy\">{acp.label('呼吸为脉动光,常亮为恒定光。', 'Breathe pulses gently; steady keeps a constant glow.')}</div>\n </div>\n <div className=\"dwa-seg\">\n {(['breath', 'steady'] as const).map(mode => (\n <button\n key={mode}\n type=\"button\"\n className={ui.tagHalo.mode === mode ? 'is-on' : ''}\n onClick={() => void acp.setTagHalo({ ...ui.tagHalo, mode })}\n >\n {mode === 'breath' ? acp.label('呼吸', 'Breathe') : acp.label('常亮', 'Steady')}\n </button>\n ))}\n </div>\n </div>\n {ui.tagHalo.mode === 'breath' && (\n <div className=\"dwa-preference-row\">\n <div>\n <div className=\"dwa-preference-label\">{acp.label('呼吸频率', 'Breathing speed')}</div>\n <div className=\"dwa-preference-copy\">{acp.label('控制光晕的脉动速度。', 'Controls how quickly the halo pulses.')}</div>\n </div>\n <div className=\"dwa-seg\">\n {(['slow', 'normal', 'fast'] as const).map(speed => (\n <button\n key={speed}\n type=\"button\"\n className={ui.tagHalo.speed === speed ? 'is-on' : ''}\n onClick={() => void acp.setTagHalo({ ...ui.tagHalo, speed })}\n >\n {speed === 'slow' ? acp.label('慢速', 'Slow') : speed === 'normal' ? acp.label('正常', 'Normal') : acp.label('快速', 'Fast')}\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </section>\n {/* Conditional on purpose: this list can hold 200 rows, and keeping it\n mounted through a `hidden` page would re-render every line on each\n `session/update`. It carries no local state, so remounting is free. */}\n {page === 'logs' && (\n <section className=\"dwa-section\" aria-label={acp.label('运行日志', 'Runtime log')}>\n <div className=\"dwa-section-heading\">\n <div>\n <h3 className=\"dwa-section-title\">{acp.label('运行日志', 'Runtime log')}</h3>\n <p className=\"dwa-section-copy\">\n {acp.label(\n `agent 运行消息,最多 ${String(LOG_MAX_ENTRIES)} 条。`,\n `Agent runtime messages, up to ${String(LOG_MAX_ENTRIES)}.`,\n )}\n </p>\n </div>\n {ui.logs.length > 0 && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" onClick={() => acp.clearLogs()}>\n {acp.label('清空', 'Clear')}\n </button>\n )}\n </div>\n {ui.logs.length === 0\n ? <div className=\"dwa-empty\" role=\"status\">{acp.label('暂无运行日志。', 'No runtime messages yet.')}</div>\n : (\n <ul className=\"dwa-log-list\">\n {[...ui.logs].reverse().map(entry => (\n <li key={entry.id} className={entry.level === 'error' ? 'dwa-log-row is-error' : 'dwa-log-row'}>\n <span className=\"dwa-log-time dwa-mono\">{logClock(entry.at)}</span>\n <span className={`dwa-badge dwa-log-level ${entry.level === 'error' ? 'is-error' : 'is-info'}`}>\n {entry.level === 'error' ? acp.label('错误', 'Error') : acp.label('信息', 'Info')}\n </span>\n {entry.agentId !== undefined && <span className=\"dwa-badge\">{entry.agentId}</span>}\n <span className=\"dwa-log-text\">{entry.text}</span>\n </li>\n ))}\n </ul>\n )}\n </section>\n )}\n <footer className=\"dwa-health\">\n <div className=\"dwa-health-status\">\n <span className=\"dwa-dot\" style={{ background: ui.stream === 'live' ? 'var(--ntr-success)' : ui.stream === 'down' ? 'var(--ntr-danger)' : 'var(--ntr-muted)' }} />\n <span>{streamText(ui.stream, acp)}</span>\n {ui.preset !== undefined && <span>{ui.preset.installed && !presetProblem ? acp.label('NTR 模式已就绪', 'NTR mode ready') : acp.label('NTR 模式需检查', 'NTR mode needs attention')}</span>}\n </div>\n {page !== 'diagnostics' && <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" onClick={() => setPage('diagnostics')}>{acp.label('查看诊断', 'View diagnostics')}</button>}\n </footer>\n </div>\n )\n}\n\n/** One row per agent: compact when collapsed, the full form when expanded. */\nfunction AgentRow({\n detected,\n activeAgent,\n acp,\n isDefault,\n synthesized,\n active,\n newCustom = false,\n onCreated,\n}: {\n detected: DetectedAgentInfo\n activeAgent?: AgentView\n acp: AcpStore\n isDefault: boolean\n synthesized: boolean\n active: boolean\n /** From-scratch creation: the id is editable and no preset defaults exist. */\n newCustom?: boolean\n /** Fired after a creation-card save succeeds (the list re-derives its selection). */\n onCreated?: () => void\n}) {\n const ui = useUi(acp)\n const configured = activeAgent !== undefined || detected.currentConfig !== undefined\n // A configured agent starts collapsed; a creation card opens straight away\n // (nothing to summarise yet).\n const [expanded, setExpanded] = useState(!configured)\n const [detailPage, setDetailPage] = useState<'models' | 'connection' | 'advanced'>(configured ? 'models' : 'connection')\n const [form, setForm] = useState<AgentForm>(() => initialForm(detected, activeAgent))\n const [saving, setSaving] = useState(false)\n const [savedSuccess, setSavedSuccess] = useState(false)\n const [testing, setTesting] = useState(false)\n const [testResult, setTestResult] = useState<{ ok: boolean; text: string } | undefined>(undefined)\n const patch = (next: Partial<AgentForm>): void => setForm(prev => ({ ...prev, ...next }))\n\n const serverExposureKey = exposureKey(detected.currentConfig?.exposedModels)\n useEffect(() => {\n const server = detected.currentConfig?.exposedModels\n setForm(prev => ({ ...prev, exposed: server === undefined ? undefined : [...server] }))\n // Re-sync the editor only when the persisted filter actually changes, so\n // background resyncs never clobber in-progress checkbox edits.\n }, [serverExposureKey])\n\n const serverAutoDiscover = detected.currentConfig?.autoDiscoverModels !== false\n useEffect(() => {\n // The switch saves itself through the lightweight RPC, so the host is the\n // truth; this keeps the form (and therefore a later full save) in step.\n setForm(prev => ({ ...prev, autoDiscoverModels: serverAutoDiscover }))\n }, [serverAutoDiscover])\n\n const trimmedId = form.agentId.trim()\n const idValid = !newCustom || AGENT_ID_PATTERN.test(trimmedId)\n const idTaken = newCustom && trimmedId !== '' && ui.agents.some(a => a.id === trimmedId)\n const cwdInvalid = form.cwd.trim() !== '' && !isAbsolutePathLike(form.cwd.trim())\n const rootValues = form.roots.map(root => root.trim()).filter(root => root !== '')\n const rootsInvalid = form.rootsOpen && (rootValues.length === 0 || rootValues.some(root => !isAbsolutePathLike(root)))\n const idleInvalid = !idleValid(form.idleStr)\n const mcpInvalid = !mcpTextValid(form.mcpText)\n const formIncomplete = splitCommandLine(form.commandLine).command === '' || !idValid || idTaken || cwdInvalid || rootsInvalid || idleInvalid || mcpInvalid\n\n const payload = buildPayload(form, detected, newCustom)\n const current = persistedDef(detected, activeAgent)\n const clearingIdleOverride = payload.idleTimeoutMs === undefined && detected.currentConfig?.idleTimeoutMs !== undefined\n const dirty = current !== undefined && (!payloadEquals(payload, current) || clearingIdleOverride)\n // A save stops the child and kills every live session (see BridgeManager.saveAgent).\n const showSaveWarning = dirty && activeAgent !== undefined && activeAgent.state !== 'stopped'\n\n const handleUseDefault = (): void => patch({ commandLine: formatCommandLine(detected.defaultCommand, detected.defaultArgs) })\n\n const handleSave = async (): Promise<void> => {\n setSaving(true)\n setSavedSuccess(false)\n try {\n await acp.saveAgent(payload)\n // A save from the creation card resets the list's selection: the agent\n // now lives in the configured list, and keeping the same card mounted\n // would render it twice. Configured rows pass no `onCreated`, so this is\n // a no-op for them.\n onCreated?.()\n if (newCustom) return\n setSavedSuccess(true)\n setTimeout(() => setSavedSuccess(false), 2000)\n } catch {\n // Error handled by store\n } finally {\n setSaving(false)\n }\n }\n\n const handleTest = async (): Promise<void> => {\n setTesting(true)\n setTestResult(undefined)\n try {\n const result = await acp.testAgent(payload)\n setTestResult(result.ok\n ? {\n ok: true,\n text: acp.label('连接成功', 'Connected') + (result.agentInfo?.name !== undefined ? ` · ${result.agentInfo.name}` : '')\n + (result.agentInfo?.version !== undefined ? ` v${result.agentInfo.version}` : '')\n + ` · ${String(result.durationMs)}ms`,\n }\n : { ok: false, text: result.error ?? acp.label('未知错误', 'Unknown error') })\n } catch (error) {\n setTestResult({ ok: false, text: error instanceof Error ? error.message : String(error) })\n } finally {\n setTesting(false)\n }\n }\n\n const state = activeAgent !== undefined ? stateVisual(activeAgent.state, acp) : undefined\n const displayName = newCustom\n ? acp.label('新建自定义 Agent', 'New Custom Agent')\n : (activeAgent?.title ?? detected.currentConfig?.title ?? detected.name)\n const stderr = activeAgent?.state === 'error' ? (activeAgent.stderrTail ?? []) : []\n const errorSummary = stderr.length > 0 ? stderr[stderr.length - 1] : undefined\n const selectedModelCount = form.exposed?.length ?? 0\n const isPreset = PRESET_IDS.has(detected.id)\n // An error row opens itself so the stderr tail is never behind a click.\n const open = expanded || (activeAgent?.state === 'error' && configured)\n\n return (\n <div className={`dwa-agent-card${open ? ' is-open' : ''}`}>\n <div className=\"dwa-agent-row\">\n <span className=\"dwa-agent-avatar\" aria-hidden>{displayName.slice(0, 2).toUpperCase()}</span>\n <div className=\"dwa-agent-summary\">\n <div className=\"dwa-agent-titleline\">\n <span className=\"dwa-agent-name\" title={detected.executablePath ?? detected.description}>{displayName}</span>\n {isDefault && <span className=\"dwa-badge dwa-badge-info\">{acp.label('默认', 'Default')}</span>}\n {!isPreset && !newCustom && <span className=\"dwa-badge\">{acp.label('自定义', 'Custom')}</span>}\n </div>\n <div className=\"dwa-agent-meta\">\n {state !== undefined && (\n <span title={state.title ?? activeAgent?.state} style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>\n <span className=\"dwa-dot\" style={{ background: state.color }} />\n {state.text}\n </span>\n )}\n {configured && (\n <>\n {state !== undefined && <span className=\"dwa-agent-meta-separator\">·</span>}\n <span>{acp.label(`${String(selectedModelCount)} 个已选模型`, `${String(selectedModelCount)} selected models`)}</span>\n </>\n )}\n {!synthesized && !newCustom && !detected.detected && (\n <span style={{ color: 'var(--ntr-warn)' }} title={detected.description}>\n · {acp.label('未在 PATH 中检测到', 'Not in PATH')}\n </span>\n )}\n </div>\n {errorSummary !== undefined && (\n <div className=\"dwa-error dwa-mono\" style={{ marginTop: 6, wordBreak: 'break-all' }}>{errorSummary}</div>\n )}\n </div>\n <div className=\"dwa-row-actions\">\n {configured && (\n <button type=\"button\" className=\"dwa-btn dwa-btn-quiet\" aria-expanded={open} aria-label={acp.label(`${open ? '收起' : '管理'} ${displayName}`, `${open ? 'Collapse' : 'Manage'} ${displayName}`)} onClick={() => setExpanded(value => !value)}>\n {open ? acp.label('收起', 'Collapse') : acp.label('管理', 'Manage')}\n </button>\n )}\n {activeAgent?.state === 'error' && (\n <button className=\"dwa-btn\" disabled={testing} onClick={() => void handleTest()}>\n {testing ? acp.label('测试中...', 'Testing...') : acp.label('测试', 'Test')}\n </button>\n )}\n {configured && (\n <RowMenu\n acp={acp}\n canStop={activeAgent !== undefined && activeAgent.state !== 'stopped'}\n onStop={() => void acp.stopAgent(detected.id)}\n // The store already surfaced the failure as a notice; swallowing it\n // here only prevents an unhandled rejection.\n onRemove={async () => { await acp.deleteAgent(detected.id).catch(() => { /* notice shown by the store */ }) }}\n />\n )}\n </div>\n </div>\n\n {/* display-toggled (not conditionally mounted) so an in-progress edit\n survives collapsing the row. */}\n <div className=\"dwa-agent-body\" style={open ? undefined : { display: 'none' }}>\n {stderr.length > 0 && <pre className=\"dwa-stderr\">{stderr.join('\\n')}</pre>}\n <nav className=\"dwa-tabs dwa-detail-tabs\" aria-label={acp.label(`${displayName} 配置`, `${displayName} settings`)}>\n {configured && <button type=\"button\" aria-pressed={detailPage === 'models'} onClick={() => setDetailPage('models')}>{acp.label('模型', 'Models')}</button>}\n <button type=\"button\" aria-pressed={detailPage === 'connection'} onClick={() => setDetailPage('connection')}>{acp.label('连接', 'Connection')}</button>\n <button type=\"button\" aria-pressed={detailPage === 'advanced'} onClick={() => setDetailPage('advanced')}>{acp.label('高级', 'Advanced')}</button>\n </nav>\n {configured && (\n <section className=\"dwa-subsection\" hidden={detailPage !== 'models'}>\n <ModelCatalogBlock\n agentId={detected.id}\n detected={detected}\n acp={acp}\n active={active && open && detailPage === 'models'}\n exposed={form.exposed}\n onExposedChange={exposed => patch({ exposed })}\n autoDiscover={form.autoDiscoverModels}\n onAutoDiscoverChange={autoDiscoverModels => patch({ autoDiscoverModels })}\n isDefault={isDefault}\n />\n </section>\n )}\n <section className=\"dwa-subsection\" hidden={detailPage === 'models'}>\n <AgentDetails\n acp={acp}\n advancedOpen={detailPage === 'advanced'}\n dirty={dirty}\n detected={detected}\n form={form}\n patch={patch}\n isConfigured={configured}\n newCustom={newCustom}\n formIncomplete={formIncomplete}\n idValid={idValid}\n idTaken={idTaken}\n cwdInvalid={cwdInvalid}\n rootsInvalid={rootsInvalid}\n idleInvalid={idleInvalid}\n mcpInvalid={mcpInvalid}\n saving={saving}\n savedSuccess={savedSuccess}\n testing={testing}\n testResult={testResult}\n showSaveWarning={showSaveWarning}\n onUseDefault={handleUseDefault}\n onSave={() => void handleSave()}\n onTest={() => void handleTest()}\n />\n </section>\n </div>\n </div>\n )\n}\n\n/** Everything the connection form edits, in one object (kept in the row). */\ninterface AgentForm {\n agentId: string\n title: string\n /**\n * ONE command line — program first, arguments after, quoting to keep a path\n * with spaces in one piece (`splitCommandLine`). The old split into\n * `command` + `argsStr` invited the single most common misconfiguration\n * there is: pasting `codebuddy --acp` into the program box, which the host\n * then looked for as a file literally named `codebuddy --acp`.\n */\n commandLine: string\n npmPackage: string\n cwd: string\n /** Advanced: edit every allowed root, not just the single workspace dir. */\n rootsOpen: boolean\n roots: string[]\n envRows: Array<{ key: string; value: string }>\n exposed?: string[]\n /** Auto-append models the agent newly publishes to {@link exposed}. */\n autoDiscoverModels: boolean\n /** Raw text of the idle-release window in ms ('' = host default, '0' = never). */\n idleStr: string\n /**\n * Raw JSON text of the agent's MCP server list (the plugin's normalized\n * shape — every entry carries a `transport`). '' = no extra MCP servers.\n *\n * Kept as TEXT so a half-typed value survives editing instead of being\n * silently reshaped; it is parsed on the way out.\n */\n mcpText: string\n}\n\n/**\n * Hand copy of `DEFAULT_TUNING.probeIdleTimeoutMs` (src/bridge.ts). A process\n * that has never hosted a session — the model probe / post-save probe — lives\n * by THIS window whatever the agent's own field says, so the placeholder has to\n * name both windows. Keep the two in sync.\n */\nconst DEFAULT_PROBE_RELEASE_MS = 300_000\n\nexport function initialForm(detected: DetectedAgentInfo, activeAgent: AgentView | undefined): AgentForm {\n const config = detected.currentConfig\n const roots = [...(activeAgent?.allowedCwdRoots ?? config?.allowedCwdRoots ?? [])]\n return {\n agentId: detected.id,\n title: activeAgent?.title ?? config?.title ?? detected.name,\n commandLine: formatCommandLine(\n activeAgent?.command ?? config?.command ?? detected.defaultCommand,\n activeAgent?.args ?? config?.args ?? detected.defaultArgs,\n ),\n npmPackage: config?.npmPackage ?? '',\n cwd: activeAgent?.cwd ?? config?.cwd ?? '',\n // An agent already carrying more than one root opens in advanced mode:\n // single-root mode narrows the allow-list to `cwd` on save, which must\n // never happen as a side effect of merely opening the form.\n rootsOpen: roots.length > 1,\n roots,\n envRows: Object.entries(config?.env ?? {}).map(([key, value]) => ({ key, value })),\n exposed: config?.exposedModels === undefined ? undefined : [...config.exposedModels],\n autoDiscoverModels: config?.autoDiscoverModels !== false,\n // The agent's OWN override only. The host's resolved value is NOT\n // written back here: it would freeze the global window into this\n // agent's definition, and the global slider would then never reach it.\n // An empty field means \"follow the global window\".\n idleStr: idleText(config?.idleTimeoutMs),\n // The MCP list comes back from the host in its NORMALIZED shape (defaults\n // filled in), so re-serializing it reproduces exactly what is stored —\n // that is what keeps the card from being permanently dirty.\n mcpText: config?.mcpServers === undefined ? '' : JSON.stringify(config.mcpServers, null, 2),\n }\n}\n\nfunction idleText(value: number | undefined): string {\n return value === undefined ? '' : String(value)\n}\n\n/** `MM-DD HH:mm`, local time — compact enough for the catalog's status line. */\nfunction formatStamp(ms: number): string {\n const date = new Date(ms)\n const pad = (value: number): string => String(value).padStart(2, '0')\n return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/**\n * Assemble the save/test payload.\n *\n * Workspace rules (P0-1): a non-empty single-root `cwd` also pins\n * `allowedCwdRoots` to exactly that path — an explicit narrowing, so saving\n * from the panel can never silently widen the sandbox. An empty `cwd` sends\n * neither field and the host falls back to `~/.dsh/dsh-ntr/workspaces`. Advanced\n * mode sends the edited roots as-is (deduplicated).\n */\n/** `''` = host default, `0` = never release, otherwise a sane millisecond window. */\nfunction idleValid(raw: string): boolean {\n const text = raw.trim()\n if (text === '') return true\n if (!/^\\d+$/.test(text)) return false\n const value = Number(text)\n return value === 0 || (value >= 1_000 && value <= 86_400_000)\n}\n\nexport function buildPayload(form: AgentForm, detected: DetectedAgentInfo, newCustom: boolean): AgentSavePayload {\n const env: Record<string, string> = {}\n for (const row of form.envRows) {\n const key = row.key.trim()\n if (key === '') continue\n env[key] = row.value\n }\n const cwd = form.cwd.trim()\n const idleRaw = form.idleStr.trim()\n // The field is a command line; the host validates it the same way, so what\n // the preview under the input shows is what will actually be spawned.\n const parsed = splitCommandLine(form.commandLine)\n // An unparseable MCP text yields `undefined` here and `mcpInvalid` in the\n // form, which disables saving — so this never throws inside a render.\n const mcpServers = parseMcpServers(form.mcpText)\n const payload: AgentSavePayload = {\n id: newCustom ? form.agentId.trim() : detected.id,\n ...(form.title.trim() === '' ? {} : { title: form.title.trim() }),\n command: parsed.command,\n args: parsed.args,\n ...(form.npmPackage.trim() === '' ? {} : { npmPackage: form.npmPackage.trim() }),\n env,\n ...(form.exposed === undefined ? {} : { exposedModels: form.exposed }),\n // Auto-discovery is ON by default: only an unchecked switch records the\n // opt-out, otherwise a full save would silently re-enable it.\n ...(form.autoDiscoverModels ? {} : { autoDiscoverModels: false }),\n // `''` sends nothing: the host then applies its own default.\n ...(idleRaw === '' || !idleValid(idleRaw) ? {} : { idleTimeoutMs: Number(idleRaw) }),\n // The MCP list is OPTIONAL: absent = the agent connects only to the\n // servers it configures for itself.\n ...(mcpServers === undefined ? {} : { mcpServers }),\n }\n if (form.rootsOpen) {\n if (cwd !== '') payload.cwd = cwd\n const roots = [...new Set(form.roots.map(root => root.trim()).filter(root => root !== ''))]\n if (roots.length > 0) payload.allowedCwdRoots = roots\n return payload\n }\n if (cwd !== '') {\n payload.cwd = cwd\n payload.allowedCwdRoots = [cwd]\n }\n return payload\n}\n\n/** The expanded body: connection form, workspace allow-list, env, actions. */\nfunction AgentDetails({\n acp,\n advancedOpen,\n dirty,\n detected,\n form,\n patch,\n isConfigured,\n newCustom,\n formIncomplete,\n idValid,\n idTaken,\n cwdInvalid,\n rootsInvalid,\n idleInvalid,\n mcpInvalid,\n saving,\n savedSuccess,\n testing,\n testResult,\n showSaveWarning,\n onUseDefault,\n onSave,\n onTest,\n}: {\n acp: AcpStore\n advancedOpen: boolean\n dirty: boolean\n detected: DetectedAgentInfo\n form: AgentForm\n patch: (next: Partial<AgentForm>) => void\n isConfigured: boolean\n newCustom: boolean\n formIncomplete: boolean\n idValid: boolean\n idTaken: boolean\n cwdInvalid: boolean\n rootsInvalid: boolean\n idleInvalid: boolean\n mcpInvalid: boolean\n saving: boolean\n savedSuccess: boolean\n testing: boolean\n testResult?: { ok: boolean; text: string }\n showSaveWarning: boolean\n onUseDefault: () => void\n onSave: () => void\n onTest: () => void\n}) {\n // The same parse the host will run (`validateAgentDef` → `splitCommandLine`).\n // Showing it is what turns \"the first box swallowed my arguments\" into a\n // visible, fixable state instead of a spawn error.\n const parsedCommand = splitCommandLine(form.commandLine)\n const commandPreview = parsedCommand.command === ''\n ? acp.label('填整条命令,例如:codebuddy --acp', 'Type the whole command, e.g. codebuddy --acp')\n : `${acp.label('程序', 'Program')}: ${parsedCommand.command}`\n + (parsedCommand.args.length === 0 ? '' : ` ${acp.label('参数', 'Arguments')}: ${parsedCommand.args.join(' ')}`)\n return (\n <div>\n <div hidden={advancedOpen}>\n {newCustom && (\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('Agent ID(唯一标识,创建后不可改)', 'Agent ID (unique, immutable after creation)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n aria-label={acp.label('Agent ID', 'Agent ID')}\n value={form.agentId}\n onChange={e => patch({ agentId: e.target.value })}\n placeholder=\"e.g. my-agent\"\n />\n <span className={idValid && !idTaken ? 'dwa-hint' : 'dwa-error'}>\n {idTaken\n ? acp.label('该 ID 已被现有 Agent 占用。', 'This id is already taken by a configured agent.')\n : idValid\n ? acp.label('小写字母或数字开头,可用 a-z 0-9 . _ -,最长 64 字符。', 'Starts with a lowercase letter or digit; a-z 0-9 . _ - allowed; max 64 chars.')\n : acp.label('ID 格式不合法:小写字母或数字开头,可用 a-z 0-9 . _ -。', 'Invalid id: start with a lowercase letter or digit; only a-z 0-9 . _ - allowed.')}\n </span>\n </div>\n </div>\n )}\n\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('显示名', 'Display Name')}</label>\n <input\n className=\"dwa-input\"\n aria-label={acp.label('显示名', 'Display Name')}\n value={form.title}\n onChange={e => patch({ title: e.target.value })}\n placeholder={detected.name === '' ? acp.label('我的 Agent', 'My Agent') : detected.name}\n />\n </div>\n </div>\n\n {/* ONE field on purpose: the program and its arguments are one command\n line, exactly as a user would type them. Splitting them into two boxes\n made \"paste the whole command into the first box\" the default mistake\n (the host then looked for a file named `codebuddy --acp`); here the\n same paste is correct, and the parse preview below shows what will\n actually run (same parser the host's `validateAgentDef` uses). */}\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n aria-label={acp.label('启动命令(程序 + 参数)', 'Launch Command (program + arguments)')}\n value={form.commandLine}\n onChange={e => patch({ commandLine: e.target.value })}\n placeholder={detected.defaultCommand === ''\n ? 'e.g. codebuddy --acp'\n : formatCommandLine(detected.defaultCommand, detected.defaultArgs)}\n />\n <span className=\"dwa-hint\">{commandPreview}</span>\n <span className=\"dwa-hint\">\n {acp.label(\n '含空格的程序路径请加引号。',\n 'Quote the program path if it contains spaces.',\n )}\n </span>\n </div>\n </div>\n\n <WorkspaceBlock acp={acp} form={form} patch={patch} cwdInvalid={cwdInvalid} rootsInvalid={rootsInvalid} />\n </div>\n\n <div hidden={!advancedOpen}>\n {!newCustom && (\n <div className=\"dwa-default-hint\">\n <div>\n <span style={{ fontWeight: 600, marginRight: 6 }}>\n {acp.label('官方测试默认命令:', 'Tested Default Command:')}\n </span>\n <code className=\"dwa-input-mono\" style={{ background: 'var(--dsw-alias-markdown-inline-code, #0000000a)', padding: '2px 6px', borderRadius: 4 }}>\n {detected.defaultCommand} {detected.defaultArgs.join(' ')}\n </code>\n </div>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n onClick={onUseDefault}\n title={acp.label('把命令与参数重置为官方验证值', 'Reset command and arguments to the tested default')}\n >\n {acp.label('使用预设值', 'Use Preset')}\n </button>\n </div>\n )}\n\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('npm 包名(PATH 中找不到时自动安装)', 'npm Package (auto-installed when missing from PATH)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n value={form.npmPackage}\n onChange={e => patch({ npmPackage: e.target.value })}\n placeholder={acp.label('原生支持 ACP 的 agent 无需填写', 'Not needed for agents with native ACP support')}\n />\n </div>\n </div>\n\n {/* Idle release: an OVERRIDE of the global window (「进程管理」). Empty\n means \"follow the global window\", so the global slider reaches every\n agent that does not pin its own. TWO windows exist\n (AgentBridge.currentIdleTimeoutMs): this field is the chat window of\n a process that HAS hosted a session; a never-used process (a probe)\n always gets the short probe window and ignores this field — which is\n why the copy below names both. */}\n <div className=\"dwa-form-row\">\n <div className=\"dwa-form-col\">\n <label className=\"dwa-form-label\">{acp.label('空闲释放覆盖(毫秒)', 'Idle Release Override (ms)')}</label>\n <input\n className=\"dwa-input dwa-input-mono\"\n aria-label={acp.label('空闲释放覆盖(毫秒)', 'Idle Release Override (ms)')}\n value={form.idleStr}\n onChange={e => patch({ idleStr: e.target.value })}\n placeholder={acp.label(\n `留空 = 跟随全局设置(探测进程固定 ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} 分钟)`,\n `Blank = follow the global setting (probes always release after ${String(DEFAULT_PROBE_RELEASE_MS / 60_000)} min)`,\n )}\n />\n <span className={idleInvalid ? 'dwa-error' : 'dwa-hint'}>\n {idleInvalid\n ? acp.label(\n '必须是 0(永不释放)或 [1000, 86400000] 之间的整数毫秒。',\n 'Must be 0 (never release) or an integer in [1000, 86400000] ms.',\n )\n : (\n <>\n {acp.label(\n '仅覆盖此智能体;留空即跟随「进程管理」的全局设置。',\n 'Overrides this agent only; leave blank to follow the global setting in “Processes”.',\n )}\n </>\n )}\n </span>\n </div>\n </div>\n\n <div className=\"dwa-form-row\" style={{ flexDirection: 'column', gap: 6 }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <span className=\"dwa-form-label\">{acp.label('环境变量', 'Environment Variables')}</span>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n onClick={() => patch({ envRows: [...form.envRows, { key: '', value: '' }] })}\n >\n {acp.label('添加', 'Add')}\n </button>\n </div>\n {form.envRows.map((row, index) => (\n <div key={index} style={{ display: 'flex', gap: 6, alignItems: 'center' }}>\n <input\n className=\"dwa-input dwa-input-mono\"\n style={{ flex: 2 }}\n value={row.key}\n onChange={e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, key: e.target.value } : r)) })}\n placeholder=\"KEY\"\n />\n <input\n className=\"dwa-input dwa-input-mono\"\n style={{ flex: 3 }}\n value={row.value}\n onChange={e => patch({ envRows: form.envRows.map((r, i) => (i === index ? { ...r, value: e.target.value } : r)) })}\n placeholder=\"VALUE\"\n />\n <button\n type=\"button\"\n className=\"dwa-btn\"\n title={acp.label('删除该变量', 'Remove this variable')}\n onClick={() => patch({ envRows: form.envRows.filter((_, i) => i !== index) })}\n >\n ✕\n </button>\n </div>\n ))}\n {form.envRows.length === 0 && (\n <span className=\"dwa-hint\">\n {acp.label('无自定义变量;子进程继承宿主环境。', 'No custom variables; the child inherits the host environment.')}\n </span>\n )}\n </div>\n\n <div className=\"dwa-form-row\" style={{ flexDirection: 'column', gap: 6 }}>\n <span className=\"dwa-form-label\">{acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)')}</span>\n <textarea\n className=\"dwa-input dwa-input-mono\"\n style={{ minHeight: 96, resize: 'vertical' }}\n aria-label={acp.label('MCP 服务器(JSON)', 'MCP Servers (JSON)')}\n value={form.mcpText}\n onChange={e => patch({ mcpText: e.target.value })}\n spellCheck={false}\n placeholder={'[\\n { \"name\": \"example\", \"transport\": \"stdio\", \"command\": \"/abs/path/to/server\", \"args\": [\"--mcp\"] }\\n]'}\n />\n {mcpInvalid && (\n <span className=\"dwa-error\">\n {acp.label('必须是一个 JSON 数组;每个条目写明 name 与 transport。', 'Must be a JSON array; every entry needs a name and a transport.')}\n </span>\n )}\n <span className=\"dwa-hint\">\n {acp.label(\n '额外追加给智能体的 MCP 服务器,不影响其已有配置。',\n 'Additional MCP servers for the agent; its own configuration is unchanged.',\n )}\n </span>\n </div>\n </div>\n\n {!advancedOpen && (mcpInvalid || idleInvalid) && <p className=\"dwa-error\" role=\"alert\">{acp.label('请检查「高级」中的无效配置。', 'Check the invalid settings in Advanced.')}</p>}\n {advancedOpen && (!idValid || idTaken || cwdInvalid || rootsInvalid || parsedCommand.command === '') && <p className=\"dwa-error\" role=\"alert\">{acp.label('请检查「连接」中的未完成或无效配置。', 'Check the incomplete or invalid settings in Connection.')}</p>}\n <div className=\"dwa-form-actions\">\n <span className=\"dwa-hint\">{isConfigured ? dirty ? acp.label('有未保存的更改', 'Unsaved changes') : acp.label('配置已保存', 'Settings saved') : ''}</span>\n <button\n className=\"dwa-btn\"\n onClick={onTest}\n disabled={testing || saving || formIncomplete}\n title={acp.label('用当前表单值拉起一次性进程并完成 ACP 握手,不保存配置', 'Spawns a throwaway process from the form values and completes the ACP handshake; nothing is saved')}\n >\n {testing ? acp.label('测试中...', 'Testing...') : acp.label('测试连接', 'Test Connection')}\n </button>\n\n <button\n className=\"dwa-btn dwa-btn-primary\"\n onClick={onSave}\n disabled={saving || testing || formIncomplete || (isConfigured && !dirty)}\n >\n {saving\n ? acp.label('保存中...', 'Saving...')\n : savedSuccess\n ? acp.label('✓ 已保存', '✓ Saved')\n : isConfigured\n ? acp.label('保存更改', 'Save changes')\n : acp.label('保存并启用', 'Save & Enable')}\n </button>\n\n {showSaveWarning && (\n <span className=\"dwa-hint\" style={{ color: 'var(--ntr-warn)' }}>\n ⚠ {acp.label('保存会停止该 Agent 进程并中断进行中的会话', 'Saving stops this agent process and interrupts live sessions')}\n </span>\n )}\n </div>\n\n {testResult !== undefined && (\n <div\n className=\"dwa-mono\"\n style={{\n marginTop: 8,\n fontSize: 12,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-all',\n color: testResult.ok ? 'var(--ntr-success)' : 'var(--ntr-danger)',\n }}\n >\n {testResult.ok ? '✓ ' : '✕ '}{testResult.text}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Workspace editor. Single-root mode is one directory; the advanced switch\n * reveals the full allow-list, because the host enforces every root the agent\n * may touch (and refuses anything else with `cwd_denied`).\n */\nfunction WorkspaceBlock({\n acp,\n form,\n patch,\n cwdInvalid,\n rootsInvalid,\n}: {\n acp: AcpStore\n form: AgentForm\n patch: (next: Partial<AgentForm>) => void\n cwdInvalid: boolean\n rootsInvalid: boolean\n}) {\n return (\n <div className=\"dwa-form-row\" style={{ flexDirection: 'column', gap: 6 }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>\n <span className=\"dwa-form-label\">{acp.label('工作空间根目录', 'Workspace Roots')}</span>\n <label className=\"dwa-hint\" style={{ display: 'inline-flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={form.rootsOpen}\n onChange={e => patch({ rootsOpen: e.target.checked })}\n style={{ margin: 0 }}\n />\n {acp.label('白名单根目录(高级)', 'Root allow-list (advanced)')}\n </label>\n </div>\n\n <div className=\"dwa-form-col\">\n <input\n className=\"dwa-input\"\n value={form.cwd}\n onChange={e => patch({ cwd: e.target.value })}\n placeholder={acp.label('默认 ~/.dsh/dsh-ntr/workspaces', 'Default: ~/.dsh/dsh-ntr/workspaces')}\n />\n {cwdInvalid && (\n <span className=\"dwa-error\">{acp.label('必须是绝对路径(如 /work 或 C:\\\\work)', 'Must be an absolute path (e.g. /work or C:\\\\work)')}</span>\n )}\n <span className=\"dwa-hint\">\n {acp.label(\n '聊天使用会话工作区;此处用于直连会话或宿主未提供工作区时。',\n 'Chats use their session workspace; this applies to direct sessions or when the host provides no workspace.',\n )}\n </span>\n {/* The effective allow-list stays visible in single-root mode too:\n saving there deliberately narrows it to this one directory. */}\n {!form.rootsOpen && (\n <span className=\"dwa-hint\">\n {acp.label('当前白名单:', 'Current allow-list: ')}\n {form.roots.length === 0 ? acp.label('(未设置)', '(unset)') : form.roots.join('、')}\n {form.cwd.trim() === '' ? '' : acp.label(';保存时将收窄为工作空间目录', '; saving narrows it to the workspace directory')}\n </span>\n )}\n </div>\n\n {form.rootsOpen && (\n <div className=\"dwa-form-col\" style={{ gap: 4 }}>\n {form.roots.map((root, index) => (\n <div key={index} style={{ display: 'flex', gap: 6, alignItems: 'center' }}>\n <input\n className=\"dwa-input dwa-input-mono\"\n value={root}\n onChange={e => patch({ roots: form.roots.map((r, i) => (i === index ? e.target.value : r)) })}\n placeholder=\"/absolute/path\"\n />\n <button\n type=\"button\"\n className=\"dwa-btn\"\n title={acp.label('删除该根目录', 'Remove this root')}\n onClick={() => patch({ roots: form.roots.filter((_, i) => i !== index) })}\n >\n ✕\n </button>\n </div>\n ))}\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n onClick={() => patch({ roots: [...form.roots, ''] })}\n >\n {acp.label('添加根目录', 'Add root')}\n </button>\n <span className=\"dwa-hint\">\n {acp.label('留空的工作空间则使用宿主默认目录。', 'An empty workspace falls back to the host default directory.')}\n </span>\n </div>\n {rootsInvalid && (\n <span className=\"dwa-error\">\n {acp.label('至少需要一个绝对路径根目录', 'At least one absolute root is required')}\n </span>\n )}\n </div>\n )}\n\n <span className=\"dwa-hint\">\n {acp.label(\n '白名单之外的路径会被宿主拒绝(cwd_denied);当前 DSH 会话自己的工作区始终被信任。',\n 'Paths outside the allow-list are refused by the host (cwd_denied); the current DSH session\\u2019s own workspace is always trusted.',\n )}\n </span>\n </div>\n )\n}\n\n/** Row overflow menu: the dangerous actions, plus the inline remove confirm. */\nfunction RowMenu({\n acp,\n canStop,\n onStop,\n onRemove,\n}: {\n acp: AcpStore\n canStop: boolean\n onStop: () => void\n onRemove: () => Promise<void>\n}) {\n const [open, setOpen] = useState(false)\n const [confirming, setConfirming] = useState(false)\n const wrap = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (event: MouseEvent): void => {\n if (wrap.current !== null && event.target instanceof Node && wrap.current.contains(event.target)) return\n setOpen(false)\n setConfirming(false)\n }\n const onKey = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n setOpen(false)\n setConfirming(false)\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n const close = (): void => {\n setOpen(false)\n setConfirming(false)\n }\n\n return (\n <div className=\"dwa-menu-wrap\" ref={wrap}>\n <button\n type=\"button\"\n className=\"dwa-btn\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={acp.label('更多操作', 'More actions')}\n onClick={() => {\n setOpen(value => !value)\n setConfirming(false)\n }}\n >\n ⋯\n </button>\n {open && (\n <div className=\"dwa-menu\" role=\"menu\">\n {canStop && (\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"dwa-menu-item\"\n onClick={() => {\n close()\n onStop()\n }}\n >\n {acp.label('停止当前运行实例', 'Stop Running Process')}\n </button>\n )}\n {confirming ? (\n <div className=\"dwa-menu-confirm\">\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-danger\"\n onClick={() => {\n close()\n void onRemove()\n }}\n >\n {acp.label('确认移除?', 'Confirm remove?')}\n </button>\n <button type=\"button\" className=\"dwa-btn\" onClick={() => setConfirming(false)}>\n {acp.label('取消', 'Cancel')}\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"dwa-btn dwa-menu-item dwa-menu-item-danger\"\n onClick={() => setConfirming(true)}\n >\n {acp.label('移除配置', 'Remove')}\n </button>\n )}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Measure the id cell on row hover so its CSS marquee can reach the end of the\n * text. The id is the only cell that yields width, and the panel is resizable,\n * so the overflow is re-read on every entry rather than cached. The animation\n * itself lives in CSS and only runs while the row is hovered; `title` on the\n * row stays the fallback for reduced-motion users.\n */\nfunction armModelIdMarquee(row: HTMLElement): void {\n const view = row.querySelector<HTMLElement>('.dwa-model-row-value')\n const text = view?.querySelector<HTMLElement>('.dwa-model-row-value-text')\n if (view === null || view === undefined || text === null || text === undefined) return\n const fade = Number.parseFloat(getComputedStyle(view).paddingRight) || 0\n const shift = Math.max(0, text.offsetWidth - (view.clientWidth - fade))\n row.style.setProperty('--dwa-id-shift', `${String(-shift)}px`)\n row.style.setProperty('--dwa-id-duration', `${String(Math.min(4, Math.max(1.2, shift / 30)))}s`)\n}\n\n/**\n * Model / thought-level catalog display plus the native-selector visibility\n * filter, as a two-pane transfer list: every known model on the left, the\n * selection that reaches the NTR surfaces on the right. Variants are still\n * opt-in — an empty selection is a legitimate state (base entry only) — and\n * edits save themselves (debounced), so there is no apply button and no second\n * dirty state to lose track of.\n *\n * The panes are ALWAYS shown, never gated on a probe: the right side comes from\n * the agent definition (`exposedModels`) and the left from the host's persisted\n * catalog cache, so opening the card shows what is already configured instead\n * of an empty box that costs one click per visit. 「检测可用模型」 therefore\n * sits with the list and acts as a REFRESH of the left pane only — models the\n * agent newly publishes appear, models it stopped advertising drop out.\n *\n * The auto-discovery switch is a per-agent POLICY, not a one-shot action: it\n * appends models the agent NEWLY publishes to the selection (see\n * `AcpAgentDef.autoDiscoverModels`). It saves itself through the lightweight\n * RPC, so flipping it never restarts the agent process.\n */\nfunction ModelCatalogBlock({\n agentId,\n detected,\n acp,\n active,\n exposed,\n onExposedChange,\n autoDiscover,\n onAutoDiscoverChange,\n isDefault,\n}: {\n agentId: string\n detected: DetectedAgentInfo\n acp: AcpStore\n /** The card's body is visible; the display read waits for the first expand. */\n active: boolean\n exposed: string[] | undefined\n onExposedChange: (next: string[] | undefined) => void\n autoDiscover: boolean\n onAutoDiscoverChange: (next: boolean) => void\n isDefault: boolean\n}) {\n const ui = useUi(acp)\n const [saveError, setSaveError] = useState<string | undefined>(undefined)\n const [search, setSearch] = useState('')\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const pending = useRef<string[] | undefined>(undefined)\n const hasPending = useRef(false)\n // In-flight auto-discovery toggle. A ref, not state: the switch saves\n // silently, and a \"saving\" render here made the row wrap and snap back\n // (user report, 2026-10-21). The guard only prevents double-submits.\n const autoDiscoverBusy = useRef(false)\n // Latest host-confirmed filter: the rollback target when a save fails.\n const serverValue = useRef(exposed)\n serverValue.current = detected.currentConfig?.exposedModels\n\n const probing = ui.probingModels[agentId] === true\n const probeError = ui.probeErrors[agentId]\n const result = ui.modelCatalogs[agentId]\n const modelChoices = result?.catalog.model?.choices ?? []\n /** Result of the last click on 「检测可用模型」 (what the refresh changed). */\n const [detectNote, setDetectNote] = useState<string | undefined>(undefined)\n // Each list folds on its own so a long selection can be parked while the\n // other pane keeps the full column. The list stays mounted (display-toggled)\n // to keep its scroll position.\n const [allCollapsed, setAllCollapsed] = useState(false)\n const [selectedCollapsed, setSelectedCollapsed] = useState(false)\n\n // Display read, not a probe: the host answers from its persisted catalog\n // cache, so the panes already list the known models the first time the card\n // opens. Gated on `active` because every configured agent's body stays\n // mounted (display-toggled) and a collapsed card has nothing to show.\n useEffect(() => {\n if (!active) return\n void acp.loadCachedModels(agentId)\n }, [active, agentId])\n\n useEffect(() => () => {\n if (timer.current === undefined) return\n clearTimeout(timer.current)\n // Flush a pending edit instead of dropping it with the unmount (the\n // panel can be closed mid-debounce); the host keeps the last word.\n if (!hasPending.current) return\n hasPending.current = false\n void acp.setExposedModels(agentId, pending.current).catch(() => { /* host keeps its value */ })\n }, [])\n\n const rollback = (error: unknown): void => {\n onExposedChange(serverValue.current === undefined ? undefined : [...serverValue.current])\n setSaveError(error instanceof Error ? error.message : String(error))\n }\n\n /** Persist a filter now (mode switch); a failure restores the editor. */\n const commit = async (next: string[] | undefined): Promise<void> => {\n setSaveError(undefined)\n try {\n await acp.setExposedModels(agentId, next)\n onExposedChange(next)\n } catch (error) {\n rollback(error)\n }\n }\n\n /** Persist selection edits after a short idle — several clicks coalesce into one call. */\n const queueExposure = (next: string[]): void => {\n pending.current = next\n hasPending.current = true\n if (timer.current !== undefined) clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n if (!hasPending.current) return\n hasPending.current = false\n void commit(pending.current)\n }, EXPOSURE_SAVE_DEBOUNCE_MS)\n }\n\n const toggleAutoDiscover = async (next: boolean): Promise<void> => {\n if (autoDiscoverBusy.current) return\n autoDiscoverBusy.current = true\n onAutoDiscoverChange(next)\n setSaveError(undefined)\n try {\n await acp.setAutoDiscoverModels(agentId, next)\n } catch (error) {\n onAutoDiscoverChange(!next)\n setSaveError(error instanceof Error ? error.message : String(error))\n } finally {\n autoDiscoverBusy.current = false\n }\n }\n\n const query = search.trim().toLowerCase()\n const visibleChoices = query === ''\n ? modelChoices\n : modelChoices.filter(choice =>\n choice.name.toLowerCase().includes(query) || choice.value.toLowerCase().includes(query))\n\n // Unset means \"nothing selected\", not \"everything\": an unset filter and an\n // empty one are the same host-side state (base entry only).\n const basis = exposed ?? []\n const selected = new Set(basis)\n // Right pane = selection in catalog order, then any selected value the last\n // probe no longer lists (still saved and still usable — hiding it would make\n // the two panes quietly disagree about what is configured).\n const selectedChoices: AcpCatalogChoice[] = []\n for (const choice of modelChoices) if (selected.has(choice.value)) selectedChoices.push(choice)\n const listed = new Set(selectedChoices.map(choice => choice.value))\n for (const value of basis) if (!listed.has(value)) selectedChoices.push({ value, name: value })\n const selectedInCatalog = basis.filter(value => modelChoices.some(choice => choice.value === value)).length\n\n const apply = (next: string[]): void => {\n onExposedChange(next)\n queueExposure(next)\n }\n\n const toggleChoice = (value: string): void => {\n apply(selected.has(value) ? basis.filter(v => v !== value) : [...basis, value])\n }\n\n /** Add every model matching the current search that is not selected yet. */\n const addVisible = (): void => {\n const added = visibleChoices.map(choice => choice.value).filter(value => !selected.has(value))\n if (added.length === 0) return\n apply([...basis, ...added])\n }\n\n const clearAll = (): void => {\n if (basis.length === 0) return\n apply([])\n }\n\n /**\n * Refresh the left pane from the agent: re-probe, then report what moved.\n * The note is what makes the button legible — a refresh that changes nothing\n * looks identical to a dead button otherwise (the catalog `updatedAt` does\n * not even move, since the host keeps the entry when the answer is equal).\n */\n const detect = async (): Promise<void> => {\n const before = new Set(modelChoices.map(choice => choice.value))\n setDetectNote(undefined)\n await acp.probeModels(agentId)\n const state = acp.getSnapshot()\n if (state.probeErrors[agentId] !== undefined) return\n const after = state.modelCatalogs[agentId]?.catalog.model?.choices ?? []\n const afterValues = new Set(after.map(choice => choice.value))\n const added = after.filter(choice => !before.has(choice.value)).length\n const removed = [...before].filter(value => !afterValues.has(value)).length\n setDetectNote(added === 0 && removed === 0\n ? acp.label('目录无变化', 'Catalog unchanged')\n : acp.label(\n `新增 ${String(added)} · 移除 ${String(removed)}`,\n `${String(added)} added · ${String(removed)} removed`,\n ))\n }\n\n const selectionBadge = modelChoices.length > 0\n ? acp.label(\n `已选 ${String(selectedInCatalog)}/${String(modelChoices.length)}`,\n `Selected ${String(selectedInCatalog)}/${String(modelChoices.length)}`,\n )\n : acp.label(`已选 ${String(basis.length)}`, `Selected ${String(basis.length)}`)\n\n // Freshness of the cached list (a probe that finds nothing new keeps the old\n // stamp by design — see `detect`), superseded by the click's own outcome.\n const catalogStatus = detectNote ?? (\n result !== undefined && result.updatedAt > 0\n ? acp.label(`目录更新于 ${formatStamp(result.updatedAt)}`, `Catalog from ${formatStamp(result.updatedAt)}`)\n : undefined\n )\n\n const leftEmptyCopy = visibleChoices.length > 0\n ? undefined\n : modelChoices.length > 0\n ? acp.label('无匹配模型', 'No matching models')\n : result === undefined\n ? acp.label('尚未读取模型目录,点「检测可用模型」从 Agent 读取。', 'No catalog yet — click \"Detect available models\" to read it from the agent.')\n : acp.label('该 Agent 未暴露模型选择器(category: model)。', 'This agent exposes no model selector (category: model).')\n\n return (\n <div>\n {!isDefault && (\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>\n <button className=\"dwa-btn\" onClick={() => void acp.setDefaultAgent(agentId)}>\n {acp.label('设为默认智能体', 'Set as default agent')}\n </button>\n </div>\n )}\n {isDefault && (\n // This default belongs to the plugin, not the deployment model route.\n <div className=\"dwa-hint\" style={{ marginTop: 8 }}>\n {acp.label(\n '当前为默认智能体:新的 NTR 会话由它服务,不影响其他模式的默认模型。',\n \"This is the default agent for new NTR sessions; other modes' default models are untouched.\",\n )}\n </div>\n )}\n\n <div className=\"dwa-inline-setting\" style={{ marginTop: 12 }}>\n <label\n className=\"dwa-switch\"\n title={acp.label(\n 'Agent 新发布的模型会自动加入右侧已选列表;首次检测到的目录只作为基线,不会一次性全选。',\n 'Models the agent newly publishes are added to the selected list; the first observed catalog is only a baseline and is not selected wholesale.',\n )}\n >\n <input\n type=\"checkbox\"\n checked={autoDiscover}\n onChange={e => void toggleAutoDiscover(e.target.checked)}\n style={{ margin: 0, accentColor: 'var(--dsw-alias-state-business-primary, #2563eb)' }}\n />\n {acp.label('自动发现新模型', 'Auto-discover new models')}\n </label>\n <span className=\"dwa-setting-copy\">\n {acp.label('新增模型自动加入已选列表,现有勾选不受影响。', 'New models join the selected list automatically; existing picks are untouched.')}\n </span>\n </div>\n {saveError !== undefined && <div className=\"dwa-error\" style={{ marginTop: 4 }}>{saveError}</div>}\n {probeError !== undefined && (\n <div className=\"dwa-error\" style={{ marginTop: 8, wordBreak: 'break-word' }}>\n ⚠ {acp.label('模型检测失败:', 'Model detection failed: ')}{probeError}\n </div>\n )}\n\n {/* Always rendered, never gated on a probe: the selection is known from\n the agent definition and the left list from the host's persisted\n catalog cache. A card that only fills in after a click makes the\n configured state invisible on every visit. */}\n <div className=\"dwa-model-section\">\n <div className=\"dwa-model-toolbar\">\n <span className=\"dwa-form-label\">\n {acp.label('对话中可见的模型', 'Models offered in the conversation')}\n <span\n className=\"dwa-hint\"\n style={{ marginLeft: 6, fontWeight: 400 }}\n title={acp.label(\n 'NTR 模式下的对话只列出这里勾选的模型;未勾选的仍可用——正在生效的那个也会以「· 当前」列出来,其余不列出。',\n 'The NTR surfaces only list what is checked here; unchecked models still work, and the one currently in force is still listed with a \"· current\" row — the rest are not offered.',\n )}\n >\n {acp.label('仅影响菜单可见性', 'Menu visibility only')}\n </span>\n </span>\n <span className=\"dwa-badge dwa-badge-info\">{selectionBadge}</span>\n {/* Freshness of the list stays on the header line, right-aligned; the\n controls below it wrap as one unit. */}\n {catalogStatus !== undefined && <span className=\"dwa-hint dwa-model-status\">{catalogStatus}</span>}\n <div className=\"dwa-model-toolbar-actions\">\n {/* Sits with the list it acts on: a refresh of the LEFT pane only.\n Detection is not what reveals the models — the cache read above\n already does — so keeping it at the top made it read as the\n price of seeing anything at all. */}\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n disabled={probing}\n onClick={() => void detect()}\n title={acp.label(\n '重新读取 Agent 的模型目录:新增的加入左侧,已下架的从左侧移除;已选列表不受影响。',\n 'Re-read the agent catalog: newly published models join the left list, withdrawn ones drop out. The selection is untouched.',\n )}\n >\n {probing ? acp.label('模型检测中...', 'Probing models...') : acp.label('检测可用模型', 'Detect available models')}\n </button>\n {/* The filter lives above BOTH panes, not inside the left one: a\n search field between the pane head and its list made that pane\n structurally taller, so the two could never share one height. */}\n <input\n className=\"dwa-input dwa-model-search\"\n aria-label={acp.label('搜索模型', 'Search models')}\n value={search}\n onChange={e => setSearch(e.target.value)}\n placeholder={acp.label('搜索模型…', 'Search models…')}\n />\n </div>\n </div>\n\n <div className=\"dwa-model-transfer\">\n <div className={`dwa-model-pane${allCollapsed ? ' is-collapsed' : ''}`}>\n <div className=\"dwa-model-pane-head\">\n <span>{acp.label('全部模型', 'All models')}</span>\n {result !== undefined && <span className=\"dwa-badge\">{modelChoices.length}</span>}\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n disabled={visibleChoices.length === 0}\n onClick={addVisible}\n title={acp.label('把当前搜索范围内的模型全部加入右侧', 'Add every model matching the current search')}\n >\n {acp.label('全部加入', 'Add all')}\n </button>\n <button\n type=\"button\"\n className=\"dwa-caret-btn\"\n aria-expanded={!allCollapsed}\n onClick={() => setAllCollapsed(value => !value)}\n title={allCollapsed ? acp.label('展开全部模型', 'Expand all models') : acp.label('收起全部模型', 'Collapse all models')}\n >\n <span className=\"dwa-caret\" aria-hidden>{allCollapsed ? '▸' : '▾'}</span>\n </button>\n </div>\n <div className=\"dwa-model-list\">\n {visibleChoices.map(choice => {\n const checked = selected.has(choice.value)\n // group 前缀仅来自 agent 返回的 SessionConfigSelectGroup(见 impl.ts\n // pushCatalogChoice);供应商可见性兜底靠原样展示 value,严禁从\n // value 路径截取供应商段做标签(约束 8)。\n const title = choice.description === undefined\n ? choice.value\n : `${choice.value}\\n${choice.description}`\n return (\n <button\n type=\"button\"\n key={choice.value}\n className={`dwa-model-row${checked ? ' is-on' : ''}`}\n title={title}\n onClick={() => toggleChoice(choice.value)}\n onPointerEnter={event => { armModelIdMarquee(event.currentTarget) }}\n >\n <span className=\"dwa-model-row-mark\">{checked ? '✓' : '+'}</span>\n <span className=\"dwa-model-row-name\">\n {choice.group !== undefined && <span style={{ opacity: 0.7 }}>{choice.group} · </span>}\n {choice.name}\n </span>\n {choice.value !== choice.name && (\n <span className=\"dwa-model-row-value\">\n <span className=\"dwa-model-row-value-text\">{choice.value}</span>\n </span>\n )}\n {/* Credit / capability notes belong on the row itself: the\n list exists to compare models, and a hover-only price\n cannot be scanned. Grouped choices reuse the group label\n as their fallback description — that one is already\n printed before the name. */}\n {choice.description !== undefined && choice.description !== choice.group && (\n <span className=\"dwa-model-row-desc\">{choice.description}</span>\n )}\n </button>\n )\n })}\n {leftEmptyCopy !== undefined && <span className=\"dwa-hint dwa-model-empty\">{leftEmptyCopy}</span>}\n </div>\n </div>\n\n <div className={`dwa-model-pane${selectedCollapsed ? ' is-collapsed' : ''}`}>\n <div className=\"dwa-model-pane-head\">\n <span>{acp.label('已选模型', 'Selected models')}</span>\n <span className=\"dwa-badge dwa-badge-info\">{basis.length}</span>\n <button\n type=\"button\"\n className=\"dwa-btn dwa-btn-secondary\"\n disabled={basis.length === 0}\n onClick={clearAll}\n >\n {acp.label('清空', 'Clear')}\n </button>\n <button\n type=\"button\"\n className=\"dwa-caret-btn\"\n aria-expanded={!selectedCollapsed}\n onClick={() => setSelectedCollapsed(value => !value)}\n title={selectedCollapsed ? acp.label('展开已选模型', 'Expand selected models') : acp.label('收起已选模型', 'Collapse selected models')}\n >\n <span className=\"dwa-caret\" aria-hidden>{selectedCollapsed ? '▸' : '▾'}</span>\n </button>\n </div>\n <div className=\"dwa-model-list\">\n {selectedChoices.length === 0 && (\n <span className=\"dwa-hint dwa-model-empty\">\n {acp.label('尚未选择模型,从「全部模型」中加入。', 'No models selected. Add from All models.')}\n </span>\n )}\n {selectedChoices.map(choice => (\n <button\n type=\"button\"\n key={choice.value}\n className=\"dwa-model-row is-on\"\n title={choice.description === undefined ? choice.value : `${choice.value}\\n${choice.description}`}\n onClick={() => toggleChoice(choice.value)}\n onPointerEnter={event => { armModelIdMarquee(event.currentTarget) }}\n >\n <span className=\"dwa-model-row-name\">\n {choice.group !== undefined && <span style={{ opacity: 0.7 }}>{choice.group} · </span>}\n {choice.name}\n </span>\n {choice.value !== choice.name && (\n <span className=\"dwa-model-row-value\">\n <span className=\"dwa-model-row-value-text\">{choice.value}</span>\n </span>\n )}\n {choice.description !== undefined && choice.description !== choice.group && (\n <span className=\"dwa-model-row-desc\">{choice.description}</span>\n )}\n <span className=\"dwa-model-row-remove\" aria-hidden=\"true\">×</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {result?.catalog.thought !== undefined && (\n <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', alignItems: 'center' }}>\n <span className=\"dwa-form-label\">{acp.label('思考等级', 'Thought Levels')}</span>\n {result.catalog.thought.choices.map(choice => (\n <span key={choice.value} className=\"dwa-badge\" title={choice.description}>\n {choice.name}\n </span>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n","/**\n * ACP-native mode control for the conversation composer\n * (`conversation.input.left` — the seat the slot contract reserves for a small\n * native control).\n *\n * THE TWO MODE CONTROLS NEVER SHARE A SCREEN (audit finding F27): the ACP-mode\n * gate that lets this entry render also occupies the host's Access mode seat\n * with a claim that renders nothing (`createPermissionSeatClaim` in\n * model-seat.tsx) — the host's write policy (仅可查看 / 工作区内修改 / 完全权限)\n * says nothing about an external agent process, so showing it next to an ACP\n * conversation only misleads. This select is therefore NOT \"complementary\" to\n * the Access mode: in the only conversation where it appears, the Access mode is\n * hidden and this IS the mode switch. What it switches is the agent's own\n * `session/set_mode` vocabulary (`plan`, `acceptEdits`, …). An ordinary\n * conversation renders nothing here at all — the entry is registered\n * unconditionally and gates itself (see `isAcp` below).\n *\n * The host supplies the data by chat session id (`session/chat-list`), so the\n * control renders nothing until an ACP agent has actually run in this\n * conversation, and nothing at all for an agent that declares no modes.\n */\n\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { AcpSessionHook } from './acp-ui.tsx'\nimport type { AcpStore } from './store.ts'\n\n/**\n * The props this occupant actually receives: our injected face (which carries\n * the chat session id, like every other entry in this plugin) plus the\n * framework's session standard kit, of which only `useSession` is read.\n *\n * `conversation.input.left` declares no `owner` share — the composer bar calls\n * `renderSlot('conversation.input.left', {})` — so the ONLY props that exist are\n * the standard kit (session-scope `sessionId` + the bound `use<Name>` selector\n * hooks + `useProjection`/`inputActions`) and whatever our `inject` returns. The\n * session id deliberately rides the injected face rather than the standard kit:\n * the kit's top-level `sessionId` is a newer renderer addition, while the\n * positional inject argument (`binding.key`) is the contract every version in\n * play honors. Inventing a prop here is a render crash, not a silent undefined.\n */\nexport interface ComposerModeProps {\n /** Injected face: the chat session id this control belongs to. */\n sessionId: string\n /** Framework-bound conversation snapshot hook (`SessionStandardProps.useSession`). */\n useSession: AcpSessionHook\n acp: AcpStore\n}\n\nfunction useUi(acp: AcpStore) {\n return useSyncExternalStore(cb => acp.subscribe(cb), () => acp.getSnapshot())\n}\n\n/**\n * The mode select's tooltip (audit finding F27). It must not call the host's\n * Access mode \"complementary\": the ACP-mode gate that puts this select on screen\n * also hides that control (`createPermissionSeatClaim` renders nothing in the\n * permission seat), so in the conversation where this appears the Access mode is\n * gone and this select is the mode switch the user actually has.\n */\nexport function agentModeTitle(agentId: string | undefined, acp: AcpStore): string {\n return acp.label(\n `agent 模式(${agentId ?? 'agent'}):由 agent 自己定义。NTR 会话里 DSH 的 Access mode 不显示,模式就在此切换。`,\n `agent mode (${agentId ?? 'agent'}): defined by the agent. In an NTR conversation the DSH access mode is not shown, so this is where the mode is switched.`,\n )\n}\n\n/**\n * Whether this conversation is one the agent-mode control may serve.\n *\n * An EXPLICIT non-ACP verdict outranks a lingering session row: switching a\n * conversation off the NTR preset does not close its ACP session, so \"a row\n * exists\" alone would keep this control on an ordinary conversation — the same\n * precedence the agent tag, the context ring and the released banner already\n * apply. Only while the host has not answered (`undefined`) does a bound\n * session count as proof, which is what keeps the control available before the\n * host's first `session/chat-list` answer lands.\n */\nexport function agentModeVisible(\n acpMode: boolean | undefined,\n hasBoundSession: boolean,\n): boolean {\n if (acpMode === false) return false\n return acpMode === true || hasBoundSession\n}\n\n/**\n * The mode the select shows as in force.\n *\n * A LIVE session is the authority (it is what the agent runs right now). A\n * RELEASED row only describes the mode the agent was parked ON, so a newer\n * pick — which `applyChatChoice` will apply on the next resume — outranks it\n * (and the parked row still beats the catalog default). Same precedence as\n * the model and thought level in `describeRoute`: a pre-switch on a parked\n * session must be visible or the control reads as dead.\n */\nexport function modeInForce(\n target: { released?: boolean; modes?: { currentModeId?: string } } | undefined,\n persisted: string | undefined,\n fallback?: string,\n): string {\n const live = target !== undefined && target.released !== true ? target.modes?.currentModeId : undefined\n return live ?? persisted ?? target?.modes?.currentModeId ?? fallback ?? ''\n}\n\nexport function AcpComposerMode({ acp, sessionId, useSession }: ComposerModeProps) {\n const ui = useUi(acp)\n const dshSessionId = String(sessionId)\n // A fresh chat session has no binding until its first turn creates one, so\n // re-read whenever the running flag settles.\n const running = useSession(snapshot => snapshot.running)\n const [switching, setSwitching] = useState(false)\n\n useEffect(() => {\n void acp.loadChatSessions(dshSessionId)\n }, [acp, dshSessionId, running])\n\n const bound = ui.chatSessionsFor === dshSessionId ? ui.chatSessions : []\n // Prefer the live binding; a released one still describes the conversation.\n const target = bound.find(session => !session.released) ?? bound[0]\n // ISOLATION — this entry is NOT inside the ACP-mode gate (`syncAcpMode` holds\n // the model/permission seats, but `conversation.input.left` is registered\n // unconditionally), so it must gate itself. The `defaultAgent` fallback below\n // would otherwise put an ACP mode select into ORDINARY conversations: the\n // agent id is plugin-global, so \"an agent is configured\" is not the same\n // question as \"this conversation is served by one\". A live session is proof\n // only while the host has not answered; an explicit `false` wins over the\n // lingering row a preset switch leaves behind (see `agentModeVisible`).\n const isAcp = agentModeVisible(ui.acpMode[dshSessionId], target !== undefined)\n // The agent this conversation is set up on, live session or not. Before the\n // first message there is no session to read a mode list from, yet the user is\n // already choosing the agent — and the mode is one of the things they may\n // want to choose with it (user report, 2026-10-18: this control vanished on a\n // conversation set up ahead of its first message).\n //\n // The `defaultAgent` fallback applies ONLY inside an ACP-mode conversation.\n // Without that gate an ordinary chat would resolve an agent id it has nothing\n // to do with and read ITS catalog — and reading a catalog can spawn a probe\n // (user report, 2026-10-18: an unrelated agent showed up \"已连接\").\n const agentId = target?.agentId\n // A locked conversation belongs to this agent even when no session is live\n // (after a restart / agent stop). It must outrank the binding read and the\n // plugin default: reading another agent's catalog here would put THAT\n // agent's modes on a locked conversation.\n ?? ui.chatLockedAgent[dshSessionId]\n ?? ui.chatBoundAgent[dshSessionId]\n ?? ui.chatChoice[dshSessionId]?.agentId\n ?? (isAcp ? ui.defaultAgent : undefined)\n const catalog = agentId === undefined ? undefined : ui.seatCatalogs[agentId]\n useEffect(() => {\n // Nothing to read for a conversation that is not served by an agent, and\n // nothing to read while a live session is already answering (its\n // `session/new` modes outrank the catalog).\n if (!isAcp || agentId === undefined || target !== undefined) return\n void acp.loadAgentCatalog(agentId)\n }, [acp, agentId, isAcp, target])\n\n // The whole control is ACP-only: a lingering live session row survives a\n // preset switch, so its `availableModes` are NOT a licence to render here.\n // Without this guard an ordinary conversation kept an agent-mode select\n // beside its native controls (reported as \"the bottom bar lost its native\n // capabilities\").\n if (!isAcp) return null\n const live = target?.modes?.availableModes ?? []\n // TWO sources, one list. A live session's `session/new` answer is the\n // authority on which modes exist and which is in force; the probed catalog\n // carries the same list for the window before one exists (it is what the\n // agent published on ITS throwaway session). A live session always wins.\n const modes = live.length > 0\n ? live\n : (catalog?.mode?.choices ?? []).map(choice => ({ id: choice.id, name: choice.name, description: choice.description }))\n if (modes.length === 0) return null\n // The mode IN FORCE: a live session is the authority, before one exists (or\n // while one is parked) the claims available are the pick this conversation\n // recorded and the agent's own reported current mode (a pick outranks the\n // agent's default, exactly like the model and thought level do in\n // `describeRoute`; the parked row yields to the pick — see `modeInForce`).\n const persisted = ui.chatChoice[dshSessionId]?.agentId === agentId\n ? ui.chatChoice[dshSessionId]?.mode\n : undefined\n const current = modeInForce(\n target,\n persisted,\n catalog?.mode?.currentValue ?? modes[0]?.id,\n )\n\n return (\n <label\n style={{ display: 'inline-flex', alignItems: 'center', fontSize: 12 }}\n // No visible \"Agent mode\" caption (user decision, 2026-10-17): the option\n // labels are the agent's own mode names (Plan / Accept edits / …), which\n // read as what they are on their own — a caption beside a single select\n // only adds a second line of chrome to a control whose value already says\n // it. The explanation stays in the tooltip for whoever goes looking.\n title={agentModeTitle(agentId, acp)}\n >\n <select\n aria-label={acp.label('agent 模式', 'agent mode')}\n value={current}\n disabled={switching}\n onChange={event => {\n const modeId = event.target.value\n setSwitching(true)\n void acp.setChatSessionMode(dshSessionId, modeId, agentId).finally(() => { setSwitching(false) })\n }}\n style={{\n font: 'inherit',\n fontSize: 12,\n padding: '2px 4px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border-l2, #d1d5db)',\n background: 'transparent',\n color: 'inherit',\n maxWidth: 180,\n }}\n >\n {modes.map(mode => (\n <option key={mode.id} value={mode.id}>{mode.name ?? mode.id}</option>\n ))}\n </select>\n </label>\n )\n}\n","/**\n * Composer context-occupancy ring at `conversation.composer.dock` — the bottom\n * info row below the input card, where the host also renders its ContextMeter.\n *\n * WHY A PLUGIN-OWNED RING: the host's meter reads its `contextPressure`\n * projection, folded from DSH provider-usage samples plus a route capacity. An\n * ACP conversation produces neither — the agent owns the request and the host\n * never sees its prompt — so the native meter renders nothing there. This ring\n * reports the agent's OWN `usage_update` used/size instead: occupancy only. ACP\n * declares no system/tools/messages split, so no breakdown is invented.\n *\n * Registered for every conversation and gating itself: it renders only for an\n * ACP conversation whose session row carries usage. The row comes from\n * `session/chat-list`; the store live-patches it from the coalesced\n * `usage_update` frames (see `AcpStore.flushUpdates`).\n */\n\nimport { useEffect } from 'react'\n// Type-only: pulls the ui-conversation SlotMap merge into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { ensureAcpStyle, useUi } from './acp-ui.tsx'\nimport type { AcpSessionHook } from './acp-ui.tsx'\nimport type { AcpStore, UiState } from './store.ts'\nimport type { SessionView } from '../protocol.ts'\n\n/** Props of the composer entry (injected face + the frame's standard kit). */\nexport interface AcpContextMeterProps {\n /** Injected face: the chat session id this entry belongs to. */\n sessionId: string\n /** Framework-bound conversation snapshot hook (`SessionStandardProps.useSession`). */\n useSession: AcpSessionHook\n acp: AcpStore\n}\n\nexport interface ContextOccupancy {\n /** Rounded occupancy percentage, clamped to 0..100. */\n percent: number\n used: number\n size: number\n}\n\n/**\n * The sample this conversation's ring should show, or `undefined` when the\n * entry must render nothing. An explicit `acpMode === false` outranks a stale\n * `chatSessions` row: the conversation's preset left NTR while its ACP session\n * was still alive, and the ring must leave with it (same precedence as the\n * agent tag's `agentTagVisible`). While the verdict is unknown, a LIVE row is\n * proof; a PARKED one is not — it can outlive the preset switch, and the ring\n * must not appear on an ordinary conversation during the answer window.\n */\nexport function acpMeterUsage(\n ui: Pick<UiState, 'acpMode' | 'chatSessions' | 'chatSessionsFor'>,\n dshSessionId: string,\n): SessionView['usage'] | undefined {\n if (ui.acpMode[dshSessionId] === false) return undefined\n if (ui.chatSessionsFor !== dshSessionId) return undefined\n const live = ui.chatSessions.find(row => !row.released)\n // A PARKED row alone is not proof this conversation is still in NTR mode:\n // switching off the preset leaves the ACP session record alive, and while the\n // host has not answered (`undefined`) the ring must not appear on an\n // ordinary conversation. A live row is proof; a parked one needs the\n // affirmative verdict.\n if (live === undefined && ui.acpMode[dshSessionId] !== true) return undefined\n return (live ?? ui.chatSessions[0])?.usage\n}\n\n/**\n * Bounded display occupancy from one agent sample. `undefined` (no meter) for\n * an absent / malformed / zero-capacity sample rather than a made-up 0%.\n */\nexport function contextOccupancy(usage: SessionView['usage'] | undefined): ContextOccupancy | undefined {\n if (usage === undefined) return undefined\n const { used, size } = usage\n if (!Number.isFinite(used) || !Number.isFinite(size) || used < 0 || size <= 0) return undefined\n return { percent: Math.min(100, Math.round(used / size * 100)), used, size }\n}\n\n/** Compact token count for the tooltip, in the host meter's K/M shape. */\nexport function compactTokenCount(value: number): string {\n if (value < 1e3) return String(Math.round(value))\n const thousands = value / 1e3\n if (value < 1e6) {\n return `${thousands >= 100 ? String(Math.round(thousands)) : String(Math.round(thousands * 10) / 10)}K`\n }\n const millions = value / 1e6\n return `${millions >= 100 ? String(Math.round(millions)) : String(Math.round(millions * 10) / 10)}M`\n}\n\n/** Ring geometry matches the host ContextMeter: 14px viewBox, 2px stroke. */\nconst RADIUS = 5.5\nconst CIRCUMFERENCE = 2 * Math.PI * RADIUS\n\nexport function AcpContextMeter({ acp, sessionId, useSession }: AcpContextMeterProps) {\n ensureAcpStyle()\n const ui = useUi(acp)\n const dshSessionId = String(sessionId)\n // A fresh conversation gets its ACP session binding during its first turn,\n // and no SSE frame announces that; the running flip is the re-read trigger\n // every composer entry shares.\n const running = useSession(snapshot => snapshot.running)\n useEffect(() => {\n void acp.loadChatSessions(dshSessionId)\n }, [acp, dshSessionId, running])\n\n const context = contextOccupancy(acpMeterUsage(ui, dshSessionId))\n if (context === undefined) return null\n\n const title = acp.label(\n `上下文已用 ${String(context.percent)}% · ${compactTokenCount(context.used)} / ${compactTokenCount(context.size)}`,\n `${String(context.percent)}% of context used · ${compactTokenCount(context.used)} / ${compactTokenCount(context.size)}`,\n )\n return (\n <span className=\"ntr-acp-meter\" role=\"img\" aria-label={title} title={title}>\n <svg viewBox=\"0 0 14 14\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <circle className=\"ntr-acp-meter-track\" cx=\"7\" cy=\"7\" r={RADIUS} />\n <circle\n className=\"ntr-acp-meter-fill\"\n cx=\"7\"\n cy=\"7\"\n r={RADIUS}\n strokeDasharray={`${String(CIRCUMFERENCE * context.percent / 100)} ${String(CIRCUMFERENCE)}`}\n transform=\"rotate(-90 7 7)\"\n />\n </svg>\n <span className=\"ntr-acp-meter-percent\">{context.percent}%</span>\n </span>\n )\n}\n","/**\n * The one-shot hand-off from the composer's agent base to the edge tag.\n *\n * The base and the tag live in DIFFERENT slots (`conversation.input.model` and\n * `conversation.input.overlay`), so neither can animate the other directly.\n * This module is the shared clock they both read: when the conversation locks\n * to its agent, the base plays a shatter for {@link SHATTER_MS} while the tag\n * stays hidden, then the tag mounts and condenses for {@link FORM_MS}. Both\n * surfaces derive the same phase from the same map, so the two slots cannot\n * drift apart.\n *\n * The ceremony plays at the START of the conversation's first turn: the host\n * locks (and broadcasts) it as soon as the first ACP session is ready, before\n * the prompt is sent. If that turn delivers nothing at all, the host rolls the\n * lock back (`chat/agent-unlocked`); the base returns and the tag disappears,\n * with no reverse ceremony — the important part there is that the picker is\n * usable again.\n *\n * It must NOT replay on a page load. The lock is durable, so opening a\n * conversation that locked yesterday would otherwise replay the ceremony every\n * time. `AgentLockPhase` is therefore parameterized by the store's\n * `agentLockReveal` serial, which only advances when THIS page witnesses a\n * conversation go from unlocked to locked (a `chat/agent-locked` frame, or a\n * later `session/chat-list` answer). A reveal with no animation entry yet reads\n * as `shatter` (the commit that first sees it renders the base with its shatter\n * class immediately — no frame without either surface), and once the clock\n * finishes it records that serial as consumed so the phase settles to `idle`.\n * A NEWER reveal (a retry after a rolled-back first turn) restarts the clock\n * even if the previous ceremony is still running; the old timers are fenced off\n * by their epoch.\n *\n * `prefers-reduced-motion` turns the whole ceremony off: the base disappears\n * and the tag appears in the same commit.\n */\n\nimport { useSyncExternalStore } from 'react'\n\nexport type AgentLockPhase = 'idle' | 'shatter' | 'form'\n\ninterface ActiveAnim {\n phase: 'shatter' | 'form'\n reveal: number\n /** Fences timers of a ceremony a newer reveal replaced. */\n epoch: number\n}\n\n/** Keep in step with the `ntr-acp-shatter-chip` / `ntr-acp-shard-out` CSS. */\nexport const SHATTER_MS = 460\n/** Keep in step with the `ntr-acp-form-chip` / `ntr-acp-shard-in` CSS. */\nexport const FORM_MS = 540\n\nconst phases = new Map<string, ActiveAnim>()\n/** The reveal serial whose ceremony has already played (or was skipped). */\nconst consumed = new Map<string, number>()\nconst listeners = new Set<() => void>()\nlet version = 0\nlet epochSeq = 0\n\nfunction emit(): void {\n version += 1\n for (const listener of [...listeners]) listener()\n}\n\nfunction reducedMotion(): boolean {\n return typeof window !== 'undefined'\n && typeof window.matchMedia === 'function'\n && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n}\n\n/**\n * The phase for one conversation. `reveal` is the store's lock-reveal serial\n * for it (absent for a conversation this page never saw lock). A brand-new,\n * not-yet-consumed reveal reads as `shatter` so the commit after the lock\n * already renders the shattering base rather than briefly rendering neither\n * surface. An active ceremony only answers for its OWN reveal: a retry after a\n * rolled-back first turn is a new reveal and must start from `shatter`.\n */\nexport function agentLockPhaseOf(sessionId: string, reveal?: number): AgentLockPhase {\n const active = phases.get(sessionId)\n if (active !== undefined && active.reveal === reveal) return active.phase\n if (reveal === undefined || consumed.get(sessionId) === reveal || reducedMotion()) return 'idle'\n return 'shatter'\n}\n\n/**\n * Start the ceremony for a reveal this page witnessed. Idempotent for the same\n * reveal (both slots call it from an effect); a newer reveal supersedes a\n * running older one, whose timers are fenced by the epoch check.\n */\nexport function playAgentLockAnim(sessionId: string, reveal: number): void {\n const active = phases.get(sessionId)\n if (active?.reveal === reveal || consumed.get(sessionId) === reveal) return\n if (reducedMotion()) {\n phases.delete(sessionId)\n consumed.set(sessionId, reveal)\n emit()\n return\n }\n const epoch = ++epochSeq\n phases.set(sessionId, { phase: 'shatter', reveal, epoch })\n emit()\n setTimeout(() => {\n const current = phases.get(sessionId)\n if (current === undefined || current.epoch !== epoch || current.phase !== 'shatter') return\n phases.set(sessionId, { ...current, phase: 'form' })\n emit()\n setTimeout(() => {\n const done = phases.get(sessionId)\n if (done === undefined || done.epoch !== epoch || done.phase !== 'form') return\n phases.delete(sessionId)\n consumed.set(sessionId, reveal)\n emit()\n }, FORM_MS)\n }, SHATTER_MS)\n}\n\n/** Subscribe to phase changes (one global clock shared by both slots). */\nexport function useAgentLockPhase(sessionId: string, reveal?: number): AgentLockPhase {\n useSyncExternalStore(\n cb => {\n listeners.add(cb)\n return () => { listeners.delete(cb) }\n },\n () => version,\n )\n return agentLockPhaseOf(sessionId, reveal)\n}\n","/**\n * The ACP base in the conversation composer's model seat\n * (`conversation.input.model`) and its picker (`conversation.input.overlay`).\n *\n * WHAT IT IS: the base of an ACP conversation. The first level is WHICH agent\n * serves it; hanging off that are the things the agent itself owns — its own\n * permission preset, its own model, its own thought level, and any further\n * config options it publishes (the \"advanced\" base). All of them come from the\n * agent (`agent/catalog`, the agent's own mode list), never from DSH's model\n * catalog: an ACP agent is not a DSH provider, and DSH's catalog has no\n * per-session seam to filter it with anyway. See\n * docs/acp-convergence.md §2.1.\n *\n * WHERE IT SITS: the composer's model seat — the trailing group of the bar,\n * immediately before the submit button. That is where the host's own base\n * (the model selector) sits, and it is the cell an ACP conversation must not\n * leave to that selector: it lists DSH's providers, none of which this\n * conversation can use, and clicking one writes the process-wide default\n * model. One cell, one occupant. The base is installed there by the ACP-mode\n * gate in `index.tsx` (see `createModelSeatClaim`), which holds it only while\n * the staged conversation runs the ACP preset — so an ordinary chat gets the\n * native selector back and sees no ACP chrome at all.\n *\n * WHAT IT ADDS over a bare model selector: the axes are separate. A native\n * selector would flatten them into one list of \"Agent · Model\" rows; here\n * \"which agent\", \"which of ITS models\", \"how hard it thinks\" and every extra\n * option the agent publishes are chips of their own. Before the first turn the\n * agent base is the fixed right-hand anchor; the advanced entry sits left of\n * the model, and model / thought sit between them. Once the lock retires the\n * base, model / thought inherit the right edge (user decision, 2026-10-21).\n *\n * ONE CONVERSATION, ONE AGENT (user decision, 2026-10-20): once this\n * conversation's first turn STARTS — its ACP session materializes, before the\n * prompt is sent — the agent base steps aside and the conversation is LOCKED to\n * that agent. The current agent then lives on a small, read-only tag that rides\n * the composer card's top edge (`AcpComposerAgentTag`, installed in\n * `conversation.input.overlay`). Before that first turn the base stays, so a\n * pick can still be changed before it is committed to a message. See\n * `agentBaseHidden`.\n *\n * The lock is provisional until that first turn delivers something: a turn\n * that ends without any visible block (an error before the first chunk, a\n * silent provider failure, a cancel) is rolled back by the host, the base\n * returns, and the picker works again. The composer learns both transitions\n * from `chat/agent-locked` / `chat/agent-unlocked`, not from a list read.\n *\n * The hand-off is a shatter-and-condense animation (`agent-lock-anim.ts`): the\n * base breaks into debris, then the tag forms from it. The two live in\n * different slots, so they share that module's phase clock rather than trying\n * to animate across slot boundaries.\n *\n * It is deliberately quiet: outside ACP mode the gate never mounts it, and\n * before the host has answered the agent roster it renders nothing. Once the\n * roster is KNOWN to be empty the base stays and its picker carries the\n * \"open NTR settings\" action — an NTR conversation with nothing configured\n * still has a way forward instead of an empty cell. The ACP view tab remains\n * the entry point for conversations no agent has served.\n */\n\nimport { useEffect, useState } from 'react'\nimport type { CSSProperties } from 'react'\n// Type-only: pulls the ui-conversation SlotMap merge into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport {\n AcpChip, AcpMenu, AcpShatterParts, buildAcpModelId, closeAcpMenu, ensureAcpStyle,\n openAcpMenu, openAcpSettingsDialog, setAcpMenuError, splitAcpModelId, useAcpMenu, useUi,\n} from './acp-ui.tsx'\nimport { playAgentLockAnim, useAgentLockPhase } from './agent-lock-anim.ts'\nimport type {\n AcpChatInjected, AcpMenuKind, AcpMenuItem, AcpMenuSection, AcpSessionHook,\n} from './acp-ui.tsx'\nimport type { AcpConversationChoice, AcpTagHalo, AgentCatalogResult, AgentView, ChatSessionView } from '../protocol.ts'\n\n/**\n * Props every entry in this file receives: the injected face (built in\n * `index.tsx` where the services live) plus the session id the slot frame\n * passes to `inject`.\n */\nexport interface AcpClusterProps extends AcpChatInjected {\n sessionId: string\n /**\n * The framework's session snapshot hook (`SessionStandardProps.useSession`,\n * see `AcpSessionHook`). The cluster watches `running` alone: a fresh\n * conversation has no ACP binding until its first turn settles, so the binding\n * read is repeated then.\n */\n useSession: AcpSessionHook\n}\n\nconst STATE_COLORS: Record<AgentView['state'], string> = {\n ready: 'var(--dsw-alias-state-success-primary, #22c55e)',\n starting: 'var(--dsw-alias-state-warn-primary, #eab308)',\n error: 'var(--dsw-alias-state-error-primary, #ef4444)',\n stopped: 'var(--dsw-alias-state-idle-primary, #9ca3af)',\n}\n\nfunction agentStateText(acp: AcpChatInjected['acp'], state: AgentView['state']): string {\n switch (state) {\n case 'ready': return acp.label('已连接', 'connected')\n case 'starting': return acp.label('启动中', 'starting')\n case 'error': return acp.label('错误', 'error')\n default: return acp.label('未启动', 'not running')\n }\n}\n\n/** Everything the chips need to describe the current agent binding. */\ninterface RouteInfo {\n /** The agent serving this conversation. */\n agentId?: string\n agentTitle?: string\n modelId?: string\n modelLabel?: string\n efforts: readonly { id: string; name: string; description?: string }[]\n /** `current.reasoningEffort` folded over the model's default (native seat semantics). */\n effectiveEffort?: string\n effortLabel?: string\n}\n\n/** The live (non-released) ACP session bound to this chat, if any. */\nfunction boundSession(ui: { chatSessions: ChatSessionView[]; chatSessionsFor?: string }, sessionId: string): ChatSessionView | undefined {\n if (ui.chatSessionsFor !== sessionId) return undefined\n return ui.chatSessions.find(session => !session.released) ?? ui.chatSessions[0]\n}\n\n/**\n * The agent serving this conversation: the plugin's own binding first (who\n * serves the NEXT message — the host reports it per chat session), then the\n * agent an ACP session for this conversation is already served by, then the\n * plugin's default — which is what lets a chip offer \"use ACP: <agent>\" for a\n * conversation that has never been routed, instead of going blank. The binding\n * outranks the live session because a switch applies from the next message:\n * until that turn materializes a session for the new agent, the live list still\n * names the previous one, and the chip must not read as \"the click did nothing\".\n *\n * `undefined` IS an answer, not a gap: with no binding, no live session and no\n * plugin default the guard's route rewrite has nothing to rewrite with and the\n * turn runs on the deployment default provider (see `boundModelFor` in\n * presets/acp/acp-guard.mjs). This function used to end in `agents[0]?.id`,\n * which put the first configured agent on the chip in exactly that state — a\n * name no message would ever reach (audit finding F5). The honest chip there is\n * the \"选择 Agent\" entry point, not a borrowed name.\n */\nfunction routeAgentId(\n boundAgentId: string | undefined,\n bound: ChatSessionView | undefined,\n agents: readonly AgentView[],\n defaultAgent: string | undefined,\n): string | undefined {\n if (boundAgentId !== undefined && agents.some(agent => agent.id === boundAgentId)) return boundAgentId\n if (bound?.agentId !== undefined) return bound.agentId\n if (defaultAgent !== undefined && agents.some(agent => agent.id === defaultAgent)) return defaultAgent\n return undefined\n}\n\n/**\n * Whether the agent base has anything honest to show.\n *\n * An empty roster has two meanings, and they must not be conflated: before the\n * host answers `agents/list` it means \"not read yet\" (rendering a setup entry\n * there would accuse a correctly configured deployment on every page load),\n * while after the answer it means \"nothing is configured\" — exactly the state\n * that must keep the base and offer the way into NTR settings.\n */\nexport function agentBaseHasData(\n bound: ChatSessionView | undefined,\n agents: readonly AgentView[],\n rosterLoaded: boolean,\n): boolean {\n return bound !== undefined || agents.length > 0 || rosterLoaded\n}\n\n/**\n * The agent this conversation is LOCKED to, if its first ACP turn has run.\n *\n * The host's `lockedAgentId` is the durable answer: it is written when the\n * first ACP session materializes and it survives restarts, so it is the only\n * source that can still answer after the session is gone. The live/released\n * session is the fallback for a host half from before the lock existed — the\n * moment a session exists, the conversation already has context with it.\n *\n * There is deliberately no roster check: a deleted agent still owns the\n * conversation's context, and pretending the conversation is unbound would\n * bring back a picker the turn path has to refuse. The tag names the id even\n * when no title is left.\n */\nexport function lockedAgentOf(\n hostLockedAgentId: string | undefined,\n bound: ChatSessionView | undefined,\n): string | undefined {\n return hostLockedAgentId ?? bound?.agentId\n}\n\n/**\n * Whether the composer's agent base has done its job and should step aside.\n *\n * The timing is the product decision (2026-10-20): at the START of this\n * conversation's first turn. A conversation that merely recorded a pick is\n * still being set up; the user may change their mind before sending, and\n * hiding the base there would strand the pick. Once locked, the base only\n * comes back if the host ROLLS THE LOCK BACK — a first turn that delivered\n * nothing, so there is no context to protect. It also stays retired when the\n * locked agent has been deleted from the roster: the conversation's context\n * still belongs to it.\n */\nexport function agentBaseHidden(lockedTo: string | undefined): boolean {\n return lockedTo !== undefined\n}\n\n/**\n * Whether the composer-edge `NTR · <agent>` tag should render.\n *\n * The tag is the locked agent's nameplate: it appears exactly when the base\n * has stood down. The `acpMode` gate matters because the overlay entry is\n * registered for EVERY conversation while `ui.chatSessions` can still carry an\n * ACP session record after a conversation switched back to an ordinary preset:\n * an explicit `false` verdict must win over that lingering record, or the tag\n * would keep advertising an agent the conversation no longer runs. `undefined`\n * is \"the host has not answered yet\" and falls back to the lock itself, which\n * is proof this conversation was served by an agent.\n */\nexport function agentTagVisible(\n acpMode: boolean | undefined,\n lockedTo: string | undefined,\n): boolean {\n return (acpMode ?? true) && lockedTo !== undefined\n}\n\n/**\n * Whether the tagged agent is LIVE: an ACP session serves this conversation\n * right now and it is not released (the process is up, or at least the host\n * has not marked the session suspended). This drives the tag's breathing\n * halo — released must turn it off, and the named agent must be the one the\n * live session actually serves. No session view at all reads as not alive:\n * before the first answer the state says nothing about the process.\n */\nexport function agentTagAlive(\n bound: ChatSessionView | undefined,\n lockedTo: string | undefined,\n): boolean {\n return bound !== undefined && !bound.released && bound.agentId === lockedTo\n}\n\n/** One breath cycle per halo speed, in seconds. Settings card offers these three. */\nexport const TAG_HALO_PERIODS: Record<AcpTagHalo['speed'], string> = {\n slow: '5.2s',\n normal: '3.2s',\n fast: '1.2s',\n}\n\n/**\n * The tag's halo presentation for one setting: the extra classes for the tag\n * span (`alive` — the session is live; steady mode replaces the pulse with a\n * constant glow) and the CSS variable the stylesheet reads for the period.\n * The variable is set for breath mode only: in steady mode nothing animates,\n * so a stale variable could never leak into the reduced-motion fallback.\n */\nexport function agentTagHaloPresentation(\n halo: AcpTagHalo,\n alive: boolean,\n): { classes: string; style: CSSProperties } {\n if (!alive) return { classes: '', style: {} }\n if (halo.mode === 'steady') return { classes: ' is-alive is-steady', style: {} }\n return { classes: ' is-alive', style: { '--ntr-halo-period': TAG_HALO_PERIODS[halo.speed] } as CSSProperties }\n}\n\n/**\n * Describe this conversation's ACP agent from the binding (the host's record of\n * which agent actually serves it) + the agent roster + the agent's own model\n * surface (`agent/catalog`).\n *\n * An `undefined` agentId means nothing serves this conversation's next message\n * (see `routeAgentId`); every axis then degrades to nothing rather than naming\n * an agent the request path will never use.\n *\n * The model axis reads the ACP catalog, NOT any DSH model directory: an ACP\n * agent's models are the agent's own (see the module header), so there is\n * nothing to look up in DSH's catalog — and reading `current` off a directory\n * was the retired route axis.\n */\nexport function describeRoute(\n agents: readonly AgentView[],\n bound: ChatSessionView | undefined,\n defaultAgent: string | undefined,\n catalog?: AgentCatalogResult,\n boundAgentId?: string,\n choice?: AcpConversationChoice,\n): RouteInfo {\n const agentId = routeAgentId(boundAgentId, bound, agents, defaultAgent)\n if (agentId === undefined) return { efforts: [] }\n const def = agents.find(agent => agent.id === agentId)\n const agentTitle = def?.title ?? bound?.agentTitle ?? catalog?.title ?? agentId\n // The conversation's persisted pick covers the window in which there is no\n // live ACP session yet (nothing has been sent). The live session outranks it\n // — that is what the agent is actually running right now — but without the\n // pick a conversation set up ahead of its first message could not show the\n // model and thought level it is about to use, which is the whole point of\n // choosing them early.\n //\n // A RELEASED row flips the precedence: it only describes what ran when the\n // process was parked, while the pick is what the next resume applies\n // (`applyChatChoice`) — a newer pick must outrank it, or a pre-switch on a\n // parked session looks like a dead control (the chip snaps back to the\n // parked value on every re-read). The parked row still beats nothing, so a\n // conversation that never picked keeps naming what it ran.\n const pick = choice !== undefined && choice.agentId === agentId ? choice : undefined\n const modelId = bound?.released === true\n ? (pick?.modelId ?? bound?.modelId)\n : (bound?.modelId ?? pick?.modelId)\n const effort = bound?.released === true\n ? (pick?.effort ?? bound?.reasoningEffort)\n : (bound?.reasoningEffort ?? pick?.effort)\n const reasoning = catalog?.reasoning\n // The model the AGENT says it is on, for a conversation nobody has picked a\n // model for yet. Without it the chip has no label and does not render at all,\n // so an agent with a perfectly good model axis looked like it had none (user\n // report, 2026-10-18). An agent publishing no `category: 'model'` select\n // genuinely has no model axis — there `currentModel` is absent and nothing is\n // faked. Display-only: `modelId` stays in the adapter-id space, so the picker\n // keeps marking rows by what this conversation actually chose.\n const agentModel = catalog?.currentModel\n if (bound === undefined && pick === undefined && agentModel === undefined) {\n return { agentId, agentTitle, efforts: [] }\n }\n const parsed = splitAcpModelId(modelId ?? '')\n const variant = catalog?.variants.find(entry => entry.id === modelId)\n // A model the conversation picked may be outside `exposedModels` (the pick\n // outlives the visibility filter). Its NAME is still known from the catalog's\n // `currentModel` when the values agree — show that instead of the raw value.\n const currentName = catalog?.currentModel !== undefined && parsed.choice === catalog.currentModel.value\n ? catalog.currentModel.name\n : undefined\n const effectiveEffort = effort ?? reasoning?.defaultEffort\n // The chip names the MODEL, not the agent (user report, 2026-10-18): the agent\n // is already the chip to its right, so \"CodeBuddy Code\" beside \"ACP · CodeBuddy\n // Code\" said nothing about which model was running. A bare `agentId` route\n // means \"the agent's own choice\", which the catalog resolves to `currentModel`.\n const modelLabel = modelId === undefined\n ? agentModel?.name\n : parsed.choice === undefined\n // Bare route: name the model it resolves to. `base.name` already spells out\n // `<agent> · <model>`, so prefer the model half when we have it.\n ? agentModel?.name ?? catalog?.base.name ?? agentTitle\n : variant?.name ?? currentName ?? parsed.choice\n return {\n agentId,\n agentTitle,\n ...(modelId === undefined ? {} : { modelId }),\n ...(modelLabel === undefined ? {} : { modelLabel }),\n efforts: reasoning?.efforts ?? [],\n ...effectiveEffort === undefined ? {} : { effectiveEffort },\n ...effectiveEffort === undefined\n ? {}\n : { effortLabel: reasoning?.efforts.find(level => level.id === effectiveEffort)?.name ?? effectiveEffort },\n }\n}\n\n/** One extra config option as the advanced base presents it. */\nexport interface AdvancedOption {\n configId: string\n name: string\n /** `'boolean'` for an on/off toggle (`value` is `'true'` / `'false'`, no choices). */\n kind?: 'boolean'\n /** The conversation's pick, else the value the agent reports as current. */\n value?: string\n choices: readonly { id: string; name: string; description?: string }[]\n}\n\n/**\n * The agent's extra config options for one conversation, with the\n * conversation's own pick folded over the agent-reported current value.\n *\n * `choice` only applies when it belongs to the catalog's agent: a stale pick\n * from a previous agent is the previous agent's vocabulary and must not label\n * the new one's menu (same rule as `describeRoute`). Options are whatever the\n * agent publishes beyond model / thought level / mode — those have their own\n * controls, and duplicating them here would present two truths for one axis.\n */\nexport function advancedOptions(\n catalog: AgentCatalogResult | undefined,\n choice?: AcpConversationChoice,\n): AdvancedOption[] {\n const extras = catalog?.extras ?? []\n if (catalog === undefined || extras.length === 0) return []\n const picked = choice !== undefined && choice.agentId === catalog.agentId ? choice.extraConfigs : undefined\n return extras.map(option => {\n const raw = picked?.[option.configId] ?? option.currentValue\n // A toggle's catalog value is 'true' / 'false'; a conversation pick stores\n // the real boolean. Both collapse to the string form the menu speaks.\n const value = typeof raw === 'boolean' ? (raw ? 'true' : 'false') : raw\n return {\n configId: option.configId,\n name: option.name,\n ...(option.kind === 'boolean' ? { kind: 'boolean' as const } : {}),\n ...(value === undefined ? {} : { value }),\n choices: option.choices,\n }\n })\n}\n\n/**\n * The agent base chip's label and tooltip (audit findings F5 and F26).\n *\n * The label names an agent ONLY when one actually serves this conversation:\n * `route.agentId` is the binding, the live session or the plugin default, and\n * each of those is a name the request path uses (see `routeAgentId`). With none\n * of them the chip is the \"选择 Agent\" entry point and says so — the old\n * `agents[0]` fallback put a stranger's name on a conversation whose next\n * message would run on the deployment default provider.\n *\n * The tooltip claims only what the menu behind the chip does: switch the agent.\n * The agent's models and thought levels have chips of their own (user decision,\n * 2026-10-17), so the previous wording — \"switch the agent, its model and the\n * thought level\" — promised a menu that holds the roster alone. An EMPTY\n * roster is its own case: the menu cannot switch to anything, it offers the\n * way into NTR settings, so the tooltip says that instead of promising a pick.\n */\nexport function agentBaseChip(\n route: RouteInfo,\n routed: boolean,\n acp: AcpChatInjected['acp'],\n rosterEmpty = false,\n): { label: string; title: string } {\n const label = route.agentTitle === undefined\n ? (routed ? 'NTR' : acp.label('选择 Agent', 'Choose agent'))\n : `NTR · ${route.agentTitle}`\n const title = route.agentId === undefined\n ? rosterEmpty\n ? acp.label(\n '这条会话当前没有 NTR agent;点开可前往 NTR 设置添加',\n 'No NTR agent is serving this conversation — open to set one up in NTR settings',\n )\n : acp.label(\n '这条会话当前没有 NTR agent 服务;点开可选择 agent',\n 'No NTR agent is serving this conversation right now — open to choose one',\n )\n : acp.label(\n '这条会话由外接 NTR agent 服务;点开可切换 agent',\n 'This conversation is served by an external NTR agent — open to switch the agent',\n )\n return { label, title }\n}\n\n/**\n * The composer-edge tag's label and tooltip.\n *\n * This tag is where the agent identity lives once the composer base steps\n * aside (see `agentBaseHidden`), so it names the agent unconditionally — the\n * caller only mounts it when the conversation is locked. It is a nameplate,\n * not a control: the tooltip says the lock is one-way and points at the only\n * way to use another agent (a new conversation).\n */\nexport function agentTagChip(agentTitle: string, acp: AcpChatInjected['acp']): { label: string; title: string } {\n return {\n label: `NTR · ${agentTitle}`,\n title: acp.label(\n `这条会话已锁定在 ${agentTitle};首轮开始后不能切换 agent(上下文属于它)。换 agent 请新建会话。`,\n `This conversation is locked to ${agentTitle}. It cannot switch agents once the first turn has started — the context belongs to it. Start a new conversation to use another agent.`,\n ),\n }\n}\n\n/**\n * The agent tag riding the composer card's top edge\n * (`conversation.input.overlay`).\n *\n * The composer's agent base retires when the conversation's first ACP turn\n * starts, and that turn LOCKS the conversation to its agent (user decision,\n * 2026-10-20): a different agent would have none of the context. This tag is\n * therefore a NAMEPLATE, not a control — it keeps \"who serves this\n * conversation\" on screen and says why it cannot change. It deliberately has\n * no click target and no caret: the only way to use another agent is a new\n * conversation. If the turn delivered nothing, the host rolls the lock back\n * and this tag leaves with it.\n *\n * WHERE IT SITS: the overlay anchor is a zero-height absolute box at the top\n * edge of the composer card, so the tag's wrapper straddles that edge (see\n * `.ntr-acp-tag` in acp-ui.tsx) — half above the card, half inside it, like a\n * small nameplate hung on the box. This is deliberately NOT the session\n * header: the tag belongs to the input it describes, and the header is shared\n * with conversation chrome that is not about the agent.\n *\n * It gates itself: the overlay slot is registered for EVERY conversation, so\n * it must not leak `NTR · <agent>` into ordinary chats, and it may only appear\n * once the base has actually stood down (see `agentTagVisible`).\n */\nexport function AcpComposerAgentTag(props: AcpClusterProps) {\n ensureAcpStyle()\n const { acp, sessionId, useSession } = props\n const ui = useUi(acp)\n const running = useSession(snapshot => snapshot.running)\n useEffect(() => {\n void acp.loadChatSessions(sessionId)\n }, [acp, sessionId, running])\n const bound = boundSession(ui, sessionId)\n const lockedTo = lockedAgentOf(ui.chatLockedAgent[sessionId], bound)\n const reveal = ui.agentLockReveal[sessionId]\n const phase = useAgentLockPhase(sessionId, reveal)\n // Both surfaces start the same clock; whichever effect runs first wins and\n // the other is a no-op (playAgentLockAnim is idempotent per reveal). The\n // lock arrives at the START of the first turn (chat/agent-locked), so the\n // hand-off plays while the turn runs — see agent-lock-anim.ts.\n useEffect(() => {\n if (reveal !== undefined) playAgentLockAnim(sessionId, reveal)\n }, [reveal, sessionId])\n // The tag stays away while the base is still breaking apart: the hand-off is\n // base → debris → tag, never both on screen at once.\n if (phase === 'shatter') return null\n if (!agentTagVisible(ui.acpMode[sessionId], lockedTo) || lockedTo === undefined) return null\n const agentTitle = ui.agents.find(agent => agent.id === lockedTo)?.title ?? bound?.agentTitle ?? lockedTo\n // The halo rides the settled tag only: while the forming animation owns the\n // chip its `animation` shorthand must not be overridden mid-flight.\n const alive = phase !== 'form' && agentTagAlive(bound, lockedTo)\n const halo = agentTagHaloPresentation(ui.tagHalo, alive)\n return (\n <span\n className={`ntr-acp-tag${phase === 'form' ? ' is-forming' : ''}${halo.classes}`}\n style={halo.style}\n >\n <AcpChip {...agentTagChip(agentTitle, acp)}>\n {phase === 'form' && <AcpShatterParts mode=\"in\" />}\n </AcpChip>\n </span>\n )\n}\n\n/** The composer cluster: agent, model and thought level — or nothing at all. */\nexport function AcpSessionControls(props: AcpClusterProps) {\n ensureAcpStyle()\n const { acp, sessionId, useSession } = props\n const ui = useUi(acp)\n const running = useSession(snapshot => snapshot.running)\n const menu = useAcpMenu()\n useEffect(() => {\n void acp.loadChatSessions(sessionId)\n }, [acp, sessionId, running])\n const bound = boundSession(ui, sessionId)\n const agents = ui.agents\n const choice = ui.chatChoice[sessionId]\n // The lock is resolved BEFORE the agent route: a locked conversation belongs\n // to its agent even when no session is live, so it must never fall back to\n // the plugin default (that would put another agent's models and thought\n // levels on its bar).\n const lockedTo = lockedAgentOf(ui.chatLockedAgent[sessionId], bound)\n // The lock hand-off (see agent-lock-anim.ts): the tag slot in the overlay\n // starts the same clock from its own effect; either call arms it once.\n const reveal = ui.agentLockReveal[sessionId]\n const lockPhase = useAgentLockPhase(sessionId, reveal)\n useEffect(() => {\n if (reveal !== undefined) playAgentLockAnim(sessionId, reveal)\n }, [reveal, sessionId])\n const agentId = lockedTo ?? routeAgentId(ui.chatBoundAgent[sessionId], bound, agents, ui.defaultAgent)\n // A deleted locked agent has no surface to show: suppress its chips instead\n // of keeping a dead agent's last-known model menu interactive.\n const agentListed = agentId !== undefined && agents.some(agent => agent.id === agentId)\n const catalog = agentListed ? ui.seatCatalogs[agentId] : undefined\n // Read the catalog ONLY for a conversation that actually points at an agent\n // (`routeAgentId`'s answer, which includes the plugin default). The old gate\n // listed only bound / pick / optimistic binding, so a NEW conversation\n // served by the default agent showed no model/thought chips until the user\n // re-picked the agent by hand. `agent/catalog` is pure memory (it never\n // spawns), so reading the default agent's surface cannot start a process.\n // (The 2026-10-18 spawn report is about the retired probe-on-read path;\n // the host answers this RPC from memory.)\n const served = agentId !== undefined\n useEffect(() => {\n if (served && agentListed && agentId !== undefined) void acp.loadAgentCatalog(agentId)\n }, [acp, served, agentListed, agentId])\n // The picker could already be open when the first turn starts (clicking the\n // send button is inside the composer card, which AcpMenu treats as its own\n // furniture, so it does not close the menu). Once locked, that menu can only\n // offer a switch the host will refuse — close it instead of leaving a live\n // control over a locked conversation. Kept above every early return so the\n // component never changes its hook order between renders.\n useEffect(() => {\n if (lockedTo === undefined || menu === null) return\n if (menu.sessionId === sessionId && menu.kind === 'agent') closeAcpMenu()\n }, [lockedTo, menu?.sessionId, menu?.kind, sessionId])\n const route = describeRoute(\n agents,\n bound,\n // A locked conversation has no default fallback: the lock is the answer.\n lockedTo === undefined ? ui.defaultAgent : undefined,\n catalog,\n lockedTo ?? ui.chatBoundAgent[sessionId],\n choice,\n )\n // An empty roster is the SETUP case, not \"no story\": the base stays and its\n // picker carries the way into NTR settings. Only the window before the host\n // has answered `agents/list` stays blank — `agents: []` there means \"not read\n // yet\", and the setup entry would accuse a configured deployment (see\n // `agentBaseHasData`).\n if (!agentBaseHasData(bound, agents, ui.agentsLoaded)) return null\n // Set up = an agent serves this conversation: a live session, a persisted\n // pick, the host's optimistic binding, OR the plugin default the guard's\n // route rewrite falls back to. The last one is what makes a brand-new\n // conversation show its model / thought chips without a manual re-pick.\n const routed = route.agentId !== undefined\n // The base's retirement and the conversation's one-way agent lock are the\n // same event (see `agentBaseHidden`): the first ACP turn. From then on the\n // base never comes back, and the tag is the only place the agent's name\n // lives.\n //\n // The gate releases this cell when the conversation leaves ACP mode, and it\n // only ever installs the claim for a session whose verdict it has already\n // folded as TRUE — so the occupant requires that same affirmative fact. A\n // claim held for one more evaluation (or a layout with more than one visible\n // session) must never put NTR chips on an ordinary conversation's bar.\n // Placed AFTER every hook: an early return above a hook would change the\n // hook order between renders.\n if (ui.acpMode[sessionId] !== true) return null\n // Keep the base mounted for the shatter half even though the lock already\n // retired it: unmounting on the same commit the lock arrives would skip the\n // transition entirely (the tag waits for the same phase to leave 'shatter').\n const shattering = lockPhase === 'shatter' && lockedTo !== undefined\n const showAgentBase = !agentBaseHidden(lockedTo) || shattering\n const modelLabel = routed ? route.modelLabel : undefined\n const showModel = agentListed && modelLabel !== undefined\n const showThought = agentListed && routed && route.efforts.length > 0\n const extras = advancedOptions(catalog, choice)\n const showAdvanced = agentListed && routed && extras.length > 0\n // Nothing left to show: the cell stays claimed (the claim owns it), but this\n // occupant renders an empty cluster rather than a bare wrapper.\n if (!showAgentBase && !showAdvanced && !showModel && !showThought) return null\n const open = (kind: AcpMenuKind, trigger: HTMLElement): void => { openAcpMenu(sessionId, kind, trigger) }\n\n // Ordering (user decision, 2026-10-21): the AGENT base stays the fixed\n // right-hand anchor BEFORE the first turn — that is where users know it\n // from, and it must not be squeezed out by chips the agent happens to\n // publish. The advanced entry sits immediately left of the model, so the\n // agent's extra options never push the primary axes around. Once the first\n // turn locks the conversation the base retires (2026-10-20) and the\n // model / thought pair inherits the right edge.\n return (\n <span className=\"ntr-acp-cluster\">\n {/* Immediately left of the model: the advanced base collects everything\n the agent publishes that is NOT one of the primary axes. */}\n {showAdvanced && (\n <AcpChip\n label={`${acp.label('高级', 'Advanced')} · ${String(extras.length)}`}\n title={acp.label(\n `agent 的额外配置项:${extras.map(option => option.name).join('、')}`,\n `extra agent config options: ${extras.map(option => option.name).join(', ')}`,\n )}\n active={menu?.sessionId === sessionId && menu.kind === 'advanced'}\n onClick={(trigger) => { open('advanced', trigger) }}\n />\n )}\n {showModel && modelLabel !== undefined && (\n <AcpChip\n label={modelLabel}\n title={acp.label('agent 自己的模型目录', \"the agent's own model directory\")}\n active={menu?.sessionId === sessionId && menu.kind === 'model'}\n onClick={(trigger) => { open('model', trigger) }}\n />\n )}\n {showThought && (\n <AcpChip\n label={`${acp.label('思考', 'think')} · ${route.effortLabel ?? acp.label('默认', 'default')}`}\n title={acp.label('思考等级(agent 的 thought_level)', \"Thought level (the agent's thought_level)\")}\n active={menu?.sessionId === sessionId && menu.kind === 'thought'}\n onClick={(trigger) => { open('thought', trigger) }}\n />\n )}\n {/* Last on purpose while present: the fixed right-hand anchor before the\n lock. CSS gives the last chip `flex: none`, so a long model name or\n an extra chip can only squeeze what is to its left — the agent base\n never disappears. It retires after the first ACP turn (user decision,\n 2026-10-20) and the composer-edge tag replaces it; the retirement\n itself is the shatter played here (see `shattering`). */}\n {showAgentBase && (\n <AcpChip\n {...agentBaseChip(route, routed, acp, agents.length === 0)}\n active={routed}\n className={shattering ? 'ntr-acp-shatter' : undefined}\n onClick={shattering ? undefined : (trigger) => { open('agent', trigger) }}\n >\n {shattering && <AcpShatterParts mode=\"out\" />}\n </AcpChip>\n )}\n </span>\n )\n}\n\n/** Inputs one picker model is built from (kept explicit so the shape stays reviewable). */\nexport interface MenuContext {\n acp: AcpChatInjected['acp']\n agents: readonly AgentView[]\n route: RouteInfo\n /** The bound agent's own model surface (`agent/catalog`), if it has loaded. */\n catalog?: AgentCatalogResult\n /** The conversation's persisted pick (the advanced menu overlays its extras). */\n choice?: AcpConversationChoice\n /** A `agent/probe-models` request for this agent is in flight. */\n probing?: boolean\n error?: string\n}\n\n/**\n * The model picker's explicit refresh row. It is an ACTION, not a model: the\n * pick handler intercepts it and probes the agent instead of recording a pick.\n * The id cannot collide with a real row — agent ids match\n * `[a-z0-9][a-z0-9._-]{0,63}` (no underscores) and variant ids always carry\n * `agentId@value`.\n */\nexport const REFRESH_CATALOG_ITEM = '__refresh-catalog__'\n\n/**\n * The empty roster's setup action. Like the model picker's refresh row it is an\n * ACTION inside the picker, not an agent: agent ids match\n * `[a-z0-9][a-z0-9._-]{0,63}` (no leading underscore), so no real id can\n * collide with it.\n */\nexport const OPEN_SETTINGS_ITEM = '__open-settings__'\n\n/** The agent roster rows: one row per configured ACP agent, current one marked. */\nfunction agentSection(context: MenuContext): AcpMenuSection {\n const { acp, route } = context\n const items: AcpMenuItem[] = context.agents.map(agent => ({\n id: agent.id,\n label: agent.title ?? agent.id,\n detail: agentStateText(acp, agent.state),\n dot: STATE_COLORS[agent.state],\n active: route.agentId === agent.id,\n }))\n if (items.length === 0) {\n // The base stays on an empty roster (see `agentBaseHasData`), so this menu\n // is the setup surface: a status row that says what is missing, then an\n // ACTION row that opens the NTR settings. The action is a row of its own\n // because a disabled status row cannot be the way forward.\n items.push({\n id: 'none',\n label: acp.label('还没有配置 NTR agent', 'No NTR agent configured yet'),\n detail: acp.label('添加并启用一个 agent 后即可开始对话', 'Add and enable an agent to start'),\n disabled: true,\n })\n items.push({\n id: OPEN_SETTINGS_ITEM,\n label: acp.label('打开 NTR 设置', 'Open NTR settings'),\n detail: acp.label('添加或启用 agent', 'Add or enable an agent'),\n })\n }\n return {\n id: 'agent',\n items,\n }\n}\n\n/**\n * The selected agent's OWN models — `undefined` when no agent is known yet.\n *\n * The rows come from `agent/catalog`, not from any DSH model directory: ACP\n * models are not advertised in DSH's catalog at all, and the exposure filter\n * (`exposedModels`) is applied host-side, so what arrives here is exactly what\n * this agent offers in this conversation.\n *\n * ONE ROW PER MODEL, and the checked row names its model (user report,\n * 2026-10-18). The bare `agentId` route (\"let the agent choose\") is NOT a model,\n * so it is folded onto the row of the model it currently resolves to: the\n * previous shape listed `<agent> · 默认` AND `<agent> · Hy3` as two entries for\n * one model, and the checked one was the row that did not say \"Hy3\".\n */\nfunction modelSection(context: MenuContext): AcpMenuSection | undefined {\n const { acp, route, catalog } = context\n if (route.agentId === undefined) return undefined\n // An explicit gesture, not a render side effect: opening the picker stays\n // cache-first (reading must never spawn), and the user asks for a fresh\n // directory by picking this row. The host's `agent/probe-models` may start\n // or reuse that agent's process.\n const refresh: AcpMenuItem = {\n id: REFRESH_CATALOG_ITEM,\n label: context.probing === true\n ? acp.label('正在检测…', 'Detecting…')\n : acp.label('刷新目录', 'Refresh list'),\n detail: acp.label('向 agent 重新读取模型列表', 're-read the model list from the agent'),\n disabled: context.probing === true,\n }\n if (catalog === undefined) {\n // No catalog yet (a fresh conversation, or a host still probing): the\n // current model is the only row we can honestly show — plus the refresh\n // action, which is how a first catalog gets fetched on demand.\n const items: AcpMenuItem[] = route.modelId === undefined\n ? []\n : [{ id: route.modelId, label: route.modelLabel ?? route.modelId, active: true }]\n items.push(refresh)\n return {\n id: 'model',\n title: acp.label(`${route.agentTitle ?? 'agent'} 的模型`, `Models of ${route.agentTitle ?? 'the agent'}`),\n items,\n }\n }\n // Which model is IN FORCE, as a model VALUE (not a route). `route.modelId` is\n // an adapter id: the bare `agentId` means \"the agent's own choice\", which the\n // catalog resolves to `currentModel.value`. A variant is a specific value.\n const parsed = splitAcpModelId(route.modelId ?? '')\n const pickedChoice = parsed.choice\n const current = catalog.currentModel\n const inForce = pickedChoice ?? current?.value\n // What the agent offers, as model VALUES. `exposedModels` is a filter over what\n // is OFFERED, not over what works, so a model in force outside this set still\n // gets a row — otherwise the picker would imply the conversation runs another.\n const offered = new Map<string, AcpMenuItem>()\n for (const variant of catalog.variants) {\n const value = splitAcpModelId(variant.id).choice\n if (value === undefined) continue\n offered.set(value, {\n id: variant.id,\n label: variant.name,\n ...(variant.description === undefined ? {} : { detail: variant.description }),\n active: value === inForce,\n })\n }\n // THE BUG IN THE SCREENSHOT (user report, 2026-10-18): the agent is on `hy3`,\n // `hy3` is also exposed, and the list showed `<agent> · 默认 ✓` above\n // `<agent> · Hy3` — two rows for one model, the checkmark on the one that named\n // no model. So: ONE row per model, and the model in force is marked wherever it\n // appears.\n const rows: AcpMenuItem[] = []\n if (inForce !== undefined && !offered.has(inForce)) {\n // Either the agent is on a model its own exposure filter hides, or the\n // conversation picked one that has dropped out of the catalog. Named either\n // way — `route.modelLabel` already resolves the stale-variant case — so the\n // row's detail must name BOTH causes: blaming only the Settings → NTR\n // checkbox sent people looking for a box they had already ticked (audit\n // finding F25). A dropped model is NOT silently replaced: the host refuses\n // the send (`model_unavailable`), so the detail must not promise a fallback.\n rows.push({\n id: buildAcpModelId(route.agentId, inForce),\n label: `${inForce === current?.value ? current?.name ?? inForce : route.modelLabel ?? inForce} · ${acp.label('当前', 'current')}`,\n detail: acp.label(\n '不在此列表出的模型里:可能在「设置 → NTR」里取消了勾选,也可能已从 agent 的目录掉出;若 agent 已不再提供,发送会被拒绝,请改选或用「刷新目录」重新读取。',\n \"Not among the models listed here: it may be unchecked in Settings → NTR, or it may have dropped out of the agent's catalog. If the agent no longer offers it, sending is refused — pick another model or use Refresh list to re-read the directory.\",\n ),\n active: true,\n })\n }\n if (current === undefined) {\n // The bare `agentId` route could not be resolved to a model — the agent has\n // never been probed (or publishes no model selector). The bare row is then\n // the only honest description of what this conversation runs, and leaving the\n // picker empty would read as broken rather than as unknown.\n //\n // When the route DOES resolve to a model this row is deliberately absent: it\n // would be a second row for that same model, which is precisely the defect\n // being fixed.\n rows.push({\n id: catalog.base.id,\n label: `${catalog.base.name} · ${acp.label('默认', 'default')}`,\n ...(catalog.base.description === undefined ? {} : { detail: catalog.base.description }),\n active: inForce === undefined,\n })\n }\n rows.push(...offered.values())\n rows.push(refresh)\n return {\n id: 'model',\n title: acp.label(`${route.agentTitle ?? 'agent'} 的模型`, `Models of ${route.agentTitle ?? 'the agent'}`),\n note: catalog.variants.length === 0\n ? acp.label(\n 'agent 自己上报的模型目录;这里只列出它当前用的模型,其余可在「设置 → NTR」里勾选可见。',\n \"The agent's own model directory; only the model it is on is offered here — enable more in Settings → NTR.\",\n )\n : acp.label(\n 'agent 自己上报的模型目录;勾选的那条就是这条会话正在用的模型。',\n \"The agent's own model directory; the checked row is the model this conversation is running.\",\n ),\n items: rows,\n }\n}\n\n/** The agent's `thought_level` levels (its own vocabulary). */\nfunction thoughtSection(context: MenuContext): AcpMenuSection {\n const { acp, route } = context\n const items: AcpMenuItem[] = [{\n // The id keeps its historical name on purpose: it is an internal sentinel\n // the pick handler compares against, and no user ever reads it. The LABEL is\n // the user-visible claim, and \"provider default\" was false (audit finding\n // F43): this axis is the agent's own `thought_level`, with no DSH provider\n // anywhere on it (`listModels('acp')` answers an empty catalog). Clearing\n // the pick leaves the agent's own `reasoning.defaultEffort` in force, which\n // is what `effectiveEffort` reports.\n id: 'provider-default',\n label: acp.label('跟随 agent 默认', 'Agent default'),\n active: route.effectiveEffort === undefined,\n }]\n for (const level of route.efforts) {\n items.push({\n id: level.id,\n label: level.name,\n ...level.description === undefined ? {} : { detail: level.description },\n active: level.id === route.effectiveEffort,\n })\n } return {\n id: 'thought',\n title: acp.label('思考等级', 'Thought level'),\n note: acp.label(\n '由 agent 的 thought_level 配置项驱动,下一次请求生效。',\n \"Driven by the agent's own thought_level option; applies to the next request.\",\n ),\n items,\n }\n}\n\n/**\n * The advanced base's sections: one per extra config option.\n *\n * Section ids carry a `config:` prefix because `AcpMenu.onPick` hands the\n * section id back and the other pickers already own `agent` / `model` /\n * `thought` — an agent could legitimately name one of its options `model`,\n * and the pick would then be read as the primary model axis.\n */\nfunction advancedSections(context: MenuContext): AcpMenuSection[] {\n return advancedOptions(context.catalog, context.choice).map(option => {\n if (option.kind === 'boolean') {\n // A toggle is ONE checkbox row, not an On/Off pair: the row's check is\n // the current value and the pick sends the OPPOSITE, which `onPick`\n // forwards verbatim (the host normalizes 'true' / 'false' to a boolean).\n const checked = option.value === 'true'\n return {\n id: `config:${option.configId}`,\n items: [{ id: checked ? 'false' : 'true', label: option.name, active: checked, checkbox: true }],\n }\n }\n return {\n id: `config:${option.configId}`,\n title: option.name,\n items: option.choices.map(choice => ({\n id: choice.id,\n label: choice.name,\n ...(choice.description === undefined ? {} : { detail: choice.description }),\n active: choice.id === option.value,\n })),\n }\n })\n}\n\n/**\n * REMOVED on purpose (2026-10-17): the agent's own `session/set_mode` list used\n * to ride inside the agent base's menu. It has its own control\n * (`AcpComposerMode`, the native-mode select in `conversation.input.left`), so\n * duplicating it in the base made two entries for one setting and put four\n * unrelated axes behind a control whose only job is \"who serves this\n * conversation\". Do not reintroduce it here — the editor belongs to\n * composer-mode.tsx.\n */\n\n/** Build the picker model for one kind. */\nexport function menuFor(kind: AcpMenuKind, context: MenuContext): {\n title: string\n note?: string\n sections: AcpMenuSection[]\n error?: string\n loading?: boolean\n} {\n const { acp, route } = context\n\n if (kind === 'agent') {\n // The agent base is ONLY the entry point for starting/replacing the agent\n // (user decision, 2026-10-17). Everything else the agent publishes — its\n // models, its thought levels, its own modes — has a seat of its own and is\n // reached from there, so stacking them in here turned one gesture (\"who\n // serves this conversation\") into a four-section dump and made the agent\n // list harder to scan. One control, one job.\n //\n // ONE heading, no note (user decision, 2026-10-18): the menu header says it\n // and the section adds no second title. The previous caption spelled out\n // where the other axes live and how switching applies — true, but it read\n // as a manual for a list that is self-explanatory, and it pushed the rows\n // down for no information the user needed at the moment of picking.\n return {\n title: acp.label('选择你的 Agent', 'Choose your agent'),\n sections: [{ ...agentSection(context), title: undefined, note: undefined }],\n }\n }\n\n if (kind === 'model') {\n const section = modelSection(context)\n if (section === undefined) return { title: acp.label('模型', 'Models'), sections: [] }\n // The single-axis picker keeps the shape it had before: one title + note on\n // the menu header, rows under a header-less section.\n return { title: section.title ?? acp.label('模型', 'Models'), note: section.note, sections: [{ ...section, title: undefined, note: undefined }] }\n }\n\n if (kind === 'thought') {\n const section = thoughtSection(context)\n return { title: section.title ?? acp.label('思考等级', 'Thought level'), note: section.note, sections: [{ ...section, title: undefined, note: undefined }] }\n }\n\n if (kind === 'advanced') {\n return {\n title: acp.label('高级选项', 'Advanced options'),\n note: acp.label(\n 'agent 自己发布的额外配置项;选中只作用于这条会话,下一条消息生效。',\n 'Extra config options published by the agent; a pick belongs to this conversation and applies from the next message.',\n ),\n sections: advancedSections(context),\n }\n }\n\n return { title: acp.label('NTR', 'NTR'), sections: [] }\n}\n\n/**\n * The picker itself, living in the composer's overlay seat. It renders nothing\n * unless a chip of THIS session opened it, so a stale menu can never leak\n * across a session switch.\n */\nexport function AcpSessionMenu(props: AcpClusterProps) {\n ensureAcpStyle()\n const { acp, sessionId } = props\n const ui = useUi(acp)\n const menu = useAcpMenu()\n const [busy, setBusy] = useState(false)\n const bound = boundSession(ui, sessionId)\n const agents = ui.agents\n const choice = ui.chatChoice[sessionId]\n // Same lock rule as the cluster: a locked conversation belongs to its agent\n // even when the host's bound-agent mirror or the plugin default says\n // otherwise — the menu must list ITS extras, not a stranger's.\n const lockedTo = lockedAgentOf(ui.chatLockedAgent[sessionId], bound)\n const agentId = lockedTo ?? routeAgentId(ui.chatBoundAgent[sessionId], bound, agents, ui.defaultAgent)\n const catalog = agentId === undefined ? undefined : ui.seatCatalogs[agentId]\n // Same rule as the cluster: a menu that is not open, or a conversation that\n // points at no agent, must not read anybody's catalog. The default agent is\n // included: the guard's route rewrite uses it, and the read is memory-only.\n const served = agentId !== undefined\n useEffect(() => {\n if (menu !== null && served && agentId !== undefined) void acp.loadAgentCatalog(agentId)\n }, [acp, menu, served, agentId])\n\n if (menu === null || menu.sessionId !== sessionId) return null\n // A stale picker must not outlive the conversation's ACP mode (the gate\n // releases the view/tab, but the overlay is unconditionally registered).\n if (ui.acpMode[sessionId] !== true) return null\n const route = describeRoute(\n agents,\n bound,\n lockedTo === undefined ? ui.defaultAgent : undefined,\n catalog,\n lockedTo ?? ui.chatBoundAgent[sessionId],\n choice,\n )\n // A menu-model bug must never retire the whole picker: a render throw inside\n // this slot entry makes the error boundary ABDICATE it for the rest of the\n // session (every later click silently opens nothing). Show the failure inside\n // the menu instead of dying.\n let model: ReturnType<typeof menuFor>\n try {\n const probing = menu.kind === 'model' && agentId !== undefined && ui.probingModels[agentId] === true\n const menuError = menu.error ?? (menu.kind === 'model' && agentId !== undefined ? ui.probeErrors[agentId] : undefined)\n model = menuFor(menu.kind, {\n acp,\n agents,\n route,\n ...catalog === undefined ? {} : { catalog },\n ...choice === undefined ? {} : { choice },\n ...probing ? { probing: true } : {},\n ...menuError === undefined ? {} : { error: menuError },\n })\n } catch (error) {\n model = {\n title: acp.label('NTR', 'NTR'),\n sections: [],\n error: error instanceof Error ? error.message : String(error),\n }\n }\n\n const pick = async (sectionId: string, itemId: string): Promise<void> => {\n // Everything here writes into the PLUGIN's own binding for this chat\n // session — never a DSH model route (see the module header).\n try {\n if (sectionId === 'model' && itemId === REFRESH_CATALOG_ITEM) {\n // The one explicit refresh gesture: an `agent/probe-models` request.\n // Opening the menu never probes; this row does. Keep the picker open so\n // the fresh list (or the probe error) lands where the user asked.\n if (agentId !== undefined) await acp.probeModels(agentId)\n return\n }\n if (sectionId === 'agent' && itemId === OPEN_SETTINGS_ITEM) {\n // The empty roster's way out (see `agentSection`): there is no agent to\n // bind, so this row opens the NTR settings surface instead. Close the\n // picker first — the dialog is a frame overlay and the menu must not\n // linger behind it.\n closeAcpMenu()\n openAcpSettingsDialog()\n return\n }\n if (sectionId === 'agent') {\n await acp.bindChatAgent(sessionId, itemId)\n } else if (sectionId === 'model') {\n await acp.setChatSessionModel(sessionId, itemId, agentId)\n } else if (sectionId === 'thought') {\n await acp.setChatSessionEffort(sessionId, itemId === 'provider-default' ? undefined : itemId, agentId)\n } else if (sectionId.startsWith('config:')) {\n // One section per extra option; the id after the prefix names the\n // agent's own `configId` (see advancedSections).\n await acp.setChatSessionConfig(sessionId, sectionId.slice('config:'.length), itemId, agentId)\n } else {\n // `agent-mode` used to be handled here; the agent's mode list now lives\n // only in the composer-mode control (see the removed-section note).\n return\n }\n await acp.loadChatSessions(sessionId)\n closeAcpMenu()\n } catch (error) {\n setAcpMenuError(error instanceof Error ? error.message : String(error))\n }\n }\n\n return (\n <AcpMenu\n model={model}\n anchor={menu.trigger}\n onPick={(itemId, sectionId) => {\n if (busy) return\n setAcpMenuError(undefined)\n setBusy(true)\n void pick(sectionId, itemId).finally(() => { setBusy(false) })\n }}\n onClose={() => { closeAcpMenu() }}\n />\n )\n}\n","/**\n * The conversation's ACP view tab: the agent's REAL stream, as the agent\n * produced it.\n *\n * WHY A TAB AS WELL AS THE CHAT: the native chat plane renders what the LLM\n * adapter emits, and the adapter's chunk vocabulary is text + reasoning — so\n * the agent's prose and thinking ride the native plane, while a fuller card (a\n * tool call with its arguments, a plan, a unified diff) has no chunk to ride\n * there. This view renders those as themselves.\n *\n * It is a VIEW, not a second client: it never prompts, never cancels, never\n * spawns. Everything it shows is host state.\n */\n\nimport type { ReactNode } from 'react'\nimport { memo, useEffect, useRef, useState } from 'react'\n// Type-only: pulls the ui-conversation SlotMap merge into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { ensureAcpStyle, useUi } from './acp-ui.tsx'\nimport type { AcpChatInjected } from './acp-ui.tsx'\nimport type { Block, ChatTranscript } from './store.ts'\nimport type { AgentView, ChatSessionView, SessionView } from '../protocol.ts'\n\n/** Props of the ACP view entry (injected face + the frame's session id). */\nexport interface AcpViewProps extends AcpChatInjected {\n sessionId: string\n}\n\n/**\n * The store's bilingual label function (`AcpStore.label`). Exported because the\n * empty-state copy builder takes one and the bundle emits declarations.\n */\nexport type Translate = (zh: string, en: string) => string\n\n/** Glyph per ACP tool kind, so a call's nature reads before its title does. */\nconst TOOL_GLYPH: Record<string, string> = {\n read: '📖',\n edit: '✏️',\n delete: '🗑️',\n move: '📦',\n search: '🔍',\n execute: '⌨️',\n think: '🧠',\n fetch: '🌐',\n switch_mode: '⇄',\n other: '🛠️',\n}\n\n/** Tone class per ACP tool status. */\nconst STATUS_TONE: Record<string, string> = {\n pending: 'idle',\n in_progress: 'busy',\n completed: 'done',\n failed: 'fail',\n}\n\n/** Cap on the rendered diff matrix: beyond it, show the edit as remove-all/add-all. */\nconst DIFF_LCS_LIMIT = 300\n/** Tool output lines shown before the \"show all\" toggle appears. */\nconst TOOL_LINES_CLIP = 12\n\n/** One line of a rendered unified diff. */\ninterface DiffRow {\n sign: '+' | '-' | ' '\n text: string\n}\n\n/** Split a text payload into lines, tolerating an empty / null side. */\nfunction linesOf(text: string | null): string[] {\n if (text === null || text === '') return []\n return text.endsWith('\\n') ? text.slice(0, -1).split('\\n') : text.split('\\n')\n}\n\n/**\n * Line diff with a common prefix/suffix trim plus an LCS over the remaining\n * middle — the standard shape that keeps an edit reviewable without pulling a\n * diff library into a browser bundle.\n */\nexport function diffRows(oldText: string | null, newText: string): DiffRow[] {\n const oldLines = linesOf(oldText)\n const newLines = linesOf(newText)\n const rows: DiffRow[] = []\n let start = 0\n while (start < oldLines.length && start < newLines.length && oldLines[start] === newLines[start]) start++\n let tail = 0\n while (\n tail < oldLines.length - start\n && tail < newLines.length - start\n && oldLines[oldLines.length - 1 - tail] === newLines[newLines.length - 1 - tail]\n ) tail++\n for (let i = 0; i < start; i++) rows.push({ sign: ' ', text: oldLines[i] as string })\n const oldMiddle = oldLines.slice(start, oldLines.length - tail)\n const newMiddle = newLines.slice(start, newLines.length - tail)\n if (oldMiddle.length > DIFF_LCS_LIMIT || newMiddle.length > DIFF_LCS_LIMIT) {\n for (const text of oldMiddle) rows.push({ sign: '-', text })\n for (const text of newMiddle) rows.push({ sign: '+', text })\n } else {\n rows.push(...lcsRows(oldMiddle, newMiddle))\n }\n for (let i = tail; i > 0; i--) rows.push({ sign: ' ', text: oldLines[oldLines.length - i] as string })\n return rows\n}\n\n/** LCS line alignment of two short blocks (DP table, bounded by the caller). */\nfunction lcsRows(oldLines: readonly string[], newLines: readonly string[]): DiffRow[] {\n const rows = oldLines.length\n const cols = newLines.length\n const table: number[][] = Array.from({ length: rows + 1 }, () => new Array<number>(cols + 1).fill(0))\n for (let i = rows - 1; i >= 0; i--) {\n for (let j = cols - 1; j >= 0; j--) {\n const row = table[i] as number[]\n const below = table[i + 1] as number[]\n row[j] = oldLines[i] === newLines[j]\n ? (below[j + 1] as number) + 1\n : Math.max(below[j] as number, row[j + 1] as number)\n }\n }\n const out: DiffRow[] = []\n let i = 0\n let j = 0\n while (i < rows && j < cols) {\n if (oldLines[i] === newLines[j]) {\n out.push({ sign: ' ', text: oldLines[i] as string })\n i++\n j++\n } else if (((table[i + 1] as number[])[j] as number) >= ((table[i] as number[])[j + 1] as number)) {\n out.push({ sign: '-', text: oldLines[i] as string })\n i++\n } else {\n out.push({ sign: '+', text: newLines[j] as string })\n j++\n }\n }\n while (i < rows) {\n out.push({ sign: '-', text: oldLines[i] as string })\n i++\n }\n while (j < cols) {\n out.push({ sign: '+', text: newLines[j] as string })\n j++\n }\n return out\n}\n\n/** Inline `code` and **bold** spans; everything else stays literal text. */\nfunction inlineNodes(text: string): ReactNode[] {\n const out: ReactNode[] = []\n const pattern = /(`[^`\\n]+`)|(\\*\\*[^*\\n]+\\*\\*)/g\n let last = 0\n let match: RegExpExecArray | null\n let key = 0\n while ((match = pattern.exec(text)) !== null) {\n if (match.index > last) out.push(text.slice(last, match.index))\n const token = match[0]\n key++\n if (token.startsWith('`')) out.push(<code key={key} className=\"ntr-acp-inline-code\">{token.slice(1, -1)}</code>)\n else out.push(<strong key={key}>{token.slice(2, -2)}</strong>)\n last = match.index + token.length\n }\n if (last < text.length) out.push(text.slice(last))\n return out\n}\n\n/** Fenced-code-aware text body: agent prose without a markdown dependency. */\nfunction RichText({ text }: { text: string }) {\n const parts = text.split('```')\n return (\n <>\n {parts.map((part, index) => (index % 2 === 1\n ? <pre key={index} className=\"ntr-acp-code\">{part.replace(/^[a-zA-Z0-9_-]*\\n/, '')}</pre>\n : <span key={index}>{inlineNodes(part)}</span>))}\n </>\n )\n}\n\n/**\n * Whether one structured edit is a NEW FILE.\n *\n * Decided by the old side, never by the removal count: an existing file that\n * only gained lines also diffs to zero removals, and labelling that \"new file\"\n * hid a real edit behind a wrong badge (the audit's F8). The only old sides\n * that mean \"nothing was there\" are an absent one and one byte-identical to\n * the new content — the degenerate case where there is no edit at all.\n */\nexport function isNewFileDiff(oldText: string | null, newText: string): boolean {\n return oldText === null || oldText === '' || oldText === newText\n}\n\n/** One structured edit, rendered as a unified diff. */\nfunction DiffView({ diff, t }: { diff: { path: string; oldText: string | null; newText: string }; t: Translate }) {\n const rows = diffRows(diff.oldText, diff.newText)\n const added = rows.filter(row => row.sign === '+').length\n const removed = rows.filter(row => row.sign === '-').length\n const isNewFile = isNewFileDiff(diff.oldText, diff.newText)\n return (\n <div className=\"ntr-acp-diff\">\n <div className=\"ntr-acp-diff-head\">\n <span className=\"ntr-acp-diff-path\">{diff.path}</span>\n <span className=\"ntr-acp-diff-stats\">\n <span className=\"is-add\">+{added}</span> <span className=\"is-del\">-{removed}</span>\n </span>\n </div>\n <div className=\"ntr-acp-diff-body\">\n {rows.map((row, index) => (\n <div key={index} className={`ntr-acp-diff-row is-${row.sign === '+' ? 'add' : row.sign === '-' ? 'del' : 'ctx'}`}>\n <span className=\"ntr-acp-diff-sign\">{row.sign}</span>\n <span className=\"ntr-acp-diff-text\">{row.text}</span>\n </div>\n ))}\n </div>\n {isNewFile && <div className=\"ntr-acp-diff-note\">{t('新增文件', 'new file')}</div>}\n </div>\n )\n}\n\n/** One tool call: header, arguments, text output, unknown payloads, and any structured edits it carried. */\nfunction ToolCard({ block, t }: { block: Extract<Block, { kind: 'tool' }>; t: Translate }) {\n const [expanded, setExpanded] = useState(false)\n const [rawOpen, setRawOpen] = useState(false)\n const glyph = TOOL_GLYPH[block.toolKind ?? 'other'] ?? TOOL_GLYPH.other\n const tone = STATUS_TONE[block.status] ?? 'idle'\n const clipped = !expanded && block.lines.length > TOOL_LINES_CLIP\n const shown = clipped ? block.lines.slice(0, TOOL_LINES_CLIP) : block.lines\n return (\n <div className={`ntr-acp-tool is-${tone}`}>\n <div className=\"ntr-acp-tool-head\">\n <span aria-hidden>{glyph}</span>\n <span className=\"ntr-acp-tool-title\">{block.title === '' ? block.toolCallId : block.title}</span>\n <span className={`ntr-acp-badge is-${tone}`}>{block.status}</span>\n </div>\n {/*\n The arguments the agent sent. Rendered as-is: `rawInput` may be a JSON\n object the agent serialized, a plain command line, or nothing at all —\n reformatting it would be this view guessing at another agent's schema.\n */}\n {block.input !== undefined && block.input !== '' && (\n <pre className=\"ntr-acp-tool-body ntr-acp-tool-input\">{block.input}</pre>\n )}\n {block.details !== undefined && block.details.length > 0 && (\n <pre className=\"ntr-acp-tool-body ntr-acp-tool-meta\">{block.details.join('\\n')}</pre>\n )}\n {shown.length > 0 && (\n <pre className=\"ntr-acp-tool-body\">{`${shown.join('\\n')}${clipped ? '\\n…' : ''}`}</pre>\n )}\n {block.extras !== undefined && block.extras.length > 0 && (\n <pre className=\"ntr-acp-tool-body\">{block.extras.join('\\n')}</pre>\n )}\n {block.diffs?.map((diff, index) => <DiffView key={`${diff.path}#${String(index)}`} diff={diff} t={t} />)}\n {block.lines.length > TOOL_LINES_CLIP && (\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { setExpanded(!expanded) }}>\n {expanded\n ? t('收起', 'collapse')\n : t(`显示全部 ${block.lines.length} 行`, `show all ${block.lines.length} lines`)}\n </button>\n )}\n {block.raw !== undefined && (\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { setRawOpen(!rawOpen) }}>\n {rawOpen ? t('收起原始数据', 'hide raw payload') : t('原始数据', 'raw payload')}\n </button>\n )}\n {rawOpen && block.raw !== undefined && <pre className=\"ntr-acp-tool-body\">{block.raw}</pre>}\n </div>\n )\n}\n\n/** One notification the view has no card of its own for: its readable fold, plus the untouched payload. */\nfunction EventCard({ block, t }: { block: Extract<Block, { kind: 'event' }>; t: Translate }) {\n const [rawOpen, setRawOpen] = useState(false)\n return (\n <div className=\"ntr-acp-tool is-done\">\n <div className=\"ntr-acp-tool-head\">\n <span aria-hidden>ℹ️</span>\n <span className=\"ntr-acp-tool-title\">{block.event}</span>\n </div>\n {block.text !== '' && <pre className=\"ntr-acp-tool-body\">{block.text}</pre>}\n {block.raw !== undefined && (\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { setRawOpen(!rawOpen) }}>\n {rawOpen ? t('收起原始数据', 'hide raw payload') : t('原始数据', 'raw payload')}\n </button>\n )}\n {rawOpen && block.raw !== undefined && <pre className=\"ntr-acp-tool-body\">{block.raw}</pre>}\n </div>\n )\n}\n\n/** One plan snapshot (the agent's own todo list). */\nfunction PlanCard({ entries, t }: { entries: Extract<Block, { kind: 'plan' }>['entries']; t: Translate }) {\n const done = entries.filter(entry => entry.status === 'completed').length\n return (\n <div className=\"ntr-acp-plan\">\n <div className=\"ntr-acp-plan-head\">{t(`计划 ${done}/${entries.length}`, `Plan ${done}/${entries.length}`)}</div>\n {entries.map((entry, index) => (\n <div key={index} className={`ntr-acp-plan-row is-${entry.status}`}>\n <span className=\"ntr-acp-plan-glyph\" aria-hidden>{entry.status === 'completed' ? '✓' : entry.status === 'in_progress' ? '▸' : '○'}</span>\n <span>{entry.content}</span>\n {entry.priority !== undefined && <span className=\"ntr-acp-plan-prio\">{entry.priority}</span>}\n </div>\n ))}\n </div>\n )\n}\n\n/**\n * One stream block, dispatched by kind. Memoized on its props — `block`, the\n * store's instance-bound `label`, and `localeRev` (it moves on a language\n * switch, so translated copy re-renders) — so a folded batch re-renders only\n * the rows whose block object changed (`foldUpdate` replaces blocks immutably)\n * instead of the whole transcript on every store notification. `key` stays the\n * index on purpose: blocks fold append-only, and identity is the block object.\n */\nconst BlockView = memo(function BlockView({ block, label }: { block: Block; label: Translate; localeRev: number }) {\n const t = label\n switch (block.kind) {\n case 'user':\n return <div className=\"ntr-acp-bubble is-user\"><RichText text={block.text} /></div>\n case 'agent':\n return <div className=\"ntr-acp-bubble is-agent\"><RichText text={block.text} /></div>\n case 'thought':\n return (\n <details className=\"ntr-acp-thought\">\n <summary>{t('思考过程', 'reasoning')}</summary>\n <div className=\"ntr-acp-thought-body\"><RichText text={block.text} /></div>\n </details>\n )\n case 'tool':\n return <ToolCard block={block} t={t} />\n case 'plan':\n return <PlanCard entries={block.entries} t={t} />\n case 'event':\n return <EventCard block={block} t={t} />\n default:\n return <div className={`ntr-acp-notice is-${block.tone}`}>{block.text}</div>\n }\n})\n\n/** The bound ACP session for this chat, preferring the live one. */\nfunction boundSession(list: readonly ChatSessionView[]): ChatSessionView | undefined {\n return list.find(session => !session.released) ?? list[0]\n}\n\n/**\n * Whether an empty stream means \"history exists but is not shown yet\": the\n * record was restored from disk after a host restart (the agent's replay has\n * not landed), or a resume cleared the buffer ahead of the replay. The view\n * then promises the replay instead of claiming the conversation never\n * happened — \"还没有消息\" to someone whose chat plane still shows yesterday's\n * turns is exactly the kind of contradiction this file's copy rules exist to\n * prevent. In-memory released sessions keep their buffer, so this only fires\n * when the buffer really is absent.\n */\nexport function historyAwaitsReplay(session: Pick<SessionView, 'released' | 'hasHistory'>): boolean {\n return session.released === true && session.hasHistory === true\n}\n\n/**\n * Everything the view can read that says \"this conversation is set up on an\n * agent\": a live or parked ACP session, the host's binding (`session/chat-list`'s\n * `boundAgentId`, which outlives a closed session), and the persisted pick.\n *\n * The transcript is deliberately NOT one of these sources: it is a record of\n * turns, and it is dropped when a session is destroyed, so reading it alone\n * answered \"has an agent ever served this conversation\" with \"no\" for\n * conversations that had just run one.\n */\nexport function boundAgentOf(\n session: ChatSessionView | undefined,\n hostBoundAgentId: string | undefined,\n pickedAgentId: string | undefined,\n agents: readonly AgentView[],\n): { id: string; title: string } | undefined {\n const id = session?.agentId ?? hostBoundAgentId ?? pickedAgentId\n if (id === undefined) return undefined\n return { id, title: session?.agentTitle ?? agents.find(agent => agent.id === id)?.title ?? id }\n}\n\n/** What the view knows about this conversation when it has no transcript. */\nexport interface AcpEmptyFacts {\n /** The agent this conversation is set up on, when there is one. */\n bound?: { id: string; title: string }\n /** Whether any agent is configured at all. */\n hasAgents: boolean\n /**\n * This conversation's NTR-mode verdict, as the seat gate reads it\n * (`UiState.acpMode`). `undefined` while the host has not answered: the view\n * must not claim either way.\n */\n acpMode?: boolean\n}\n\n/** The three lines of the empty state, already localized. */\nexport interface AcpEmptyCopy {\n title: string\n body: string\n /** The \"what to do next\" line; absent when nothing honest can be said. */\n hint?: string\n}\n\n/**\n * The empty state's copy, decided from the facts the view can actually read.\n *\n * A pure function on purpose: the bundle has no DOM test, and this decision is\n * where two real defects lived. `chatTranscriptOf` answering `undefined` covers\n * TWO different states — \"this conversation has never been served by an NTR\n * agent\" and \"its ACP session record is gone\" (a closed session is destroyed by\n * `closeSession`/`forgetDshSession`, a stopped agent drops its bridge) while the\n * binding and the composer chip still name the agent. Claiming the first state\n * in both cases made the view contradict the chip sitting one row above it.\n *\n * The hint has its own defect: it pointed at the composer's NTR base, which only\n * the ACP-mode gate installs (`syncAcpMode` in src/client/index.tsx) — in an\n * ordinary conversation that cell still holds the host's own model selector, so\n * the hint sent the user to a control that is not there.\n */\nexport function acpEmptyCopy(facts: AcpEmptyFacts, t: Translate): AcpEmptyCopy {\n const bound = facts.bound\n if (bound !== undefined) {\n return {\n title: t('这个会话当前没有可显示的 ACP 会话', 'This conversation has no ACP session to show right now'),\n body: t(\n '给它发一条消息,它就会开始服务这个会话:原生聊天里会出现它的文字回复与思考过程,而它的工具调用、计划和 diff 会完整地出现在这个视图里。',\n 'Send it a message and it starts serving this conversation: the native chat shows its prose and reasoning, while its tool calls, plans and diffs appear here in full.',\n ),\n hint: `${t('绑定的是', 'bound to')} · ${bound.title}`,\n }\n }\n const copy: AcpEmptyCopy = {\n title: t('这个会话还没有 NTR agent 参与', 'No NTR agent has run in this conversation yet'),\n body: t(\n '选中一个 NTR agent 后,本会话的对话就由它服务:原生聊天里会出现它的文字回复与思考过程,而它的工具调用、计划和 diff 会完整地出现在这个视图里。',\n \"Pick a NTR agent and it serves this conversation: the native chat shows its prose and reasoning, while its tool calls, plans and diffs appear here in full.\",\n ),\n }\n if (!facts.hasAgents) {\n return { ...copy, hint: `${t('还没有配置 agent', 'No agent configured')} · ${t('设置 → NTR', 'Settings → NTR')}` }\n }\n // The base is only on screen in NTR mode; naming it anywhere else points at\n // the host's model selector. A host that has not answered yet gets no line\n // rather than a guess (the seat diagnostics below still say what they saw).\n if (facts.acpMode === true) {\n return { ...copy, hint: t('用输入框右侧的 NTR 底座选择一个 agent', 'pick one from the NTR base in the composer') }\n }\n if (facts.acpMode === false) {\n return {\n ...copy,\n hint: `${t('这个会话不在 NTR 模式', 'This conversation is not in NTR mode')} · ${t('新建会话时可以选择 NTR 模式', 'NTR is chosen when a conversation starts')}`,\n }\n }\n return copy\n}\n\n/**\n * The ACP view tab body: who serves this conversation and how it is doing,\n * then the agent's own stream.\n */\nexport function AcpConversationView(props: AcpViewProps) {\n ensureAcpStyle()\n const { acp, sessionId } = props\n const t: Translate = (zh, en) => acp.label(zh, en)\n const ui = useUi(acp)\n const bound = ui.chatSessionsFor === sessionId ? boundSession(ui.chatSessions) : undefined\n const transcript: ChatTranscript | undefined = acp.chatTranscriptOf(sessionId)\n // Who this conversation is set up on — readable with no transcript at all,\n // which is the whole point: the transcript is a record of turns, the binding\n // is the conversation's own state.\n const boundAgent = boundAgentOf(bound, ui.chatBoundAgent[sessionId], ui.chatChoice[sessionId]?.agentId, ui.agents)\n const streamRef = useRef<HTMLDivElement>(null)\n const pinned = useRef(true)\n\n // Observe this chat session's ACP transcripts whenever the binding could\n // have changed (first turn, agent switch, crash → resume).\n useEffect(() => {\n void acp.loadChatSessions(sessionId)\n void acp.loadChatTranscript(sessionId)\n }, [acp, sessionId, bound?.sessionId, bound?.released])\n\n const blockCount = transcript?.blocks.length ?? 0\n const tail = transcript?.blocks[blockCount - 1]\n useEffect(() => {\n const node = streamRef.current\n if (node === null || !pinned.current) return\n node.scrollTop = node.scrollHeight\n }, [blockCount, tail])\n\n // The gate installs the NTR tab only for a conversation it has confirmed as\n // ACP; a stale release must not paint NTR content into an ordinary one for a\n // frame, so anything but the affirmative verdict renders nothing.\n if (ui.acpMode[sessionId] !== true) return null\n\n if (transcript === undefined) {\n // The guard above makes this a confirmed ACP conversation; the copy\n // builder keeps its \"unknown\" branch for callers that only read facts\n // (`docs` and tests), not for this rendered surface.\n const empty = acpEmptyCopy({\n bound: boundAgent,\n hasAgents: ui.agents.length > 0,\n acpMode: ui.acpMode[sessionId],\n }, t)\n return (\n <div className=\"ntr-acp-view\">\n <div className=\"ntr-acp-empty\">\n <div className=\"ntr-acp-empty-title\">{empty.title}</div>\n <p className=\"ntr-acp-empty-body\">{empty.body}</p>\n {empty.hint !== undefined && <div className=\"ntr-acp-empty-hint\">{empty.hint}</div>}\n {/*\n The model-seat gate's last verdict. When the ACP base does not take\n over the composer's model seat — the native model selector is still\n sitting there — the four possible reasons are otherwise\n indistinguishable, because they all end with the same visible state.\n Publishing the reason here turns \"the base still is not replaced\"\n into something readable.\n */}\n {ui.seatDiag !== 'idle' && ui.seatDiag !== 'gate disposed' && (\n <div className=\"ntr-acp-empty-hint ntr-acp-seat-diag\">\n {t('模型座位', 'model seat')} · {ui.seatDiag}\n </div>\n )}\n {ui.permissionSeatDiag !== 'idle' && ui.permissionSeatDiag !== 'gate disposed' && (\n <div className=\"ntr-acp-empty-hint ntr-acp-seat-diag\">\n {t('权限底座', 'permission seat')} · {ui.permissionSeatDiag}\n </div>\n )}\n {/*\n Slash isolation degrades open (an unrecognized host source shape\n leaves the host's commands in charge). That is the safe failure,\n but it looks exactly like the feature never having existed — the\n menu simply lists DSH's commands again. Say so.\n */}\n {ui.slashDiag !== 'idle' && (\n <div className=\"ntr-acp-empty-hint ntr-acp-seat-diag\">\n {t('斜杠菜单', 'slash menu')} · {ui.slashDiag}\n </div>\n )}\n </div>\n </div>\n )\n }\n\n const session = transcript.session\n const usage = session.usage\n const agentTitle = ui.agents.find(agent => agent.id === session.agentId)?.title ?? bound?.agentTitle ?? session.agentId\n const health = session.released\n ? { text: t('已挂起 · 下一条消息自动恢复', 'paused · resumes on your next message'), tone: 'warn' }\n : session.closed\n ? { text: t('已关闭', 'closed'), tone: 'idle' }\n : session.running\n ? { text: t('运行中', 'running'), tone: 'busy' }\n : { text: t('空闲', 'idle'), tone: 'done' }\n\n return (\n <div className=\"ntr-acp-view\">\n <div className=\"ntr-acp-view-head\">\n <span className={`ntr-acp-badge is-${health.tone}`}>{health.text}</span>\n <span className=\"ntr-acp-view-agent\">{agentTitle}</span>\n <span className=\"ntr-acp-view-meta\" title={session.cwd}>{session.cwd}</span>\n {usage !== undefined && <span className=\"ntr-acp-view-meta\">{usage.used}/{usage.size}</span>}\n <button type=\"button\" className=\"ntr-acp-more\" onClick={() => { void acp.loadChatTranscript(sessionId) }}>\n {t('刷新', 'refresh')}\n </button>\n </div>\n <div\n className=\"ntr-acp-stream\"\n ref={streamRef}\n onScroll={event => {\n const node = event.currentTarget\n pinned.current = node.scrollHeight - node.scrollTop - node.clientHeight < 40\n }}\n >\n {transcript.blocks.length === 0\n ? (\n <div className=\"ntr-acp-empty-hint\">\n {historyAwaitsReplay(session)\n ? t('进程已释放;历史将在恢复后回放显示。', 'The process is released — history replays once it is restored.')\n : t('还没有消息:在下面的输入框里发给它。', 'Nothing yet — send it a message below.')}\n </div>\n )\n : transcript.blocks.map((block, index) => <BlockView key={index} block={block} label={acp.label} localeRev={ui.localeRev} />)}\n </div>\n </div>\n )\n}\n","/**\n * The NTR settings dialog, opened from the agent base's empty-roster menu.\n *\n * WHY A DIALOG OF OUR OWN: the host settings shell owns its open state and\n * exposes no programmatic way in. The sidebar seat that receives `openSettings`\n * (`settings.launcher`) is a single-occupant slot already held by the account\n * menu, and `openSection` is only handed to `settings.onboarding` registrants —\n * so a conversation surface cannot drive the shell's modal. This dialog mounts\n * the SAME `AcpSettingsSection` the shell mounts, on the frame's overlay layer\n * (`shell.overlay`): one settings surface, two entrances, no duplicated\n * configuration logic.\n *\n * The trigger (composer model seat) and this dialog (root overlay) live in\n * unrelated slot trees, so the open state is the module-level store in\n * `acp-ui.tsx` (`openAcpSettingsDialog`).\n */\n\nimport { useEffect } from 'react'\nimport { AcpSettingsSection } from './panel.tsx'\nimport {\n closeAcpSettingsDialog,\n ensureAcpStyle,\n useAcpSettingsDialogOpen,\n} from './acp-ui.tsx'\nimport type { AcpStore } from './store.ts'\n\n/** The root overlay entry's inject face. */\nexport interface AcpSettingsDialogProps {\n acp: AcpStore\n}\n\n/** Frame-wide NTR settings surface; renders nothing while closed. */\nexport function AcpSettingsDialog(props: AcpSettingsDialogProps) {\n ensureAcpStyle()\n const { acp } = props\n const open = useAcpSettingsDialogOpen()\n useEffect(() => {\n if (!open) return\n const onKey = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n event.preventDefault()\n event.stopPropagation()\n closeAcpSettingsDialog()\n }\n document.addEventListener('keydown', onKey, true)\n return () => { document.removeEventListener('keydown', onKey, true) }\n }, [open])\n if (!open) return null\n const title = acp.label('NTR 设置', 'NTR settings')\n return (\n <div className=\"ntr-acp-dialog-layer\">\n <div className=\"ntr-acp-dialog-mask\" aria-hidden onClick={closeAcpSettingsDialog} />\n <div className=\"ntr-acp-dialog\" role=\"dialog\" aria-modal=\"true\" aria-label={title}>\n <div className=\"ntr-acp-dialog-head\">\n <span className=\"ntr-acp-dialog-title\">{title}</span>\n <button\n type=\"button\"\n className=\"ntr-acp-dialog-close\"\n title={acp.label('关闭', 'Close')}\n aria-label={acp.label('关闭 NTR 设置', 'Close NTR settings')}\n autoFocus\n onClick={closeAcpSettingsDialog}\n >\n ✕\n </button>\n </div>\n <div className=\"ntr-acp-dialog-body\">\n <AcpSettingsSection acp={acp} />\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Pending ACP permission asks for the staged DSH conversation, rendered as\n * composer-width entries above the composer card (`conversation.input.dock`).\n * The slot itself spans the conversation column; the dock mirrors the host's\n * InputBar width formula so the cards line up with the input box.\n *\n * The host broadcasts every ask as `permission/request`, carrying the DSH chat\n * session id when the ask belongs to a conversation. This dock is the primary\n * answer surface: it renders exactly the asks whose `dshSessionId` is the\n * conversation it was mounted for, and nothing otherwise. There is no\n * ACP-mode gate — the frame set itself is the gate, so an ordinary\n * conversation shows this card if and only if it actually has an ask.\n *\n * The ask's description and options are the agent's own: buttons appear in the\n * agent's order with the agent's names (including \"Always Allow\"), and\n * `kind` is not rendered. A separate cancel button answers with no option,\n * which the host forwards as `cancelled`. No Enter/Escape handlers: the\n * composer owns its keyboard, and these are ordinary focusable buttons.\n *\n * Multiple asks stack in ARRIVAL order, oldest first: the question ignored\n * longest stays at the top. The list is capped in height by CSS and scrolls.\n * Asks without a chat identity never appear here — they stay in the\n * settings-panel ledger, the cross-session mirror.\n */\n\nimport { useEffect, useState } from 'react'\nimport { ensureAcpStyle, useUi } from './acp-ui.tsx'\nimport {\n deadlineRemainingLabel,\n PERMISSION_DEADLINE_EXPIRED,\n permissionsForSession,\n} from './store.ts'\nimport type { AcpStore, PendingPermission } from './store.ts'\n\nexport interface PermissionDockProps {\n /** Injected face: the store plus the chat session id this dock belongs to. */\n acp: AcpStore\n sessionId: string\n}\n\n/** Tick interval for the countdown (seconds are all the label shows). */\nconst COUNTDOWN_TICK_MS = 1000\n\nexport function PermissionDock({ acp, sessionId }: PermissionDockProps) {\n ensureAcpStyle()\n const ui = useUi(acp)\n const dshSessionId = String(sessionId)\n const pending = permissionsForSession(ui.permissions, dshSessionId)\n const counting = pending.some(ask => ask.timeoutMode !== 'wait' && ask.deadlineAt !== undefined)\n const [now, setNow] = useState(() => Date.now())\n useEffect(() => {\n if (!counting) return\n setNow(Date.now())\n const timer = setInterval(() => { setNow(Date.now()) }, COUNTDOWN_TICK_MS)\n return () => { clearInterval(timer) }\n }, [counting])\n if (pending.length === 0) return null\n return (\n <div\n className=\"ntr-permission-dock\"\n role=\"region\"\n aria-label={acp.label('待处理的权限请求', 'Pending permission requests')}\n >\n {pending.map(permission => (\n <PermissionAskCard key={permission.requestId} acp={acp} permission={permission} now={now} />\n ))}\n </div>\n )\n}\n\nfunction PermissionAskCard(props: { acp: AcpStore; permission: PendingPermission; now: number }) {\n const { acp, permission, now } = props\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string>()\n const remaining = deadlineRemainingLabel(permission.deadlineAt, now, permission.timeoutMode)\n const expired = remaining === PERMISSION_DEADLINE_EXPIRED\n const respond = (optionId?: string): void => {\n if (busy) return\n setBusy(true)\n setError(undefined)\n void acp.respondPermission(permission.requestId, optionId)\n // `respondPermission` reports to the store's notice too, but that notice\n // lives on the settings page; this card pins the failure where the user\n // is looking.\n .catch((failure: unknown) => {\n setError(failure instanceof Error ? failure.message : String(failure))\n })\n .finally(() => { setBusy(false) })\n }\n return (\n <div className=\"ntr-permission-card\">\n <div className=\"ntr-permission-head\">\n <span className=\"ntr-permission-tool\">{permission.toolName}</span>\n {permission.reason !== undefined && (\n <span className=\"ntr-permission-reason\" title={permission.reason}>{permission.reason}</span>\n )}\n {remaining !== undefined && (\n <span className={`ntr-permission-deadline${expired ? ' is-expired' : ''}`}>\n {expired\n ? acp.label('已超时', 'expired')\n : acp.label(`剩余 ${remaining}`, `${remaining} left`)}\n </span>\n )}\n </div>\n <div className=\"ntr-permission-actions\">\n {permission.options.map(option => (\n <button\n key={option.optionId}\n type=\"button\"\n className=\"ntr-permission-option\"\n disabled={busy}\n title={option.name}\n onClick={() => { respond(option.optionId) }}\n >\n {option.name}\n </button>\n ))}\n <button\n type=\"button\"\n className=\"ntr-permission-cancel\"\n disabled={busy}\n title={acp.label('取消这次询问(不选择任何选项)', 'Cancel this ask without choosing an option')}\n onClick={() => { respond() }}\n >\n {acp.label('取消', 'Cancel')}\n </button>\n </div>\n {error !== undefined && (\n <div className=\"ntr-permission-error\" role=\"alert\">{error}</div>\n )}\n </div>\n )\n}\n","/**\n * The conversation's released-session banner: one row above the composer\n * (`conversation.input.dock`) saying the agent process behind this\n * conversation has been released, with the explicit resume button.\n *\n * WHY A BANNER: releasing a process is deliberately silent (constraint 19 —\n * an idle teardown is the expected lifecycle, no `agent/notice`), and the\n * only other trace was the NTR view tab's status badge — a surface most\n * users never open. The \"your next message resumes it\" promise is real but\n * unverifiable from the chat itself, so the banner states it here and the\n * button turns it into a gesture (`session/chat-resume`), the explicit form\n * of the same recovery the next prompt performs lazily.\n *\n * The gate is a pure function ({@link releasedBannerVisible}) — the seam the\n * composer's other visibility decisions use, so the rules are testable with\n * no DOM. Like the agent tag and the context ring, an explicit\n * `acpMode === false` outranks a lingering `chatSessions` row: a conversation\n * that left NTR mode must not keep claiming a parked NTR process.\n */\n\nimport { useEffect, useState } from 'react'\nimport { ensureAcpStyle, useUi } from './acp-ui.tsx'\nimport type { AcpStore, UiState } from './store.ts'\n\nexport interface ReleasedBannerProps {\n /** Injected face: the store plus the chat session id this banner belongs to. */\n acp: AcpStore\n sessionId: string\n}\n\n/**\n * Whether the banner should render: this conversation's ACP session records\n * are all released — the process is parked (idle release or crash) and\n * nothing is live to serve the next message yet. An empty list (no agent has\n * run here) and a live row anywhere both mean \"nothing to recover\", so the\n * banner stays out of the way.\n */\nexport function releasedBannerVisible(\n ui: Pick<UiState, 'acpMode' | 'chatSessions' | 'chatSessionsFor'>,\n dshSessionId: string,\n): boolean {\n if (ui.acpMode[dshSessionId] === false) return false\n if (ui.chatSessionsFor !== dshSessionId) return false\n return ui.chatSessions.length > 0 && ui.chatSessions.every(row => row.released === true)\n}\n\nexport function ReleasedBanner({ acp, sessionId }: ReleasedBannerProps) {\n ensureAcpStyle()\n const ui = useUi(acp)\n const dshSessionId = String(sessionId)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string>()\n // The binding has no announcing frame on load (a conversation opened on an\n // already-parked session shows nothing else that would read the list), so\n // this entry reads it once itself — deduped in flight by the store.\n useEffect(() => {\n void acp.loadChatSessions(dshSessionId)\n }, [acp, dshSessionId])\n if (!releasedBannerVisible(ui, dshSessionId)) return null\n const resume = (): void => {\n if (busy) return\n setBusy(true)\n setError(undefined)\n void acp.resumeChatSession(dshSessionId)\n // `resumeChatSession` reports to the store's notice too, but that notice\n // lives on the settings page; this pins the failure where the user is\n // looking (same contract as the permission card).\n .catch((failure: unknown) => {\n setError(failure instanceof Error ? failure.message : String(failure))\n })\n .finally(() => { setBusy(false) })\n }\n return (\n <div className=\"ntr-released-banner\" role=\"region\" aria-label={acp.label('进程已释放', 'Process released')}>\n <div className=\"ntr-released-banner-row\">\n <div className=\"ntr-released-banner-text\">\n <span className=\"ntr-released-banner-title\">{acp.label('进程已释放', 'Process released')}</span>\n <span className=\"ntr-released-banner-body\">\n {acp.label('上下文已保留;下一条消息将自动恢复,或点击【恢复进程】按钮。', 'Context is kept — your next message resumes it, or click [Resume process].')}\n </span>\n </div>\n <button\n type=\"button\"\n className=\"ntr-acp-more is-primary\"\n disabled={busy}\n title={acp.label('立即拉起 agent 进程并恢复这个会话', 'Bring the agent process back and restore this session now')}\n onClick={resume}\n >\n {busy ? acp.label('正在恢复…', 'Resuming…') : acp.label('恢复进程', 'Resume process')}\n </button>\n </div>\n {error !== undefined && (\n <div className=\"ntr-permission-error\" role=\"alert\">{error}</div>\n )}\n </div>\n )\n}\n","/**\n * The NTR branch button (slot `conversation.chat.assistant-actions`): in an\n * ACP conversation this is the ONLY branch entry — the host's like / dislike /\n\n * branch icons are hidden under `html[data-ntr-acp]` (see acp-ui.tsx), and\n * this button takes the host branch's place with the same icon, size and\n * row position (order 20, after the hidden feedback entry's order 10).\n *\n * Clicking runs the fixed two-stage branch: the HOST forks the DSH session\n * tree first (`sessions.fork`, structurally resolved), then the plugin adopts\n * the ACP side (`session/chat-fork-adopt` → the agent's own `session/fork`),\n * so the child conversation's agent actually remembers the parent context —\n * see the NTR message-actions plan, §D5/§D7.\n *\n * Availability is the store's `forkStateOf` six-state mapping over facts the\n * button never derives itself: the host's per-row `fork` verdict\n * (`session/chat-list`), the SSE running flag, the host-fork structural probe,\n * and the DOM tail verdict. The tail check reads the host's OWN\n * `[data-turn-tail]` marker (`data-actions-reveal === \"always\"` = the latest\n * completed turn) and fails CLOSED: a missing marker disables the button\n * rather than risking a fork cut at a point the user is not looking at.\n */\n\nimport { useLayoutEffect, useRef, useState } from 'react'\nimport type { ComponentType } from 'react'\nimport { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives'\nimport * as primitives from '@deepseek-ai/dsh-client-ui-primitives'\nimport { useUi } from './acp-ui.tsx'\nimport type { AcpChatInjected, AcpHostForkFace } from './acp-ui.tsx'\nimport { forkStateOf } from './store.ts'\nimport type { ChatForkState } from './store.ts'\n\n/**\n * Resolves the runtime's `sessions.fork` primitive, binding it to the service\n * instance so that internal `this` accesses (such as `this.list.getSnapshot()`)\n * resolve correctly when called through `face.fork(...)`.\n * Returns `undefined` if the service or `fork` method is absent or not a function.\n */\nexport function resolveHostFork(sessionsService: unknown): AcpHostForkFace | undefined {\n try {\n const sessions = sessionsService as {\n fork?: (options: { sessionId: string; increaseTitle?: boolean }) => Promise<unknown>\n } | null | undefined\n const fork = sessions?.fork\n return typeof fork === 'function' ? { fork: fork.bind(sessions) } : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * The host's own branch-button icon, picked per host generation. The icon\n * exports MOVED between shipped hosts — desktop 0.2.1-rc.2 has\n * `IconBranchOutlineRegular` / `…Medium` but NOT `…16`, newer packages export\n * `…16` — and a hard import of one name compiles to a plain property read\n * (`primitives.IconBranchOutline16`), so the missing export is `undefined`\n * and `<undefined />` throws \"Element type is invalid\". The slot's error\n * boundary then abdicates the whole entry: no button, no tooltip, nothing in\n * any log — the exact silent disappearance this picker exists to prevent.\n * Rendering the button never depends on the icon: with all three absent the\n * click still works, only the 28px glyph is missing.\n */\nexport function branchIconOf(source: Record<string, unknown>): ComponentType<Record<string, never>> | undefined {\n return (source.IconBranchOutline16 ?? source.IconBranchOutlineRegular ?? source.IconBranchOutlineMedium) as\n | ComponentType<Record<string, never>>\n | undefined\n}\nconst BranchIcon = branchIconOf(primitives as unknown as Record<string, unknown>)\n/** What the label reads for each state (zh / en pairs, per the plan's §D4). */\nfunction tooltipFor(state: ChatForkState, label: (zh: string, en: string) => string): string {\n switch (state) {\n case 'ready': return label('在新对话中分支', 'Branch into a new conversation')\n case 'unsupported': return label('该 agent 不支持原生分叉', 'This agent cannot fork natively')\n case 'released': return label('会话已挂起,先发一条消息恢复再分叉', 'Resume this session before branching')\n case 'busy': return label('当前轮次结束后才能分叉', 'Wait for the current turn to finish')\n case 'notTail': return label('仅可从已完成轮次的最后一条消息分支', 'Available only on the last message of a completed turn')\n case 'hostUnavailable': return label('当前宿主不提供分叉', 'This host does not support branching')\n }\n}\n\nexport function AcpBranchAction(props: AcpChatInjected & { sessionId: string }) {\n const ui = useUi(props.acp)\n const buttonRef = useRef<HTMLButtonElement>(null)\n const [pending, setPending] = useState(false)\n /**\n * The host's own availability verdict for the turn tail this button sits\n * in, re-read after EVERY render (no dep array): the marker flips without\n * any store event of our own when a new turn lands, and before the first\n * layout pass the ref is not attached yet. Two facts, both the HOST's:\n * `data-actions-reveal === \"always\"` (this tail is the latest turn and\n * completed) and the host branch button's own `data-unavailable` (a stricter\n * gate — a tail whose final node is not the transcript's last, or a turn\n * with later nodes, is one the HOST refuses to fork). Reading the host's\n * button keeps the plugin's readiness a SUBSET of the host's: wherever the\n * two would disagree about the cut point, the plugin button disables, and\n * the remaining clicks fork at the same latest-completed prefix the host\n * would (the tail marked `always` is the latest completed turn, which is\n * exactly where the host's fallback `atSeq` lands).\n */\n const [tailReady, setTailReady] = useState(false)\n useLayoutEffect(() => {\n const tail = buttonRef.current?.closest('[data-turn-tail]')\n const hostBranch = tail?.querySelector('button[aria-label=\"在新对话中分支\"]:not([data-ntr-branch]), button[aria-label=\"Branch into a new conversation\"]:not([data-ntr-branch])')\n setTailReady(\n tail?.getAttribute('data-actions-reveal') === 'always'\n && hostBranch?.hasAttribute('data-unavailable') !== true,\n )\n })\n /**\n * The click's local failure line. The store ALSO flashes the same reason\n * into the settings banner, but the user is HERE — without this row the\n * click would \"do nothing\" visibly, the exact invisibility the permission\n * dock's pinned-error pattern exists to prevent.\n */\n const [failure, setFailure] = useState<string | undefined>(undefined)\n\n // ACP conversations only, fail-closed: the slot renders in EVERY\n // conversation, and the confirmed verdict (`session/chat-list`, folded from\n // the runtime row by the seat gate) is the same fact the scope marker and\n // the CSS hiding run on. `undefined` renders nothing. The row guard keeps\n // the button from rendering on a conversation whose rows have not landed —\n // without it a rc.2-style host (verdict folded from the row preset, ahead\n // of the list read) would show a disabled button flashing the `released`\n // tooltip for a perfectly live conversation.\n if (ui.acpMode[props.sessionId] !== true) return null\n if (ui.chatSessionsFor !== props.sessionId) return null\n\n const row = ui.chatSessionsFor === props.sessionId ? ui.chatSessions[0] : undefined\n const forkFace = props.hostFork?.()\n // The row's `running` only refreshes on a chat-list read, so a turn that\n // just started would leave it stale for seconds; the SSE-facing session\n // view is the live copy of the same fact.\n const liveRunning = row !== undefined && ui.sessions.some(s => s.sessionId === row.sessionId && s.running)\n const state = forkStateOf(row, forkFace !== undefined, tailReady, liveRunning)\n const disabled = state !== 'ready' || pending\n const branchLabel = props.acp.label('在新对话中分支', 'Branch into a new conversation')\n\n const onClick = (): void => {\n if (state !== 'ready' || pending) return\n setPending(true)\n setFailure(undefined)\n void props.acp.forkChatSession(props.sessionId, sessionId => {\n // Re-resolved per click: a face that vanished between render and click\n // fails loudly in the notice banner and the local line instead of\n // throwing here.\n const face = props.hostFork?.()\n if (face === undefined) throw new Error('acp: host fork service is gone')\n return face.fork({ sessionId, increaseTitle: true })\n }).then(outcome => {\n // A failure opens NOTHING (an unadopted child is a copied-history\n // conversation the user did not ask to visit) and says why, right here.\n if ('childId' in outcome) props.openSession?.(outcome.childId)\n else setFailure(outcome.failure)\n }).finally(() => { setPending(false) })\n }\n\n return (\n <>\n <Tooltip label={tooltipFor(state, props.acp.label)} side=\"bottom\">\n <button\n ref={buttonRef}\n type=\"button\"\n className=\"ntr-branch-action\"\n aria-label={branchLabel}\n aria-disabled={disabled || undefined}\n data-ntr-branch=\"\"\n data-unavailable={disabled || undefined}\n onClick={disabled ? undefined : onClick}\n >\n {BranchIcon === undefined ? null : <BranchIcon />}\n </button>\n </Tooltip>\n {failure !== undefined && (\n <span className=\"ntr-branch-error\" role=\"alert\" title={failure}>{failure}</span>\n )}\n </>\n )\n}\n","/**\n * The ACP conversation's claim on two cells of the host's composer bar.\n *\n * Both cells are `single`, session-scoped slots the composer bar renders\n * unconditionally, and both are held by claims this module defines — one that\n * REPLACES what is there (`conversation.input.model`, see\n * {@link createModelSeatClaim}) and one that EMPTIES it\n * (`conversation.input.permission`, see {@link createPermissionSeatClaim}).\n *\n * WHAT IT IS: `conversation.input.model` is a `single` slot the host's composer\n * bar renders unconditionally, in the TRAILING group (`margin-left: auto`),\n * immediately before the activity/stop/submit chrome — which is exactly where\n * the host's own model selector sits. Its shipped occupant is\n * `client-ui-model-selection`'s `ModelSelect`, which reads the DSH model\n * directory and submits through `session.selectModel`, which in turn writes\n * `agentDefaultModel.saveSelection` — the process-wide default. On an ACP\n * conversation none of that describes anything real: the models belong to the\n * external agent (`agent/catalog`), so the seat would list providers this\n * conversation cannot use, and clicking one would poison the default for every\n * other conversation. Third-party seat plugins land in this same cell.\n *\n * SO THE CLAIM PUTS THE ACP BASE THERE. The ACP base (`AcpSessionControls`) is\n * the surface that already describes what this conversation runs on, and the\n * seat it has to displace is the one that would otherwise misdescribe it — one\n * cell, one occupant, and the position the host's own base occupied. The base\n * is installed by the ACP-mode gate in `index.tsx`, which holds the claim only\n * while the staged conversation runs the ACP preset and releases it the moment\n * it does not, so an ordinary conversation gets the native selector back and\n * sees no ACP chrome at all. That gate is the whole isolation story: the entry\n * does not exist outside ACP mode, rather than existing and rendering nothing.\n *\n * WHY A CLAIM AND NOT A `priority: -1` REGISTRATION: the declared rank is NOT\n * what decides this cell on the desktop runtime. The client runner (the facade\n * every bundle — including this one — is loaded through) REWRITES the priority\n * of every non-chain registration with its own page-global counter, which only\n * decreases:\n *\n * // @deepseek-ai/dsh-cordis-client-runner, `guardedSlots`\n * const spec = slots.spec(slot);\n * let priority = options.priority;\n * if (spec === undefined || spec.kind !== \"chain\") {\n * priority = env.allocatePriority(); // `allocatePriority: () => --this.nextPriority`\n * options.priority = priority;\n * }\n *\n * So in a `single` cell (lowest live rank renders) the winner is simply\n * whoever registered LAST — a rival seat plugin that loads a moment after us\n * takes the cell no matter what either side declares. `dsh-thinking-effort`\n * provably does exactly that: it registers a model + reasoning slider into this\n * very cell, and it is a later registration, so it would stay on screen in ACP\n * conversations regardless of any rank we declare (and its own declared `-10`\n * is discarded just the same).\n *\n * The fix is therefore to treat the cell as a claim we VERIFY and, when a rival\n * took it, retake by registering again (a new registration is the lowest rank\n * on the page, which is what wins). That is the whole mechanism: read the\n * renderer's own winner view (`entriesOfSlot`, the same call the renderer's\n * outlet makes), compare it with our own component identities, and re-register\n * when it is not ours — bounded, and honest about giving up. Nothing else from\n * the retired seat machinery comes back: no abdicate recovery, no session-long\n * `seatBroken`, no rank arithmetic as an end in itself.\n *\n * WHAT THE CLAIM MUST CARRY: the occupant's own inject face. That is not an\n * optional extra — this cell is SESSION-scoped, so the renderer gives an entry\n * only the framework's standard kit (`sessionId`, `useSession`, …) unless the\n * entry declares an `inject`, and the ACP base reads `acp` and `faceFor`, both\n * of which arrive ONLY through it. Registering the occupant without one makes\n * it render with `acp === undefined`; the slot's error boundary catches the\n * resulting crash and reports it as an ABDICATION, which retires our entry from\n * the cell for the rest of that registration's life and hands the cell back to\n * the shipped `ModelSelect` — the native seat returns, the ACP base is gone,\n * and the gate never hears about it because `sync()` already verified the cell\n * and reported \"held by the ACP base\". So `inject` is a required option (a\n * compile error at the one call site) instead of an optional one (a silent\n * abdication at runtime). See {@link ModelSeatClaimOptions.inject}.\n *\n * The gate reads the session's own preset (`stagedIsAcpMode` in `acp-ui.tsx`)\n * rather than anything the plugin recorded, so it can never drift from the\n * roster.\n */\n\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n\n/** Exact key of the composer's model seat (a `single`, session-scoped slot). */\nexport const MODEL_SEAT_KEY = 'conversation.input.model'\n\n/**\n * Exact key of the composer's permission seat (a `single`, session-scoped\n * slot): the cell the host's Access mode control occupies. It selects the\n * file-write policy the HOST applies to its own tools, read from the\n * `permissions` projection `dsh-permission-presets` owns. An ACP conversation's\n * writes belong to the external agent's process, which never consults that\n * policy, so there is nothing to put in this cell in ACP mode — which is why\n * {@link createPermissionSeatClaim} installs a `null`-rendering occupant rather\n * than a replacement control, and why its lifetime must be exactly the ACP-mode\n * verdict's lifetime (see the claim's own note in `index.tsx`).\n */\nexport const PERMISSION_SEAT_KEY = 'conversation.input.permission'\n\n/**\n * Declared rank of the first occupant we install.\n *\n * Honoured only by a registry that keeps what we declare (a statically composed\n * client, or a future runner that stops rewriting non-chain priorities): it\n * displaces a shipped occupant registering at the default rank 0. Under the\n * desktop runner the rank is overwritten with its own counter — see the module\n * header — which is why the claim verifies ownership instead of trusting this.\n */\nexport const MODEL_SEAT_PRIORITY = -1\n\n/**\n * Rank of the first occupant the permission claim installs. Same meaning (and\n * same caveat) as {@link MODEL_SEAT_PRIORITY}; see {@link createSeatClaim} for\n * why a declared rank is never trusted as proof of ownership.\n */\nexport const PERMISSION_SEAT_PRIORITY = -1\n\n/**\n * How many times ONE claim may retake the cell before it stops and says so.\n *\n * Each retake is triggered by evidence (the cell's winner is not ours), so a\n * normal page needs at most one; the cap only bounds a rival that registers in\n * a loop, and reaching it is reported rather than hidden.\n */\nexport const MAX_SEAT_CLAIMS = 4\n\n/** One live entry in the cell, narrowed to what the claim reads. */\nexport interface SeatEntry {\n component?: unknown\n options?: { priority?: number; registrant?: string; id?: string; order?: number }\n /** Entry-owned inject face; needed when the claim reuses a shipped occupant. */\n inject?: (...args: unknown[]) => unknown\n /** Locale namespace the occupant's `t` is bound to; needed for the same reuse. */\n locale?: string\n}\n\n/** The slot-registry face this module uses (structural: the registry is not a dependency of this bundle). */\nexport interface SeatSlots {\n inject(key: string, callback: () => (() => void) | Iterable<() => void>): () => void\n /** Returns the entry's disposer (the registry hangs it on the caller's fiber too). */\n register(options: SeatRegisterOptions, component: unknown): () => void\n /** The renderer's winner view per cell; absent on a runtime that does not expose it. */\n entriesOfSlot?(key: string): readonly SeatEntry[]\n /** The declaration probe; absent on a runtime that does not expose it. */\n spec?(key: string): { kind?: string } | undefined\n}\n\n/**\n * One registration's options, narrowed to what the claim passes. `inject` is\n * NOT optional in practice — see {@link ModelSeatClaimOptions.inject}.\n */\nexport interface SeatRegisterOptions {\n name: string\n /** List-slot cell to shadow; omitted for single/keyed registers. */\n id?: string\n /** Display position within the list; only meaningful with `id`. */\n order?: number\n /** Locale namespace the occupant's `t` is bound to. */\n locale?: string\n priority?: number\n /**\n * The session-scoped face factory the renderer calls with the scope binding's\n * key (and the store's actions) before rendering, merging its result into the\n * component's props. A session-scoped occupant that renders conversation\n * chrome without one arrives with only the standard kit — no `acp`, no\n * `faceFor` — and crashes on its first render.\n */\n inject?(sessionId: unknown, actions?: unknown): unknown\n}\n\nexport interface ModelSeatClaimOptions {\n slots: SeatSlots\n /**\n * The occupant to install into the cell — the ACP base for an ACP-mode\n * conversation. Called once per installed entry and receives whatever props\n * the seat frame hands an entry (the injected face plus the standard kit),\n * so it must be the component itself and not a bound closure over one\n * session's props.\n */\n occupant(): unknown\n /**\n * The inject face every installed entry declares — REQUIRED, and the reason\n * is a failure mode that is silent by construction.\n *\n * `conversation.input.model` is a SESSION-scoped slot. The renderer composes\n * an entry's props as `{ ...standardKit, ...entryInject(sessionId, actions),\n * ...slotInject, ...ownerProps }` (`SessionEntry` → `renderEntry`), so the\n * standard kit alone carries only the framework's own shares (`sessionId`,\n * `useSession`, …). Everything this plugin's occupant reads beyond that —\n * `acp`, `faceFor` — arrives ONLY through the entry's own `inject`.\n *\n * Register without it and the occupant renders with `acp === undefined`. The\n * crash is caught by the slot's error boundary, which reports it as an\n * ABDICATION: the registry retires our entry from its cell for the rest of\n * that registration's life, and the outlet re-projects onto the cell's next\n * survivor — the shipped `ModelSelect`. The native seat is back, the ACP base\n * is gone, and nothing on the gate's side moves: the abdication happens\n * during React's render phase, after `sync()` already verified the cell and\n * reported \"held by the ACP base\". That is exactly how the base went missing\n * with the diagnostic still claiming it was installed.\n *\n * So this is a required option rather than an optional one: the omission is\n * then a compile error at the single call site instead of a runtime\n * abdication nobody can see.\n */\n inject(sessionId: unknown, actions?: unknown): unknown\n /** Every evaluation reports its verdict here (the gate forwards it to the ACP view's diagnostic line). */\n onDiag?(line: string): void\n maxClaims?: number\n}\n\n/**\n * Options for the permission claim. Unlike {@link ModelSeatClaimOptions}, both\n * the occupant and the inject face are OPTIONAL: this claim exists to hold an\n * EMPTY cell, so the defaults express what it is — nothing rendered, nothing\n * injected — and a caller only passes them to customize (the tests do).\n *\n * The default inject still matters: the cell is session-scoped, and an entry\n * without any inject receives only the framework's standard kit. A `null`\n * occupant reads none of it, so an omitted inject is harmless here — whereas\n * omitting one on the model seat is a silent abdication. The asymmetry is\n * deliberate: required where silence hides a failure, defaulted where silence\n * is the correct answer.\n */\nexport interface PermissionSeatClaimOptions {\n slots: SeatSlots\n /** Optional custom occupant; defaults to a component rendering null. */\n occupant?(): unknown\n /** Optional inject factory; defaults to providing an empty object `{}`. */\n inject?(sessionId: unknown, actions?: unknown): unknown\n /** Every evaluation reports its verdict here (the gate forwards it to the ACP view's diagnostic line). */\n onDiag?(line: string): void\n maxClaims?: number\n}\n\nexport interface SeatClaimOptions {\n slots: SeatSlots\n slotKey: string\n /**\n * List-slot cell id to hold. Omitted for single/keyed seats; a list slot\n * needs it because its winner view is one entry PER id, not one per slot.\n */\n entryId?: string\n /** Display order for the installed list entry (same cell, so usually the shipped order). */\n order?: number\n /** Locale namespace the installed occupant resolves `t` against. */\n locale?: string\n basePriority: number\n occupant(): unknown\n inject?(sessionId: unknown, actions?: unknown): unknown\n label?: string\n onDiag?(line: string): void\n maxClaims?: number\n}\n\nexport interface ModelSeatClaim {\n /** Hold or retake the cell; idempotent, so it can run on every gate evaluation. */\n sync(): void\n /** Whether the last verified observation found our entry winning the cell. */\n readonly holding: boolean\n /** Release every entry this claim installed (the gate's stand-down). */\n release(): void\n}\n\n/** Where the cell stood, as far as this bundle can tell. */\ninterface CellView {\n /**\n * `false` while the composer bar has not mounted the seat. `inject` WAITS\n * for the declaration instead of failing, so an undeclared seat and a seat\n * held by someone else both read as \"the winner is not ours\" through the\n * winner view alone — and only one of them justifies a retake.\n */\n declared: boolean\n /** `false` when the runtime exposes no winner view — ownership cannot be verified there. */\n verified: boolean\n entry?: SeatEntry\n}\n\n/**\n * Core generic slot claim engine.\n *\n * Installs and holds an occupant into a single-occupant slot, verifying via\n * `entriesOfSlot` and performing bounded retakes if displaced by later registrations.\n */\nexport function createSeatClaim(options: SeatClaimOptions): ModelSeatClaim {\n const { slots, slotKey, basePriority, occupant, inject, entryId } = options\n const label = options.label ?? slotKey\n const onDiag = options.onDiag\n const maxClaims = options.maxClaims ?? MAX_SEAT_CLAIMS\n const ours = new Set<unknown>()\n const declared = new Set<number>()\n const installed: Array<() => void> = []\n let retakes = 0\n let holding = false\n\n const say = (line: string): void => onDiag?.(line)\n\n const describe = (entry: SeatEntry | undefined): string => {\n if (entry === undefined) return 'no occupant'\n const who = entry.options?.registrant\n const rank = entry.options?.priority\n if (who === undefined && rank === undefined) return 'another entry'\n return `${who ?? 'an unnamed entry'} (rank ${rank ?? 0})`\n }\n\n const cell = (): CellView => {\n let isDeclared = true\n if (typeof slots.spec === 'function') {\n try { isDeclared = slots.spec(slotKey) !== undefined } catch { isDeclared = false }\n }\n if (typeof slots.entriesOfSlot !== 'function') return { declared: isDeclared, verified: false }\n try {\n const heads = slots.entriesOfSlot(slotKey) ?? []\n // A list slot projects one winner per id, so the cell this claim owns is\n // the head WITH that id — not heads[0], which may be a sibling cell.\n const entry = entryId === undefined ? heads[0] : heads.find(head => head.options?.id === entryId)\n return { declared: isDeclared, verified: true, ...entry === undefined ? {} : { entry } }\n } catch {\n return { declared: isDeclared, verified: false }\n }\n }\n\n const install = (head: SeatEntry | undefined): boolean => {\n const base = installed.length === 0 ? basePriority : basePriority - 1 - retakes\n let rank = Math.min((head?.options?.priority ?? 0) - 1, base)\n while (declared.has(rank)) rank -= 1\n const component = (props: unknown): unknown => {\n const target = occupant() as (p: unknown) => unknown\n return target(props)\n }\n ours.add(component)\n declared.add(rank)\n try {\n installed.push(slots.inject(slotKey, () => slots.register({\n name: slotKey,\n ...(entryId === undefined ? {} : { id: entryId }),\n ...(options.order === undefined ? {} : { order: options.order }),\n ...(options.locale === undefined ? {} : { locale: options.locale }),\n priority: rank,\n ...(inject ? { inject } : {}),\n }, component)))\n return true\n } catch (error) {\n ours.delete(component)\n declared.delete(rank)\n say(`${label}: registration failed (${error instanceof Error ? error.message : String(error)})`)\n return false\n }\n }\n\n return {\n get holding(): boolean { return holding },\n sync(): void {\n if (installed.length === 0 && !install(undefined)) return\n const view = cell()\n if (!view.declared) {\n holding = false\n say(`${slotKey}: waiting for the host to declare the seat`)\n return\n }\n if (!view.verified) {\n holding = true\n say(`${slotKey}: occupied (ownership is not verifiable on this runtime)`)\n return\n }\n if (ours.has(view.entry?.component)) {\n holding = true\n say(`${slotKey}: held by the ACP base${retakes === 0 ? '' : ` (retook it ${retakes}×)`}`)\n return\n }\n const incumbent = view\n if (retakes >= maxClaims) {\n holding = false\n say(`${slotKey}: taken by ${describe(incumbent.entry)} — gave up after ${maxClaims} retakes`)\n return\n }\n retakes += 1\n if (!install(incumbent.entry)) return\n const settled = cell()\n if (settled.verified && ours.has(settled.entry?.component)) {\n holding = true\n say(`${slotKey}: retook it from ${describe(incumbent.entry)} (retake ${retakes})`)\n return\n }\n holding = false\n say(`${slotKey}: still held by ${describe(settled.entry ?? incumbent.entry)} after retake ${retakes}`)\n },\n release(): void {\n while (installed.length > 0) {\n try { installed.pop()?.() } catch {}\n }\n ours.clear()\n declared.clear()\n retakes = 0\n holding = false\n },\n }\n}\n\n/**\n * Take and hold the composer's model seat for one ACP-mode spell.\n *\n * Lifecycle: the gate creates a claim when the staged conversation enters ACP\n * mode, calls `sync()` on every evaluation (store mutation, session-list\n * notification, preset frame, and any change to the cell itself), and calls\n * `release()` when the conversation leaves ACP mode or the gate tears down.\n */\nexport function createModelSeatClaim(options: ModelSeatClaimOptions): ModelSeatClaim {\n return createSeatClaim({\n slots: options.slots,\n slotKey: MODEL_SEAT_KEY,\n basePriority: MODEL_SEAT_PRIORITY,\n occupant: options.occupant,\n inject: options.inject,\n label: 'model seat',\n onDiag: options.onDiag,\n maxClaims: options.maxClaims,\n })\n}\n\n/** Null placeholder component for blanking a slot. */\nconst BlankSlotOccupant = (): null => null\n\n/** Empty inject face provider for slots that require an inject definition. */\nconst emptyInject = (): Record<string, never> => ({})\n\n/**\n * Take and hold the composer's permission seat for one ACP-mode spell.\n *\n * Same lifecycle as {@link createModelSeatClaim} — created when the staged\n * conversation enters ACP mode, `sync()`ed on every gate evaluation,\n * `release()`d the instant it leaves — with one difference worth saying out\n * loud: this claim has NO occupant to show for itself. Its entire product is\n * the ABSENCE of the host's Access mode control, and absence is exactly what\n * cannot be inspected after the fact. A permission claim that outlives its\n * reason does not render an obvious wrong thing on an ACP conversation; it\n * silently removes a control from ordinary ones, and every path through the\n * gate that cannot confirm the verdict releases it.\n */\nexport function createPermissionSeatClaim(options: PermissionSeatClaimOptions): ModelSeatClaim {\n return createSeatClaim({\n slots: options.slots,\n slotKey: PERMISSION_SEAT_KEY,\n basePriority: PERMISSION_SEAT_PRIORITY,\n occupant: options.occupant ?? (() => BlankSlotOccupant),\n inject: options.inject ?? emptyInject,\n label: 'permission seat',\n onDiag: options.onDiag,\n maxClaims: options.maxClaims,\n })\n}\n","/**\n * The composer dock's `x轮x步 · xx tok/s` pill on an ACP conversation.\n *\n * WHY SHADOW INSTEAD OF A SECOND PILL: the host's `ActivityPill` already states\n * the two things this conversation has — turn/step counts and a whole-session\n * decode speed — in the one place a speed belongs. Only its speed is wrong for\n * ACP: the host divides the tokens a step emitted by `firstTokenTime →\n * completedTime`, and that window contains every tool wait. Rendering a second\n * speed beside it would leave two numbers on screen and no way to tell which\n * is the honest one. So this module takes the host pill's own cell (`list` slot\n * `conversation.composer.dock`, id `activity`) at a lower priority, renders the\n * HOST's component, and hands it a patched `sessionStats` whose decode fields\n * are the plugin's burst aggregate. Style, counts, dialog, aria and locale stay\n * the host's; only the number changes.\n *\n * NON-ACP CONVERSATIONS PASS THROUGH. The shadow is installed only while the\n * gate confirms the staged conversation runs the ACP preset, but a list entry\n * is projected into EVERY session's composer. The wrapper therefore renders the\n * host component with the ORIGINAL projection whenever `acpMode` is not\n * affirmatively true, making the shadow visually indistinguishable from the\n * shipped entry there.\n *\n * FAIL-OPEN IS STRUCTURAL: if the host entry, its inject face, its locale or\n * its `performanceUsage` source is missing, no claim is created and the native\n * pill stays exactly as shipped. If the host component rejects the patched\n * projection, the slot's error boundary retires this entry and the native\n * `activity` entry wins again. No CSS hiding and no DOM patching are involved.\n */\n\nimport { createElement, useMemo } from 'react'\nimport type { ComponentType } from 'react'\n// Type-only: pulls the conversation SlotMap merge into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { SessionBurst } from '../protocol.ts'\nimport { useUi } from './acp-ui.tsx'\nimport type { AcpStore, UiState } from './store.ts'\nimport { createSeatClaim } from './model-seat.tsx'\nimport type { ModelSeatClaim, SeatEntry, SeatSlots } from './model-seat.tsx'\n\nexport const DOCK_SLOT = 'conversation.composer.dock'\nexport const ACTIVITY_ENTRY_ID = 'activity'\n\ntype ActivityProjection = (key: string, selector?: (value: unknown) => unknown) => unknown\ntype ActivityPerformanceHook = (selector?: (value: unknown) => unknown) => unknown\ntype ActivityTranslate = (key: string, params?: Record<string, unknown>) => string\n\n/** The standard kit + injected face the shadow renders the host component with. */\nexport interface ActivityPillHostProps {\n sessionId: string\n useChat: unknown\n useProjection: ActivityProjection\n usePerformanceUsage: ActivityPerformanceHook\n t?: ActivityTranslate\n acp: AcpStore\n}\n\ninterface OccupantProps extends ActivityPillHostProps {\n host: ComponentType<Record<string, unknown>>\n}\n\n/**\n * The aggregate the dock should show: the sum of every ACP session backing this\n * chat (the host pill's counts are whole-chat too), with a live `turn/metrics`\n * entry preferred per session over its persisted row. `undefined` means \"no\n * measurable burst yet\" — the wrapper then hides the speed instead of showing\n * the host's tool-inclusive number.\n */\nexport function activityBurst(\n ui: Pick<UiState, 'chatSessions' | 'chatSessionsFor' | 'burstStats'>,\n dshSessionId: string,\n): SessionBurst | undefined {\n if (ui.chatSessionsFor !== dshSessionId) return undefined\n let outputTokens = 0\n let burstMs = 0\n for (const row of ui.chatSessions) {\n const measured = ui.burstStats[row.sessionId] ?? row.burst\n if (measured === undefined) continue\n outputTokens += measured.outputTokens\n burstMs += measured.burstMs\n }\n return outputTokens > 0 && burstMs > 0 ? { outputTokens, burstMs } : undefined\n}\n\n/**\n * Replace ONLY the decode half of the host's session stats.\n *\n * `decodeTokens` and `decodeMs` are the two fields the host's `decodeSpeed`\n * reads (`decodeTokens / (decodeMs / 1000)`), so writing the plugin's aggregate\n * there makes the host's own formatter, dialog row and aria label display the\n * plugin's burst rate. Every other field — turns, steps, LLM/tool/TTFT — stays\n * host-native. With no measurable burst the two decode fields are zeroed: the\n * host then hides the speed (`decodeMs > 0` guard) while keeping the counts.\n */\nexport function patchedActivityStats(\n native: Record<string, unknown> | undefined,\n burst: SessionBurst | undefined,\n acp: boolean,\n): Record<string, unknown> | undefined {\n if (!acp || native === undefined) return native\n if (burst !== undefined && burst.outputTokens > 0 && burst.burstMs > 0) {\n return { ...native, decodeTokens: burst.outputTokens, decodeMs: burst.burstMs }\n }\n return { ...native, decodeTokens: 0, decodeMs: 0 }\n}\n\nfunction ActivityPillOccupant({ host: Host, ...props }: OccupantProps) {\n const ui = useUi(props.acp)\n const dshSessionId = String(props.sessionId)\n const acpMode = ui.acpMode[dshSessionId] === true\n const native = props.useProjection('sessionStats') as Record<string, unknown> | undefined\n const burst = acpMode ? activityBurst(ui, dshSessionId) : undefined\n const stats = useMemo(\n () => patchedActivityStats(native, burst, acpMode),\n [native, burst?.outputTokens, burst?.burstMs, acpMode],\n )\n // The current host pill reads exactly one projection. Returning `undefined`\n // for another key keeps the wrapper from claiming support it has not been\n // taught; a future host that asks for more will abdicate to the native entry.\n const projection = useMemo<ActivityProjection>(\n () => (key, selector) => {\n if (key !== 'sessionStats') return undefined\n return selector === undefined ? stats : selector(stats)\n },\n [stats],\n )\n return createElement(Host, {\n useChat: props.useChat,\n useProjection: acpMode ? projection : props.useProjection,\n usePerformanceUsage: props.usePerformanceUsage,\n t: props.t,\n })\n}\n\nfunction findHostActivity(slots: SeatSlots): SeatEntry | undefined {\n try {\n if (slots.spec?.(DOCK_SLOT)?.kind !== 'list') return undefined\n const heads = slots.entriesOfSlot?.(DOCK_SLOT) ?? []\n return heads.find(entry => entry.options?.id === ACTIVITY_ENTRY_ID)\n } catch {\n return undefined\n }\n}\n\nexport interface ActivityPillClaimOptions {\n slots: SeatSlots\n /** Session-scoped face factory (the gate's `chatFace`). */\n inject(sessionId: unknown, actions?: unknown): unknown\n onDiag?(line: string): void\n maxClaims?: number\n}\n\n/**\n * Take the dock's `activity` cell for one ACP-mode spell.\n *\n * Returns `undefined` on any host shape this bundle cannot serve — an older\n * host without the `activity` entry (0.1.5 ships `stats`), a slot kind change,\n * a missing inject/locale, or no `performanceUsage` source. The native pill is\n * then left untouched; that is the honest failure mode, not a hidden control.\n */\nexport function createActivityPillClaim(options: ActivityPillClaimOptions): ModelSeatClaim | undefined {\n const host = findHostActivity(options.slots)\n if (host === undefined\n || host.component === undefined\n || typeof host.inject !== 'function'\n || typeof host.locale !== 'string'\n || host.locale === '') {\n options.onDiag?.('activity pill: host entry not available')\n return undefined\n }\n let performanceUsage: unknown\n try {\n const face = host.inject() as { hooks?: { performanceUsage?: unknown } } | undefined\n performanceUsage = face?.hooks?.performanceUsage\n } catch {\n performanceUsage = undefined\n }\n if (performanceUsage === undefined) {\n options.onDiag?.('activity pill: host performance hook missing')\n return undefined\n }\n const Host = host.component as ComponentType<Record<string, unknown>>\n return createSeatClaim({\n slots: options.slots,\n slotKey: DOCK_SLOT,\n entryId: ACTIVITY_ENTRY_ID,\n order: host.options?.order ?? 0,\n locale: host.locale,\n basePriority: -1,\n occupant: () => (props: Record<string, unknown>) => createElement(ActivityPillOccupant, {\n ...props,\n host: Host,\n } as unknown as OccupantProps),\n inject: (sessionId, actions) => ({\n ...(options.inject(sessionId, actions) as Record<string, unknown>),\n hooks: { performanceUsage },\n }),\n label: 'activity pill',\n onDiag: options.onDiag,\n maxClaims: options.maxClaims ?? 1,\n })\n}\n","/**\n * Slash isolation for ACP conversations.\n *\n * In an ACP conversation the composer's `/` menu belongs to the AGENT: the\n * commands it declared over `available_commands_update`. The host's own slash\n * machinery (`dsh-client-ui-commands` plus the client skill source) describes\n * commands THIS runtime would execute — `/model`, `/permission`, `/export`,\n * the DSH skill catalog — and a name it recognizes is consumed before the\n * draft ever reaches the prompt sink, so the agent never sees the line.\n *\n * There is no supported seam for \"this session only\": `InputTriggerService`\n * adjudicates in REGISTRATION order (`order` only sorts the menu), duplicate\n * `(trigger, name)` registration throws, and sources get no per-session\n * availability hook — so a source registered later can never outrank the\n * host's `command` source for one conversation. Replacing the whole composer\n * is the other option and is not worth the price (attachments, queue, draft,\n * shortcuts).\n *\n * What remains is wrapping the LIVE source objects in memory: same\n * registration, same menu group, different answers while the session is in ACP\n * mode. Nothing on disk is patched or imported, so an upstream change cannot\n * make a replacement file go missing — the worst case is that the source no\n * longer looks like what this module knows, and then nothing is wrapped and\n * the conversation keeps the host's behaviour (fail-open; a diagnostic line is\n * reported instead of an error).\n *\n * The wrappers are per CALL, not per instance: the source objects are shared\n * by every conversation, and the session id each hook receives is what tells\n * an ACP conversation from an ordinary one.\n */\n\nimport type { AcpSessionsSnapshot } from './acp-ui.tsx'\nimport { ACP_PRESET_ID, rowAgentPreset, stagedSessionOf } from './acp-ui.tsx'\nimport type { AgentCommand } from './store.ts'\n\n/** One menu row: the ACP command name, plus whatever makes it findable. */\nexport interface SlashCandidate {\n name: string\n label?: string\n description?: string\n hint?: string\n}\n\n/** The only field these hooks receive about the owning conversation. */\nexport interface SlashSession {\n readonly sessionId: string\n}\n\nexport interface SlashCandidateRequest {\n readonly query: string\n /** Leading token vs. a `/` inside running text; hint-bearing rows are leading-only. */\n readonly position?: 'leading' | 'inline'\n readonly signal?: AbortSignal\n}\n\nexport interface SlashPick {\n readonly candidate: SlashCandidate\n readonly session: SlashSession\n}\n\n/**\n * One registered trigger source, narrowed to what isolation touches. Every\n * hook is optional because the contract makes them optional — a source that\n * never participated in Enter adjudication must not start participating just\n * because we wrapped its `candidates`.\n */\nexport interface SlashSource {\n readonly trigger: string\n readonly name: string\n candidates?: (session: SlashSession, req: SlashCandidateRequest) => Promise<readonly SlashCandidate[]>\n matchEnter?: (session: SlashSession, line: string, signal: AbortSignal, envelope: { attachments: number }) => Promise<unknown>\n matchSpace?: (session: SlashSession, token: string) => unknown\n onPick?: (pick: SlashPick) => unknown\n warm?: (session: SlashSession) => void\n lexicon?: (session: SlashSession) => readonly string[] | undefined\n header?: (session: SlashSession, req: { query: string }) => readonly unknown[] | undefined\n}\n\nexport interface SlashIsolationOptions {\n /** The live source registry (`inputTriggers.live.sources`), re-read on every sync. */\n sources(): readonly SlashSource[] | undefined\n /** The ACP verdict this page holds; `undefined` = no answer yet. */\n verdict(sessionId: string): boolean | undefined\n /** Ask the host and wait for the in-flight read, then answer again. */\n ensureVerdict(sessionId: string): Promise<boolean | undefined>\n /**\n * The agent's declared commands for this conversation (live session first,\n * agent-level cache second); `undefined` = nothing known yet. May load the\n * cached surface first — that read is pure memory on the host.\n */\n commands(sessionId: string): readonly AgentCommand[] | undefined | Promise<readonly AgentCommand[] | undefined>\n /** Kick a fresh `session/chat-list` read (fire and forget). */\n refresh(sessionId: string): void\n /** Fail-open reports (console and the caller's own sink). */\n onDiag?(line: string): void\n}\n\nexport interface SlashIsolation {\n /**\n * Find and wrap every live `/` source. Idempotent and safe to re-run.\n * Returns whether isolation is in force (at least one `/` source is wrapped)\n * — `false` is what lets the caller keep retrying until the source exists.\n */\n sync(): boolean\n /** Restore every wrapped source — only where our wrapper is still installed. */\n dispose(): void\n}\n\n/**\n * Cross-instance marker. `apply()` can run again over the same live sources (a\n * page reload of the plugin, not just a page load), and a wrapper owned by the\n * previous instance closes over that instance's store — it must be restored\n * before the new one wraps, or the new callbacks never run.\n */\nconst WRAP_MARKER = Symbol.for('dsh-ntr.slash-isolation')\n\ninterface WrapMarker {\n /** The instance that installed these wrappers. */\n owner: object\n restore(): void\n}\n\n/**\n * Whether one chat session runs the ACP preset, under the same two fact\n * sources the seat gate uses: the staged row's own preset when it speaks, and\n * the host-read verdict otherwise. `undefined` = no evidence yet, which every\n * caller reads as \"not ACP\" (fail-open: an ordinary conversation must never\n * lose its native commands to a guess).\n */\nexport function slashAcpVerdict(\n snapshot: AcpSessionsSnapshot | undefined,\n sessionId: string,\n verdict: (sessionId: string) => boolean | undefined,\n currentSessionId?: string,\n): boolean | undefined {\n const staged = stagedSessionOf(snapshot, currentSessionId)\n if (staged !== undefined && staged.sessionId === sessionId) {\n const listed = rowAgentPreset(staged.row)\n if (listed !== undefined) return listed === ACP_PRESET_ID\n }\n return verdict(sessionId)\n}\n\nconst NO_MATCH = Number.NEGATIVE_INFINITY\n\n/** Extra weight for name starts and separator boundaries (same as the host's ranker). */\nfunction boundaryBonus(name: string, index: number): number {\n return index === 0 || name.charAt(index - 1) === '-' || name.charAt(index - 1) === '_' ? 8 : 0\n}\n\n/**\n * Score the strongest ordered-subsequence alignment of `query` in `name`, or\n * `undefined` when the query is not a subsequence.\n *\n * A hand copy of `alignmentScore` from\n * `@deepseek-ai/dsh-client-ui-primitives`'s `rankByName`: this bundle does not\n * depend on that package, and the menu should rank exactly the way the host\n * ranks its own commands (prefix hits first, then this score). Keep the two in\n * sync if the host's ranker changes.\n */\nfunction alignmentScore(name: string, query: string): number | undefined {\n if (query.length > name.length) return undefined\n let previous = new Array<number>(name.length).fill(NO_MATCH)\n for (let index = 0; index < name.length; index++) {\n if (name.charAt(index) === query.charAt(0)) previous[index] = 1 + boundaryBonus(name, index) - index\n }\n for (let queryIndex = 1; queryIndex < query.length; queryIndex++) {\n const current = new Array<number>(name.length).fill(NO_MATCH)\n let left = NO_MATCH\n let leftLeft = NO_MATCH\n let bestGapped = NO_MATCH\n for (let index = 0; index < name.length; index++) {\n const prior = previous[index] ?? NO_MATCH\n if (leftLeft !== NO_MATCH) bestGapped = Math.max(bestGapped, leftLeft + index - 2)\n if (name.charAt(index) === query.charAt(queryIndex)) {\n const bonus = 1 + boundaryBonus(name, index)\n let score = NO_MATCH\n if (left !== NO_MATCH) score = left + bonus + 4\n if (bestGapped !== NO_MATCH) score = Math.max(score, bestGapped + bonus + 1 - index)\n current[index] = score\n }\n leftLeft = left\n left = prior\n }\n previous = current\n }\n let best = NO_MATCH\n for (const score of previous) best = Math.max(best, score)\n return best === NO_MATCH ? undefined : best\n}\n\n/**\n * The agent's commands as menu rows, ranked the way the host ranks its own\n * menu: the query matches as a case-insensitive ordered subsequence of the\n * command name; prefix hits come first, then the strongest alignment score,\n * then the agent's own order (empty query = everything, in that order).\n *\n * Matching is name-only, like `rankByName`: fuzzy-matching whole descriptions\n * surfaced rows nobody would think to look for, and the native menu has never\n * searched them.\n */\nexport function commandCandidates(commands: readonly AgentCommand[], query: string): SlashCandidate[] {\n const q = query.trim().toLowerCase()\n const seen = new Set<string>()\n const ranked: Array<{ candidate: SlashCandidate; order: number; prefix: boolean; score: number }> = []\n let order = 0\n for (const command of commands) {\n if (command === null || typeof command !== 'object') continue\n const name = typeof command.name === 'string' ? command.name.trim() : ''\n if (name === '' || seen.has(name)) continue\n seen.add(name)\n // Source order counts EVERY accepted row, matched or not — the host's\n // ranker breaks score ties by the input array's index, not by a compacted\n // list of matches.\n const position = order\n order += 1\n const description = typeof command.description === 'string' && command.description !== '' ? command.description : undefined\n const input = command.input as { hint?: unknown } | undefined\n const hint = typeof input?.hint === 'string' && input.hint !== '' ? input.hint : undefined\n const candidate: SlashCandidate = {\n name,\n ...(description !== undefined ? { description } : {}),\n ...(hint !== undefined ? { hint } : {}),\n }\n if (q === '') {\n ranked.push({ candidate, order: position, prefix: true, score: 0 })\n continue\n }\n const lower = name.toLowerCase()\n const score = alignmentScore(lower, q)\n if (score === undefined) continue\n ranked.push({ candidate, order: position, prefix: lower.startsWith(q), score })\n }\n if (q === '') return ranked.map(match => match.candidate)\n ranked.sort((left, right) =>\n Number(right.prefix) - Number(left.prefix)\n || right.score - left.score\n || left.order - right.order)\n return ranked.map(match => match.candidate)\n}\n\nexport function createSlashIsolation(options: SlashIsolationOptions): SlashIsolation {\n /** This instance's identity in the wrap marker (see `dispose`). */\n const owner = {}\n /** Sources this instance wrapped, so dispose can restore them. */\n const owned = new Set<SlashSource>()\n /**\n * The ONE `/` source whose menu group serves the agent's commands. Every\n * other `/` source is silenced in ACP mode — otherwise the same list would\n * appear once per group (`command` AND `skill` both register on `/`). The\n * host's command group is the natural carrier; it is picked by name so the\n * choice does not depend on registration order.\n */\n let serving: SlashSource | undefined\n let lastDiag = 'idle'\n /** A problem line was reported during the current sync. */\n let failed = false\n\n const report = (line: string): void => {\n failed = true\n if (lastDiag === line) return\n lastDiag = line\n options.onDiag?.(line)\n }\n\n const sessionIdOf = (session: SlashSession | undefined): string | undefined =>\n typeof session?.sessionId === 'string' && session.sessionId !== '' ? session.sessionId : undefined\n\n /** The verdict held synchronously, or `undefined`. Never asks. */\n const heldVerdict = (session: SlashSession | undefined): boolean | undefined => {\n const sessionId = sessionIdOf(session)\n if (sessionId === undefined) return undefined\n try {\n return options.verdict(sessionId)\n } catch {\n return undefined\n }\n }\n\n /**\n * Last host ask per session. The menu re-polls `candidates` on every\n * keystroke, and a host that never answers the verdict query would otherwise\n * turn each one into a `session/chat-list` round-trip — in ORDINARY\n * conversations too, since the wrapped source is shared by all of them.\n */\n const askedAt = new Map<string, number>()\n const ASK_INTERVAL_MS = 1000\n /** Same idea for the \"no commands known\" refresh: one read per window, not per keystroke. */\n const refreshedAt = new Map<string, number>()\n const REFRESH_INTERVAL_MS = 1500\n\n /** The verdict, asking the host at most once per {@link ASK_INTERVAL_MS}. */\n const settledVerdict = async (session: SlashSession | undefined): Promise<boolean | undefined> => {\n const sessionId = sessionIdOf(session)\n if (sessionId === undefined) return undefined\n const held = heldVerdict(session)\n if (held !== undefined) return held\n const last = askedAt.get(sessionId) ?? 0\n if (Date.now() - last < ASK_INTERVAL_MS) return undefined\n askedAt.set(sessionId, Date.now())\n try {\n return await options.ensureVerdict(sessionId)\n } catch {\n return undefined\n }\n }\n\n const wrap = (source: SlashSource): boolean => {\n if (owned.has(source)) return false\n const existing = (source as unknown as Record<symbol, WrapMarker | undefined>)[WRAP_MARKER]\n try {\n existing?.restore()\n } catch {\n // A stale wrapper that cannot restore itself is still better replaced\n // than left in place: the new wrapper re-reads the current property.\n }\n if (typeof source.candidates !== 'function') {\n report(`slash source \"${source.name}\" has no candidates hook; left alone`)\n return false\n }\n\n const originals = {\n candidates: source.candidates,\n matchEnter: source.matchEnter,\n matchSpace: source.matchSpace,\n onPick: source.onPick,\n warm: source.warm,\n lexicon: source.lexicon,\n header: source.header,\n }\n const hadOwn = new Map<keyof typeof originals, boolean>()\n for (const key of Object.keys(originals) as Array<keyof typeof originals>) {\n hadOwn.set(key, Object.prototype.hasOwnProperty.call(source, key))\n }\n\n const patched: Array<{ key: keyof typeof originals; wrapper: unknown }> = []\n\n // The menu. In ACP mode its only rows are the agent's own commands; the\n // host directory is never read (which is also why a broken host catalog\n // cannot fail the menu open here).\n const candidates = async (session: SlashSession, req: SlashCandidateRequest): Promise<readonly SlashCandidate[]> => {\n if (await settledVerdict(session) !== true) return originals.candidates!.call(source, session, req)\n // A second `/` group would list the same commands again (and an empty\n // group auto-hides), so only the carrier source answers in ACP mode.\n if (serving !== source) return []\n const sessionId = sessionIdOf(session)!\n let commands: readonly AgentCommand[] | undefined\n try {\n commands = await options.commands(sessionId)\n } catch {\n // A failed cache read is \"nothing known\", never a failed menu.\n commands = undefined\n }\n if (commands === undefined) {\n // Nothing known for this conversation yet. Ask the host once per\n // window (the menu re-polls on every keystroke) and show nothing\n // rather than the host's catalog in the meantime.\n const last = refreshedAt.get(sessionId) ?? 0\n if (Date.now() - last >= REFRESH_INTERVAL_MS) {\n refreshedAt.set(sessionId, Date.now())\n options.refresh(sessionId)\n }\n return []\n }\n const rows = commandCandidates(commands, req?.query ?? '')\n // Same rule the host menu applies: a command that takes arguments is\n // only a completion at the leading position, not inside running text.\n return req?.position === 'inline' ? rows.filter(row => row.hint === undefined) : rows\n }\n source.candidates = candidates\n patched.push({ key: 'candidates', wrapper: candidates })\n\n // Enter/Space. `undefined` = \"no claimant\": the line falls through to the\n // default sink and reaches the agent byte-for-byte. A trailing space or\n // arguments are the agent's to parse, not ours.\n if (typeof originals.matchEnter === 'function') {\n const matchEnter: NonNullable<SlashSource['matchEnter']> = async (session, line, signal, envelope) => {\n if (await settledVerdict(session) === true) return undefined\n return originals.matchEnter!.call(source, session, line, signal, envelope)\n }\n source.matchEnter = matchEnter\n patched.push({ key: 'matchEnter', wrapper: matchEnter })\n }\n if (typeof originals.matchSpace === 'function') {\n const matchSpace: NonNullable<SlashSource['matchSpace']> = (session, token) => {\n if (heldVerdict(session) === true) return undefined\n return originals.matchSpace!.call(source, session, token)\n }\n source.matchSpace = matchSpace\n patched.push({ key: 'matchSpace', wrapper: matchSpace })\n }\n\n // A pick inserts the command into the draft; it never executes anything.\n // The user still presses Enter, and Enter is not ours to consume — the\n // same line then travels to the agent like any other message.\n if (typeof originals.onPick === 'function') {\n const onPick: NonNullable<SlashSource['onPick']> = (pick) => {\n if (heldVerdict(pick?.session) !== true) return originals.onPick!.call(source, pick)\n const name = typeof pick?.candidate?.name === 'string' ? pick.candidate.name.trim() : ''\n return name === '' ? undefined : { text: `/${name} ` }\n }\n source.onPick = onPick\n patched.push({ key: 'onPick', wrapper: onPick })\n }\n\n // Host-side warmups and decoration are harness work in an ACP\n // conversation: no catalog to fetch, and no host skill names to underline\n // in the draft (the agent's commands are not DSH references).\n if (typeof originals.warm === 'function') {\n const warm: NonNullable<SlashSource['warm']> = (session) => {\n if (heldVerdict(session) === true) return\n originals.warm!.call(source, session)\n }\n source.warm = warm\n patched.push({ key: 'warm', wrapper: warm })\n }\n if (typeof originals.lexicon === 'function') {\n const lexicon: NonNullable<SlashSource['lexicon']> = (session) => {\n if (heldVerdict(session) === true) return undefined\n return originals.lexicon!.call(source, session)\n }\n source.lexicon = lexicon\n patched.push({ key: 'lexicon', wrapper: lexicon })\n }\n if (typeof originals.header === 'function') {\n const header: NonNullable<SlashSource['header']> = (session, req) => {\n if (heldVerdict(session) === true) return undefined\n return originals.header!.call(source, session, req)\n }\n source.header = header\n patched.push({ key: 'header', wrapper: header })\n }\n\n const slots = source as unknown as Record<string, unknown>\n const restore = (): void => {\n for (const { key, wrapper } of patched) {\n // Only undo OUR assignment: a later wrapper (another instance, or an\n // upstream reassignment) owns the property now, and restoring over it\n // would resurrect code nobody is running.\n if (slots[key] !== wrapper) continue\n const original = originals[key]\n if (original !== undefined) {\n slots[key] = original\n } else if (hadOwn.get(key) === false) {\n delete slots[key]\n } else {\n slots[key] = undefined\n }\n }\n }\n\n ;(source as unknown as Record<symbol, WrapMarker>)[WRAP_MARKER] = { owner, restore }\n owned.add(source)\n return true\n }\n\n return {\n sync(): boolean {\n failed = false\n let sources: readonly SlashSource[] | undefined\n try {\n sources = options.sources()\n } catch {\n sources = undefined\n }\n if (sources === undefined) {\n report('the slash registry is unavailable; host slash commands stay in charge')\n return false\n }\n // Prefer the host's command group by name; fall back to any `/` source so\n // a live menu still exists if upstream renames it.\n serving = sources.find(source => source?.trigger === '/' && source.name === 'command')\n ?? sources.find(source => source?.trigger === '/')\n let inForce = false\n for (const source of sources) {\n if (source === null || typeof source !== 'object') continue\n if (source.trigger !== '/') continue\n try {\n if (wrap(source) || owned.has(source)) inForce = true\n } catch (error) {\n report(`could not wrap slash source \"${String(source.name)}\": ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n // Back to `idle` after a problem healed. The caller's sink is the store,\n // which no-ops when the value did not change.\n if (!failed && inForce && lastDiag !== 'idle') {\n lastDiag = 'idle'\n options.onDiag?.('idle')\n }\n return inForce\n },\n dispose(): void {\n for (const source of owned) {\n const marker = (source as unknown as Record<symbol, WrapMarker | undefined>)[WRAP_MARKER]\n // A newer instance replaced our wrappers (a plugin reload re-applies\n // over the same live sources). Restoring on top of it would resurrect\n // hooks nobody is running — its own dispose is what ends that wrap.\n if (marker?.owner !== owner) continue\n try {\n marker.restore()\n } catch {\n // Disposal must never throw: a source that cannot be restored is\n // simply left as the host finds it.\n }\n ;(source as unknown as Record<symbol, WrapMarker | undefined>)[WRAP_MARKER] = undefined\n }\n owned.clear()\n askedAt.clear()\n refreshedAt.clear()\n },\n }\n}\n","/**\n * dsh-ntr browser entry: builds the store over the Connection RPC face and\n * contributes every public seat — the settings section, the ACP conversation\n * view, the agent-native mode control, and the ACP base in the composer's\n * model seat (installed by the ACP-mode gate, which is what keeps it out of\n * every non-ACP conversation).\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SlotComponent } from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// The chat package's client types carry the SlotMap augmentation for\n// `conversation.chat.assistant-actions` (the branch entry's seat). Type-only:\n// erased at build, and the seat is owned by the host's chat module at runtime.\nimport type {} from '@deepseek-ai/dsh-client-ui-chat/client'\nimport { AcpStore, type RpcCall } from './store.ts'\nimport { AcpSettingsSection } from './panel.tsx'\nimport { AcpComposerMode } from './composer-mode.tsx'\nimport { AcpContextMeter } from './context-meter.tsx'\nimport { AcpComposerAgentTag, AcpSessionControls, AcpSessionMenu } from './session-controls.tsx'\nimport { AcpConversationView } from './acp-view.tsx'\nimport { AcpSettingsDialog } from './settings-dialog.tsx'\nimport { PermissionDock } from './permission-dock.tsx'\nimport { ReleasedBanner } from './released-banner.tsx'\nimport { AcpBranchAction, resolveHostFork } from './branch-action.tsx'\nimport { createActivityPillClaim, DOCK_SLOT } from './activity-pill.tsx'\nimport {\n createModelSeatClaim,\n createPermissionSeatClaim,\n MODEL_SEAT_KEY,\n PERMISSION_SEAT_KEY,\n} from './model-seat.tsx'\nimport type { ModelSeatClaim, SeatSlots } from './model-seat.tsx'\nimport {\n acpModeFromPresetEvent, ACP_PRESET_ID, rowAgentPreset, stagedIsAcpMode, stagedSessionOf,\n} from './acp-ui.tsx'\nimport type { AcpChatInjected, AcpHostForkFace, AcpSessionFace, AcpSessionsSnapshot, SessionListSource } from './acp-ui.tsx'\nimport { createSlashIsolation, slashAcpVerdict } from './slash-isolation.ts'\nimport type { SlashSource } from './slash-isolation.ts'\n\nexport const name = 'dsh-ntr-client'\nexport const inject = ['slots', 'connection']\n\n/** Structural view of the browser Connection handle (official type is not merged into Context). */\ninterface BrowserConnection {\n rpc?: {\n call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<\n | { ok: true; value: unknown }\n | { ok: false; error: { code: string; message: string } }\n >\n }\n}\n\n/**\n * Optional services, resolved structurally rather than injected. Declaring\n * `sessions` in `inject` would make this plugin fail to load in a deployment\n * that composes only a subset of the official client modules — and it is read\n * through `ctx.get` at call time, so a late-arriving service still answers.\n */\ninterface StructuralServices {\n sessions: {\n binding(sessionId: string): { session?: unknown } | undefined\n /** The client runtime's session list store (see `AcpSessionsSnapshot`). */\n list?: SessionListSource\n /**\n * The runtime's own branching primitive (`forkAt`'s engine): forks the DSH\n * session tree at the latest completed turn and resolves the child id.\n * Read structurally and used under try/catch — an absent `fork` is the\n * branch button's `hostUnavailable` state, never a load failure.\n */\n fork?: (options: { sessionId: string; increaseTitle?: boolean }) => Promise<unknown>\n }\n /**\n * The renderer's Session scope adapter (`uiSession.adapter`). Its `current`\n * observable is the host's own selection — the session the composer is\n * actually staged on — while the list can retain more than one row in a\n * multi-view layout. Read structurally and used under try/catch: a host\n * without it falls back to the list's main-view scan.\n */\n uiSession: {\n adapter?: {\n current?: {\n getSnapshot(): { key?: unknown } | undefined\n subscribe?(listener: () => void): () => void\n }\n }\n }\n /**\n * The shell workspace service (`ctx.get(\"uiWorkspace\")`): `openSession`\n * focuses a session in the main view — the branch click's final step.\n */\n uiWorkspace: {\n openSession?(sessionId: string): void\n }\n}\n\nconst browserPrefersZh = typeof navigator !== 'undefined' && /^zh\\b/i.test(navigator.language)\n\n/**\n * The NTR scope marker (`html[data-ntr-acp]`): the ONLY hook the message-action\n * hiding CSS keys on, so the host's like / dislike / branch icons disappear in\n * exactly the conversations this gate has confirmed to run the ACP preset —\n * and reappear the moment the evidence stops answering (every `standDown`\n * clears it; non-ACP sessions never see it). Idempotent per call.\n */\nconst setScopeMarker = (active: boolean): void => {\n if (typeof document === 'undefined') return\n document.documentElement.toggleAttribute('data-ntr-acp', active)\n}\n\n/**\n * One-shot sanity probe for the aria-label hiding: the host's action buttons\n * carry LOCALIZED labels and no data-* hook, so a copy change (or a third\n * language) silently un-hides them while everything else keeps working. Once\n * the marker is on and a turn tail is actually on screen, check that at least\n * one label the rules key on still exists and leave one console line for\n * whoever debugs \"the icons came back\" — dev-visible, never user UI.\n */\nlet probedHostLabels = false\nconst probeHostActionLabels = (): void => {\n if (probedHostLabels || typeof document === 'undefined') return\n // No turn tail rendered yet: the check would answer from nothing, so leave\n // the probe armed for the next activation instead of burning it once.\n if (document.querySelector('[data-turn-tail]') === null) return\n probedHostLabels = true\n const found = document.querySelector([\n 'button:not([data-ntr-branch])[aria-label=\"在新对话中分支\"]',\n 'button:not([data-ntr-branch])[aria-label=\"Branch into a new conversation\"]',\n 'button[aria-label=\"好的回答\"]',\n 'button[aria-label=\"Good response\"]',\n ].join(', '))\n if (found === null) {\n console.warn('[dsh-ntr] none of the host message-action labels matched; the like/dislike/branch icons may not be hidden in NTR conversations')\n }\n}\n\n/**\n * Structural view of the shell's locale service (`@deepseek-ai/dsh-client-locale`).\n *\n * Read through {@link ctx.get} rather than injected: a composition without the\n * service must still load, and the label pairs carry their own browser-derived\n * fallback for exactly that case.\n */\ninterface LocaleFace {\n resolveText?(text: Record<string, string>): string\n subscribe?(listener: () => void): () => void\n}\n\n/**\n * How long a host verdict may stand as the gate's ONLY fact source before the\n * gate asks the host again.\n *\n * The gate prefers the staged session row's own preset. On a host whose client\n * rows carry no `agentPreset` the row is silent, and then the\n * `session/chat-list` verdict is all there is — and it used to be asked ONCE\n * per session, for the rest of the page's life. A preset switch that no\n * `agent-preset/selected` frame announced (the `remote` face absent when this\n * effect ran, or a runtime that never forwards the frame) therefore left the\n * gate judging a conversation that had already left ACP mode: both cells stayed\n * claimed, and the permission cell's product is an ABSENCE — an ordinary\n * conversation silently lost its Access mode control with nothing on screen to\n * say why. The host reads the session log every switch is appended to, so\n * re-asking is what lets the truth arrive late; the bound keeps that at one\n * round-trip per window per staged session instead of one per store mutation\n * (the resolve-loop the one-shot ask was bought to prevent).\n */\nconst ACP_MODE_VERDICT_TTL_MS = 2000\n\n/**\n * Slash-source discovery retry (see the slash-isolation effect): a source\n * registers with no event, so a module that loads after this one is found by\n * polling the roster a bounded number of times. 20 × 500ms covers a slow page\n * load; the timer stops as soon as a source is wrapped.\n */\nconst SLASH_SYNC_ATTEMPTS = 20\nconst SLASH_SYNC_DELAY_MS = 500\n\nexport function apply(ctx: Context): void {\n const connection = ctx.get('connection') as BrowserConnection | undefined\n const rpc = connection?.rpc\n if (rpc === undefined) {\n throw new Error('dsh-ntr: browser connection.rpc is unavailable')\n }\n const call: RpcCall = (channel, endpoint, payload) => rpc.call(channel, endpoint, payload)\n\n /**\n * Resolve one optional service without injecting it. A plain `ctx.get` at\n * call time keeps the plugin loadable when the service is absent AND picks it\n * up when it arrives later — which an `inject` dependency cannot do without\n * also making its absence fatal.\n */\n const service = <T,>(name: string): T | undefined => (ctx as unknown as { get(key: string): unknown }).get(name) as T | undefined\n\n /**\n * Resolve a Chinese/English pair against the shell's active locale. Read per\n * call, so a language switch reaches every render-time `label()` call without\n * rebuilding the store. A host without the locale service keeps the\n * browser-derived choice the pairs shipped with.\n */\n const label = (zhText: string, enText: string): string => {\n const resolved = service<LocaleFace>('locale')?.resolveText?.({ zh: zhText, en: enText })\n return typeof resolved === 'string' && resolved !== '' ? resolved : browserPrefersZh ? zhText : enText\n }\n\n const store = new AcpStore(call, label)\n store.start()\n\n // A locale switch changes no data, only which side of each pair renders;\n // republishing the snapshot is what re-runs them. Attached through\n // `internal/service` so a late-arriving locale service is still hooked.\n ctx.effect(() => {\n let stop: (() => void) | undefined\n const attach = (): void => {\n if (stop !== undefined) return\n stop = service<LocaleFace>('locale')?.subscribe?.(() => { store.refreshLocale() })\n }\n attach()\n const stopService = ctx.on('internal/service', attach)\n return () => {\n stopService()\n stop?.()\n }\n }, 'dsh-ntr: locale switch re-render')\n\n /** This session's command/projection face (`ctx.sessions`); `undefined` before it materializes. */\n const faceFor = (sessionId: string): AcpSessionFace | undefined => {\n try {\n return service<StructuralServices['sessions']>('sessions')?.binding(sessionId)?.session as AcpSessionFace | undefined\n } catch {\n return undefined\n }\n }\n\n /**\n * The runtime's `sessions.fork`, resolved per call: `undefined` renders the\n * branch button disabled (`hostUnavailable`) instead of hiding it, so a\n * host that gains the primitive later is picked up on the next render.\n */\n const hostFork = (): AcpHostForkFace | undefined => {\n try {\n return resolveHostFork(service<StructuralServices['sessions']>('sessions'))\n } catch {\n return undefined\n }\n }\n\n /** Focus a session in the workspace (the branch click's last step). */\n const openSession = (sessionId: string): void => {\n try {\n service<StructuralServices['uiWorkspace']>('uiWorkspace')?.openSession?.(sessionId)\n } catch {\n // Opening is presentation; a host without the workspace service leaves\n // the branched session in the sidebar list, reachable by hand.\n }\n }\n\n /** The injected face every ACP conversation entry receives. */\n const chatFace = (sessionId: unknown): AcpChatInjected & { sessionId: string } => ({\n acp: store,\n sessionId: String(sessionId),\n faceFor,\n hostFork,\n openSession,\n })\n\n ctx.effect(() => ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'acp-agents',\n order: 120,\n label: () => 'NTR',\n inject: () => ({ acp: store }),\n }, AcpSettingsSection)), 'dsh-ntr: settings section')\n\n // The same NTR settings, opened from the composer when the agent roster is\n // empty (the agent base's picker action). It rides the frame's overlay layer\n // because the composer seat and the dialog live in unrelated slot trees; the\n // host settings shell has no programmatic open seam, so the dialog mounts the\n // same section the shell mounts (see settings-dialog.tsx). Renders nothing\n // while closed.\n ctx.effect(() => ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'acp-settings-dialog',\n order: 30,\n inject: () => ({ acp: store }),\n }, AcpSettingsDialog as unknown as SlotComponent<any>)), 'dsh-ntr: NTR settings dialog')\n\n // The agent's OWN modes sit in the composer tool row — the seat the slot\n // contract reserves for exactly this (\"entries sit beside that chrome, never\n // replace it\"). They are a different layer from everything the host controls:\n // this is HOW the agent works, not what it may touch.\n ctx.effect(() => ctx.slots.inject('conversation.input.left', () => ctx.slots.register({\n name: 'conversation.input.left',\n id: 'acp-mode',\n order: 20,\n // The id rides the injected face rather than the standard kit: the kit's\n // top-level `sessionId` only exists in newer renderers, while the positional\n // inject argument is the contract every version honors (see\n // composer-mode.tsx).\n inject: (sessionId: unknown) => chatFace(sessionId),\n // The cast is the price of the slot map's published `.d.ts` dropping its\n // `owner` field: the props contract is declared structurally in\n // composer-mode.tsx, so the composed-props constraint cannot be met by type\n // (see the note there). Runtime passes the real shares either way.\n }, AcpComposerMode as unknown as SlotComponent<any>)), 'dsh-ntr: composer agent-mode control')\n\n // The agent's OWN context occupancy in the composer's bottom info row\n // (`conversation.composer.dock` — the dock below the input card where the\n // host renders its native ContextMeter) is installed by the ACP-mode gate\n // below, with the same lifetime as the NTR view tab and the seats. It is\n // NOT registered unconditionally: in an ordinary conversation this row must\n // hold the host's native meter and nothing of ours (see `holdAuxEntries`).\n\n // Its picker rides the composer's overlay seat — the platform's home for\n // menus, so the popup floats above the card instead of being clipped by it.\n ctx.effect(() => ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register({\n name: 'conversation.input.overlay',\n id: 'acp-menu',\n order: 10,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpSessionMenu as unknown as SlotComponent<any>)), 'dsh-ntr: composer ACP picker')\n\n // The agent tag riding the composer card's top edge. The agent base steps\n // aside after the conversation's first ACP turn, which also LOCKS the\n // conversation to its agent (`agentBaseHidden` in session-controls.tsx); this\n // tag is the read-only nameplate that keeps naming it. It shares the overlay\n // seat with the picker itself; both are positioned floating entries inside\n // the card, while the tag is pinned to the card's top edge\n // (`.ntr-acp-tag`). Registered for every conversation, gated inside the\n // component.\n ctx.effect(() => ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register({\n name: 'conversation.input.overlay',\n id: 'acp-agent-tag',\n order: 5,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpComposerAgentTag as unknown as SlotComponent<any>)), 'dsh-ntr: composer agent tag')\n\n // Pending permission asks for the staged conversation, above the composer\n // card (`conversation.input.dock` — a session-scoped list slot; the host's\n // own queue/todo docks register the same way). The slot spans the whole\n // conversation column, so the dock component applies the composer's own\n // width formula to line up with the input box. Every ask is dispatched as\n // broadcast as `permission/request`; the dock renders only the ones carrying\n // this conversation's id, so it needs no ACP-mode gate. Asks without a chat\n // identity stay on the settings-panel ledger.\n ctx.effect(() => ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({\n name: 'conversation.input.dock',\n id: 'acp-permissions',\n order: 10,\n // The positional inject argument is the session binding key — the chat\n // session id this dock belongs to (same contract as every other entry).\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, PermissionDock as unknown as SlotComponent<any>)), 'dsh-ntr: conversation permission dock')\n\n // The released-session banner in the same dock seat: when this\n // conversation's agent process is parked (idle release or crash), one row\n // says so and offers the explicit resume — the gesture form of the\n // \"next message resumes it\" promise. Gated inside the component on the\n // chat-list rows (released banner renders iff nothing live is left).\n ctx.effect(() => ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({\n name: 'conversation.input.dock',\n id: 'acp-released-banner',\n order: 20,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, ReleasedBanner as unknown as SlotComponent<any>)), 'dsh-ntr: released-session banner')\n\n // The NTR branch entry riding the host's own assistant-actions row\n // (`conversation.chat.assistant-actions` — list, session scope; the\n // positional inject argument is the chat session id, like every other\n // session slot). Registered for EVERY conversation; the component gates\n // itself on the confirmed ACP verdict (non-ACP renders nothing), and the\n // `data-ntr-branch` attribute it stamps is the CSS pass that keeps the\n // hiding rules from swallowing the plugin's own button (same aria-label as\n // the host's). Order 20 puts it after the (hidden) feedback entry's 10 —\n // visually the spot the host's own branch button occupied.\n ctx.effect(() => ctx.slots.inject('conversation.chat.assistant-actions', () => ctx.slots.register({\n name: 'conversation.chat.assistant-actions',\n id: 'ntr-branch',\n order: 20,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpBranchAction as unknown as SlotComponent<any>)), 'dsh-ntr: message branch action')\n\n /**\n * The client runtime's session list store, read structurally. Used to find\n * the session the composer is staged on and whether it runs the ACP preset —\n * which is what every ACP conversation entry keys off.\n */\n const sessionsList = (): SessionListSource | undefined => {\n const list = service<StructuralServices['sessions']>('sessions')?.list\n if (list === undefined || typeof list.getSnapshot !== 'function' || typeof list.subscribe !== 'function') return undefined\n return list\n }\n\n /**\n * The renderer's current Session scope observable, when this host exposes it.\n * This is the source the host's own main-view picker prefers; the list's\n * retained rows are only the fallback for hosts without the adapter.\n */\n const currentScopeSource = (): NonNullable<StructuralServices['uiSession']['adapter']>['current'] => {\n try {\n const current = service<StructuralServices['uiSession']>('uiSession')?.adapter?.current\n return current !== undefined && typeof current.getSnapshot === 'function' ? current : undefined\n } catch {\n return undefined\n }\n }\n\n /** The session the composer is actually staged on, if the runtime can say. */\n const currentScopeSessionId = (): string | undefined => {\n try {\n const key = currentScopeSource()?.getSnapshot?.()?.key\n return typeof key === 'string' && key !== '' ? key : undefined\n } catch {\n return undefined\n }\n }\n\n /**\n * Chat session ids the ACP-mode tracker has already asked the host about (the\n * `session/chat-list` fallback), mapped to WHEN the last ask went out. The\n * ask is once per id so a host whose answer carries no `acpMode` cannot turn\n * every store mutation into a fresh RPC — the classic resolve-loop this file\n * already guards against elsewhere. `agent-preset/selected` clears an id so a\n * committed preset switch re-asks.\n *\n * An id is REMOVED again when its ask comes back without a verdict: a\n * one-shot ask that cannot answer must not wedge the gate for the rest of\n * the page's life. The next store mutation or list notification retries it,\n * which is what makes the fallback recoverable rather than a single coin\n * flip (a host half that transiently fails, or answers `{}` because the\n * session's projection row is not resident yet, used to leave the native\n * model seat visible forever with nothing in the console to say why).\n *\n * A verdict that IS held is re-asked once per {@link ACP_MODE_VERDICT_TTL_MS}\n * while the row stays silent — see the constant for why a held verdict may\n * not be permanent.\n */\n const acpModeAsked = new Map<string, number>()\n\n /** The host-side verdict for the seat gate, when we hold one (`AcpStore.acpMode`). */\n const acpModeVerdict = (sessionId: string): boolean | undefined => store.getSnapshot().acpMode[sessionId]\n\n ctx.effect(() => {\n let stopWatch: (() => void) | undefined\n /** Subscription to the renderer's current Session binding (see `currentScopeSource`). */\n let stopCurrent: (() => void) | undefined\n /**\n * The claim on the host's model seat, held while the staged conversation\n * runs the ACP preset. That cell is where the host's own base sits (the\n * trailing group, right before the submit button), so the claim installs\n * the ACP base there rather than blanking it — one cell, one occupant, and\n * the position the user expects. Ownership of the cell is NOT settled by\n * the rank a registration declares — the client runner rewrites non-chain\n * priorities with its own page-global counter, so the last registrant\n * wins — which is why the claim verifies the cell's winner and retakes it\n * when a rival (a shipped occupant, or a third-party seat plugin later in\n * the load order) is holding it. See model-seat.tsx.\n */\n let claim: ModelSeatClaim | undefined\n /**\n * Claim on the host's native permission seat (`conversation.input.permission`),\n * held while the staged conversation runs in ACP mode to blank out the\n * Access mode dropdown — the DSH read-only / workspace-write /\n * danger-full-access knob, which describes policies this runtime applies to\n * ITS OWN tools and therefore says nothing about a separate agent process.\n *\n * Unlike the model seat, the right answer here is an empty cell rather than\n * an ACP replacement: the ACP agent's own permission surface is what it\n * sends over `session/request_permission`, and there is nothing of ours to\n * put in this slot. So the occupant is a component that renders `null`.\n *\n * That makes the lifecycle discipline load-bearing in a way the model seat\n * does not rely on: the evidence for taking the cell (the staged preset) is\n * derived from a store that can go silent, while the entry itself is\n * indistinguishable from a permanent one to anyone looking at the screen.\n * A claim that outlives its reason has silently removed a control from\n * ordinary conversations, so {@link standDown} runs on every path that is\n * not a confirmed ACP verdict — including the ones where no verdict is\n * available yet.\n */\n let permissionClaim: ModelSeatClaim | undefined\n /**\n * Claim on the dock's `activity` cell (`conversation.composer.dock`, id\n * `activity`), held for the same ACP-mode spell as the two seats. The\n * occupant renders the HOST's ActivityPill with the plugin's burst speed\n * patched into its `sessionStats`; it is a list cell, so the claim is\n * identified by id rather than by the slot's heads[0]. If the host shape is\n * not one this bundle knows, no claim is created and the native pill stays.\n */\n let activityClaim: ModelSeatClaim | undefined\n let activityDiag = 'idle'\n const noteActivityDiag = (line: string): void => {\n if (line === activityDiag) return\n activityDiag = line\n // Success and the ordinary older-host \"no activity entry\" are silent; a\n // shape mismatch or a lost registration must leave a breadcrumb. The ACP\n // view has no line for this surface, and a missing speed fix is otherwise\n // invisible.\n if (!/failed|missing|taken|gave up|still held/.test(line)) return\n console.warn(`[dsh-ntr] ${line}`)\n }\n /**\n * ACP-only auxiliary entries: the NTR view tab and the composer's context\n * ring. Unlike the two seats these are `list` slots with no visibility\n * predicate of their own, so \"self-hide in the component\" cannot remove a\n * tab from the header or free the native bottom row. They are installed by\n * this gate for exactly the same lifetime as the claims and disposed on\n * every stand-down — a standard conversation then has no NTR tab at all,\n * and the host's native context meter sits alone in its dock row.\n */\n let viewEntry: (() => void) | undefined\n let meterEntry: (() => void) | undefined\n const releaseAuxEntries = (): void => {\n try { viewEntry?.() } catch {}\n viewEntry = undefined\n try { meterEntry?.() } catch {}\n meterEntry = undefined\n }\n const holdAuxEntries = (): void => {\n // A refused registration leaves the native surface in place (fail-open);\n // the next evaluation retries when the slot is declared.\n if (viewEntry === undefined) {\n try {\n viewEntry = ctx.slots.inject('conversation.view', () => ctx.slots.register({\n name: 'conversation.view',\n id: 'acp',\n order: 20,\n label: () => label('NTR', 'NTR'),\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpConversationView as unknown as SlotComponent<any>))\n } catch {\n viewEntry = undefined\n }\n }\n if (meterEntry === undefined) {\n try {\n meterEntry = ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register({\n name: 'conversation.composer.dock',\n id: 'acp-context-meter',\n order: 10,\n inject: (sessionId: unknown) => chatFace(sessionId),\n }, AcpContextMeter as unknown as SlotComponent<any>))\n } catch {\n meterEntry = undefined\n }\n }\n }\n /**\n * Last diagnostic line from {@link syncAcpMode}. Every way this gate can\n * end with the native seat still on screen used to be silent — an inner\n * `catch { return [] }` and an outer `catch { … }` between them swallowed\n * both the registration failure and the inject failure, leaving \"the ACP\n * conversation still shows the native model picker\" with nothing to read\n * it from. This string is the gate's own account of what it last decided,\n * verified or not, and why; the ACP view renders it.\n */\n let seatDiag = 'idle'\n let permissionSeatDiag = 'idle'\n /**\n * Hand every ACP-only surface back: both composer cells, the NTR view tab\n * and the context ring.\n *\n * Every path that is not \"this staged conversation is CONFIRMED to run the\n * ACP preset\" ends here — including the paths that carry no verdict at all\n * (no session list, nothing staged, an addressed subagent route). Standing\n * down is the only safe default, because keeping a blanking entry installed\n * while the evidence has gone silent hides a control the user needs: the\n * cells sit in every conversation's composer bar, so an entry that outlives\n * its reason is indistinguishable from a permanent one. The next evaluation\n * re-installs if ACP mode is confirmed again, so this is not a decision\n * about the conversation — only about whose chrome is on screen while we\n * cannot answer.\n */\n const standDown = (reason: string): void => {\n seatDiag = reason\n // The permission line names the release explicitly: \"staged X is not in\n // ACP mode\" and \"staged X is ACP mode\" are the two states a reader has\n // to tell apart, and the seat this claim holds renders nothing — so\n // without the word, \"released\" and \"re-installed on the next evidence\"\n // read identically in the one place either is observable.\n permissionSeatDiag = `${reason} (released)`\n disarmVerdictReask()\n setScopeMarker(false)\n releaseAuxEntries()\n if (claim !== undefined) { claim.release(); claim = undefined }\n if (permissionClaim !== undefined) { permissionClaim.release(); permissionClaim = undefined }\n if (activityClaim !== undefined) { activityClaim.release(); activityClaim = undefined }\n }\n /**\n * Re-ask timer for the staged session, armed while the row is silent and a\n * verdict is held (see {@link ACP_MODE_VERDICT_TTL_MS}). A re-ask needs a\n * trigger of its own: on the host shape this covers nothing else moves —\n * the row never speaks, the frame path may be dead, and a page with no\n * store traffic would otherwise never re-run the gate, which is exactly\n * how a stale verdict became permanent. One timer at a time, because the\n * gate judges exactly one staged session; cleared whenever the row speaks,\n * whenever the gate stands down, and on teardown.\n */\n let verdictReask: ReturnType<typeof setTimeout> | undefined\n const armVerdictReask = (): void => {\n if (verdictReask !== undefined) return\n verdictReask = setTimeout(() => {\n verdictReask = undefined\n runGate()\n }, ACP_MODE_VERDICT_TTL_MS)\n }\n const disarmVerdictReask = (): void => {\n if (verdictReask === undefined) return\n clearTimeout(verdictReask)\n verdictReask = undefined\n }\n const syncAcpMode = (): void => {\n const list = sessionsList()\n const snapshot = list?.getSnapshot() as AcpSessionsSnapshot | undefined\n if (list === undefined) {\n standDown('no session list service')\n return\n }\n // Prefer the renderer's own current selection; the list's retained rows\n // are the fallback for hosts without the scope adapter. A multi-view\n // layout can leave several rows retained, and judging the first one (or\n // the one the previous switch left behind) would hold the ACP chrome\n // while an ordinary conversation is on screen — the reported drift.\n const current = currentScopeSessionId()\n const staged = stagedSessionOf(snapshot, current)\n if (staged === undefined) {\n standDown('no staged session (nothing retains the main view)')\n return\n }\n if (staged.subagent) {\n standDown(`staged ${staged.sessionId} is an addressed subagent route`)\n return\n }\n // The gate prefers the runtime's own session row; on hosts whose rows\n // carry no `agentPreset` the row is silent and only our host-side report\n // can answer. Ask while the row is silent AND we hold no verdict — the\n // store subscriber below re-runs this gate when the answer lands\n // (`loadChatSessions` dedups racing asks in flight too). An ask that came\n // back without an answer is reported unsettled and retracted here, so the\n // next tick asks again rather than parking the session forever.\n //\n // A verdict that IS held is re-asked once it ages past\n // {@link ACP_MODE_VERDICT_TTL_MS}: while the row is silent it is this\n // gate's only fact source, and the host's log read is the one place a\n // switch the runtime never framed can still be learned from.\n const verdictHeld = acpModeVerdict(staged.sessionId) !== undefined || store.hasAcpModeVerdict(staged.sessionId)\n const askedAt = acpModeAsked.get(staged.sessionId)\n const askIsDue = askedAt === undefined\n || (!verdictHeld || Date.now() - askedAt >= ACP_MODE_VERDICT_TTL_MS)\n if (rowAgentPreset(staged.row) === undefined && askIsDue) {\n acpModeAsked.set(staged.sessionId, Date.now())\n // `settleVerdict` on the TTL re-ask only: the answer of a read the gate\n // itself commissioned may settle a verdict a frame had settled (see\n // `acpModeLive`), which is what lets a switch the runtime never framed\n // still reach this gate. A composer reader's answer keeps deferring to\n // the frame, because the host's log read can lag a switch by one.\n void store.loadChatSessions(staged.sessionId, verdictHeld ? { settleVerdict: true } : undefined)\n } else if (!verdictHeld && askedAt !== undefined) {\n // The previous ask for this session came back without a verdict (or is\n // still in flight). Withdraw the one-shot mark so the round-trip can be\n // repeated: without this, one silent failure pins the native seat in\n // place for the rest of the page's life with nothing to show why.\n acpModeAsked.delete(staged.sessionId)\n }\n // An ACP conversation's models belong to the external agent, so the\n // host's native model seat must not be on screen here — it would list\n // providers this conversation cannot use, and clicking one writes the\n // process-wide default model. The seat is also exactly where the host's\n // own base sat (the trailing group, before the submit button), which is\n // where the ACP base belongs — so instead of blanking the cell we install\n // the ACP base in it. See model-seat.tsx for why occupying it is not\n // enough on its own.\n //\n // The Access mode selector beside it (`conversation.input.permission`)\n // has no ACP counterpart to be replaced with: it selects the file-write\n // policy THIS runtime applies to its own tools, which a separate agent\n // process never consults, so in ACP mode the honest thing to render is\n // nothing. Both cells follow the same evidence — the staged\n // conversation's preset — through one {@link standDown}, so neither can\n // stay claimed after the evidence for it stops answering.\n if (!stagedIsAcpMode(snapshot, acpModeVerdict, current)) {\n // The row's own verdict is authoritative even when it says no: fold it\n // into the store so every per-conversation surface (the branch\n // button's ACP gate included) stops rendering without waiting for a\n // host read. A silent row folds nothing — that case is the ask path's.\n if (rowAgentPreset(staged.row) !== undefined) store.noteAcpMode(staged.sessionId, false)\n standDown(`staged ${staged.sessionId} is not in ACP mode`)\n return\n }\n // The row speaks, so the host verdict is not this gate's only fact\n // source and no re-ask timer is needed. The same fold in the affirmative\n // direction: the runtime row IS the preset truth, and the branch\n // button's gate reads the folded map, not the snapshot.\n if (rowAgentPreset(staged.row) !== undefined) {\n store.noteAcpMode(staged.sessionId, rowAgentPreset(staged.row) === ACP_PRESET_ID)\n disarmVerdictReask()\n }\n // Stays re-runnable on purpose: the seat is not declared before the\n // composer bar mounts, a registration can be refused, and the cell can be\n // taken from us later. The claim reports each of those through the\n // diagnostic line instead of pretending the base is installed — the\n // next evaluation (store mutation, list notification, preset frame, or a\n // change to the cell itself) simply tries again.\n seatDiag = `staged ${staged.sessionId} is ACP mode`\n setScopeMarker(true)\n probeHostActionLabels()\n claim ??= createModelSeatClaim({\n slots: ctx.slots as unknown as SeatSlots,\n occupant: () => AcpSessionControls,\n // The occupant's own face — REQUIRED by the claim, and the reason is a\n // silent failure this gate could never see: `conversation.input.model`\n // is session-scoped, so without it the entry renders with the standard\n // kit alone (`acp === undefined`), the slot's error boundary abdicates\n // our entry from the cell, and the native seat comes back while the\n // diagnostic still says \"held by the ACP base\". See model-seat.tsx.\n inject: (sessionId: unknown) => chatFace(sessionId),\n onDiag: (line) => { seatDiag = `staged ${staged.sessionId} · ${line}` },\n })\n claim.sync()\n\n permissionSeatDiag = `staged ${staged.sessionId} is ACP mode`\n permissionClaim ??= createPermissionSeatClaim({\n slots: ctx.slots as unknown as SeatSlots,\n onDiag: (line) => { permissionSeatDiag = `staged ${staged.sessionId} · ${line}` },\n })\n permissionClaim.sync()\n // The dock's activity pill is a list CELL, not a whole slot: this claim\n // shadows the shipped `activity` entry by id and renders it back with the\n // plugin's burst speed. No host entry / inject / locale -> undefined and\n // the native pill is untouched; the next evaluation retries.\n activityClaim ??= createActivityPillClaim({\n slots: ctx.slots as unknown as SeatSlots,\n inject: (sessionId: unknown) => chatFace(sessionId),\n onDiag: noteActivityDiag,\n })\n activityClaim?.sync()\n // The tab and the occupancy ring have the same lifetime as the claims:\n // they exist only while this staged conversation is an ACP one, and a\n // stand-down disposes them (a standard conversation must not see an NTR\n // tab, and the host's native bottom row must stand alone there).\n holdAuxEntries()\n // Confirmed ACP on a silent row: the verdict is all the evidence there\n // is, so it gets a re-ask deadline (see {@link ACP_MODE_VERDICT_TTL_MS}).\n if (rowAgentPreset(staged.row) === undefined) armVerdictReask()\n }\n // A committed preset switch rewrites the session the gate judged. The\n // runtime's frame carries the new preset, so fold it straight into the\n // verdict map — see the handler for why re-reading the log there would be a\n // step behind. Structural `remote` (not injected): an older runtime without\n // it simply never gets this refresh path, and the one-shot ask still applies.\n //\n // Resolved through {@link attach} rather than once when this effect ran: a\n // page that loads this bundle before the gateway publishes `remote` used to\n // lose the frame path for the rest of its life, and on a host whose rows\n // are silent the frame is the only live fact source there is. The\n // subscription is made once (the guard), and `internal/service` re-runs the\n // attach whenever a service appears — the same late-arrival path the\n // session list already uses.\n let stopPreset: (() => void) | undefined\n const attachPresetFrames = (): void => {\n if (stopPreset !== undefined) return\n const remote = (ctx as unknown as { get(key: string): unknown }).get('remote') as\n | { $on?(event: string, listener: (...args: unknown[]) => void): () => void }\n | undefined\n if (typeof remote?.$on !== 'function') return\n stopPreset = remote.$on('agent-preset/selected', (...args: unknown[]) => {\n // `[sessionId, agentPreset]`: the frame carries the committed preset, so\n // fold IT rather than re-asking the host. The runtime broadcasts this\n // the moment the switch commits, while the appended session-log row is\n // not yet visible to a fresh `session/chat-list` cold read — reading\n // there folds the PREVIOUS preset, which kept the surface one switch\n // behind until a page reload (the reported \"switching into or out of ACP\n // mode only moves the model seat after a refresh\" symptom). The host read\n // stays the cold path for a session this tab has no frame for.\n const sessionId = args[0]\n if (typeof sessionId !== 'string') return\n // A frame supersedes both a held verdict and a pending one-shot ask.\n acpModeAsked.delete(sessionId)\n if (args.length >= 2) {\n // The late answer to an ask that predates the frame must not win: it\n // describes the log from before the switch.\n store.abandonChatSessionRead(sessionId)\n store.noteAcpMode(sessionId, acpModeFromPresetEvent(args[1]))\n } else {\n // A frame carrying no preset can only be honoured by a fresh host\n // read — the behaviour the fold replaced.\n store.invalidateAcpMode(sessionId)\n }\n runGate()\n })\n }\n const attach = (): void => {\n const list = sessionsList()\n if (list !== undefined && stopWatch === undefined) stopWatch = list.subscribe(runGate)\n // The selection can move without the list changing (a tab or sidebar\n // click), and `uiSession.current` is the host's own answer to \"which\n // conversation is on screen\". Re-run the gate on that transition so the\n // ACP chrome leaves before the newly staged ordinary conversation paints.\n const current = currentScopeSource()\n if (current?.subscribe !== undefined && stopCurrent === undefined) {\n try {\n stopCurrent = current.subscribe(runGate)\n } catch {\n stopCurrent = undefined\n }\n }\n attachPresetFrames()\n runGate()\n }\n /**\n * Wrap {@link syncAcpMode} so every evaluation also publishes its verdict\n * to the store. The gate runs on every store mutation, list notification,\n * preset frame and change to the model-seat cell itself, so the published\n * line is always the latest verdict — which is the whole point: the visible\n * symptom (\"the native seat is still there\") looks identical whether the\n * seat was never declared, refused us, or was taken by a rival, and only\n * this line tells them apart.\n *\n * NOT RE-ENTRANT, and deliberately so. The host's slot registry emits\n * `slots/changed` SYNCHRONOUSLY from inside `slots.register` and from\n * inside an entry's disposer (`SlotCore.markDirty` → the registry's\n * `onMutate` bridge → `ctx.emit`), and the store re-runs this gate from\n * inside its own `mutate`. So an evaluation that installs or releases an\n * entry re-enters itself from within its own call stack — and a nested\n * evaluation reads the SAME evidence (neither the session snapshot nor the\n * store has changed), so it can only repeat the decision. Worse, a nested\n * install sees `installed.length === 0` because the outer install has not\n * pushed its disposer yet, registers a second entry, and that entry's own\n * `slots/changed` re-enters again: an unbounded registration pile whose\n * store-subscriber feedback never settles. A nested run is therefore\n * dropped — the evaluation already in flight judged this evidence and is\n * about to publish its verdict, and the next trigger tries again.\n */\n let gateRunning = false\n const runGate = (): void => {\n if (gateRunning) return\n gateRunning = true\n try {\n syncAcpMode()\n store.noteSeatDiag(seatDiag)\n store.notePermissionSeatDiag(permissionSeatDiag)\n } finally {\n gateRunning = false\n }\n }\n attach()\n // The store mutates when `session/chat-list` answers, so the fallback\n // verdict arriving is what re-runs this gate — the list subscription alone\n // cannot see it (the runtime rows are silent on these hosts).\n const stopStore = store.subscribe(runGate)\n // The cell can be taken by someone else at any moment (the runner ranks the\n // most recent registration lowest, so a seat plugin loading or re-mounting\n // after us wins). `slots/changed` carries the key, and the claim's `sync`\n // retakes the cell only when it is actually holding something that is not\n // ours — so this is evidence-driven, not a poll.\n const stopCell = ctx.on('slots/changed', (...args: unknown[]) => {\n if (args[0] !== MODEL_SEAT_KEY && args[0] !== PERMISSION_SEAT_KEY && args[0] !== DOCK_SLOT) return\n runGate()\n })\n // Late services: the runtime's session list normally exists before this\n // module applies, but a build that loads us first must not end up without\n // the gate for the rest of the page's life.\n const stopService = ctx.on('internal/service', attach)\n return () => {\n stopService()\n stopStore()\n stopCell()\n stopPreset?.()\n stopWatch?.()\n stopCurrent?.()\n disarmVerdictReask()\n // The scope marker dies with the gate: a reload (HMR) must not leave the\n // hiding CSS pointed at conversations nothing confirms anymore.\n setScopeMarker(false)\n releaseAuxEntries()\n if (claim !== undefined) { claim.release(); claim = undefined }\n if (permissionClaim !== undefined) { permissionClaim.release(); permissionClaim = undefined }\n if (activityClaim !== undefined) { activityClaim.release(); activityClaim = undefined }\n store.noteSeatDiag('gate disposed')\n store.notePermissionSeatDiag('gate disposed')\n }\n }, 'dsh-ntr: ACP mode tracking')\n\n /**\n * Slash isolation (`src/client/slash-isolation.ts`): in an ACP conversation\n * the composer's `/` menu, Enter and Space adjudication belong to the AGENT.\n * The host's slash sources describe commands THIS runtime would execute, and\n * a name they recognize never reaches the prompt sink — so `/model` in an NTR\n * conversation would run DSH's model picker instead of reaching the agent.\n *\n * The host pipeline offers no per-session seam (adjudication polls sources in\n * registration order; duplicates throw; no availability hook), so the live\n * sources are wrapped in memory. The verdict is the same two-source read the\n * seat gate uses: the staged row's own preset, else the host's answer.\n *\n * Re-runs on `internal/service` (a late `inputTriggers`) and on store traffic\n * (a source registering after us), and is idempotent. Everything is restored\n * on teardown so a plugin reload never leaves the previous instance's\n * callbacks behind.\n */\n ctx.effect(() => {\n let warned = ''\n const isolation = createSlashIsolation({\n sources: () => {\n const triggers = service<{ live?: { sources?: unknown } }>('inputTriggers')\n // A host WITHOUT the trigger service has no slash menu, so there is\n // nothing to isolate and nothing to report. A service whose shape is\n // not what this module knows is different: that returns undefined and\n // is reported (the seam degraded open, which needs a line).\n if (triggers === undefined) return []\n const sources = triggers.live?.sources\n return Array.isArray(sources) ? sources as SlashSource[] : undefined\n },\n verdict: (sessionId) => slashAcpVerdict(\n sessionsList()?.getSnapshot(),\n sessionId,\n id => store.getSnapshot().acpMode[id],\n currentScopeSessionId(),\n ),\n ensureVerdict: async (sessionId) => {\n const held = (): boolean | undefined => slashAcpVerdict(\n sessionsList()?.getSnapshot(),\n sessionId,\n id => store.getSnapshot().acpMode[id],\n currentScopeSessionId(),\n )\n const known = held()\n if (known !== undefined) return known\n await store.loadChatSessions(sessionId)\n return held()\n },\n // The LIVE session's declaration wins; a conversation that has not sent\n // its first message yet falls back to the agent-level cache learned from\n // earlier sessions (`agent/catalog` → `commands`). That fallback is what\n // makes `/` list the agent's commands immediately, like the native menu,\n // instead of waiting for a session that only the first message creates.\n commands: async (sessionId) => {\n const live = store.chatSessionCommands(sessionId)\n if (live !== undefined) return live\n const agentId = store.chatAgentFor(sessionId)\n if (agentId === undefined) return undefined\n const cached = store.agentCommands(agentId)\n if (cached !== undefined) return cached\n // `agent/catalog` is pure memory on the host — no probe is spawned by\n // this read (constraint 8); an agent with no cached list simply stays\n // absent, and the menu shows nothing until its first session declares.\n await store.loadAgentCatalog(agentId)\n return store.agentCommands(agentId)\n },\n refresh: (sessionId) => { void store.loadChatSessions(sessionId) },\n onDiag: (line) => {\n store.noteSlashDiag(line)\n // Console once per distinct line: the NTR view shows the same string,\n // but a shape change is exactly the kind of thing a bug report needs a\n // timestamped console record of.\n if (warned === line) return\n warned = line\n if (line !== 'idle') console.warn(`[dsh-ntr] slash isolation: ${line}`)\n },\n })\n /**\n * Sources register with no event of their own: `inputTriggers` announces\n * the SERVICE, not the roster. A client module that loads after this one\n * therefore adds the `command` source in silence. The retry below is the\n * bounded answer to that gap — stop as soon as something is wrapped, and\n * give up after {@link SLASH_SYNC_ATTEMPTS} so a host with no slash\n * pipeline does not keep a timer alive.\n */\n let retryTimer: ReturnType<typeof setTimeout> | undefined\n let attempts = 0\n const sync = (): void => {\n if (isolation.sync()) {\n if (retryTimer !== undefined) {\n clearTimeout(retryTimer)\n retryTimer = undefined\n }\n return\n }\n // The service's arrival is announced (`internal/service`), so there is\n // nothing to poll for until it exists; the gap this retry closes is a\n // roster that is still empty while the service is already there.\n if (service('inputTriggers') === undefined) return\n if (retryTimer !== undefined || attempts >= SLASH_SYNC_ATTEMPTS) return\n attempts += 1\n retryTimer = setTimeout(() => {\n retryTimer = undefined\n sync()\n }, SLASH_SYNC_DELAY_MS)\n }\n sync()\n const stopService = ctx.on('internal/service', sync)\n // Store traffic is guaranteed after mount; sync is cheap and idempotent\n // once everything is wrapped.\n const stopStore = store.subscribe(sync)\n return () => {\n if (retryTimer !== undefined) clearTimeout(retryTimer)\n stopStore()\n stopService()\n isolation.dispose()\n store.noteSlashDiag('idle')\n }\n }, 'dsh-ntr: slash isolation')\n\n // The ACP view tab: the agent's real stream (tool cards, plans, unified\n // diffs). Session-scope list entry; the view ring renders it one tab at a\n // time. Meanwhile the agent's prose and thinking ride the NATIVE chat plane\n // through the LLM adapter — see docs/acp-convergence.md §2.1.\n //\n // It is installed by the ACP-mode gate above (`holdAuxEntries`) and NOT at\n // plugin load: the host projects every registered `conversation.view` entry\n // into EVERY session's tab strip, so an unconditional registration showed\n // the NTR tab on ordinary conversations too. The gate's lifetime is the\n // only per-session seam the host offers.\n}\n"],"mappings":"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"}
|