@shendeguize/dsh-agent-sidecar 0.1.0
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/LICENSE +21 -0
- package/README.md +167 -0
- package/cordis.patch.yml +10 -0
- package/lib/client.js +8062 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +396 -0
- package/lib/index.js +4166 -0
- package/package.json +101 -0
- package/src/analysis.ts +782 -0
- package/src/bridge.ts +841 -0
- package/src/client/analysis/AnalysisPanel.tsx +191 -0
- package/src/client/analysis/analysis.module.css +183 -0
- package/src/client/analysis-glue.ts +331 -0
- package/src/client/api.ts +380 -0
- package/src/client/board/Board.tsx +214 -0
- package/src/client/board/board.module.css +302 -0
- package/src/client/board/logic.ts +556 -0
- package/src/client/board/project-view-logic.ts +361 -0
- package/src/client/board/project-view.module.css +307 -0
- package/src/client/board/project-view.tsx +189 -0
- package/src/client/board/strings.ts +112 -0
- package/src/client/commands.ts +484 -0
- package/src/client/controller.ts +360 -0
- package/src/client/css-modules.d.ts +11 -0
- package/src/client/detail/SessionDetail.tsx +270 -0
- package/src/client/detail/detail.module.css +433 -0
- package/src/client/detail/logic.ts +779 -0
- package/src/client/detail/strings.ts +98 -0
- package/src/client/detail/transport.ts +175 -0
- package/src/client/detail-glue.ts +397 -0
- package/src/client/detail-view.module.css +79 -0
- package/src/client/detail-view.tsx +233 -0
- package/src/client/dsh-tools/LineageTree.tsx +210 -0
- package/src/client/dsh-tools/SearchPanel.tsx +169 -0
- package/src/client/dsh-tools/dsh-tools.module.css +374 -0
- package/src/client/dsh-tools/logic.ts +596 -0
- package/src/client/dsh-tools/strings.ts +90 -0
- package/src/client/index.ts +315 -0
- package/src/client/inject/InjectPanel.tsx +482 -0
- package/src/client/inject/inject.module.css +446 -0
- package/src/client/inject/logic.ts +516 -0
- package/src/client/inject/overlay.module.css +22 -0
- package/src/client/inject-glue.ts +171 -0
- package/src/client/locales/command.ts +48 -0
- package/src/client/locales/en.ts +385 -0
- package/src/client/locales/index.ts +123 -0
- package/src/client/locales/zh.ts +402 -0
- package/src/client/m3-transport.ts +151 -0
- package/src/client/mount.tsx +307 -0
- package/src/client/project-glue.ts +134 -0
- package/src/client/search-glue.ts +143 -0
- package/src/client/settings-card.module.css +359 -0
- package/src/client/settings-card.tsx +565 -0
- package/src/client/settings-glue.ts +130 -0
- package/src/client/sidebar-tab.tsx +494 -0
- package/src/client/sse.ts +366 -0
- package/src/client/widget.tsx +80 -0
- package/src/config.ts +193 -0
- package/src/dsh-inject.ts +240 -0
- package/src/fusion.ts +988 -0
- package/src/guard.ts +274 -0
- package/src/index.ts +950 -0
- package/src/inject-gateway.ts +574 -0
- package/src/routes.ts +1133 -0
- package/src/send-cli.ts +340 -0
- package/src/session-store.ts +184 -0
- package/src/skills-provider.ts +293 -0
- package/src/supervisor.ts +463 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["defaultSetTimeout","defaultClearTimeout","defaultCreateAbortController","resolveFetch","MINUTE_MS","HOUR_MS","DAY_MS","formatTemplate","KNOWN_STATUSES","STATUS_TONE","formatRelativeTime","AGENT_GLYPHS","agentGlyph","KEY_SEP","agentGlyph","formatRelativeTime","formatTemplate","MINUTE_MS","HOUR_MS","DAY_MS","agentGlyph","defaultSetTimeout","defaultClearTimeout","defaultCreateAbortController","styles","formatTemplate","SessionRow","styles","DOT_COLORS","rootStyle","css","t","defaultT","formatTemplate","styles","S","css","css","css","css","t","defaultT","defaultSetTimeout","defaultClearTimeout","defaultCreateAbortController","resolveFetch","getJson","INITIAL_STATE","css","overlay","css","face","agentGlyph","formatRelativeTime"],"sources":["../src/client/api.ts","../src/client/board/strings.ts","../src/client/board/logic.ts","../src/client/detail/strings.ts","../src/client/dsh-tools/strings.ts","../src/client/board/project-view-logic.ts","../src/client/locales/zh.ts","../src/client/locales/en.ts","../src/client/locales/index.ts","../src/client/locales/command.ts","../src/client/commands.ts","../src/client/sse.ts","../src/client/controller.ts","../src/client/inject-glue.ts","../src/client/board/Board.tsx","../src/client/board/project-view.tsx","../src/client/widget.tsx","../src/client/settings-card.tsx","../src/client/detail/logic.ts","../src/client/detail/SessionDetail.tsx","../src/client/dsh-tools/logic.ts","../src/client/dsh-tools/LineageTree.tsx","../src/client/dsh-tools/SearchPanel.tsx","../src/client/analysis/AnalysisPanel.tsx","../src/client/inject/logic.ts","../src/client/inject/InjectPanel.tsx","../src/client/detail/transport.ts","../src/client/m3-transport.ts","../src/client/detail-glue.ts","../src/client/search-glue.ts","../src/client/analysis-glue.ts","../src/client/project-glue.ts","../src/client/detail-view.tsx","../src/client/settings-glue.ts","../src/client/mount.tsx","../src/client/sidebar-tab.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Browser-half data layer: same-origin fetch wrappers for the plugin's\n * self-registered route namespace (design §5.3; server: src/routes.ts).\n *\n * Wire types below are hand-mirrored from the host half (routes.ts /\n * session-store.ts / supervisor.ts / bridge.ts response shapes): the\n * client TS program cannot import host modules because they live in the\n * node-typed host program. The API_PREFIX mirror is pinned against the\n * routes.ts constant by test/client-data.test.ts.\n *\n * Calls are same-origin relative paths with no auth headers (ADR-8 trust\n * posture). Browser primitives — fetch, AbortController, timers — are\n * injectable so everything runs under plain node in tests; defaults\n * resolve from globalThis at call time. Pure data layer: no React, no\n * slots SDK; the UI half imports its types from here.\n *\n * @module\n */\n\n/** Route namespace; must mirror routes.ts `API_PREFIX` (pinned by test). */\nexport const API_PREFIX = '/plugins/agent-sidecar/api'\n\n/** Default overall request deadline (design §5.3: 15s fetch timeout). */\nexport const DEFAULT_TIMEOUT_MS = 15_000\n\n// ---------------------------------------------------------------------------\n// Wire types (host source of truth noted per type).\n// ---------------------------------------------------------------------------\n\n/** Daemon supervisor state machine states (host: supervisor.ts). */\nexport type SupervisorState =\n | 'probe'\n | 'adopted'\n | 'defer'\n | 'reprobe'\n | 'hosting'\n | 'hosted'\n | 'backoff'\n | 'failed'\n\n/** Health of the host↔daemon subscribe stream (host: bridge.ts). */\nexport type StreamHealth = 'ok' | 'degraded' | 'unknown'\n\n/** Daemon self-description from the Unix-socket ping (host: supervisor.ts). */\nexport interface PingInfo {\n pid: number\n version: string\n http: { enabled: boolean; host?: string; port?: number }\n}\n\n/** Compact most-recent-event summary per session (host: session-store.ts). */\nexport interface SessionEventSummary {\n ts: string\n kind: string\n text: string\n}\n\n/** One board row (host: session-store.ts). */\nexport interface SessionView {\n agent: string\n session_id: string\n status: string\n title: string\n project: string\n updated_at: number\n last_event: SessionEventSummary | null\n /** True when a dsh seq discontinuity was observed since the last snapshot. */\n gap: boolean\n}\n\n/** Full board state (host: session-store.ts). */\nexport interface BoardState {\n sessions: SessionView[]\n streamHealth: StreamHealth\n lastReconcileAt: number | null\n}\n\n/** Body of `GET state` and of every SSE `state` event (host: routes.ts). */\nexport interface StateSnapshot {\n daemon: { state: SupervisorState; lastPing: PingInfo | null }\n board: BoardState\n capabilities: { inject: boolean }\n}\n\n/** Body of `GET session/<id>` (host: routes.ts, M1 shape). */\nexport interface SessionDetail {\n session: SessionView\n /** Always null in M1; the event timeline lands in M3. */\n timeline: null\n timelineNote: string\n}\n\n/** `POST action` body for injection phase one (host: routes.ts `handlePrepare`). */\nexport interface PrepareActionBody {\n type: 'inject.prepare'\n target: { agent: string; sessionId: string }\n mode: 'queue' | 'steer'\n message: string\n}\n\n/** `POST action` body for injection phase two (host: routes.ts `handleExecute`). */\nexport interface ExecuteActionBody {\n type: 'inject.execute'\n requestId: string\n confirmToken: string\n message: string\n}\n\n/** `POST action` body for daemon management (host: routes.ts `handleAction`). */\nexport interface DaemonRetryActionBody {\n type: 'daemon.retry'\n}\n\n/** Analysis target selector (host: routes.ts `AnalysisTargetRequest`). */\nexport type AnalysisTargetKind = 'session' | 'project' | 'cross-agent'\n\n/** `POST action` body starting one analysis (host: routes.ts, M3). */\nexport interface AnalysisRequestActionBody {\n type: 'analysis.request'\n targetKind: AnalysisTargetKind\n /** Session id / project path; required for session and project kinds. */\n targetId?: string\n question?: string\n}\n\n/** `POST action` body asking a follow-up in a live analysis session. */\nexport interface AnalysisFollowupActionBody {\n type: 'analysis.followup'\n analysisSessionId: string\n question: string\n}\n\n/** `POST action` body releasing an analysis session (idempotent). */\nexport interface AnalysisCancelActionBody {\n type: 'analysis.cancel'\n analysisSessionId: string\n}\n\n/**\n * The action dispatcher envelope (host: routes.ts `handleAction`):\n * a `{type, ...}` union with the phase fields at the top level. M2 inject\n * phases + daemon management + the M3 `analysis.*` trio.\n */\nexport type ActionEnvelope =\n | PrepareActionBody\n | ExecuteActionBody\n | DaemonRetryActionBody\n | AnalysisRequestActionBody\n | AnalysisFollowupActionBody\n | AnalysisCancelActionBody\n\n// ---------------------------------------------------------------------------\n// Injectable browser primitives (structural, so node tests can fake them).\n// ---------------------------------------------------------------------------\n\n/** Opaque timer handle so DOM numbers and fake-timer handles interoperate. */\nexport type TimerHandle = unknown\n\nexport interface AbortSignalLike {\n readonly aborted: boolean\n addEventListener(type: 'abort', listener: () => void): void\n removeEventListener(type: 'abort', listener: () => void): void\n}\n\nexport interface AbortControllerLike {\n readonly signal: AbortSignalLike\n abort(): void\n}\n\nexport interface ResponseLike {\n ok: boolean\n status: number\n json(): Promise<unknown>\n}\n\nexport interface RequestInitLike {\n method: string\n headers?: Record<string, string>\n body?: string\n signal?: AbortSignalLike\n}\n\nexport type FetchLike = (url: string, init: RequestInitLike) => Promise<ResponseLike>\n\n/** Injectable primitives; every field defaults to the globalThis flavor. */\nexport interface ApiDeps {\n fetch?: FetchLike\n createAbortController?: () => AbortControllerLike\n setTimeout?: (fn: () => void, ms: number) => TimerHandle\n clearTimeout?: (handle: TimerHandle) => void\n}\n\n/** Per-call options for the request helpers. */\nexport interface RequestOptions extends ApiDeps {\n /** Overall deadline; default {@link DEFAULT_TIMEOUT_MS}. */\n timeoutMs?: number\n /** Caller-side cancellation; aborting it aborts the underlying fetch. */\n signal?: AbortSignalLike\n}\n\n// ---------------------------------------------------------------------------\n// Normalized errors.\n// ---------------------------------------------------------------------------\n\nexport type ApiErrorKind = 'timeout' | 'aborted' | 'network' | 'http' | 'parse'\n\n/**\n * Single normalized failure shape for every request path, so the UI has\n * one catch surface. `reason` carries the server `{reason}` envelope for\n * kind 'http' and a stable local code otherwise.\n */\nexport class ApiError extends Error {\n readonly kind: ApiErrorKind\n readonly reason: string\n /** HTTP status when a response was received, else null. */\n readonly status: number | null\n\n constructor(\n kind: ApiErrorKind,\n reason: string,\n status: number | null = null,\n cause?: unknown,\n ) {\n super(\n status === null ? `api ${kind}: ${reason}` : `api ${kind}: ${reason} (http ${status})`,\n cause === undefined ? undefined : { cause },\n )\n this.name = 'ApiError'\n this.kind = kind\n this.reason = reason\n this.status = status\n }\n}\n\nexport function isApiError(value: unknown): value is ApiError {\n return value instanceof ApiError\n}\n\n// ---------------------------------------------------------------------------\n// Defaults (resolved lazily so late-stubbed globals and fake timers work).\n// ---------------------------------------------------------------------------\n\nconst defaultSetTimeout = (fn: () => void, ms: number): TimerHandle =>\n globalThis.setTimeout(fn, ms)\n\nconst defaultClearTimeout = (handle: TimerHandle): void => {\n globalThis.clearTimeout(handle as ReturnType<typeof globalThis.setTimeout>)\n}\n\nconst defaultCreateAbortController = (): AbortControllerLike => new AbortController()\n\nfunction resolveFetch(opts: ApiDeps): FetchLike {\n if (opts.fetch !== undefined) return opts.fetch\n // DOM fetch is only nominally stricter than FetchLike (its RequestInit\n // wants a branded AbortSignal); every value this module actually passes\n // is a real one when the default controller factory is in play.\n return globalThis.fetch as unknown as FetchLike\n}\n\n// ---------------------------------------------------------------------------\n// Core request helper.\n// ---------------------------------------------------------------------------\n\ninterface RequestInitInput {\n method: string\n headers?: Record<string, string>\n body?: string\n}\n\nasync function request(\n path: string,\n init: RequestInitInput,\n opts: RequestOptions,\n): Promise<unknown> {\n const doFetch = resolveFetch(opts)\n const controller = (opts.createAbortController ?? defaultCreateAbortController)()\n const setT = opts.setTimeout ?? defaultSetTimeout\n const clearT = opts.clearTimeout ?? defaultClearTimeout\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n let timedOut = false\n let externallyAborted = false\n const timer = setT(() => {\n timedOut = true\n controller.abort()\n }, timeoutMs)\n\n const external = opts.signal\n const onExternalAbort = (): void => {\n externallyAborted = true\n controller.abort()\n }\n if (external !== undefined) {\n if (external.aborted) onExternalAbort()\n else external.addEventListener('abort', onExternalAbort)\n }\n\n try {\n let res: ResponseLike\n try {\n res = await doFetch(path, { ...init, signal: controller.signal })\n } catch (err) {\n if (timedOut) throw new ApiError('timeout', 'request_timeout', null, err)\n if (externallyAborted) throw new ApiError('aborted', 'request_aborted', null, err)\n throw new ApiError('network', 'network_error', null, err)\n }\n if (!res.ok) {\n // routes.ts answers most failures with a `{reason}` envelope; the M2\n // inject.execute failure answers the result view itself ({outcome:\n // 'failed', errorCode}) with no reason key, so the vocabulary code is\n // read as the fallback. Status-derived reason covers non-JSON bodies.\n let reason = `http_${res.status}`\n try {\n const body = await res.json()\n if (typeof body === 'object' && body !== null) {\n const record = body as Record<string, unknown>\n const value = record['reason']\n const code = record['errorCode']\n if (typeof value === 'string' && value !== '') reason = value\n else if (typeof code === 'string' && code !== '') reason = code\n }\n } catch {\n // Non-JSON error body: the fallback reason stands.\n }\n throw new ApiError('http', reason, res.status)\n }\n try {\n return await res.json()\n } catch (err) {\n if (timedOut) throw new ApiError('timeout', 'request_timeout', null, err)\n throw new ApiError('parse', 'invalid_json', res.status, err)\n }\n } finally {\n clearT(timer)\n if (external !== undefined) external.removeEventListener('abort', onExternalAbort)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public surface.\n// ---------------------------------------------------------------------------\n\n/** `GET <prefix>/state` — full board snapshot. */\nexport async function fetchState(opts: RequestOptions = {}): Promise<StateSnapshot> {\n return (await request(`${API_PREFIX}/state`, { method: 'GET' }, opts)) as StateSnapshot\n}\n\n/**\n * `GET <prefix>/session/<id>` — single-session detail. Unknown ids reject\n * with an ApiError carrying the server's `session_not_found` reason.\n */\nexport async function fetchSession(\n sessionId: string,\n opts: RequestOptions = {},\n): Promise<SessionDetail> {\n const path = `${API_PREFIX}/session/${encodeURIComponent(sessionId)}`\n return (await request(path, { method: 'GET' }, opts)) as SessionDetail\n}\n\n/**\n * `POST <prefix>/action` — transport layer only: the envelope is passed\n * through verbatim, failures are normalized, and there is deliberately NO\n * retry here — requestId idempotency and the `delivery:unknown` no-retry\n * rule (S6) are gateway/UI policy, not transport policy.\n */\nexport async function postAction(\n body: ActionEnvelope,\n opts: RequestOptions = {},\n): Promise<unknown> {\n return request(\n `${API_PREFIX}/action`,\n {\n method: 'POST',\n // The guard requires application/json on POST (415 otherwise).\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n },\n opts,\n )\n}\n","/**\n * Centralized user-facing strings for the board view and the footer widget\n * (design §5.1 view 1 / view 4, §5.3 honesty wording).\n *\n * Chinese is the primary UI language for now; every string the components\n * render lives in this single table so the i18n skeleton (T2.3,\n * `ctx.locale.register`) can take it over without hunting through JSX.\n * Templates use `{name}` placeholders resolved by `formatTemplate` in\n * `logic.ts` — plain message strings, locale-registry friendly.\n *\n * Pure data module: no imports, no logic.\n *\n * @module\n */\n\nexport const BOARD_STRINGS = {\n /** Session status badge labels (observed values, see hover disclaimer). */\n status: {\n working: '工作中',\n waiting: '等待中',\n idle: '空闲',\n dead: '已结束',\n unknown: '未知',\n },\n /** Badge attention markers; gap outranks stale (per-session > global). */\n attention: {\n gap: '事件缺口',\n stale: '可能滞后',\n },\n /** Daemon supervisor state labels for the top-bar badge. */\n daemon: {\n probe: '探测中',\n adopted: '已连接 · 领养',\n defer: '等待系统服务',\n reprobe: '重新探测中',\n hosting: '正在启动',\n hosted: '已连接 · 托管',\n backoff: '重启退避中',\n failed: '离线',\n },\n /** Stream health indicator labels. */\n stream: {\n ok: '实时流正常',\n degraded: '实时流重连中',\n unknown: '实时流未建立',\n },\n /** Top banner texts (design §5.3: degraded warning / FAILED red banner). */\n banner: {\n daemonFailed: 'sidecar 离线:看板显示最后一次快照,数据不再更新',\n streamDegraded: '实时流重连中,数据可能滞后',\n },\n /** Empty-state guidance blocks. */\n empty: {\n daemonFailedTitle: 'sidecar 已离线',\n daemonFailedHint:\n 'daemon 连续启动失败已熔断。可在设置卡重试,或手动运行 agent-sidecar daemon start 后等待自动领养。',\n daemonDeferTitle: '等待系统服务拉起 daemon',\n daemonDeferHint:\n '检测到 LaunchAgent 托管,插件不会自行启动 daemon;服务拉起后看板会自动出数。',\n filteredTitle: '当前过滤条件下没有会话',\n filteredHint: '试试放宽时间窗,或打开「显示已结束」。',\n noSessionsTitle: '暂无被观测的会话',\n noSessionsHint:\n '本机 agent(claude / codex / cursor / dsh …)开始工作后会自动出现在这里。',\n },\n /** Top bar controls. */\n topbar: {\n title: 'Sidecar 多 agent 看板',\n refresh: '刷新',\n refreshTitle: '手动拉取最新快照',\n showDead: '显示已结束',\n timeWindow: '时间窗',\n },\n /** Session card texts. */\n card: {\n noEvent: '暂无事件',\n untitled: '(无标题)',\n /** Design §5.3 / SKILL.md wording: statuses are inferred observations. */\n observedDisclaimer: '状态为从持久化数据推断的观察值,可能滞后',\n observedValue: '观察值: {status}',\n lastReconcile: '最近对账: {time}',\n neverReconciled: '尚未对账',\n },\n /** Relative time templates. */\n time: {\n justNow: '刚刚',\n minutesAgo: '{n} 分钟前',\n hoursAgo: '{n} 小时前',\n daysAgo: '{n} 天前',\n },\n /** Time-window option templates. */\n timeWindow: {\n hours: '{n} 小时',\n days: '{n} 天',\n },\n /** Group header session count. */\n groupCount: '{n} 个会话',\n /** Sessions with an empty `project` fall into this group (task spec). */\n unknownProject: '未知项目',\n /** Footer widget. */\n widget: {\n label: 'Sidecar',\n connection: {\n ok: '已连接',\n degraded: '连接不稳定',\n off: '离线',\n },\n working: '{n} 个会话工作中',\n },\n} as const\n\nexport type BoardStrings = typeof BOARD_STRINGS\n","/**\n * Pure view-model logic for the cross-agent session board (design §5.1\n * view 1) and the footer status widget (view 4). No React, no I/O, no\n * imports from the data layer (api/sse) — everything arrives as plain\n * values and leaves as plain values, so this whole module is unit-testable\n * in a bare node environment.\n *\n * Decoupling contract (T2.2 ↔ T2.4): the types below are this module's OWN\n * view models. The integration task maps the host wire shapes\n * (`SessionView` / `StateSnapshot` from the state endpoint) onto them.\n * Notably every timestamp here is **epoch milliseconds** — the sidecar\n * snapshot carries epoch seconds (`updated_at`), so the mapping layer\n * multiplies by 1000 once at the boundary.\n *\n * @module\n */\n\nimport { BOARD_STRINGS } from './strings.ts'\n\n// ---------------------------------------------------------------------------\n// View-model types (the props contract consumed by Board.tsx / widget.tsx).\n// ---------------------------------------------------------------------------\n\n/** Normalized session status vocabulary; anything else maps to 'unknown'. */\nexport type SessionStatusToken = 'working' | 'waiting' | 'idle' | 'dead' | 'unknown'\n\n/** Mirror of the host's StreamHealth union (kept local by design). */\nexport type StreamHealthToken = 'ok' | 'degraded' | 'unknown'\n\n/** Mirror of the host's SupervisorState union (kept local by design). */\nexport type DaemonStateToken =\n | 'probe'\n | 'adopted'\n | 'defer'\n | 'reprobe'\n | 'hosting'\n | 'hosted'\n | 'backoff'\n | 'failed'\n\n/** Color-token buckets the CSS maps to `--dsw-alias-state-*` variables. */\nexport type BadgeTone = 'success' | 'warn' | 'neutral' | 'muted' | 'danger'\n\n/** One session card as fed by the integration layer (raw, underived). */\nexport interface SessionCardVM {\n agent: string\n sessionId: string\n /** Raw observed status string from the sidecar snapshot (open vocabulary). */\n status: string\n title: string\n /** Working directory path; empty string groups under 未知项目. */\n project: string\n /** Epoch milliseconds (mapping layer converts sidecar epoch seconds). */\n updatedAtMs: number\n /** Most recent normalized event summary, if any. */\n lastEvent: { kind: string; text: string } | null\n /** True when a dsh seq discontinuity was observed since the last reconcile. */\n gap: boolean\n}\n\n/** Board filter controls (wired to ui.time-window-hours / ui.show-dead). */\nexport interface BoardFilterState {\n timeWindowHours: number\n showDead: boolean\n}\n\n/** Derived status badge: color token + label + attention marker. */\nexport interface StatusBadgeVM {\n status: SessionStatusToken\n tone: BadgeTone\n label: string\n /** 'gap' (per-session data hole) outranks 'stale' (global stream health). */\n attention: 'gap' | 'stale' | null\n attentionLabel: string | null\n}\n\n/** A session card with every render-ready derivation attached. */\nexport interface DerivedSessionCardVM extends SessionCardVM {\n badge: StatusBadgeVM\n glyph: string\n shortId: string\n relativeTime: string\n /** Hover title: observed-value disclaimer + raw status + lastReconcileAt. */\n hoverTitle: string\n}\n\n/** One project section on the board. */\nexport interface ProjectGroupVM<T extends SessionCardVM = DerivedSessionCardVM> {\n /** Raw project path; '' for the unknown-project bucket. */\n key: string\n /** Display name (path basename, or 未知项目). */\n label: string\n /** Full path for the hover title; '' when unknown. */\n fullPath: string\n cards: T[]\n}\n\nexport interface BoardBannerVM {\n tone: 'danger' | 'warn'\n text: string\n}\n\nexport type BoardEmptyKind = 'daemon-failed' | 'daemon-defer' | 'filtered' | 'no-sessions'\n\nexport interface BoardEmptyStateVM {\n kind: BoardEmptyKind\n title: string\n hint: string\n}\n\nexport interface DaemonBadgeVM {\n tone: BadgeTone\n label: string\n}\n\n/** Footer widget connection dot state. */\nexport type WidgetConnection = 'ok' | 'degraded' | 'off'\n\n/** Everything `buildBoardViewModel` needs (all plain values). */\nexport interface BoardComputeInput {\n sessions: SessionCardVM[]\n filters: BoardFilterState\n daemonState: DaemonStateToken\n streamHealth: StreamHealthToken\n /** Epoch ms of the last authoritative snapshot reconcile, or null. */\n lastReconcileAtMs: number | null\n /** Clock injection (epoch ms) for deterministic derivation. */\n nowMs: number\n}\n\n/** Fully derived board render model. */\nexport interface BoardViewModel {\n groups: Array<ProjectGroupVM<DerivedSessionCardVM>>\n banner: BoardBannerVM | null\n emptyState: BoardEmptyStateVM | null\n daemonBadge: DaemonBadgeVM\n streamLabel: string\n streamTone: BadgeTone\n visibleCount: number\n totalCount: number\n workingCount: number\n}\n\n// ---------------------------------------------------------------------------\n// Small shared helpers.\n// ---------------------------------------------------------------------------\n\nconst MINUTE_MS = 60_000\nconst HOUR_MS = 3_600_000\nconst DAY_MS = 86_400_000\n\n/** Default board time window, aligned with the ui.time-window-hours setting. */\nexport const DEFAULT_TIME_WINDOW_HOURS = 48\n\n/** Resolve `{name}` placeholders in a message template. */\nexport function formatTemplate(\n template: string,\n params: Record<string, string | number>,\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n const value = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n// ---------------------------------------------------------------------------\n// Status normalization and ordering.\n// ---------------------------------------------------------------------------\n\nconst KNOWN_STATUSES: readonly SessionStatusToken[] = ['working', 'waiting', 'idle', 'dead']\n\n/** Map a raw observed status onto the badge vocabulary ('unknown' fallback). */\nexport function normalizeStatus(raw: string): SessionStatusToken {\n const cleaned = raw.trim().toLowerCase()\n return (KNOWN_STATUSES as readonly string[]).includes(cleaned)\n ? (cleaned as SessionStatusToken)\n : 'unknown'\n}\n\nconst STATUS_RANK: Record<SessionStatusToken, number> = {\n working: 0,\n waiting: 1,\n idle: 2,\n unknown: 3,\n dead: 4,\n}\n\n/** Sort rank: working > waiting > idle > unknown > dead (lower sorts first). */\nexport function statusRank(status: SessionStatusToken): number {\n return STATUS_RANK[status]\n}\n\nconst STATUS_TONE: Record<SessionStatusToken, BadgeTone> = {\n working: 'success',\n waiting: 'warn',\n idle: 'neutral',\n unknown: 'neutral',\n dead: 'muted',\n}\n\n// ---------------------------------------------------------------------------\n// Time-window filtering.\n// ---------------------------------------------------------------------------\n\n/**\n * Visibility rules (task spec):\n * - dead sessions are hidden unless `showDead`;\n * - working sessions are always visible (even outside the window);\n * - everything else hides once `updatedAtMs` falls strictly beyond the\n * window (age === window is still visible);\n * - a non-finite or non-positive window disables the age filter entirely.\n */\nexport function isSessionVisible(\n session: SessionCardVM,\n filters: BoardFilterState,\n nowMs: number,\n): boolean {\n const status = normalizeStatus(session.status)\n if (status === 'dead' && !filters.showDead) return false\n if (status === 'working') return true\n const windowMs = filters.timeWindowHours * HOUR_MS\n if (!Number.isFinite(windowMs) || windowMs <= 0) return true\n return nowMs - session.updatedAtMs <= windowMs\n}\n\n/** Apply {@link isSessionVisible} across a session list (order preserved). */\nexport function filterSessions<T extends SessionCardVM>(\n sessions: readonly T[],\n filters: BoardFilterState,\n nowMs: number,\n): T[] {\n return sessions.filter((s) => isSessionVisible(s, filters, nowMs))\n}\n\n// ---------------------------------------------------------------------------\n// Project grouping.\n// ---------------------------------------------------------------------------\n\n/** Human display name for a project path ('' → 未知项目). */\nexport function projectDisplayName(project: string): string {\n const trimmed = project.trim().replace(/[/\\\\]+$/, '')\n if (trimmed === '') return BOARD_STRINGS.unknownProject\n const segments = trimmed.split(/[/\\\\]/)\n const base = segments[segments.length - 1] ?? ''\n return base === '' ? trimmed : base\n}\n\n/** Card ordering inside a group: status rank, then recency, then id. */\nexport function compareCards(a: SessionCardVM, b: SessionCardVM): number {\n const rankDelta = statusRank(normalizeStatus(a.status)) - statusRank(normalizeStatus(b.status))\n if (rankDelta !== 0) return rankDelta\n if (a.updatedAtMs !== b.updatedAtMs) return b.updatedAtMs - a.updatedAtMs\n return a.sessionId.localeCompare(b.sessionId)\n}\n\n/**\n * Group sessions by project. Empty/whitespace projects share the\n * unknown-project bucket (key ''). Groups are ordered by their most recent\n * `updatedAtMs` descending, except the unknown bucket which always sorts\n * last (named projects are more actionable than the catch-all). Cards\n * inside each group follow {@link compareCards}.\n */\nexport function groupSessions<T extends SessionCardVM>(\n sessions: readonly T[],\n): Array<ProjectGroupVM<T>> {\n const buckets = new Map<string, T[]>()\n for (const session of sessions) {\n const key = session.project.trim() === '' ? '' : session.project\n const bucket = buckets.get(key)\n if (bucket === undefined) buckets.set(key, [session])\n else bucket.push(session)\n }\n const groups: Array<ProjectGroupVM<T>> = []\n for (const [key, cards] of buckets) {\n cards.sort(compareCards)\n groups.push({\n key,\n label: key === '' ? BOARD_STRINGS.unknownProject : projectDisplayName(key),\n fullPath: key,\n cards,\n })\n }\n const newest = (group: ProjectGroupVM<T>): number =>\n group.cards.reduce((max, card) => Math.max(max, card.updatedAtMs), Number.NEGATIVE_INFINITY)\n groups.sort((a, b) => {\n if (a.key === '') return b.key === '' ? 0 : 1\n if (b.key === '') return -1\n return newest(b) - newest(a) || a.key.localeCompare(b.key)\n })\n return groups\n}\n\n// ---------------------------------------------------------------------------\n// Badge derivation.\n// ---------------------------------------------------------------------------\n\n/**\n * status + gap + streamHealth → badge tone/label/attention.\n *\n * Priority: a per-session `gap` marker (a known data hole for THIS session)\n * outranks the global stale marker (stream reconnecting affects everyone\n * and is already surfaced by the top banner). Unknown raw statuses keep\n * their raw text as the label — the board never invents a state.\n */\nexport function deriveBadge(\n rawStatus: string,\n gap: boolean,\n streamHealth: StreamHealthToken,\n): StatusBadgeVM {\n const status = normalizeStatus(rawStatus)\n const trimmed = rawStatus.trim()\n const label =\n status === 'unknown'\n ? trimmed === ''\n ? BOARD_STRINGS.status.unknown\n : trimmed\n : BOARD_STRINGS.status[status]\n let attention: StatusBadgeVM['attention'] = null\n if (gap) attention = 'gap'\n else if (streamHealth !== 'ok') attention = 'stale'\n return {\n status,\n tone: STATUS_TONE[status],\n label,\n attention,\n attentionLabel: attention === null ? null : BOARD_STRINGS.attention[attention],\n }\n}\n\n/**\n * Hover title for a status badge: the observed-value disclaimer (design\n * §5.3 wording), the raw observed status, and the last reconcile time.\n */\nexport function badgeHoverTitle(\n rawStatus: string,\n lastReconcileAtMs: number | null,\n nowMs: number,\n): string {\n const observed = rawStatus.trim() === '' ? BOARD_STRINGS.status.unknown : rawStatus.trim()\n const reconcile =\n lastReconcileAtMs === null\n ? BOARD_STRINGS.card.neverReconciled\n : formatTemplate(BOARD_STRINGS.card.lastReconcile, {\n time: formatRelativeTime(lastReconcileAtMs, nowMs),\n })\n return [\n BOARD_STRINGS.card.observedDisclaimer,\n formatTemplate(BOARD_STRINGS.card.observedValue, { status: observed }),\n reconcile,\n ].join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Time formatting.\n// ---------------------------------------------------------------------------\n\n/**\n * Coarse relative time: <60s (including clock skew into the future) is\n * 刚刚, then whole minutes/hours/days. Non-finite input renders empty.\n */\nexport function formatRelativeTime(thenMs: number, nowMs: number): string {\n if (!Number.isFinite(thenMs)) return ''\n const delta = nowMs - thenMs\n if (delta < MINUTE_MS) return BOARD_STRINGS.time.justNow\n if (delta < HOUR_MS) {\n return formatTemplate(BOARD_STRINGS.time.minutesAgo, { n: Math.floor(delta / MINUTE_MS) })\n }\n if (delta < DAY_MS) {\n return formatTemplate(BOARD_STRINGS.time.hoursAgo, { n: Math.floor(delta / HOUR_MS) })\n }\n return formatTemplate(BOARD_STRINGS.time.daysAgo, { n: Math.floor(delta / DAY_MS) })\n}\n\n/** Label for a time-window option: whole days as 天, otherwise 小时. */\nexport function timeWindowLabel(hours: number): string {\n if (hours >= 24 && hours % 24 === 0) {\n return formatTemplate(BOARD_STRINGS.timeWindow.days, { n: hours / 24 })\n }\n return formatTemplate(BOARD_STRINGS.timeWindow.hours, { n: hours })\n}\n\n// ---------------------------------------------------------------------------\n// Top bar / banner / empty-state derivation.\n// ---------------------------------------------------------------------------\n\nconst DAEMON_TONE: Record<DaemonStateToken, BadgeTone> = {\n probe: 'neutral',\n adopted: 'success',\n defer: 'warn',\n reprobe: 'neutral',\n hosting: 'neutral',\n hosted: 'success',\n backoff: 'warn',\n failed: 'danger',\n}\n\n/** Daemon state → top-bar badge (tone + label). */\nexport function deriveDaemonBadge(state: DaemonStateToken): DaemonBadgeVM {\n return { tone: DAEMON_TONE[state], label: BOARD_STRINGS.daemon[state] }\n}\n\n/** Stream health → indicator tone. */\nexport function streamHealthTone(health: StreamHealthToken): BadgeTone {\n if (health === 'ok') return 'success'\n if (health === 'degraded') return 'warn'\n return 'neutral'\n}\n\n/**\n * Top banner: daemon FAILED (red, last-snapshot notice) outranks a\n * degraded stream (yellow, may-lag notice). 'unknown' stream health alone\n * raises no banner — it is the pre-first-connect startup state and a\n * warning bar on every mount would be noise.\n */\nexport function deriveBanner(\n daemonState: DaemonStateToken,\n streamHealth: StreamHealthToken,\n): BoardBannerVM | null {\n if (daemonState === 'failed') {\n return { tone: 'danger', text: BOARD_STRINGS.banner.daemonFailed }\n }\n if (streamHealth === 'degraded') {\n return { tone: 'warn', text: BOARD_STRINGS.banner.streamDegraded }\n }\n return null\n}\n\n/**\n * Empty-state guidance, only when nothing is visible. Daemon trouble\n * (failed > defer) explains an empty board better than filter settings;\n * otherwise distinguish \"all filtered out\" from \"nothing observed yet\".\n */\nexport function deriveEmptyState(\n daemonState: DaemonStateToken,\n visibleCount: number,\n totalCount: number,\n): BoardEmptyStateVM | null {\n if (visibleCount > 0) return null\n if (daemonState === 'failed') {\n return {\n kind: 'daemon-failed',\n title: BOARD_STRINGS.empty.daemonFailedTitle,\n hint: BOARD_STRINGS.empty.daemonFailedHint,\n }\n }\n if (daemonState === 'defer') {\n return {\n kind: 'daemon-defer',\n title: BOARD_STRINGS.empty.daemonDeferTitle,\n hint: BOARD_STRINGS.empty.daemonDeferHint,\n }\n }\n if (totalCount > 0) {\n return {\n kind: 'filtered',\n title: BOARD_STRINGS.empty.filteredTitle,\n hint: BOARD_STRINGS.empty.filteredHint,\n }\n }\n return {\n kind: 'no-sessions',\n title: BOARD_STRINGS.empty.noSessionsTitle,\n hint: BOARD_STRINGS.empty.noSessionsHint,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Card presentation helpers.\n// ---------------------------------------------------------------------------\n\nconst AGENT_GLYPHS: Record<string, string> = {\n dsh: '◆',\n claude: '✳',\n codex: '▣',\n cursor: '▮',\n 'cursor-cli': '▮',\n 'cursor-ide': '▮',\n copilot: '◉',\n kimi: '◐',\n}\n\n/** Single-character agent marker; unknown agents get a neutral dot. */\nexport function agentGlyph(agent: string): string {\n return AGENT_GLYPHS[agent.trim().toLowerCase()] ?? '●'\n}\n\n/** Head…tail abbreviation for long session ids (full id goes in `title`). */\nexport function abbreviateSessionId(id: string, max = 20): string {\n if (id.length <= max) return id\n return `${id.slice(0, 12)}…${id.slice(-6)}`\n}\n\n// ---------------------------------------------------------------------------\n// Footer widget derivation.\n// ---------------------------------------------------------------------------\n\n/**\n * Connection dot: green only when the daemon is connected (adopted/hosted)\n * AND the event stream is healthy; FAILED is the only hard-off state;\n * every transitional state shows the cautious yellow.\n */\nexport function deriveWidgetConnection(\n daemonState: DaemonStateToken,\n streamHealth: StreamHealthToken,\n): WidgetConnection {\n if (daemonState === 'failed') return 'off'\n if ((daemonState === 'adopted' || daemonState === 'hosted') && streamHealth === 'ok') {\n return 'ok'\n }\n return 'degraded'\n}\n\n/** Count of sessions currently observed as working. */\nexport function countWorking(sessions: ReadonlyArray<{ status: string }>): number {\n let count = 0\n for (const session of sessions) {\n if (normalizeStatus(session.status) === 'working') count += 1\n }\n return count\n}\n\n/** Widget hover/aria text: connection state, plus the count when nonzero. */\nexport function widgetTitle(connection: WidgetConnection, workingCount: number): string {\n const base = `${BOARD_STRINGS.widget.label}: ${BOARD_STRINGS.widget.connection[connection]}`\n if (workingCount <= 0) return base\n return `${base} · ${formatTemplate(BOARD_STRINGS.widget.working, { n: workingCount })}`\n}\n\n// ---------------------------------------------------------------------------\n// Full pipeline.\n// ---------------------------------------------------------------------------\n\n/** filter → group → per-card derive; the one call Board.tsx renders from. */\nexport function buildBoardViewModel(input: BoardComputeInput): BoardViewModel {\n const { sessions, filters, daemonState, streamHealth, lastReconcileAtMs, nowMs } = input\n const visible = filterSessions(sessions, filters, nowMs)\n const derived: DerivedSessionCardVM[] = visible.map((session) => ({\n ...session,\n badge: deriveBadge(session.status, session.gap, streamHealth),\n glyph: agentGlyph(session.agent),\n shortId: abbreviateSessionId(session.sessionId),\n relativeTime: formatRelativeTime(session.updatedAtMs, nowMs),\n hoverTitle: badgeHoverTitle(session.status, lastReconcileAtMs, nowMs),\n }))\n return {\n groups: groupSessions(derived),\n banner: deriveBanner(daemonState, streamHealth),\n emptyState: deriveEmptyState(daemonState, visible.length, sessions.length),\n daemonBadge: deriveDaemonBadge(daemonState),\n streamLabel: BOARD_STRINGS.stream[streamHealth],\n streamTone: streamHealthTone(streamHealth),\n visibleCount: visible.length,\n totalCount: sessions.length,\n workingCount: countWorking(sessions),\n }\n}\n","/**\n * Centralized user-facing strings for the session-detail view (design §5.1\n * view 2, §5.3 honesty wording). Deliberately a module-local table — NOT\n * part of the main locale registry (locales/zh.ts / locales/en.ts): the\n * locale tables are owned by parallel tasks and the S7 integration wave\n * folds this table in centrally. Same posture as board/strings.ts.\n *\n * Chinese is the primary UI language. Templates use `{name}` placeholders\n * resolved by `formatTemplate` in `logic.ts`.\n *\n * Pure data module: no imports, no logic.\n *\n * @module\n */\n\nexport const DETAIL_STRINGS = {\n /** Header block (session meta + controls). */\n header: {\n close: '返回看板',\n listenOn: '监听中',\n listenOff: '监听',\n listenHint: '开启后新事件将实时追加并高亮',\n untitled: '(无标题)',\n unknownProject: '未知项目',\n /** Design §5.3 / SKILL.md wording: statuses are inferred observations. */\n observedDisclaimer: '状态为从持久化数据推断的观察值,可能滞后',\n },\n /** Session status badge labels (observed values, see disclaimer). */\n status: {\n working: '工作中',\n waiting: '等待中',\n idle: '空闲',\n dead: '已结束',\n unknown: '未知',\n },\n /** Timeline source badges (provenance of the merged page, honest labels). */\n sources: {\n title: '数据来源',\n dshLive: 'dsh 实时',\n dshCold: 'dsh 冷读',\n sidecarReplay: 'sidecar 重放',\n sidecarBuffer: 'sidecar 缓冲',\n none: '来源未知',\n },\n /** Normalized event-kind labels; unknown kinds keep their raw text. */\n kind: {\n user: '用户消息',\n assistant: '助手回复',\n thinking: '思考',\n toolCall: '工具调用',\n toolResult: '工具结果',\n turn: '回合',\n step: '步骤',\n error: '错误',\n other: '事件',\n },\n /** Seq-discontinuity marker row (design §4.b.3 honest presentation). */\n gap: {\n label: '缺口:可能有 {n} 条事件未捕获(256 队列上限或未持久化)',\n },\n /** Timeline list chrome. */\n timeline: {\n loadMore: '加载更多历史',\n loadingMore: '加载中…',\n noMore: '已到时间线起点',\n expand: '展开',\n collapse: '收起',\n newBadge: '新',\n seq: 'seq {n}',\n hiddenNotice: '为保持流畅,较早的 {n} 条已折叠',\n showAll: '全部显示',\n },\n /** Loading / empty / error body states. */\n states: {\n loadingTitle: '正在加载时间线…',\n emptyTitle: '暂无事件',\n emptyHint: '该会话还没有可展示的规范化事件。',\n errorTitle: '时间线加载失败',\n /** Fallback template when the reason code has no friendly mapping. */\n errorFallback: '错误码:{reason}',\n errors: {\n session_not_found: '会话不存在或已不可见',\n invalid_cursor: '分页游标无效,请重新打开详情',\n fusion_not_wired: '当前 host 未启用时间线能力',\n network_error: '网络错误,无法联系 dsh host',\n request_timeout: '请求超时',\n },\n },\n /** Relative time templates (coarse buckets, matches the board wording). */\n time: {\n justNow: '刚刚',\n minutesAgo: '{n} 分钟前',\n hoursAgo: '{n} 小时前',\n daysAgo: '{n} 天前',\n },\n} as const\n\nexport type DetailStrings = typeof DETAIL_STRINGS\n","/**\n * Centralized user-facing strings for the dsh deep-query tools (design\n * §5.1 view 2 dsh 会话专属区: lineage tree / provenance jump / full-text\n * search; §5.3 honest degradation wording).\n *\n * Deliberately a MODULE-LOCAL table (task T5.4 constraint): the main\n * locale table (client/locales) and test/locales.test.ts are untouched —\n * S7 merges this table into the locale registry in one move. Chinese is\n * the primary UI language, same posture as board/strings.ts. Templates\n * use `{name}` placeholders resolved by `formatTemplate` in logic.ts.\n *\n * Pure data module: no imports, no logic.\n *\n * @module\n */\n\nexport const DSH_TOOLS_STRINGS = {\n /** Lineage tree panel (dsh 谱系/溯源). */\n lineage: {\n title: '会话谱系',\n loading: '谱系加载中…',\n /** Transport/HTTP failure headline (the detail carries the cause). */\n error: '谱系加载失败',\n /** available:true but the trace body is missing — honest empty state. */\n empty: '暂无谱系数据',\n /** Badge on the session the user is currently inspecting. */\n currentBadge: '当前会话',\n /** Badge on records live in this dsh process. */\n liveBadge: '运行中',\n /** Badge on records the trace knows but dsh has not persisted. */\n notPersistedBadge: '未持久化',\n /** Role labels for tree rows. */\n role: {\n ancestor: '祖先',\n target: '目标',\n descendant: '子会话',\n },\n /** Hover title for a clickable node (provenance jump). */\n jumpTitle: '跳转到该会话',\n /** Hover title for the highlighted current node (no jump). */\n currentTitle: '正在查看该会话',\n expand: '展开',\n collapse: '折叠',\n nodeCount: '{n} 个会话',\n /** trace.complete === false with a known unresolved parent id. */\n incompleteWithId: '谱系不完整:父会话 {id} 无法解析',\n /** trace.complete === false without an unresolved parent id. */\n incomplete: '谱系不完整:部分父链无法解析',\n /** Degradation cards — available:false is data, not an error (§4.e.4). */\n degrade: {\n /** Client-side reason for sessions of non-dsh agents (task spec copy). */\n notDshTitle: '谱系/溯源为 dsh 会话专属',\n notDshBody: '当前会话来自外部 agent,dsh 的谱系与溯源能力不适用。',\n /** Backend reason `session_query_unavailable`. */\n queryUnavailableTitle: 'dsh 谱系服务不可用',\n queryUnavailableBody:\n '当前 dsh 组合未挂载 sessionQuery,谱系与溯源暂不可用。',\n /** Backend reason `trace_failed`. */\n traceFailedTitle: '谱系追溯失败',\n traceFailedBody: 'dsh 无法解析该会话的谱系。',\n /** Any reason outside the known vocabulary (never invent a state). */\n unknownTitle: '谱系不可用',\n unknownBody: '后端报告谱系不可用(原因: {reason})。',\n },\n },\n /** Cross-agent search panel (dsh 全文检索 + 降级过滤). */\n search: {\n title: '会话检索',\n placeholder: '检索会话(标题 / 项目 / 全文)',\n submit: '检索',\n loading: '检索中…',\n error: '检索失败',\n /** Query submitted, zero hits. */\n empty: '没有匹配的会话',\n /** mode: 'filter-only' degradation bar (task spec copy, verbatim). */\n filterOnlyNotice: 'dsh 全文检索不可用,已降级为标题/项目过滤',\n /** Active project-filter chip. */\n projectFilter: '项目过滤: {project}',\n /** matchedBy tags on result rows. */\n matchedBy: {\n 'full-text': '全文',\n title: '标题',\n project: '项目',\n other: '其他',\n },\n untitled: '(无标题)',\n },\n} as const\n\nexport type DshToolsStrings = typeof DSH_TOOLS_STRINGS\n","/**\n * Pure view-model logic for the cross-agent project correlation view\n * (design §4.e.2 / §5.1 M3, T5.5). No React, no I/O, no data-layer\n * imports — plain values in, plain values out, unit-testable in a bare\n * node environment (same discipline as `logic.ts`).\n *\n * Wire contract (hand-written mirror, per module-ownership rules): the\n * input types below mirror the host's `GET <prefix>/projects` response —\n * `{groups: ProjectGroup[]}` where `ProjectGroup` is fusion.ts\n * `getProjectGroups()` output: `project` (normalized path, '' when\n * unknown), `agents` (distinct, sorted), `sessions` (UnifiedSession[],\n * camelCase, **epoch milliseconds** `lastActivityAt`, most recent first)\n * and the group-level `lastActivityAt`. Only the fields this view renders\n * are mirrored; extra wire fields are structurally ignored.\n *\n * Reuse contract (T2.2, read-only): tone/glyph/relative-time/label tools\n * are imported from `./logic.ts` without touching its exports, so both\n * board views speak the same visual language. Strings NEW to this view\n * live in the module-local {@link PROJECT_VIEW_STRINGS} table (S7 unifies\n * locales later); strings emitted by reused board tools (status labels,\n * relative time, the unknown-project label) intentionally stay with the\n * board table so the two views can never drift apart.\n *\n * @module\n */\n\nimport {\n abbreviateSessionId,\n agentGlyph,\n deriveBadge,\n formatRelativeTime,\n formatTemplate,\n normalizeStatus,\n projectDisplayName,\n statusRank,\n type StatusBadgeVM,\n} from './logic.ts'\n\n// ---------------------------------------------------------------------------\n// Module-local strings (project-view-only vocabulary; Chinese primary).\n// ---------------------------------------------------------------------------\n\nexport const PROJECT_VIEW_STRINGS = {\n /** View heading. */\n title: '项目关联',\n /** Header count summary. */\n summary: '{projects} 个项目 · {sessions} 个会话',\n /** Cross-agent badge, shown when a project hosts 2+ agent kinds. */\n crossAgent: '{n} 种 agent',\n /** Group header session count. */\n sessionCount: '{n} 个会话',\n /** Group header recency line. */\n lastActive: '最近活跃 {time}',\n /** Marker on sessions live in this dsh process (UnifiedSession.live). */\n liveChip: '实时',\n /** Untitled-session fallback. */\n untitled: '(无标题)',\n /** Empty state (no groups at all). */\n empty: {\n title: '暂无项目关联',\n hint: '时间窗内没有跨 agent 的项目活动;agent 在某个项目目录下开始工作后会出现在这里。',\n },\n /** Loading placeholder (first fetch, nothing to show yet). */\n loading: '正在加载项目关联…',\n /** Error banner prefix; the raw error detail is appended by the view. */\n errorTitle: '项目关联加载失败',\n} as const\n\nexport type ProjectViewStrings = typeof PROJECT_VIEW_STRINGS\n\n// ---------------------------------------------------------------------------\n// Input view models (hand-written wire mirror, see module doc).\n// ---------------------------------------------------------------------------\n\n/** One session row of a project group (mirror of `UnifiedSession` subset). */\nexport interface ProjectSessionVM {\n agent: string\n sessionId: string\n /** Raw observed status (open vocabulary; sidecar-inferred). */\n status: string\n title: string\n /** Unix epoch MILLISECONDS (fusion already normalized the sidecar seconds). */\n lastActivityAt: number\n /** True when the session is live in this dsh process. */\n live?: boolean\n /** Sidecar-observed seq discontinuity flag. */\n gap?: boolean\n}\n\n/** One wire project group (mirror of fusion.ts `ProjectGroup`). */\nexport interface ProjectGroupVM {\n /** Project path; '' for the unknown-project bucket. */\n project: string\n /** Distinct agents as reported by the host (re-derived defensively here). */\n agents: string[]\n sessions: ProjectSessionVM[]\n /** Unix epoch MILLISECONDS of the freshest member session. */\n lastActivityAt: number\n}\n\n// ---------------------------------------------------------------------------\n// Derived view models (what project-view.tsx renders).\n// ---------------------------------------------------------------------------\n\n/** A session row with every render-ready derivation attached. */\nexport interface DerivedProjectSessionVM {\n agent: string\n sessionId: string\n status: string\n title: string\n lastActivityAt: number\n live: boolean\n gap: boolean\n badge: StatusBadgeVM\n glyph: string\n shortId: string\n relativeTime: string\n /** Title with the untitled fallback already resolved. */\n displayTitle: string\n}\n\n/** One per-agent column/section inside a project group. */\nexport interface AgentLaneVM {\n agent: string\n glyph: string\n sessions: DerivedProjectSessionVM[]\n}\n\n/** One agent participation marker of the group header. */\nexport interface AgentBadgeVM {\n agent: string\n glyph: string\n}\n\n/** One fully derived project section. */\nexport interface DerivedProjectGroupVM {\n /** Normalized project path; '' for the unknown-project bucket. */\n key: string\n /** Display name (path basename; board's 未知项目 label for ''). */\n label: string\n /** Full path for the hover title; '' when unknown. */\n fullPath: string\n /** Distinct participating agents, sorted (derived from sessions). */\n agentBadges: AgentBadgeVM[]\n /** Cross-agent marker text; null when a single agent kind participates. */\n crossAgentLabel: string | null\n sessionCount: number\n sessionCountLabel: string\n /** Unix epoch ms of the freshest member session. */\n lastActivityAt: number\n /** '最近活跃 {relative time}'; empty when the group has no finite time. */\n lastActiveLabel: string\n /** Per-agent lanes, sorted by agent name (same order as agentBadges). */\n lanes: AgentLaneVM[]\n}\n\n/** Full render model for the project view. */\nexport interface ProjectViewModel {\n groups: DerivedProjectGroupVM[]\n /** Non-null exactly when there is no group to render. */\n emptyState: { title: string; hint: string } | null\n projectCount: number\n sessionCount: number\n /** Header summary line ('{projects} 个项目 · {sessions} 个会话'). */\n summaryLabel: string\n}\n\n// ---------------------------------------------------------------------------\n// Normalization helpers.\n// ---------------------------------------------------------------------------\n\nconst KEY_SEP = '\\u0000'\n\n/**\n * Group-key normalization, aligned with fusion's correlation key: trim,\n * strip trailing slashes (keeping root '/'), whitespace-only → '' (the\n * unknown bucket). The host already normalizes; this re-run makes the\n * view robust to hand-fed or merged inputs.\n */\nexport function normalizeProjectKey(project: string): string {\n const trimmed = project.trim()\n if (trimmed === '') return ''\n if (trimmed.length > 1 && trimmed.endsWith('/')) {\n const stripped = trimmed.replace(/\\/+$/, '')\n return stripped === '' ? '/' : stripped\n }\n return trimmed\n}\n\n/**\n * Distinct participating agents of a session list, sorted by name. This\n * is derived from the sessions themselves (not the wire `agents` field)\n * so the badge row can never disagree with the lanes actually rendered —\n * notably after two wire groups merge under one normalized key.\n */\nexport function deriveAgentBadges(\n sessions: ReadonlyArray<{ agent: string }>,\n): AgentBadgeVM[] {\n const names = new Set<string>()\n for (const session of sessions) names.add(session.agent)\n return [...names].sort().map((agent) => ({ agent, glyph: agentGlyph(agent) }))\n}\n\n/**\n * Session ordering inside a lane, mirroring the board's card order so\n * both views read the same way: status rank (working first, dead last),\n * then recency, then id as the deterministic tiebreak.\n */\nexport function compareProjectSessions(a: ProjectSessionVM, b: ProjectSessionVM): number {\n const rankDelta = statusRank(normalizeStatus(a.status)) - statusRank(normalizeStatus(b.status))\n if (rankDelta !== 0) return rankDelta\n if (a.lastActivityAt !== b.lastActivityAt) return b.lastActivityAt - a.lastActivityAt\n return a.sessionId.localeCompare(b.sessionId)\n}\n\nfunction deriveSession(session: ProjectSessionVM, nowMs: number): DerivedProjectSessionVM {\n const live = session.live === true\n const gap = session.gap === true\n return {\n agent: session.agent,\n sessionId: session.sessionId,\n status: session.status,\n title: session.title,\n lastActivityAt: session.lastActivityAt,\n live,\n gap,\n // 'ok' stream health: the project view has no global stream signal,\n // so badge attention reflects only the per-session gap marker.\n badge: deriveBadge(session.status, gap, 'ok'),\n glyph: agentGlyph(session.agent),\n shortId: abbreviateSessionId(session.sessionId),\n relativeTime: formatRelativeTime(session.lastActivityAt, nowMs),\n displayTitle: session.title.trim() === '' ? PROJECT_VIEW_STRINGS.untitled : session.title,\n }\n}\n\n/**\n * Split a group's sessions into per-agent lanes. Lanes are sorted by\n * agent name (matching the header badge order); sessions inside a lane\n * follow {@link compareProjectSessions}.\n */\nexport function buildAgentLanes(\n sessions: readonly ProjectSessionVM[],\n nowMs: number,\n): AgentLaneVM[] {\n const byAgent = new Map<string, ProjectSessionVM[]>()\n for (const session of sessions) {\n const lane = byAgent.get(session.agent)\n if (lane === undefined) byAgent.set(session.agent, [session])\n else lane.push(session)\n }\n return [...byAgent.keys()].sort().map((agent) => {\n const members = byAgent.get(agent) ?? []\n members.sort(compareProjectSessions)\n return {\n agent,\n glyph: agentGlyph(agent),\n sessions: members.map((session) => deriveSession(session, nowMs)),\n }\n })\n}\n\n// ---------------------------------------------------------------------------\n// Full pipeline.\n// ---------------------------------------------------------------------------\n\nexport interface ProjectViewComputeInput {\n groups: readonly ProjectGroupVM[]\n /** Clock injection (epoch ms) for deterministic derivation. */\n nowMs: number\n}\n\n/**\n * normalize/merge → derive → sort; the one call project-view.tsx renders\n * from.\n *\n * Normalization: groups collapsing onto the same normalized key are\n * merged; duplicate sessions (same agent + sessionId) within a merged\n * group keep the freshest copy. Group order is `lastActivityAt`\n * descending (recomputed from member sessions, so a stale wire value\n * cannot misplace a group), with the unknown-project bucket ('') always\n * last — same rule as the board.\n */\nexport function buildProjectViewModel(input: ProjectViewComputeInput): ProjectViewModel {\n const { groups, nowMs } = input\n\n interface Bucket {\n sessions: Map<string, ProjectSessionVM>\n wireLastActivityAt: number\n }\n const buckets = new Map<string, Bucket>()\n for (const group of groups) {\n const key = normalizeProjectKey(group.project)\n let bucket = buckets.get(key)\n if (bucket === undefined) {\n bucket = { sessions: new Map(), wireLastActivityAt: Number.NEGATIVE_INFINITY }\n buckets.set(key, bucket)\n }\n if (Number.isFinite(group.lastActivityAt)) {\n bucket.wireLastActivityAt = Math.max(bucket.wireLastActivityAt, group.lastActivityAt)\n }\n for (const session of group.sessions) {\n const id = `${session.agent}${KEY_SEP}${session.sessionId}`\n const existing = bucket.sessions.get(id)\n if (existing === undefined || session.lastActivityAt > existing.lastActivityAt) {\n bucket.sessions.set(id, session)\n }\n }\n }\n\n const derived: DerivedProjectGroupVM[] = []\n let sessionCount = 0\n for (const [key, bucket] of buckets) {\n const sessions = [...bucket.sessions.values()]\n sessionCount += sessions.length\n const memberMax = sessions.reduce(\n (max, s) => (Number.isFinite(s.lastActivityAt) ? Math.max(max, s.lastActivityAt) : max),\n Number.NEGATIVE_INFINITY,\n )\n const lastActivityAt = Math.max(memberMax, bucket.wireLastActivityAt)\n const agentBadges = deriveAgentBadges(sessions)\n derived.push({\n key,\n label: projectDisplayName(key),\n fullPath: key,\n agentBadges,\n crossAgentLabel:\n agentBadges.length > 1\n ? formatTemplate(PROJECT_VIEW_STRINGS.crossAgent, { n: agentBadges.length })\n : null,\n sessionCount: sessions.length,\n sessionCountLabel: formatTemplate(PROJECT_VIEW_STRINGS.sessionCount, {\n n: sessions.length,\n }),\n lastActivityAt: Number.isFinite(lastActivityAt) ? lastActivityAt : 0,\n lastActiveLabel: Number.isFinite(lastActivityAt)\n ? formatTemplate(PROJECT_VIEW_STRINGS.lastActive, {\n time: formatRelativeTime(lastActivityAt, nowMs),\n })\n : '',\n lanes: buildAgentLanes(sessions, nowMs),\n })\n }\n\n derived.sort((a, b) => {\n if (a.key === '') return b.key === '' ? 0 : 1\n if (b.key === '') return -1\n return b.lastActivityAt - a.lastActivityAt || a.key.localeCompare(b.key)\n })\n\n return {\n groups: derived,\n emptyState: derived.length === 0 ? { ...PROJECT_VIEW_STRINGS.empty } : null,\n projectCount: derived.length,\n sessionCount,\n summaryLabel: formatTemplate(PROJECT_VIEW_STRINGS.summary, {\n projects: derived.length,\n sessions: sessionCount,\n }),\n }\n}\n","/**\n * Simplified-Chinese dictionary — the key-set source of truth for the\n * agent-sidecar client locale table (the en dictionary is checked complete\n * against this key union; see ./en.ts).\n *\n * Key-space convention (compile-enforced by the `satisfies` clause): every\n * key is `<domain>.<leaf>` where the domain is one of\n * {@link SidecarLocaleDomain}. `settings.*` is owned by the settings card\n * (T2.3); `inject.*` by the inject panel (T4.5, src/client/inject/);\n * `board.*` by the board-tab chrome (view switcher); `detail.*` /\n * `dshtools.*` / `project.*` mirror the M3 module tables (see below);\n * `analysis.*` is owned by the analysis panel (T5.10b); `command.*` by the\n * `/sidecar` slash command (T4.6, re-exported via ./command.ts);\n * `sidebar.*` by the optional better-sidebar mini tab (T6.3,\n * src/client/sidebar-tab.tsx) — keeping the prefixes in one flat namespace\n * avoids cross-task collisions.\n *\n * M3 unification (T5.10b): the component-local tables `detail/strings.ts`,\n * `dsh-tools/strings.ts` and `PROJECT_VIEW_STRINGS` stay the rendering\n * source for their components (decoupling kept), and this table REFERENCES\n * them entry-by-entry so `t()` covers every M3 string with zero copy drift\n * (parity is pinned by test/locales.test.ts). en translations for those\n * domains live in ./en.ts (the module tables are zh-only).\n *\n * Copy sources: the schemastery `.description()` strings in src/config.ts\n * (the authoritative zh copy for each field) and design doc §4.a/§5.3/§6/§8.\n */\n\nimport { DETAIL_STRINGS } from '../detail/strings.ts'\nimport { DSH_TOOLS_STRINGS } from '../dsh-tools/strings.ts'\nimport { PROJECT_VIEW_STRINGS } from '../board/project-view-logic.ts'\n\n/** Locale key domains (one owner surface per domain, see module doc). */\nexport type SidecarLocaleDomain =\n | 'settings'\n | 'board'\n | 'inject'\n | 'detail'\n | 'dshtools'\n | 'project'\n | 'analysis'\n | 'command'\n | 'sidebar'\n\nconst D = DETAIL_STRINGS\nconst Q = DSH_TOOLS_STRINGS\nconst P = PROJECT_VIEW_STRINGS\n\nexport const zh = {\n // ── card chrome ────────────────────────────────────────────────────────\n 'settings.cardTitle': 'Agent Sidecar',\n 'settings.cardDescription': '跨 agent 会话监控、注入与旁路分析的运行设置。',\n 'settings.docsLink': '查看文档',\n 'settings.readOnly': '当前设置文档为只读,修改不可保存。',\n 'settings.unsaved': '未保存',\n 'settings.save': '保存',\n 'settings.saving': '保存中…',\n 'settings.discard': '放弃修改',\n 'settings.saveFailed': '保存失败,请重试。',\n 'settings.expand': '展开',\n 'settings.collapse': '收起',\n 'settings.invalidNumber': '请输入不小于 {min} 的整数',\n\n // ── daemon lifecycle ───────────────────────────────────────────────────\n 'settings.sectionDaemon': 'daemon 生命周期',\n 'settings.daemonPolicyLabel': '托管策略',\n 'settings.daemonPolicyHint':\n 'adopt-or-host=探测并领养既有 daemon,否则自行拉起;adopt-only=只领养绝不拉起;off=不管理生命周期(仍只读对账既有 daemon 的数据)。',\n 'settings.daemonPolicyAdoptOrHost': 'adopt-or-host(领养或拉起)',\n 'settings.daemonPolicyAdoptOnly': 'adopt-only(只领养)',\n 'settings.daemonPolicyOff': 'off(不管理)',\n 'settings.daemonBackoffLimitLabel': '熔断阈值',\n 'settings.daemonBackoffLimitHint': '连续托管失败达到该次数后停止重启并进入 failed。',\n 'settings.daemonStatusLabel': 'daemon 状态',\n 'settings.daemonStateProbe': '探测中',\n 'settings.daemonStateAdopted': '已领养既有 daemon',\n 'settings.daemonStateDefer': '等待系统服务拉活',\n 'settings.daemonStateReprobe': '重新探测中',\n 'settings.daemonStateHosting': '正在拉起',\n 'settings.daemonStateHosted': '插件托管中',\n 'settings.daemonStateBackoff': '退避重试中',\n 'settings.daemonStateFailed': '已熔断',\n 'settings.daemonPidVersion': 'pid {pid} · v{version}',\n 'settings.daemonDeferNote':\n 'daemon 由系统服务(LaunchAgent)托管;插件只探测等待,不重复拉起,也不会终止它。',\n 'settings.daemonFailedNote':\n '连续托管失败已达熔断阈值;看板降级为最后快照。排查 sidecar 命令后可点击重试。',\n 'settings.daemonRetry': '重试',\n\n // ── sidecar invocation ─────────────────────────────────────────────────\n 'settings.sectionSidecar': 'sidecar 调用',\n 'settings.sidecarCommandLabel': '可执行命令',\n 'settings.sidecarCommandHint':\n 'PATH 名、绝对路径或空格分隔的多段命令(如 python3 /path/agent-sidecar.pyz);插件绝不代装 sidecar。',\n 'settings.sidecarRuntimeDirLabel': '运行时目录',\n 'settings.sidecarRuntimeDirHint':\n '留空使用默认 ~/.agent_sidecar(尊重 AGENT_SIDECAR_RUNTIME_DIR 环境变量);非空时经环境变量传给受托管的 daemon。',\n\n // ── stream reconciliation ──────────────────────────────────────────────\n 'settings.sectionStream': '数据流对账',\n 'settings.streamActiveMsLabel': '活跃对账周期(毫秒)',\n 'settings.streamActiveMsHint': '有会话工作中时的 status 快照周期。',\n 'settings.streamIdleMsLabel': '空闲对账周期(毫秒)',\n 'settings.streamIdleMsHint': '无会话工作时的 status 快照周期。',\n\n // ── injection ──────────────────────────────────────────────────────────\n 'settings.sectionInject': '消息注入',\n 'settings.injectEnabledLabel': '启用注入',\n 'settings.injectEnabledHint':\n '关闭时看板隐藏全部注入入口,写接口在服务端同步拒绝。',\n 'settings.injectDefaultModeLabel': '默认注入模式',\n 'settings.injectDefaultModeHint': '注入面板打开时预选的模式。',\n 'settings.injectModeQueue': 'queue(排队下一轮)',\n 'settings.injectModeSteer': 'steer(中途注入)',\n 'settings.injectSafetyNote':\n '安全须知:注入默认关闭;开启后每次注入仍须经确认对话框逐次放行,无任何批量或定时注入。多用户主机不建议开启。',\n\n // ── bypass analysis ────────────────────────────────────────────────────\n 'settings.sectionAnalysis': '旁路分析',\n 'settings.analysisEnabledLabel': '启用 AI 旁路分析',\n 'settings.analysisEnabledHint':\n '按需拉起 dsh 分析会话解读被观测会话(消耗模型 token,默认关闭)。',\n\n // ── board UI ───────────────────────────────────────────────────────────\n 'settings.sectionUi': '看板界面',\n 'settings.uiTimeWindowHoursLabel': '会话时间窗(小时)',\n 'settings.uiTimeWindowHoursHint': '看板只显示该时间窗内活动过的会话。',\n 'settings.uiShowDeadLabel': '显示 dead 会话',\n 'settings.uiShowDeadHint': '把已结束(dead)的会话也列入看板。',\n\n // ── skill mode ─────────────────────────────────────────────────────────\n 'settings.sectionSkill': 'skill 模式',\n 'settings.skillProvideLabel': '内嵌提供 skill',\n 'settings.skillProvideHint':\n '经 registerProvider 向 dsh 提供 agent-sidecar skill(M4 启用;重启后生效)。',\n\n // ── inject panel: chrome & editor (T4.5, design §5.1 view 3) ───────────\n 'inject.title': '注入消息',\n 'inject.confirmTitle': '确认注入',\n 'inject.close': '关闭',\n 'inject.done': '完成',\n 'inject.capabilityOff': '注入功能未开启;请在设置中开启注入。',\n 'inject.noTarget': '未选择注入目标;请从看板卡片或会话详情发起注入。',\n 'inject.targetLabel': '注入目标',\n 'inject.messageLabel': '消息内容',\n 'inject.messagePlaceholder': '输入要注入的消息(上限 16 KiB;请勿粘贴密钥等敏感内容)',\n 'inject.byteCount': '{bytes} / {limit} 字节',\n 'inject.msgEmpty': '消息不能为空。',\n 'inject.msgNul': '消息包含非法 NUL 字符。',\n 'inject.msgTooLarge': '消息 {bytes} 字节,超出 {limit} 字节上限。',\n 'inject.modeLabel': '注入模式',\n 'inject.modeQueue': 'queue(排队下一轮)',\n 'inject.modeQueueHint': '消息排队等待,目标会话下一轮开始时处理。',\n 'inject.modeSteer': 'steer(中途注入)',\n 'inject.modeSteerHint': '消息在目标会话当前轮次中途注入,立即介入其工作。',\n 'inject.argvWarning':\n '目标为 cursor-cli:注入经其原生子进程执行,消息在该进程存续期间对本机进程列表可见;请勿包含密钥等敏感内容。',\n 'inject.auditNote':\n '本次注入会被记入 sidecar 审计日志(含字节数与内容指纹,不含消息明文)。',\n 'inject.prepare': '准备注入',\n 'inject.preparing': '校验中…',\n\n // ── inject panel: confirm phase ──────────────────────────────────────\n 'inject.planTargetLabel': '目标现状',\n 'inject.planStatus': '当前状态:{status}',\n 'inject.statusObservedNote': '状态为从持久化数据推断的观察值,可能滞后。',\n 'inject.planModeLabel': '模式',\n 'inject.planPreviewLabel': '消息摘要({bytes} 字节)',\n 'inject.countdown': '确认令牌 {seconds} 秒后过期',\n 'inject.confirmExecute': '确认注入',\n 'inject.executing': '注入中…',\n 'inject.cancel': '取消',\n 'inject.tokenExpired': '确认已超时,令牌失效;请重新准备注入。',\n\n // ── inject panel: result phase ───────────────────────────────────────\n 'inject.resultDelivered': '已投递:消息已注入目标会话。',\n 'inject.resultFailed': '注入失败。',\n 'inject.resultUnknown':\n '结果未知:消息可能已投递。请勿重试;请前往目标会话核对后再决定下一步。',\n 'inject.resultReplayed': '幂等重放:返回的是此前同一请求的结果,未发生二次注入。',\n 'inject.reprepare': '重新准备',\n\n // ── inject panel: error vocabulary (gateway + transport) ─────────────\n 'inject.errInjectDisabled': '注入功能已在服务端关闭;请在设置中开启注入。',\n 'inject.errInvalidMessage': '消息未通过服务端校验。',\n 'inject.errTargetNotFound': '目标会话不存在或已离开观测范围。',\n 'inject.errTargetDead': '目标会话已结束(dead),无法注入。',\n 'inject.errTooManyPending': '待确认的注入请求过多,请稍后再试。',\n 'inject.errTokenMissing': '确认令牌缺失或未被签发;请重新准备。',\n 'inject.errTokenExpired': '确认令牌已过期;请重新准备。',\n 'inject.errTokenReused': '确认令牌已被消费;请重新准备。',\n 'inject.errTokenMismatch': '确认内容与准备时不一致;请重新准备。',\n 'inject.errUnsupportedAgent': '该 agent 没有可用的注入通道。',\n 'inject.errExecutorError': '注入通路执行出错。',\n 'inject.errTimeout': '请求超时,未收到服务端回执。',\n 'inject.errAborted': '请求已取消。',\n 'inject.errNetwork': '网络错误,请求未能送达。',\n 'inject.errParse': '服务端响应无法解析。',\n 'inject.errGeneric': '请求失败({code})。',\n\n // ── board tab chrome: main-view switcher (T5.10b, design §5.1) ─────────\n 'board.viewBoard': '会话看板',\n 'board.viewProjects': '项目视图',\n\n // ── session detail (T5.3 detail/strings.ts, referenced verbatim) ───────\n 'detail.header.close': D.header.close,\n 'detail.header.listenOn': D.header.listenOn,\n 'detail.header.listenOff': D.header.listenOff,\n 'detail.header.listenHint': D.header.listenHint,\n 'detail.header.untitled': D.header.untitled,\n 'detail.header.unknownProject': D.header.unknownProject,\n 'detail.header.observedDisclaimer': D.header.observedDisclaimer,\n 'detail.status.working': D.status.working,\n 'detail.status.waiting': D.status.waiting,\n 'detail.status.idle': D.status.idle,\n 'detail.status.dead': D.status.dead,\n 'detail.status.unknown': D.status.unknown,\n 'detail.sources.title': D.sources.title,\n 'detail.sources.dshLive': D.sources.dshLive,\n 'detail.sources.dshCold': D.sources.dshCold,\n 'detail.sources.sidecarReplay': D.sources.sidecarReplay,\n 'detail.sources.sidecarBuffer': D.sources.sidecarBuffer,\n 'detail.sources.none': D.sources.none,\n 'detail.kind.user': D.kind.user,\n 'detail.kind.assistant': D.kind.assistant,\n 'detail.kind.thinking': D.kind.thinking,\n 'detail.kind.toolCall': D.kind.toolCall,\n 'detail.kind.toolResult': D.kind.toolResult,\n 'detail.kind.turn': D.kind.turn,\n 'detail.kind.step': D.kind.step,\n 'detail.kind.error': D.kind.error,\n 'detail.kind.other': D.kind.other,\n 'detail.gap.label': D.gap.label,\n 'detail.timeline.loadMore': D.timeline.loadMore,\n 'detail.timeline.loadingMore': D.timeline.loadingMore,\n 'detail.timeline.noMore': D.timeline.noMore,\n 'detail.timeline.expand': D.timeline.expand,\n 'detail.timeline.collapse': D.timeline.collapse,\n 'detail.timeline.newBadge': D.timeline.newBadge,\n 'detail.timeline.seq': D.timeline.seq,\n 'detail.timeline.hiddenNotice': D.timeline.hiddenNotice,\n 'detail.timeline.showAll': D.timeline.showAll,\n 'detail.states.loadingTitle': D.states.loadingTitle,\n 'detail.states.emptyTitle': D.states.emptyTitle,\n 'detail.states.emptyHint': D.states.emptyHint,\n 'detail.states.errorTitle': D.states.errorTitle,\n 'detail.states.errorFallback': D.states.errorFallback,\n 'detail.states.errors.session_not_found': D.states.errors.session_not_found,\n 'detail.states.errors.invalid_cursor': D.states.errors.invalid_cursor,\n 'detail.states.errors.fusion_not_wired': D.states.errors.fusion_not_wired,\n 'detail.states.errors.network_error': D.states.errors.network_error,\n 'detail.states.errors.request_timeout': D.states.errors.request_timeout,\n 'detail.time.justNow': D.time.justNow,\n 'detail.time.minutesAgo': D.time.minutesAgo,\n 'detail.time.hoursAgo': D.time.hoursAgo,\n 'detail.time.daysAgo': D.time.daysAgo,\n\n // ── session detail: integration chrome (T5.10b literals) ───────────────\n 'detail.actions.inject': '注入',\n 'detail.actions.analyze': 'AI 分析',\n 'detail.actions.analyzeDisabledHint': '在设置中开启「启用 AI 旁路分析」后可用',\n\n // ── dsh deep-query tools (T5.4 dsh-tools/strings.ts, referenced) ───────\n 'dshtools.lineage.title': Q.lineage.title,\n 'dshtools.lineage.loading': Q.lineage.loading,\n 'dshtools.lineage.error': Q.lineage.error,\n 'dshtools.lineage.empty': Q.lineage.empty,\n 'dshtools.lineage.currentBadge': Q.lineage.currentBadge,\n 'dshtools.lineage.liveBadge': Q.lineage.liveBadge,\n 'dshtools.lineage.notPersistedBadge': Q.lineage.notPersistedBadge,\n 'dshtools.lineage.role.ancestor': Q.lineage.role.ancestor,\n 'dshtools.lineage.role.target': Q.lineage.role.target,\n 'dshtools.lineage.role.descendant': Q.lineage.role.descendant,\n 'dshtools.lineage.jumpTitle': Q.lineage.jumpTitle,\n 'dshtools.lineage.currentTitle': Q.lineage.currentTitle,\n 'dshtools.lineage.expand': Q.lineage.expand,\n 'dshtools.lineage.collapse': Q.lineage.collapse,\n 'dshtools.lineage.nodeCount': Q.lineage.nodeCount,\n 'dshtools.lineage.incompleteWithId': Q.lineage.incompleteWithId,\n 'dshtools.lineage.incomplete': Q.lineage.incomplete,\n 'dshtools.lineage.degrade.notDshTitle': Q.lineage.degrade.notDshTitle,\n 'dshtools.lineage.degrade.notDshBody': Q.lineage.degrade.notDshBody,\n 'dshtools.lineage.degrade.queryUnavailableTitle': Q.lineage.degrade.queryUnavailableTitle,\n 'dshtools.lineage.degrade.queryUnavailableBody': Q.lineage.degrade.queryUnavailableBody,\n 'dshtools.lineage.degrade.traceFailedTitle': Q.lineage.degrade.traceFailedTitle,\n 'dshtools.lineage.degrade.traceFailedBody': Q.lineage.degrade.traceFailedBody,\n 'dshtools.lineage.degrade.unknownTitle': Q.lineage.degrade.unknownTitle,\n 'dshtools.lineage.degrade.unknownBody': Q.lineage.degrade.unknownBody,\n 'dshtools.search.title': Q.search.title,\n 'dshtools.search.placeholder': Q.search.placeholder,\n 'dshtools.search.submit': Q.search.submit,\n 'dshtools.search.loading': Q.search.loading,\n 'dshtools.search.error': Q.search.error,\n 'dshtools.search.empty': Q.search.empty,\n 'dshtools.search.filterOnlyNotice': Q.search.filterOnlyNotice,\n 'dshtools.search.projectFilter': Q.search.projectFilter,\n 'dshtools.search.matchedBy.full-text': Q.search.matchedBy['full-text'],\n 'dshtools.search.matchedBy.title': Q.search.matchedBy.title,\n 'dshtools.search.matchedBy.project': Q.search.matchedBy.project,\n 'dshtools.search.matchedBy.other': Q.search.matchedBy.other,\n 'dshtools.search.untitled': Q.search.untitled,\n\n // ── project correlation view (T5.5 PROJECT_VIEW_STRINGS, referenced) ───\n 'project.title': P.title,\n 'project.summary': P.summary,\n 'project.crossAgent': P.crossAgent,\n 'project.sessionCount': P.sessionCount,\n 'project.lastActive': P.lastActive,\n 'project.liveChip': P.liveChip,\n 'project.untitled': P.untitled,\n 'project.empty.title': P.empty.title,\n 'project.empty.hint': P.empty.hint,\n 'project.loading': P.loading,\n 'project.errorTitle': P.errorTitle,\n\n // ── AI bypass analysis panel (T5.10b, design §4.e.3 / §5.1) ────────────\n 'analysis.title': 'AI 分析',\n 'analysis.close': '关闭',\n 'analysis.disabledNote': 'AI 旁路分析未开启;请在设置中开启「启用 AI 旁路分析」。',\n 'analysis.idleHint':\n '按需拉起一次 dsh 旁路分析,解读该会话的当前状态与走向(消耗模型 token)。',\n 'analysis.start': '开始分析',\n 'analysis.requesting': '分析中…(最长约 60 秒)',\n 'analysis.exchangeInitial': '分析摘要',\n 'analysis.followupLabel': '追问',\n 'analysis.truncatedNotice': '输入超出预算已截断,分析基于部分上下文。',\n 'analysis.emptySummary': '(分析会话未返回摘要)',\n 'analysis.disclaimerFallback': 'AI 分析仅供参考,结论以实际会话为准。',\n 'analysis.followupPlaceholder': '继续追问这次分析…',\n 'analysis.followupSubmit': '追问',\n 'analysis.answering': '回答中…',\n 'analysis.stop': '停止分析',\n 'analysis.stopped': '分析已停止,分析会话已释放。',\n 'analysis.restart': '重新分析',\n 'analysis.noticeTimeout': '本次追问超时,可稍后重试;分析会话仍保留。',\n 'analysis.noticeNetwork': '请求未能送达,可重试;分析会话仍保留。',\n 'analysis.noticeCancelFailed': '停止请求未送达,请重试。',\n 'analysis.errDisabled': 'AI 分析已在服务端关闭;请在设置中开启后重试。',\n 'analysis.errUnavailable': '当前 host 未接入 AI 分析能力(agents 服务不可用)。',\n 'analysis.errTargetNotFound': '分析目标不存在或已离开观测范围。',\n 'analysis.errTooManyActive': '并发分析会话已达上限,请稍后再试。',\n 'analysis.errTimeout': '分析超时,分析会话已释放;可重新发起。',\n 'analysis.errCreateFailed': '分析会话创建失败。',\n 'analysis.errCancelled': '分析已取消。',\n 'analysis.errNetwork': '网络错误,分析请求未能完成。',\n 'analysis.errGeneric': '分析失败({code})。',\n\n // ── /sidecar slash command (T4.6, folded from locales/command.ts) ──────\n 'command.description': '查看 Sidecar 状态速览(daemon、连接与会话)',\n 'command.daemon.probe': '探测中',\n 'command.daemon.adopted': '已连接 · 领养',\n 'command.daemon.defer': '等待系统服务',\n 'command.daemon.reprobe': '重新探测中',\n 'command.daemon.hosting': '正在启动',\n 'command.daemon.hosted': '已连接 · 托管',\n 'command.daemon.backoff': '重启退避中',\n 'command.daemon.failed': '离线',\n 'command.daemon.unknown': '状态未知',\n 'command.connection.ok': '已连接',\n 'command.connection.degraded': '连接不稳定',\n 'command.connection.off': '离线',\n 'command.status.working': '工作中',\n 'command.status.waiting': '等待中',\n 'command.status.idle': '空闲',\n 'command.status.dead': '已结束',\n 'command.status.unknown': '未知',\n 'command.daemonRow': 'Sidecar · {state}',\n 'command.countsRow': '{working} 个工作中 · {waiting} 个等待中',\n 'command.countsDetail': '共 {total} 个会话',\n 'command.sessionDetail': '{project} · {status} · {time}',\n 'command.noSessions': '暂无被观测的会话',\n 'command.unknownProject': '未知项目',\n 'command.untitled': '(无标题)',\n 'command.truncated': '还有 {n} 个活跃会话未列出',\n 'command.boardHint': '打开会话视图的「Sidecar」Tab 查看完整看板',\n 'command.unreachable': 'sidecar 未连接',\n 'command.unreachableHint':\n '无法获取状态快照;请确认 agent-sidecar 插件已启用、daemon 可用后重试。',\n 'command.offlineFailed': 'sidecar 已离线(daemon 连续启动失败已熔断)',\n 'command.offlineFailedHint':\n '以下为最后一次快照。可在设置卡重试,或手动运行 agent-sidecar daemon start 后等待自动领养。',\n 'command.offlineDefer': '等待系统服务拉起 daemon',\n 'command.offlineDeferHint':\n '检测到 LaunchAgent 托管,插件只探测等待;服务拉起后速览会自动恢复。',\n 'command.time.justNow': '刚刚',\n 'command.time.minutesAgo': '{n} 分钟前',\n 'command.time.hoursAgo': '{n} 小时前',\n 'command.time.daysAgo': '{n} 天前',\n\n // ── better-sidebar mini tab (T6.3, design §5.2 optional soft dep) ──────\n 'sidebar.tabTitle': 'Sidecar',\n 'sidebar.countsRow': '{working} 工作中 · {waiting} 等待中',\n 'sidebar.recentTitle': '最近活跃',\n 'sidebar.connecting': '等待 sidecar 快照…',\n 'sidebar.noSessions': '暂无活跃会话',\n 'sidebar.noEvent': '暂无事件记录',\n 'sidebar.untitled': '(无标题)',\n 'sidebar.boardHint': '完整看板见会话视图的「Sidecar」Tab',\n} satisfies Record<`${SidecarLocaleDomain}.${string}`, string>\n\n/** Key union of the locale table (zh is the source of truth). */\nexport type SidecarLocaleKey = keyof typeof zh\n","/**\n * English dictionary, compile-checked complete against the zh key set (a\n * missing or extra key is a type error; see ./zh.ts for the key-space\n * convention).\n */\n\nimport type { SidecarLocaleKey } from './zh.ts'\n\nexport const en = {\n // ── card chrome ────────────────────────────────────────────────────────\n 'settings.cardTitle': 'Agent Sidecar',\n 'settings.cardDescription':\n 'Runtime settings for cross-agent session monitoring, injection and bypass analysis.',\n 'settings.docsLink': 'Documentation',\n 'settings.readOnly': 'The settings document is read-only; edits cannot be saved.',\n 'settings.unsaved': 'Unsaved',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving…',\n 'settings.discard': 'Discard',\n 'settings.saveFailed': 'Save failed; please retry.',\n 'settings.expand': 'Expand',\n 'settings.collapse': 'Collapse',\n 'settings.invalidNumber': 'Enter an integer no less than {min}',\n\n // ── daemon lifecycle ───────────────────────────────────────────────────\n 'settings.sectionDaemon': 'Daemon lifecycle',\n 'settings.daemonPolicyLabel': 'Management policy',\n 'settings.daemonPolicyHint':\n 'adopt-or-host probes and adopts an existing daemon, else spawns one; adopt-only never spawns; off leaves the lifecycle alone (read-only reconcile still runs).',\n 'settings.daemonPolicyAdoptOrHost': 'adopt-or-host (adopt, else spawn)',\n 'settings.daemonPolicyAdoptOnly': 'adopt-only (never spawn)',\n 'settings.daemonPolicyOff': 'off (unmanaged)',\n 'settings.daemonBackoffLimitLabel': 'Backoff limit',\n 'settings.daemonBackoffLimitHint':\n 'After this many consecutive hosting failures the supervisor stops restarting and trips failed.',\n 'settings.daemonStatusLabel': 'Daemon status',\n 'settings.daemonStateProbe': 'Probing',\n 'settings.daemonStateAdopted': 'Adopted an existing daemon',\n 'settings.daemonStateDefer': 'Waiting for the system service',\n 'settings.daemonStateReprobe': 'Re-probing',\n 'settings.daemonStateHosting': 'Spawning',\n 'settings.daemonStateHosted': 'Hosted by the plugin',\n 'settings.daemonStateBackoff': 'Backing off before retry',\n 'settings.daemonStateFailed': 'Tripped (circuit open)',\n 'settings.daemonPidVersion': 'pid {pid} · v{version}',\n 'settings.daemonDeferNote':\n 'The daemon is managed by a system service (LaunchAgent); the plugin only probes and waits — it never spawns a duplicate or terminates it.',\n 'settings.daemonFailedNote':\n 'Consecutive hosting failures reached the backoff limit; the board degraded to the last snapshot. Check the sidecar command, then retry.',\n 'settings.daemonRetry': 'Retry',\n\n // ── sidecar invocation ─────────────────────────────────────────────────\n 'settings.sectionSidecar': 'Sidecar invocation',\n 'settings.sidecarCommandLabel': 'Executable command',\n 'settings.sidecarCommandHint':\n 'A PATH name, an absolute path, or a space-separated multi-part command (e.g. python3 /path/agent-sidecar.pyz); the plugin never installs the sidecar for you.',\n 'settings.sidecarRuntimeDirLabel': 'Runtime directory',\n 'settings.sidecarRuntimeDirHint':\n 'Empty uses the default ~/.agent_sidecar (honoring AGENT_SIDECAR_RUNTIME_DIR); a non-empty value is passed to spawned daemons via the environment.',\n\n // ── stream reconciliation ──────────────────────────────────────────────\n 'settings.sectionStream': 'Stream reconciliation',\n 'settings.streamActiveMsLabel': 'Active cadence (ms)',\n 'settings.streamActiveMsHint': 'Status snapshot cadence while any session is working.',\n 'settings.streamIdleMsLabel': 'Idle cadence (ms)',\n 'settings.streamIdleMsHint': 'Status snapshot cadence while no session is working.',\n\n // ── injection ──────────────────────────────────────────────────────────\n 'settings.sectionInject': 'Message injection',\n 'settings.injectEnabledLabel': 'Enable injection',\n 'settings.injectEnabledHint':\n 'When off, the board hides every inject affordance and the server rejects write actions.',\n 'settings.injectDefaultModeLabel': 'Default injection mode',\n 'settings.injectDefaultModeHint': 'The mode preselected when the inject panel opens.',\n 'settings.injectModeQueue': 'queue (next turn)',\n 'settings.injectModeSteer': 'steer (mid-turn)',\n 'settings.injectSafetyNote':\n 'Safety: injection is off by default; even when enabled, every injection still passes a per-request confirmation dialog — there is no batch or scheduled injection. Not recommended on multi-user hosts.',\n\n // ── bypass analysis ────────────────────────────────────────────────────\n 'settings.sectionAnalysis': 'Bypass analysis',\n 'settings.analysisEnabledLabel': 'Enable AI bypass analysis',\n 'settings.analysisEnabledHint':\n 'Spins up a dsh analysis session over an observed session on demand (consumes model tokens; off by default).',\n\n // ── board UI ───────────────────────────────────────────────────────────\n 'settings.sectionUi': 'Board UI',\n 'settings.uiTimeWindowHoursLabel': 'Session time window (hours)',\n 'settings.uiTimeWindowHoursHint': 'The board lists only sessions active within this window.',\n 'settings.uiShowDeadLabel': 'Show dead sessions',\n 'settings.uiShowDeadHint': 'Also list finished (dead) sessions on the board.',\n\n // ── skill mode ─────────────────────────────────────────────────────────\n 'settings.sectionSkill': 'Skill mode',\n 'settings.skillProvideLabel': 'Provide the skill in-process',\n 'settings.skillProvideHint':\n 'Provide the agent-sidecar skill to dsh via registerProvider (enabled in M4; applies after restart).',\n\n // ── inject panel: chrome & editor (T4.5, design §5.1 view 3) ───────────\n 'inject.title': 'Inject message',\n 'inject.confirmTitle': 'Confirm injection',\n 'inject.close': 'Close',\n 'inject.done': 'Done',\n 'inject.capabilityOff': 'Injection is not enabled; turn it on in Settings first.',\n 'inject.noTarget':\n 'No injection target selected; start from a board card or a session detail page.',\n 'inject.targetLabel': 'Target',\n 'inject.messageLabel': 'Message',\n 'inject.messagePlaceholder': 'Message to inject (16 KiB max; never paste secrets)',\n 'inject.byteCount': '{bytes} / {limit} bytes',\n 'inject.msgEmpty': 'The message must not be empty.',\n 'inject.msgNul': 'The message contains an illegal NUL character.',\n 'inject.msgTooLarge': 'The message is {bytes} bytes, over the {limit}-byte limit.',\n 'inject.modeLabel': 'Injection mode',\n 'inject.modeQueue': 'queue (next turn)',\n 'inject.modeQueueHint':\n 'The message queues and is handled when the target session starts its next turn.',\n 'inject.modeSteer': 'steer (mid-turn)',\n 'inject.modeSteerHint':\n 'The message is injected mid-turn and steers the target session immediately.',\n 'inject.argvWarning':\n \"cursor-cli target: injection runs through its native subprocess, and the message is visible in this machine's process list while that process lives; never include secrets.\",\n 'inject.auditNote':\n 'This injection is recorded in the sidecar audit log (byte size and content fingerprint; never the message plaintext).',\n 'inject.prepare': 'Prepare injection',\n 'inject.preparing': 'Validating…',\n\n // ── inject panel: confirm phase ──────────────────────────────────────\n 'inject.planTargetLabel': 'Target snapshot',\n 'inject.planStatus': 'Current status: {status}',\n 'inject.statusObservedNote':\n 'Status is an observed value inferred from persisted data and may lag.',\n 'inject.planModeLabel': 'Mode',\n 'inject.planPreviewLabel': 'Message digest ({bytes} bytes)',\n 'inject.countdown': 'The confirm token expires in {seconds}s',\n 'inject.confirmExecute': 'Confirm injection',\n 'inject.executing': 'Injecting…',\n 'inject.cancel': 'Cancel',\n 'inject.tokenExpired':\n 'Confirmation timed out and the token is void; prepare the injection again.',\n\n // ── inject panel: result phase ───────────────────────────────────────\n 'inject.resultDelivered': 'Delivered: the message was injected into the target session.',\n 'inject.resultFailed': 'Injection failed.',\n 'inject.resultUnknown':\n 'Outcome unknown: the message may have been delivered. Do NOT retry; check the target session before deciding anything.',\n 'inject.resultReplayed':\n 'Idempotent replay: this is the earlier result of the same request — no second injection happened.',\n 'inject.reprepare': 'Prepare again',\n\n // ── inject panel: error vocabulary (gateway + transport) ─────────────\n 'inject.errInjectDisabled': 'Injection is disabled on the server; enable it in Settings.',\n 'inject.errInvalidMessage': 'The message failed server-side validation.',\n 'inject.errTargetNotFound':\n 'The target session does not exist or left the observation window.',\n 'inject.errTargetDead': 'The target session has ended (dead); it cannot be injected.',\n 'inject.errTooManyPending':\n 'Too many injections are pending confirmation; try again later.',\n 'inject.errTokenMissing': 'The confirm token is missing or was never issued; prepare again.',\n 'inject.errTokenExpired': 'The confirm token expired; prepare again.',\n 'inject.errTokenReused': 'The confirm token was already consumed; prepare again.',\n 'inject.errTokenMismatch':\n 'The confirmation no longer matches what was prepared; prepare again.',\n 'inject.errUnsupportedAgent': 'This agent has no injection path.',\n 'inject.errExecutorError': 'The injection path failed while executing.',\n 'inject.errTimeout': 'The request timed out without a server receipt.',\n 'inject.errAborted': 'The request was cancelled.',\n 'inject.errNetwork': 'Network error; the request could not be sent.',\n 'inject.errParse': 'The server response could not be parsed.',\n 'inject.errGeneric': 'Request failed ({code}).',\n\n // ── board tab chrome: main-view switcher ───────────────────────────────\n 'board.viewBoard': 'Session board',\n 'board.viewProjects': 'Projects',\n\n // ── session detail ───────────────────────────────────────────────────\n 'detail.header.close': 'Back to board',\n 'detail.header.listenOn': 'Listening',\n 'detail.header.listenOff': 'Listen',\n 'detail.header.listenHint': 'New events append live and get highlighted while on',\n 'detail.header.untitled': '(untitled)',\n 'detail.header.unknownProject': 'Unknown project',\n 'detail.header.observedDisclaimer':\n 'Status is an observed value inferred from persisted data and may lag',\n 'detail.status.working': 'Working',\n 'detail.status.waiting': 'Waiting',\n 'detail.status.idle': 'Idle',\n 'detail.status.dead': 'Finished',\n 'detail.status.unknown': 'Unknown',\n 'detail.sources.title': 'Data sources',\n 'detail.sources.dshLive': 'dsh live',\n 'detail.sources.dshCold': 'dsh cold read',\n 'detail.sources.sidecarReplay': 'sidecar replay',\n 'detail.sources.sidecarBuffer': 'sidecar buffer',\n 'detail.sources.none': 'Unknown source',\n 'detail.kind.user': 'User message',\n 'detail.kind.assistant': 'Assistant reply',\n 'detail.kind.thinking': 'Thinking',\n 'detail.kind.toolCall': 'Tool call',\n 'detail.kind.toolResult': 'Tool result',\n 'detail.kind.turn': 'Turn',\n 'detail.kind.step': 'Step',\n 'detail.kind.error': 'Error',\n 'detail.kind.other': 'Event',\n 'detail.gap.label':\n 'Gap: about {n} events may be uncaptured (256-slot queue cap or not persisted)',\n 'detail.timeline.loadMore': 'Load older history',\n 'detail.timeline.loadingMore': 'Loading…',\n 'detail.timeline.noMore': 'Start of the timeline',\n 'detail.timeline.expand': 'Expand',\n 'detail.timeline.collapse': 'Collapse',\n 'detail.timeline.newBadge': 'New',\n 'detail.timeline.seq': 'seq {n}',\n 'detail.timeline.hiddenNotice': '{n} earlier entries collapsed to stay smooth',\n 'detail.timeline.showAll': 'Show all',\n 'detail.states.loadingTitle': 'Loading the timeline…',\n 'detail.states.emptyTitle': 'No events yet',\n 'detail.states.emptyHint': 'This session has no normalized events to show yet.',\n 'detail.states.errorTitle': 'Timeline failed to load',\n 'detail.states.errorFallback': 'Error code: {reason}',\n 'detail.states.errors.session_not_found': 'The session does not exist or is no longer visible',\n 'detail.states.errors.invalid_cursor': 'The paging cursor is invalid; reopen the detail view',\n 'detail.states.errors.fusion_not_wired': 'This host has no timeline capability enabled',\n 'detail.states.errors.network_error': 'Network error; the dsh host is unreachable',\n 'detail.states.errors.request_timeout': 'The request timed out',\n 'detail.time.justNow': 'just now',\n 'detail.time.minutesAgo': '{n} min ago',\n 'detail.time.hoursAgo': '{n} h ago',\n 'detail.time.daysAgo': '{n} d ago',\n\n // ── session detail: integration chrome ─────────────────────────────────\n 'detail.actions.inject': 'Inject',\n 'detail.actions.analyze': 'AI analysis',\n 'detail.actions.analyzeDisabledHint':\n 'Enable \"AI bypass analysis\" in Settings to use this',\n\n // ── dsh deep-query tools ────────────────────────────────────────────────\n 'dshtools.lineage.title': 'Session lineage',\n 'dshtools.lineage.loading': 'Loading lineage…',\n 'dshtools.lineage.error': 'Lineage failed to load',\n 'dshtools.lineage.empty': 'No lineage data',\n 'dshtools.lineage.currentBadge': 'Current session',\n 'dshtools.lineage.liveBadge': 'Live',\n 'dshtools.lineage.notPersistedBadge': 'Not persisted',\n 'dshtools.lineage.role.ancestor': 'Ancestor',\n 'dshtools.lineage.role.target': 'Target',\n 'dshtools.lineage.role.descendant': 'Child session',\n 'dshtools.lineage.jumpTitle': 'Jump to this session',\n 'dshtools.lineage.currentTitle': 'Currently viewing this session',\n 'dshtools.lineage.expand': 'Expand',\n 'dshtools.lineage.collapse': 'Collapse',\n 'dshtools.lineage.nodeCount': '{n} sessions',\n 'dshtools.lineage.incompleteWithId':\n 'Lineage incomplete: parent session {id} could not be resolved',\n 'dshtools.lineage.incomplete': 'Lineage incomplete: part of the parent chain is unresolved',\n 'dshtools.lineage.degrade.notDshTitle': 'Lineage/provenance is dsh-session only',\n 'dshtools.lineage.degrade.notDshBody':\n \"This session comes from an external agent; dsh's lineage and provenance do not apply.\",\n 'dshtools.lineage.degrade.queryUnavailableTitle': 'dsh lineage service unavailable',\n 'dshtools.lineage.degrade.queryUnavailableBody':\n 'This dsh composition has no sessionQuery mounted; lineage and provenance are unavailable.',\n 'dshtools.lineage.degrade.traceFailedTitle': 'Lineage trace failed',\n 'dshtools.lineage.degrade.traceFailedBody': \"dsh could not resolve this session's lineage.\",\n 'dshtools.lineage.degrade.unknownTitle': 'Lineage unavailable',\n 'dshtools.lineage.degrade.unknownBody':\n 'The backend reports lineage unavailable (reason: {reason}).',\n 'dshtools.search.title': 'Session search',\n 'dshtools.search.placeholder': 'Search sessions (title / project / full text)',\n 'dshtools.search.submit': 'Search',\n 'dshtools.search.loading': 'Searching…',\n 'dshtools.search.error': 'Search failed',\n 'dshtools.search.empty': 'No matching sessions',\n 'dshtools.search.filterOnlyNotice':\n 'dsh full-text search is unavailable; degraded to title/project filtering',\n 'dshtools.search.projectFilter': 'Project filter: {project}',\n 'dshtools.search.matchedBy.full-text': 'Full text',\n 'dshtools.search.matchedBy.title': 'Title',\n 'dshtools.search.matchedBy.project': 'Project',\n 'dshtools.search.matchedBy.other': 'Other',\n 'dshtools.search.untitled': '(untitled)',\n\n // ── project correlation view ────────────────────────────────────────────\n 'project.title': 'Project correlation',\n 'project.summary': '{projects} projects · {sessions} sessions',\n 'project.crossAgent': '{n} agent kinds',\n 'project.sessionCount': '{n} sessions',\n 'project.lastActive': 'Last active {time}',\n 'project.liveChip': 'Live',\n 'project.untitled': '(untitled)',\n 'project.empty.title': 'No project correlation yet',\n 'project.empty.hint':\n 'No cross-agent project activity within the time window; projects appear here once an agent works inside a project directory.',\n 'project.loading': 'Loading project correlation…',\n 'project.errorTitle': 'Project correlation failed to load',\n\n // ── AI bypass analysis panel ────────────────────────────────────────────\n 'analysis.title': 'AI analysis',\n 'analysis.close': 'Close',\n 'analysis.disabledNote':\n 'AI bypass analysis is off; enable \"AI bypass analysis\" in Settings first.',\n 'analysis.idleHint':\n 'Spin up one dsh bypass-analysis pass over this session on demand (consumes model tokens).',\n 'analysis.start': 'Start analysis',\n 'analysis.requesting': 'Analyzing… (up to ~60 s)',\n 'analysis.exchangeInitial': 'Analysis summary',\n 'analysis.followupLabel': 'Follow-up',\n 'analysis.truncatedNotice':\n 'The input exceeded the budget and was truncated; the analysis covers partial context.',\n 'analysis.emptySummary': '(the analysis session returned no summary)',\n 'analysis.disclaimerFallback':\n 'AI analysis is for reference only; trust the actual session over its conclusions.',\n 'analysis.followupPlaceholder': 'Ask a follow-up about this analysis…',\n 'analysis.followupSubmit': 'Ask',\n 'analysis.answering': 'Answering…',\n 'analysis.stop': 'Stop analysis',\n 'analysis.stopped': 'Analysis stopped; the analysis session was released.',\n 'analysis.restart': 'Analyze again',\n 'analysis.noticeTimeout':\n 'This follow-up timed out; retry later — the analysis session is kept.',\n 'analysis.noticeNetwork':\n 'The request could not be sent; retry — the analysis session is kept.',\n 'analysis.noticeCancelFailed': 'The stop request did not go through; try again.',\n 'analysis.errDisabled': 'AI analysis is disabled on the server; enable it in Settings and retry.',\n 'analysis.errUnavailable':\n 'This host has no AI analysis capability (agents service unavailable).',\n 'analysis.errTargetNotFound': 'The analysis target does not exist or left the observation window.',\n 'analysis.errTooManyActive': 'Too many concurrent analysis sessions; try again later.',\n 'analysis.errTimeout': 'The analysis timed out and its session was released; start again.',\n 'analysis.errCreateFailed': 'Failed to create the analysis session.',\n 'analysis.errCancelled': 'The analysis was cancelled.',\n 'analysis.errNetwork': 'Network error; the analysis request could not complete.',\n 'analysis.errGeneric': 'Analysis failed ({code}).',\n\n // ── /sidecar slash command (folded from locales/command.ts) ────────────\n 'command.description': 'Sidecar status at a glance (daemon, connection, sessions)',\n 'command.daemon.probe': 'Probing',\n 'command.daemon.adopted': 'Connected · adopted',\n 'command.daemon.defer': 'Waiting for the system service',\n 'command.daemon.reprobe': 'Re-probing',\n 'command.daemon.hosting': 'Starting',\n 'command.daemon.hosted': 'Connected · hosted',\n 'command.daemon.backoff': 'Restart backoff',\n 'command.daemon.failed': 'Offline',\n 'command.daemon.unknown': 'State unknown',\n 'command.connection.ok': 'Connected',\n 'command.connection.degraded': 'Connection unstable',\n 'command.connection.off': 'Offline',\n 'command.status.working': 'Working',\n 'command.status.waiting': 'Waiting',\n 'command.status.idle': 'Idle',\n 'command.status.dead': 'Finished',\n 'command.status.unknown': 'Unknown',\n 'command.daemonRow': 'Sidecar · {state}',\n 'command.countsRow': '{working} working · {waiting} waiting',\n 'command.countsDetail': '{total} sessions total',\n 'command.sessionDetail': '{project} · {status} · {time}',\n 'command.noSessions': 'No observed sessions yet',\n 'command.unknownProject': 'Unknown project',\n 'command.untitled': '(untitled)',\n 'command.truncated': '{n} more active sessions not listed',\n 'command.boardHint': 'Open the \"Sidecar\" tab in the conversation view for the full board',\n 'command.unreachable': 'sidecar is not connected',\n 'command.unreachableHint':\n 'The state snapshot could not be fetched; check that the agent-sidecar plugin is enabled and the daemon is available, then retry.',\n 'command.offlineFailed': 'sidecar is offline (daemon start failures tripped the breaker)',\n 'command.offlineFailedHint':\n 'Showing the last snapshot. Retry from the settings card, or run agent-sidecar daemon start manually and wait for adoption.',\n 'command.offlineDefer': 'Waiting for the system service to start the daemon',\n 'command.offlineDeferHint':\n 'A LaunchAgent manages the daemon; the plugin only probes and waits. The overview recovers automatically once the service brings it up.',\n 'command.time.justNow': 'just now',\n 'command.time.minutesAgo': '{n} min ago',\n 'command.time.hoursAgo': '{n} h ago',\n 'command.time.daysAgo': '{n} d ago',\n\n // ── better-sidebar mini tab (T6.3) ─────────────────────────────────────\n 'sidebar.tabTitle': 'Sidecar',\n 'sidebar.countsRow': '{working} working · {waiting} waiting',\n 'sidebar.recentTitle': 'Recently active',\n 'sidebar.connecting': 'Waiting for the sidecar snapshot…',\n 'sidebar.noSessions': 'No active sessions',\n 'sidebar.noEvent': 'No events recorded yet',\n 'sidebar.untitled': '(untitled)',\n 'sidebar.boardHint': 'Full board: the \"Sidecar\" tab in the conversation view',\n} satisfies Record<SidecarLocaleKey, string>\n","/**\n * Module-owned lightweight locale table for the agent-sidecar client half.\n *\n * WHY MODULE-OWNED (source-audit conclusion, T2.3): the installed SDK\n * surface (`@deepseek-ai/dsh-client-runtime` 0.1.1-rc.2 +\n * `@deepseek-ai/dsh-client-ui-slots`) ships only the locale TYPE currency\n * (`LocaleNamespaceMap`, `Translate`, the `locale:` register option and the\n * `t` standard seat) plus the boot-once `slots.installLocale(face)` hook;\n * the `ctx.locale` service itself lives in a separate plugin\n * (`@deepseek-ai/dsh-client-locale`, harness `packages/client/locale`) that\n * is NOT part of this package's dependency set. This table therefore keeps\n * the copy self-contained: default zh, switchable en, with a `t(key)`\n * helper whose lookup chain is active locale → zh → the key itself (fail\n * visible, never blank — same posture as the ecosystem LocaleRuntime,\n * which falls back to en; ours falls back to zh per this plugin's spec).\n *\n * BRIDGE PATH (for the wiring task): each dictionary is a flat\n * `Record<string, string>` — exactly the shape the ecosystem locale\n * service's untyped overload `ctx.locale.register(ns, locale, dict)`\n * accepts — so when the runtime composition provides `ctx.locale`, the\n * wiring half can feed `dictionaries.zh` / `dictionaries.en` straight into\n * it and hand the slot-injected `t` seat to the components instead of the\n * module-local {@link t}. Template params use the same `{name}` syntax as\n * the ecosystem translate.\n */\n\nimport { zh } from './zh.ts'\nimport { en } from './en.ts'\nimport type { SidecarLocaleDomain, SidecarLocaleKey } from './zh.ts'\n\nexport { zh, en }\nexport type { SidecarLocaleDomain, SidecarLocaleKey }\n\n/** Shipped locales. */\nexport type SidecarLocale = 'zh' | 'en'\n\n/** Default locale AND the final dictionary consulted before echoing the key. */\nexport const BASE_LOCALE: SidecarLocale = 'zh'\n\n/** One flat dictionary (the `ctx.locale.register(ns, locale, dict)` currency). */\nexport type SidecarDict = Readonly<Record<string, string>>\n\n/** Complete shipped dictionaries keyed by locale id. */\nexport const dictionaries: Readonly<Record<SidecarLocale, SidecarDict>> = { zh, en }\n\n/** Keys of one domain (`settings.` / `inject.` are live; `board.` stays reserved). */\nexport type KeysOfDomain<D extends SidecarLocaleDomain> = Extract<\n SidecarLocaleKey,\n `${D}.${string}`\n>\n\n/** The settings card's own key union. */\nexport type SettingsLocaleKey = KeysOfDomain<'settings'>\n\n/**\n * Substitute `{name}` template params; unknown placeholders stay verbatim\n * (same semantics as the ecosystem LocaleRuntime.translate).\n */\nfunction interpolate(template: string, params?: Record<string, unknown>): string {\n if (!params) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n}\n\n/**\n * Build a translate engine over an arbitrary (possibly partial) dictionary\n * set. Lookup chain per key: requested locale → {@link BASE_LOCALE} (zh) →\n * the key itself. Exposed for tests and for compositions that need a\n * non-shipped dictionary set; the shipped {@link t} is this engine bound to\n * {@link dictionaries} and the module's active locale.\n * @param dicts - dictionaries keyed by locale id (missing locales allowed).\n * @returns pure translate function addressed by explicit locale.\n */\nexport function createTranslator(\n dicts: Partial<Record<SidecarLocale, SidecarDict>>,\n): (locale: SidecarLocale, key: string, params?: Record<string, unknown>) => string {\n return (locale, key, params) => {\n const template = dicts[locale]?.[key] ?? dicts[BASE_LOCALE]?.[key] ?? key\n return interpolate(template, params)\n }\n}\n\nconst shippedTranslate = createTranslator(dictionaries)\n\nlet activeLocale: SidecarLocale = BASE_LOCALE\nconst localeListeners = new Set<() => void>()\n\n/** @returns the active locale id. */\nexport function getLocale(): SidecarLocale {\n return activeLocale\n}\n\n/**\n * Switch the active locale and notify subscribers (no-op when unchanged).\n * @param locale - a shipped locale id.\n */\nexport function setLocale(locale: SidecarLocale): void {\n if (activeLocale === locale) return\n activeLocale = locale\n for (const fn of [...localeListeners]) fn()\n}\n\n/**\n * Observe active-locale switches (React consumers pair this with\n * {@link getLocale} as a uSES source).\n * @param fn - change callback.\n * @returns unsubscribe.\n */\nexport function subscribeLocale(fn: () => void): () => void {\n localeListeners.add(fn)\n return () => { localeListeners.delete(fn) }\n}\n\n/**\n * Translate a typed key in the active locale. Missing entries fall back to\n * zh, then to the key itself (see module doc for why the chain ends visible).\n * @param key - a key of the shipped table.\n * @param params - optional `{name}` template params.\n * @returns the translated string.\n */\nexport function t(key: SidecarLocaleKey, params?: Record<string, unknown>): string {\n return shippedTranslate(activeLocale, key, params)\n}\n","/**\n * `command.*` locale segment for the `/sidecar` slash command (T4.6).\n *\n * T5.10b unification: the `command.*` copy now LIVES in the main\n * dictionaries (./zh.ts + ./en.ts, domain `command`) and this module is a\n * thin derived view kept for its consumers ({@link tCommand} for\n * commands.ts, {@link commandZh}/{@link commandEn} for tests and for the\n * `ctx.locale.register(ns, locale, dict)` bridge). Translation goes\n * through the shared engine and the shared active-locale switch, so\n * `setLocale` drives every surface consistently and the main-table parity\n * test covers these keys too.\n *\n * @module\n */\n\nimport { t, zh, en } from './index.ts'\nimport type { KeysOfDomain } from './index.ts'\n\n/** Key union of the command locale segment (main zh table is the source). */\nexport type CommandLocaleKey = KeysOfDomain<'command'>\n\nfunction commandSlice(dict: Readonly<Record<string, string>>): Record<CommandLocaleKey, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(dict)) {\n if (key.startsWith('command.')) out[key] = value\n }\n return out as Record<CommandLocaleKey, string>\n}\n\n/** The `command.*` slice of the main zh dictionary. */\nexport const commandZh: Readonly<Record<CommandLocaleKey, string>> = commandSlice(zh)\n\n/** The `command.*` slice of the main en dictionary. */\nexport const commandEn: Readonly<Record<CommandLocaleKey, string>> = commandSlice(en)\n\n/** Derived dictionaries of this segment, keyed by locale id. */\nexport const commandDictionaries = { zh: commandZh, en: commandEn } as const\n\n/**\n * Translate a command-segment key in the shared active locale (delegates\n * to the main-table {@link t}; lookup chain: active locale → zh → key).\n * @param key - a key of the command segment.\n * @param params - optional `{name}` template params.\n * @returns the translated string.\n */\nexport function tCommand(key: CommandLocaleKey, params?: Record<string, unknown>): string {\n return t(key, params)\n}\n","/**\n * `/sidecar` slash command (design §4.c `ctx.commands` row / M2 交付,\n * T4.6): a client-owned quick status overview of the sidecar — daemon\n * state, connection health, working/waiting counts, and the top-N active\n * sessions grouped by project — plus a pointer to the full board tab.\n *\n * MECHANISM (source audit, code-first): this dsh version has a CLIENT-side\n * slash-command extension point. The web GUI's slash menu is served by the\n * harness `ui-commands` client package, whose `CommandUiRuntime` service\n * (registered as `commandUi`) accepts client-owned contributions:\n *\n * - `commandUi.register({ name, description, available, ui })` adds one\n * slash-menu entry whose behavior lives entirely on the client (no host\n * descriptor) — merged with the host catalog by name, collisions fail\n * loud, duplicate contributions throw at registration\n * (harness `packages/client/ui-commands/src/client/contract.ts` +\n * `service.ts`; ecosystem precedent for the host-side flavor:\n * `dsh-agent-teams/src/command.ts`).\n * - The only `ui.kind` this dsh version supports is `popupSelect`:\n * an async `options(session, signal)` provider plus `onSelect`. The\n * `/sidecar` overview therefore presents as a popup card of rows — a\n * read-only glance; every row's onSelect is a no-op.\n *\n * The `commandUi` service type is NOT part of the published plugin SDK\n * (same situation as `settingsScope` in ./index.ts), so this module keeps\n * STRUCTURAL mirrors of the harness contract, verified against the source\n * above. Registration goes through `ctx.inject(['commandUi'], …)` — lazy,\n * like the design's `ctx.commands` row: a composition without the service\n * simply never gains the command.\n *\n * DATA: the overview reuses the existing client data layer (`fetchState`,\n * ./api.ts) and the board's pure derivations (./board/logic.ts). No new\n * backend endpoint. The snapshot→overview derivation is the pure, fully\n * unit-testable {@link buildOverview}.\n *\n * Wiring note (S5 integration wave): call {@link registerSidecarCommand}\n * from the client `apply`; this module performs no self-registration.\n *\n * @module\n */\n\nimport { fetchState, isApiError } from './api.ts'\nimport type { RequestOptions, SessionView, StateSnapshot } from './api.ts'\nimport {\n abbreviateSessionId,\n agentGlyph,\n countWorking,\n deriveWidgetConnection,\n groupSessions,\n normalizeStatus,\n} from './board/logic.ts'\nimport type {\n DaemonStateToken,\n SessionCardVM,\n SessionStatusToken,\n WidgetConnection,\n} from './board/logic.ts'\nimport { tCommand } from './locales/command.ts'\n\n/** The slash command name (without the leading slash). */\nexport const SIDECAR_COMMAND_NAME = 'sidecar'\n\n/** Default cap on session rows listed in the overview (top-N truncation). */\nexport const DEFAULT_OVERVIEW_TOP_N = 5\n\n// ---------------------------------------------------------------------------\n// Overview model (the pure snapshot→structure derivation contract).\n// ---------------------------------------------------------------------------\n\n/** One session line of the overview. */\nexport interface OverviewSessionRow {\n agent: string\n /** Single-character agent marker (board vocabulary). */\n glyph: string\n sessionId: string\n /** Head…tail abbreviation for display. */\n shortId: string\n status: SessionStatusToken\n statusLabel: string\n /** Session title with the untitled fallback applied. */\n title: string\n relativeTime: string\n}\n\n/** One project section of the overview (board grouping order). */\nexport interface OverviewProjectGroup {\n /** Raw project path; '' for the unknown-project bucket. */\n key: string\n /** Display label (path basename, or the localized unknown-project name). */\n label: string\n /** Full path; '' when unknown. */\n fullPath: string\n rows: OverviewSessionRow[]\n}\n\n/** Guidance block shown when the sidecar is offline or unreachable. */\nexport interface OverviewGuidance {\n kind: 'unreachable' | 'daemon-failed' | 'daemon-defer'\n title: string\n hint: string\n}\n\n/** The full overview structure `/sidecar` renders from. */\nexport interface OverviewModel {\n /** False only when no snapshot could be fetched at all. */\n reachable: boolean\n /** Daemon supervisor state; null when unreachable. */\n daemonState: DaemonStateToken | null\n daemonLabel: string\n connection: WidgetConnection\n connectionLabel: string\n workingCount: number\n waitingCount: number\n /** Every session in the snapshot (dead included). */\n totalCount: number\n /** Active (non-dead) sessions, grouped by project, capped at top N. */\n groups: OverviewProjectGroup[]\n /** Active sessions dropped by the top-N cap. */\n truncatedCount: number\n /** Non-null when offline/unreachable (design: 「sidecar 未连接」引导). */\n guidance: OverviewGuidance | null\n /** Pointer to the full board tab. */\n boardHint: string\n}\n\n/** Tunables of {@link buildOverview} (all injectable for tests). */\nexport interface OverviewComputeOptions {\n /** Clock (epoch ms); defaults to Date.now() at call time. */\n nowMs?: number\n /** Session-row cap; defaults to {@link DEFAULT_OVERVIEW_TOP_N}. */\n topN?: number\n}\n\n// ---------------------------------------------------------------------------\n// Pure derivation helpers.\n// ---------------------------------------------------------------------------\n\nconst MINUTE_MS = 60_000\nconst HOUR_MS = 3_600_000\nconst DAY_MS = 86_400_000\n\n/**\n * Coarse relative time over the command locale segment (same thresholds as\n * the board's formatRelativeTime, which is bound to the zh-only board\n * table and therefore not reused here).\n */\nfunction relativeTime(thenMs: number, nowMs: number): string {\n if (!Number.isFinite(thenMs)) return ''\n const delta = nowMs - thenMs\n if (delta < MINUTE_MS) return tCommand('command.time.justNow')\n if (delta < HOUR_MS) {\n return tCommand('command.time.minutesAgo', { n: Math.floor(delta / MINUTE_MS) })\n }\n if (delta < DAY_MS) {\n return tCommand('command.time.hoursAgo', { n: Math.floor(delta / HOUR_MS) })\n }\n return tCommand('command.time.daysAgo', { n: Math.floor(delta / DAY_MS) })\n}\n\n/** Wire SessionView → board card VM (epoch seconds → epoch ms boundary). */\nfunction toCardVM(view: SessionView): SessionCardVM {\n return {\n agent: view.agent,\n sessionId: view.session_id,\n status: view.status,\n title: view.title,\n project: view.project,\n updatedAtMs: view.updated_at * 1000,\n lastEvent:\n view.last_event === null\n ? null\n : { kind: view.last_event.kind, text: view.last_event.text },\n gap: view.gap,\n }\n}\n\n/** Card VM → overview row (labels resolved in the active locale). */\nfunction toRow(card: SessionCardVM, nowMs: number): OverviewSessionRow {\n const status = normalizeStatus(card.status)\n const title = card.title.trim()\n return {\n agent: card.agent,\n glyph: agentGlyph(card.agent),\n sessionId: card.sessionId,\n shortId: abbreviateSessionId(card.sessionId),\n status,\n statusLabel: tCommand(`command.status.${status}`),\n title: title === '' ? tCommand('command.untitled') : title,\n relativeTime: relativeTime(card.updatedAtMs, nowMs),\n }\n}\n\n/** Offline/unreachable guidance for the given daemon situation. */\nfunction deriveGuidance(daemonState: DaemonStateToken | null): OverviewGuidance | null {\n if (daemonState === null) {\n return {\n kind: 'unreachable',\n title: tCommand('command.unreachable'),\n hint: tCommand('command.unreachableHint'),\n }\n }\n if (daemonState === 'failed') {\n return {\n kind: 'daemon-failed',\n title: tCommand('command.offlineFailed'),\n hint: tCommand('command.offlineFailedHint'),\n }\n }\n if (daemonState === 'defer') {\n return {\n kind: 'daemon-defer',\n title: tCommand('command.offlineDefer'),\n hint: tCommand('command.offlineDeferHint'),\n }\n }\n return null\n}\n\n/**\n * Pure snapshot→overview derivation. `null` means the state endpoint was\n * unreachable (daemon offline / plugin host down): the model degrades to\n * the 「sidecar 未连接」 guidance. A snapshot with daemon failed/defer keeps\n * its (last-snapshot) sessions and counts — same honesty posture as the\n * board's degraded banner — with the matching guidance attached.\n *\n * Grouping and ordering reuse the board's pure logic: groups by project\n * (most recent first, unknown-project bucket last), cards by status rank\n * then recency. The top-N cap walks groups in that order; groups beyond\n * the cap are dropped and their sessions counted in `truncatedCount`.\n * Dead sessions are never listed (the overview is an \"active sessions\"\n * glance) but still count into `totalCount`.\n */\nexport function buildOverview(\n snapshot: StateSnapshot | null,\n opts: OverviewComputeOptions = {},\n): OverviewModel {\n const boardHint = tCommand('command.boardHint')\n if (snapshot === null) {\n return {\n reachable: false,\n daemonState: null,\n daemonLabel: tCommand('command.daemon.unknown'),\n connection: 'off',\n connectionLabel: tCommand('command.connection.off'),\n workingCount: 0,\n waitingCount: 0,\n totalCount: 0,\n groups: [],\n truncatedCount: 0,\n guidance: deriveGuidance(null),\n boardHint,\n }\n }\n\n const nowMs = opts.nowMs ?? Date.now()\n const topN = opts.topN ?? DEFAULT_OVERVIEW_TOP_N\n const daemonState = snapshot.daemon.state\n const sessions = snapshot.board.sessions\n const connection = deriveWidgetConnection(daemonState, snapshot.board.streamHealth)\n\n const active = sessions\n .filter((view) => normalizeStatus(view.status) !== 'dead')\n .map(toCardVM)\n\n const groups: OverviewProjectGroup[] = []\n let remaining = Math.max(0, topN)\n let truncatedCount = 0\n for (const group of groupSessions(active)) {\n if (remaining <= 0) {\n truncatedCount += group.cards.length\n continue\n }\n const taken = group.cards.slice(0, remaining)\n truncatedCount += group.cards.length - taken.length\n remaining -= taken.length\n groups.push({\n key: group.key,\n label: group.key === '' ? tCommand('command.unknownProject') : group.label,\n fullPath: group.fullPath,\n rows: taken.map((card) => toRow(card, nowMs)),\n })\n }\n\n return {\n reachable: true,\n daemonState,\n daemonLabel: tCommand(`command.daemon.${daemonState}`),\n connection,\n connectionLabel: tCommand(`command.connection.${connection}`),\n workingCount: countWorking(sessions),\n waitingCount: sessions.filter((view) => normalizeStatus(view.status) === 'waiting').length,\n totalCount: sessions.length,\n groups,\n truncatedCount,\n guidance: deriveGuidance(daemonState),\n boardHint,\n }\n}\n\n// ---------------------------------------------------------------------------\n// popupSelect rendering (overview model → option rows).\n// ---------------------------------------------------------------------------\n\n/** Structural mirror of ui-commands `SelectOption` (contract.ts). */\nexport interface SidecarSelectOption {\n readonly id: string\n readonly label: string\n readonly detail?: string\n readonly active?: boolean\n}\n\n/**\n * Render the overview as popupSelect rows (stable unique ids). Row order:\n * daemon/connection status, offline guidance (when any), counts or the\n * empty notice, session rows in group order (project carried in the\n * detail line), the truncation marker, and the board-tab pointer.\n */\nexport function overviewToOptions(model: OverviewModel): SidecarSelectOption[] {\n const options: SidecarSelectOption[] = [\n {\n id: 'daemon',\n label: tCommand('command.daemonRow', { state: model.daemonLabel }),\n detail: model.connectionLabel,\n },\n ]\n if (model.guidance !== null) {\n options.push({\n id: `guidance:${model.guidance.kind}`,\n label: model.guidance.title,\n detail: model.guidance.hint,\n })\n }\n if (model.reachable) {\n if (model.totalCount === 0) {\n options.push({ id: 'empty', label: tCommand('command.noSessions') })\n } else {\n options.push({\n id: 'counts',\n label: tCommand('command.countsRow', {\n working: model.workingCount,\n waiting: model.waitingCount,\n }),\n detail: tCommand('command.countsDetail', { total: model.totalCount }),\n })\n }\n for (const group of model.groups) {\n for (const row of group.rows) {\n options.push({\n id: `session:${row.sessionId}`,\n label: `${row.glyph} ${row.title}`,\n detail: tCommand('command.sessionDetail', {\n project: group.label,\n status: row.statusLabel,\n time: row.relativeTime,\n }),\n })\n }\n }\n if (model.truncatedCount > 0) {\n options.push({\n id: 'truncated',\n label: tCommand('command.truncated', { n: model.truncatedCount }),\n })\n }\n }\n options.push({ id: 'board', label: model.boardHint })\n return options\n}\n\n// ---------------------------------------------------------------------------\n// Command contribution (structural mirrors of the ui-commands contract).\n// ---------------------------------------------------------------------------\n\n/** Structural mirror of ui-commands `CommandUiSpec` (popupSelect kind). */\nexport interface SidecarCommandUiSpec {\n readonly kind: 'popupSelect'\n options(session: unknown, signal: AbortSignal): Promise<readonly SidecarSelectOption[]>\n onSelect(option: SidecarSelectOption, session: unknown): void | Promise<void>\n}\n\n/** Structural mirror of ui-commands `CommandContribution`. */\nexport interface SidecarCommandContribution {\n readonly name: string\n readonly description: string\n available(session: unknown): boolean\n readonly ui: SidecarCommandUiSpec\n}\n\n/** The `commandUi.register` face this module consumes (contract.ts). */\nexport interface CommandRegistryFace {\n register(contribution: SidecarCommandContribution): () => void\n}\n\n/** Injectable seams (tests fake the data layer and the clock). */\nexport interface SidecarCommandDeps {\n /** State fetcher; defaults to the shared data layer's fetchState. */\n fetchState?: (opts?: RequestOptions) => Promise<StateSnapshot>\n /** Clock (epoch ms); defaults to Date.now. */\n now?: () => number\n /** Session-row cap; defaults to {@link DEFAULT_OVERVIEW_TOP_N}. */\n topN?: number\n}\n\n/**\n * Build the `/sidecar` client command contribution. `options` fetches a\n * fresh snapshot per popup open; an abort (popup closed) propagates so the\n * shell can drop the stale request, while every other failure degrades to\n * the unreachable-guidance overview instead of throwing into the shell.\n * `description` is a live getter so a locale switch after registration\n * still reaches the slash menu's next candidate pass.\n */\nexport function createSidecarCommandContribution(\n deps: SidecarCommandDeps = {},\n): SidecarCommandContribution {\n const doFetch = deps.fetchState ?? fetchState\n const now = deps.now ?? Date.now\n return {\n name: SIDECAR_COMMAND_NAME,\n get description(): string {\n return tCommand('command.description')\n },\n // The glance works in every session state; nothing to gate on.\n available: () => true,\n ui: {\n kind: 'popupSelect',\n options: async (_session, signal) => {\n let snapshot: StateSnapshot | null = null\n try {\n snapshot = await doFetch({ signal })\n } catch (err) {\n if (isApiError(err) && err.kind === 'aborted') throw err\n console.error('agent-sidecar: /sidecar state fetch failed', err)\n }\n const overviewOpts: OverviewComputeOptions = { nowMs: now() }\n if (deps.topN !== undefined) overviewOpts.topN = deps.topN\n return overviewToOptions(buildOverview(snapshot, overviewOpts))\n },\n onSelect: () => {\n // Read-only glance: rows are informational, selection is a no-op.\n },\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration.\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal mount-context face ({@link registerSidecarCommand}); the real\n * `ClientContext` satisfies it structurally (cordis `ctx.inject`).\n */\nexport interface CommandMountContext {\n inject(deps: readonly string[], callback: (ctx: unknown) => void): unknown\n}\n\n/**\n * Register `/sidecar` once the `commandUi` service is available (lazy, per\n * the design's `ctx.commands` consumption row — a composition without the\n * slash-menu runtime simply never gains the command).\n *\n * Idempotency: the ui-commands registry throws on a duplicate contribution\n * name; that throw is caught and logged, so a double apply (HMR re-apply\n * before the old fiber unloads) degrades to a no-op instead of taking the\n * client half down. Disposal is owned by the registry's effect on the\n * injected fiber — unloading the plugin unregisters the command.\n */\nexport function registerSidecarCommand(\n ctx: CommandMountContext,\n deps: SidecarCommandDeps = {},\n): void {\n try {\n ctx.inject(['commandUi'], (injected) => {\n const { commandUi } = injected as { commandUi: CommandRegistryFace }\n try {\n commandUi.register(createSidecarCommandContribution(deps))\n } catch (err) {\n console.error('agent-sidecar: /sidecar command registration skipped', err)\n }\n })\n } catch (err) {\n console.error('agent-sidecar: commandUi injection failed', err)\n }\n}\n","/**\n * StateStream — the browser half's live data feed (design §5.3 / ADR-3).\n *\n * One class, two modes, one UI-facing surface (onSnapshot/onStatus):\n *\n * - 'sse' (main channel): EventSource on `GET <prefix>/stream`. Every\n * `state` event carries a full StateSnapshot (routes.ts pushes full\n * snapshots; heartbeats are comment frames the browser never surfaces).\n * On top of EventSource's native auto-reconnect, consecutive errors\n * beyond the threshold — or a CLOSED readyState — degrade the stream:\n * the instance is torn down and manually rebuilt on a 5s→30s capped\n * exponential backoff. `degraded` is a visible status, never a circuit\n * break; a successful reconnect resets the ladder.\n * - 'poll' (settings fallback, stream.mode=poll): pollFn (defaults to\n * api.fetchState) on a dual cadence — 3s while the latest snapshot has\n * a `working` session, 15s otherwise. `visible()` returning false\n * pauses fetching (ticks keep running as cheap visibility checks), and\n * {@link StateStream.pollNow} gives the visibilitychange handler an\n * immediate resume fetch.\n *\n * The status vocabulary is unified across modes:\n * 'connecting' | 'open' | 'degraded'. stop() is terminal and tears\n * everything down: timers, the EventSource, the in-flight poll fetch\n * (aborted), and both listener sets.\n *\n * Pure data layer: no React, no slots SDK, injectable primitives only.\n *\n * @module\n */\n\nimport {\n API_PREFIX,\n fetchState,\n type AbortControllerLike,\n type AbortSignalLike,\n type StateSnapshot,\n type TimerHandle,\n} from './api.ts'\n\n/** SSE endpoint within the plugin route namespace (host: routes.ts). */\nexport const STREAM_PATH = `${API_PREFIX}/stream`\n\nexport type StreamMode = 'sse' | 'poll'\n\n/** Unified connection status shown to the UI in both modes. */\nexport type StreamStatus = 'connecting' | 'open' | 'degraded'\n\nexport type SnapshotListener = (snapshot: StateSnapshot) => void\nexport type StatusListener = (status: StreamStatus) => void\n\n/** Minimal shape of a named SSE message event. */\nexport interface EventSourceMessageLike {\n data?: unknown\n}\n\n/** Structural EventSource, so node tests can fake it and DOM satisfies it. */\nexport interface EventSourceLike {\n readonly readyState: number\n addEventListener(type: string, listener: (ev: EventSourceMessageLike) => void): void\n close(): void\n}\n\nexport type EventSourceFactory = (url: string) => EventSourceLike\n\n/** Poll-mode snapshot source; receives an abort signal wired to stop(). */\nexport type PollFn = (opts: { signal?: AbortSignalLike }) => Promise<StateSnapshot>\n\n/** EventSource.CLOSED (numeric literal so fakes need no DOM constants). */\nconst EVENTSOURCE_CLOSED = 2\n\nconst DEFAULT_POLL_ACTIVE_MS = 3_000\nconst DEFAULT_POLL_IDLE_MS = 15_000\nconst DEFAULT_ERROR_THRESHOLD = 3\nconst DEFAULT_BACKOFF_BASE_MS = 5_000\nconst DEFAULT_BACKOFF_CAP_MS = 30_000\n\nexport interface StateStreamOptions {\n /** Stream endpoint for 'sse' mode; see {@link STREAM_PATH}. */\n url: string\n mode: StreamMode\n /** SSE transport factory. Default: `new EventSource(url)`. */\n eventSourceFactory?: EventSourceFactory\n /** Poll-mode snapshot source. Default: api.fetchState. */\n pollFn?: PollFn\n /** Poll cadence while some session is `working`. Default 3000. */\n pollActiveMs?: number\n /** Poll cadence while everything is idle. Default 15000. */\n pollIdleMs?: number\n /** Visibility gate (inverse of document.hidden); absent = always visible. */\n visible?: () => boolean\n setTimeout?: (fn: () => void, ms: number) => TimerHandle\n clearTimeout?: (handle: TimerHandle) => void\n /** Abort factory for in-flight poll fetches. Default: real AbortController. */\n createAbortController?: () => AbortControllerLike\n /** Consecutive SSE errors tolerated before degrading. Default 3. */\n errorThreshold?: number\n /** First manual-rebuild backoff delay. Default 5000. */\n backoffBaseMs?: number\n /** Manual-rebuild backoff ceiling. Default 30000. */\n backoffCapMs?: number\n}\n\nconst defaultSetTimeout = (fn: () => void, ms: number): TimerHandle =>\n globalThis.setTimeout(fn, ms)\n\nconst defaultClearTimeout = (handle: TimerHandle): void => {\n globalThis.clearTimeout(handle as ReturnType<typeof globalThis.setTimeout>)\n}\n\nconst defaultCreateAbortController = (): AbortControllerLike => new AbortController()\n\nconst defaultEventSourceFactory: EventSourceFactory = (url) => {\n const Ctor = (globalThis as { EventSource?: new (url: string) => unknown }).EventSource\n if (Ctor === undefined) {\n throw new Error('EventSource unavailable: inject eventSourceFactory or use poll mode')\n }\n return new Ctor(url) as EventSourceLike\n}\n\nconst defaultPollFn: PollFn = (opts) => fetchState({ signal: opts.signal })\n\nexport class StateStream {\n private readonly url: string\n private readonly streamMode: StreamMode\n private readonly esFactory: EventSourceFactory\n private readonly pollFn: PollFn\n private readonly pollActiveMs: number\n private readonly pollIdleMs: number\n private readonly visibleFn: (() => boolean) | undefined\n private readonly setT: (fn: () => void, ms: number) => TimerHandle\n private readonly clearT: (handle: TimerHandle) => void\n private readonly createController: () => AbortControllerLike\n private readonly errorThreshold: number\n private readonly backoffBaseMs: number\n private readonly backoffCapMs: number\n\n private currentStatus: StreamStatus = 'connecting'\n private started = false\n private stopped = false\n private readonly snapshotListeners = new Set<SnapshotListener>()\n private readonly statusListeners = new Set<StatusListener>()\n\n // --- sse state ---\n private es: EventSourceLike | null = null\n private sseErrors = 0\n private rebuildAttempts = 0\n private rebuildTimer: TimerHandle | null = null\n\n // --- poll state ---\n private pollTimer: TimerHandle | null = null\n private inFlight: AbortControllerLike | null = null\n private lastSnapshot: StateSnapshot | null = null\n\n constructor(opts: StateStreamOptions) {\n this.url = opts.url\n this.streamMode = opts.mode\n this.esFactory = opts.eventSourceFactory ?? defaultEventSourceFactory\n this.pollFn = opts.pollFn ?? defaultPollFn\n this.pollActiveMs = opts.pollActiveMs ?? DEFAULT_POLL_ACTIVE_MS\n this.pollIdleMs = opts.pollIdleMs ?? DEFAULT_POLL_IDLE_MS\n this.visibleFn = opts.visible\n this.setT = opts.setTimeout ?? defaultSetTimeout\n this.clearT = opts.clearTimeout ?? defaultClearTimeout\n this.createController = opts.createAbortController ?? defaultCreateAbortController\n this.errorThreshold = opts.errorThreshold ?? DEFAULT_ERROR_THRESHOLD\n this.backoffBaseMs = opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS\n this.backoffCapMs = opts.backoffCapMs ?? DEFAULT_BACKOFF_CAP_MS\n }\n\n get mode(): StreamMode {\n return this.streamMode\n }\n\n get status(): StreamStatus {\n return this.currentStatus\n }\n\n /** Begin streaming. One-shot: calling again (or after stop) is a no-op. */\n start(): void {\n if (this.started || this.stopped) return\n this.started = true\n if (this.streamMode === 'sse') this.connectSse()\n else this.pollTick()\n }\n\n /**\n * Terminal teardown: clears every timer, closes the EventSource, aborts\n * the in-flight poll fetch, and drops all listeners. Idempotent; a\n * stopped stream cannot be restarted (build a fresh instance instead).\n */\n stop(): void {\n if (this.stopped) return\n this.stopped = true\n if (this.rebuildTimer !== null) {\n this.clearT(this.rebuildTimer)\n this.rebuildTimer = null\n }\n if (this.pollTimer !== null) {\n this.clearT(this.pollTimer)\n this.pollTimer = null\n }\n const es = this.es\n this.es = null\n if (es !== null) es.close()\n const inFlight = this.inFlight\n this.inFlight = null\n if (inFlight !== null) inFlight.abort()\n this.snapshotListeners.clear()\n this.statusListeners.clear()\n }\n\n /** Subscribe to snapshots; returns the unsubscribe function. */\n onSnapshot(cb: SnapshotListener): () => void {\n if (this.stopped) return () => {}\n this.snapshotListeners.add(cb)\n return () => {\n this.snapshotListeners.delete(cb)\n }\n }\n\n /**\n * Subscribe to status changes; fires synchronously with the current\n * status on subscription, then on every transition. Returns the\n * unsubscribe function.\n */\n onStatus(cb: StatusListener): () => void {\n if (this.stopped) return () => {}\n this.statusListeners.add(cb)\n cb(this.currentStatus)\n return () => {\n this.statusListeners.delete(cb)\n }\n }\n\n /**\n * Immediate out-of-cadence poll (poll mode only). This is the\n * visibility-resume hook: wire the document's `visibilitychange` event\n * to call this when the page becomes visible again, so \"resume → fetch\n * immediately\" holds without waiting for the next scheduled tick. Any\n * pending tick is cancelled first, so cadence never doubles up.\n */\n pollNow(): void {\n if (!this.started || this.stopped || this.streamMode !== 'poll') return\n if (this.pollTimer !== null) {\n this.clearT(this.pollTimer)\n this.pollTimer = null\n }\n this.pollTick()\n }\n\n // ------------------------------------------------------------------ sse\n\n private connectSse(): void {\n if (this.stopped) return\n const es = this.esFactory(this.url)\n this.es = es\n es.addEventListener('open', () => {\n if (this.stopped || this.es !== es) return\n this.sseErrors = 0\n this.rebuildAttempts = 0\n this.setStatus('open')\n })\n es.addEventListener('state', (ev) => {\n if (this.stopped || this.es !== es) return\n if (typeof ev.data !== 'string') return\n let parsed: unknown\n try {\n parsed = JSON.parse(ev.data)\n } catch {\n return\n }\n if (typeof parsed !== 'object' || parsed === null) return\n this.emitSnapshot(parsed as StateSnapshot)\n })\n es.addEventListener('error', () => {\n if (this.stopped || this.es !== es) return\n this.sseErrors += 1\n if (es.readyState === EVENTSOURCE_CLOSED || this.sseErrors > this.errorThreshold) {\n this.scheduleRebuild()\n } else {\n // EventSource's native auto-reconnect is still in charge.\n this.setStatus('connecting')\n }\n })\n }\n\n /**\n * Take over from native auto-reconnect: close the instance, surface\n * `degraded`, and rebuild after a doubling delay capped at\n * backoffCapMs. Never gives up — the ladder just stays at the cap.\n */\n private scheduleRebuild(): void {\n const es = this.es\n this.es = null\n if (es !== null) es.close()\n this.setStatus('degraded')\n const delay = Math.min(this.backoffBaseMs * 2 ** this.rebuildAttempts, this.backoffCapMs)\n this.rebuildAttempts += 1\n this.rebuildTimer = this.setT(() => {\n this.rebuildTimer = null\n this.connectSse()\n }, delay)\n }\n\n // ----------------------------------------------------------------- poll\n\n private hasWorkingSession(): boolean {\n const snap = this.lastSnapshot\n if (snap === null) return false\n return snap.board.sessions.some((s) => s.status === 'working')\n }\n\n private scheduleNextPoll(): void {\n if (this.stopped) return\n const delay = this.hasWorkingSession() ? this.pollActiveMs : this.pollIdleMs\n this.pollTimer = this.setT(() => {\n this.pollTimer = null\n this.pollTick()\n }, delay)\n }\n\n private pollTick(): void {\n if (this.stopped) return\n if (this.visibleFn !== undefined && !this.visibleFn()) {\n // Hidden: pause fetching but keep ticking as a cheap visibility\n // check, so a tab left hidden resumes by itself at the next tick.\n this.scheduleNextPoll()\n return\n }\n if (this.inFlight !== null) {\n // A slow previous poll is still running; never overlap fetches.\n this.scheduleNextPoll()\n return\n }\n const controller = this.createController()\n this.inFlight = controller\n this.pollFn({ signal: controller.signal }).then(\n (snapshot) => {\n if (this.stopped || this.inFlight !== controller) return\n this.inFlight = null\n this.lastSnapshot = snapshot\n this.setStatus('open')\n this.emitSnapshot(snapshot)\n this.scheduleNextPoll()\n },\n () => {\n if (this.stopped || this.inFlight !== controller) return\n this.inFlight = null\n this.setStatus('degraded')\n this.scheduleNextPoll()\n },\n )\n }\n\n // ------------------------------------------------------------- plumbing\n\n private setStatus(status: StreamStatus): void {\n if (this.currentStatus === status) return\n this.currentStatus = status\n for (const cb of [...this.statusListeners]) cb(status)\n }\n\n private emitSnapshot(snapshot: StateSnapshot): void {\n for (const cb of [...this.snapshotListeners]) cb(snapshot)\n }\n}\n","/**\n * SidecarController — the browser half's one data controller (T2.4).\n *\n * Owns the live feed (a {@link StateStream}, default 'sse' mode on the\n * plugin's stream route) and folds it into two externally-subscribable\n * stores consumed via `useSyncExternalStore`:\n *\n * - view state: the host `StateSnapshot` mapped onto the board view models\n * (wire `updated_at` epoch SECONDS → `updatedAtMs` epoch MILLISECONDS at\n * this boundary, per board/logic.ts's contract), plus the composite\n * stream health (browser stream status overrides the host-reported\n * subscribe health — see {@link combineStreamHealth});\n * - filters: board filter state persisted to localStorage under a\n * package-name-prefixed key; until the user touches them, the ui.*\n * settings defaults may be adopted ({@link SidecarController.adoptConfigDefaults}).\n *\n * Pure mapping functions are exported for unit tests. Browser primitives\n * (stream, storage) are injectable so the whole controller runs under\n * plain node. No React, no slots SDK.\n *\n * @module\n */\n\nimport {\n fetchState,\n type PingInfo,\n type SessionView,\n type StateSnapshot,\n type StreamHealth,\n} from './api.ts'\nimport { StateStream, STREAM_PATH, type StreamMode, type StreamStatus } from './sse.ts'\nimport {\n DEFAULT_TIME_WINDOW_HOURS,\n type BoardFilterState,\n type DaemonStateToken,\n type SessionCardVM,\n type StreamHealthToken,\n} from './board/logic.ts'\n\n/**\n * Package name, used as the localStorage key prefix and the style-tag owner\n * mark. Mirrors package.json `name` (client code cannot read package.json at\n * runtime); pinned against it by test/client-integration.test.ts.\n */\nexport const PLUGIN_ID = '@shendeguize/dsh-agent-sidecar'\n\n/** localStorage key for the persisted board filters. */\nexport const FILTERS_STORAGE_KEY = `${PLUGIN_ID}:board-filters`\n\n// ---------------------------------------------------------------------------\n// View state and pure mapping (exported for tests).\n// ---------------------------------------------------------------------------\n\n/** Everything the three mounted surfaces render from. */\nexport interface SidecarViewState {\n daemonState: DaemonStateToken\n /** Last successful daemon ping, if any. */\n lastPing: PingInfo | null\n /** Hover detail for the daemon badge, e.g. \"pid 123 · v0.6.0\". */\n daemonDetail: string | undefined\n /** Composite health: browser stream status folded over host-reported health. */\n streamHealth: StreamHealthToken\n /** Raw browser-side stream status. */\n streamStatus: StreamStatus\n lastReconcileAtMs: number | null\n sessions: SessionCardVM[]\n /** Host capabilities.inject (M2 write surface; informational in M1). */\n injectCapability: boolean\n /** False until the first snapshot arrives in this page life. */\n hasSnapshot: boolean\n}\n\n/** Pre-first-snapshot state: probing daemon, unknown health, empty board. */\nexport function initialViewState(): SidecarViewState {\n return {\n daemonState: 'probe',\n lastPing: null,\n daemonDetail: undefined,\n streamHealth: 'unknown',\n streamStatus: 'connecting',\n lastReconcileAtMs: null,\n sessions: [],\n injectCapability: false,\n hasSnapshot: false,\n }\n}\n\n/**\n * Wire sessions → board card view models. The one place the epoch-seconds\n * `updated_at` becomes the epoch-milliseconds `updatedAtMs` (board/logic.ts\n * consumes milliseconds everywhere).\n */\nexport function mapSessions(sessions: readonly SessionView[]): SessionCardVM[] {\n return sessions.map((session) => ({\n agent: session.agent,\n sessionId: session.session_id,\n status: session.status,\n title: session.title,\n project: session.project,\n updatedAtMs: session.updated_at * 1000,\n lastEvent:\n session.last_event === null\n ? null\n : { kind: session.last_event.kind, text: session.last_event.text },\n gap: session.gap,\n }))\n}\n\n/** Daemon badge hover detail from the last ping (\"pid 123 · v0.6.0\"). */\nexport function daemonDetailOf(ping: PingInfo | null): string | undefined {\n if (ping === null) return undefined\n return `pid ${ping.pid} · v${ping.version}`\n}\n\n/**\n * Composite stream health for the UI:\n * - before the first snapshot nothing is known → 'unknown';\n * - a degraded BROWSER stream overrides (the host may still be healthy,\n * but what this page shows is stale);\n * - otherwise the host-reported daemon-subscribe health stands ('connecting'\n * during a native EventSource reconnect keeps the last host verdict —\n * the reconcile timestamp already conveys staleness).\n */\nexport function combineStreamHealth(\n hostHealth: StreamHealth | null,\n browserStatus: StreamStatus,\n hasSnapshot: boolean,\n): StreamHealthToken {\n if (!hasSnapshot || hostHealth === null) return 'unknown'\n if (browserStatus === 'degraded') return 'degraded'\n return hostHealth\n}\n\n/** Full snapshot → view state fold (pure; exported for tests). */\nexport function mapSnapshot(\n snapshot: StateSnapshot,\n browserStatus: StreamStatus,\n): SidecarViewState {\n return {\n daemonState: snapshot.daemon.state,\n lastPing: snapshot.daemon.lastPing,\n daemonDetail: daemonDetailOf(snapshot.daemon.lastPing),\n streamHealth: combineStreamHealth(snapshot.board.streamHealth, browserStatus, true),\n streamStatus: browserStatus,\n lastReconcileAtMs: snapshot.board.lastReconcileAt,\n sessions: mapSessions(snapshot.board.sessions),\n injectCapability: snapshot.capabilities.inject,\n hasSnapshot: true,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Filter persistence.\n// ---------------------------------------------------------------------------\n\n/** Structural localStorage face (node tests inject a Map-backed fake). */\nexport interface StorageLike {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n}\n\n/** Resolve the real localStorage; some privacy modes throw on access. */\nfunction defaultStorage(): StorageLike | null {\n try {\n const storage = (globalThis as { localStorage?: StorageLike }).localStorage\n return storage ?? null\n } catch {\n return null\n }\n}\n\n/** Parse + validate persisted filters; anything malformed reads as absent. */\nexport function readStoredFilters(storage: StorageLike | null): BoardFilterState | null {\n if (storage === null) return null\n try {\n const raw = storage.getItem(FILTERS_STORAGE_KEY)\n if (raw === null) return null\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null) return null\n const candidate = parsed as { timeWindowHours?: unknown; showDead?: unknown }\n if (\n typeof candidate.timeWindowHours !== 'number'\n || !Number.isFinite(candidate.timeWindowHours)\n || candidate.timeWindowHours <= 0\n || typeof candidate.showDead !== 'boolean'\n ) {\n return null\n }\n return { timeWindowHours: candidate.timeWindowHours, showDead: candidate.showDead }\n } catch {\n return null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Controller.\n// ---------------------------------------------------------------------------\n\n/** Structural stream face ({@link StateStream} satisfies it; tests fake it). */\nexport interface StateStreamLike {\n readonly status: StreamStatus\n readonly mode: StreamMode\n start(): void\n stop(): void\n onSnapshot(cb: (snapshot: StateSnapshot) => void): () => void\n onStatus(cb: (status: StreamStatus) => void): () => void\n pollNow(): void\n}\n\nexport interface SidecarControllerOptions {\n /** Live feed; default: `new StateStream({url: STREAM_PATH, mode: 'sse'})`. */\n stream?: StateStreamLike\n /** Filter persistence; `null` disables, absent resolves real localStorage. */\n storage?: StorageLike | null\n /** Manual-refresh fetch; default api.fetchState. */\n fetchStateFn?: typeof fetchState\n /** Visibility gate handed to the default stream (poll-mode pause). */\n visible?: () => boolean\n}\n\nconst defaultVisible = (): boolean =>\n typeof document === 'undefined' || !document.hidden\n\n/**\n * One instance per plugin apply. `start()` wires the stream, `stop()` is\n * terminal (the underlying StateStream cannot restart — a re-apply builds\n * a fresh controller).\n */\nexport class SidecarController {\n private readonly stream: StateStreamLike\n private readonly storage: StorageLike | null\n private readonly fetchFn: typeof fetchState\n private readonly listeners = new Set<() => void>()\n\n private state: SidecarViewState = initialViewState()\n private filters: BoardFilterState\n /** True once filters came from storage or a user gesture (config defaults then stop adopting). */\n private filtersTouched: boolean\n private lastHostHealth: StreamHealth | null = null\n private started = false\n\n constructor(opts: SidecarControllerOptions = {}) {\n this.storage = opts.storage === undefined ? defaultStorage() : opts.storage\n this.fetchFn = opts.fetchStateFn ?? fetchState\n this.stream =\n opts.stream\n ?? new StateStream({\n url: STREAM_PATH,\n mode: 'sse',\n visible: opts.visible ?? defaultVisible,\n })\n const stored = readStoredFilters(this.storage)\n this.filters = stored ?? { timeWindowHours: DEFAULT_TIME_WINDOW_HOURS, showDead: false }\n this.filtersTouched = stored !== null\n }\n\n /** Wire stream listeners and begin streaming (idempotent). */\n start(): void {\n if (this.started) return\n this.started = true\n this.stream.onSnapshot((snapshot) => {\n this.applySnapshot(snapshot)\n })\n this.stream.onStatus((status) => {\n this.applyStatus(status)\n })\n this.stream.start()\n }\n\n /** Terminal teardown of the live feed. */\n stop(): void {\n this.stream.stop()\n }\n\n /** Forward the visibility resume to the stream (poll-mode immediate fetch). */\n pollNow(): void {\n this.stream.pollNow()\n }\n\n /** Change notifications for BOTH stores (state and filters). */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /** Stable-reference view state (uSES getSnapshot source). */\n getState(): SidecarViewState {\n return this.state\n }\n\n /** Stable-reference filters (uSES getSnapshot source). */\n getFilters(): BoardFilterState {\n return this.filters\n }\n\n /** User filter change: persist (package-prefixed key) and notify. */\n setFilters(next: BoardFilterState): void {\n this.filters = { ...next }\n this.filtersTouched = true\n if (this.storage !== null) {\n try {\n this.storage.setItem(FILTERS_STORAGE_KEY, JSON.stringify(this.filters))\n } catch {\n // Quota/privacy failures degrade to session-only filters.\n }\n }\n this.notify()\n }\n\n /**\n * Adopt ui.* settings defaults as the filter values — only while the user\n * has never touched the filters (no stored value, no gesture). Not\n * persisted: an untouched board keeps following the settings defaults.\n */\n adoptConfigDefaults(ui: { timeWindowHours: number; showDead: boolean }): void {\n if (this.filtersTouched) return\n if (\n this.filters.timeWindowHours === ui.timeWindowHours\n && this.filters.showDead === ui.showDead\n ) {\n return\n }\n this.filters = { timeWindowHours: ui.timeWindowHours, showDead: ui.showDead }\n this.notify()\n }\n\n /** Manual refresh (board's refresh button): one out-of-band snapshot pull. */\n async refresh(): Promise<void> {\n try {\n const snapshot = await this.fetchFn({})\n this.applySnapshot(snapshot)\n } catch (err) {\n // The stream (and its status surface) remains the health authority;\n // a failed manual pull only logs.\n console.error('agent-sidecar: manual refresh failed', err)\n }\n }\n\n private applySnapshot(snapshot: StateSnapshot): void {\n this.lastHostHealth = snapshot.board.streamHealth\n this.state = mapSnapshot(snapshot, this.stream.status)\n this.notify()\n }\n\n private applyStatus(status: StreamStatus): void {\n if (status === this.state.streamStatus) return\n this.state = {\n ...this.state,\n streamStatus: status,\n streamHealth: combineStreamHealth(this.lastHostHealth, status, this.state.hasSnapshot),\n }\n this.notify()\n }\n\n private notify(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n","/**\n * Inject integration glue (S5 wiring, T4.9): everything that stands between\n * the presentational {@link InjectPanel} (T4.5, src/client/inject/) and the\n * rest of the client half, kept pure/injectable so it unit-tests under\n * plain node (same posture as controller.ts / settings-glue.ts).\n *\n * - {@link prepareEnvelope} / {@link executeEnvelope}: build the M2 action\n * wire bodies of the host dispatcher (src/routes.ts `handleAction`:\n * `{type:'inject.prepare'|'inject.execute', ...}` with the phase fields\n * at the top level). The body types are api.ts's `ActionEnvelope` union\n * members — the data layer owns the wire mirror, so `postAction` is\n * typed end-to-end and this module casts nothing on the way out.\n * - {@link createInjectActions}: the panel's `onPrepare`/`onExecute` props.\n * Execute posts with {@link EXECUTE_TIMEOUT_MS} (path two's server-side\n * budget outlives the 15s data-layer default; prepare keeps the default,\n * it has no delivery side effect).\n * Per the panel contract, transport failures resolve AS VALUES: the data\n * layer's normalized ApiError satisfies the panel's structural\n * {@link ApiErrorLike} and is returned, never thrown (anything that is\n * not an ApiError is re-thrown and lands in the panel's defensive catch:\n * retryable notice for prepare, terminal unknown for execute — S6).\n * A delivered execute fires the optional `onDelivered` hook so the owner\n * can pull one fresh snapshot; failed/unknown never do.\n * - {@link findInjectTarget}: board card selection → panel target (the\n * design §5.1 view-3 target summary comes from the selected SessionView\n * as mapped into the controller's card VMs). A session that has left the\n * snapshot resolves to null — the panel then shows its no-target hint\n * instead of injecting into a stale target.\n *\n * @module\n */\n\nimport { isApiError, postAction } from './api.ts'\nimport type { ExecuteActionBody, PrepareActionBody, RequestOptions } from './api.ts'\nimport type { SessionCardVM } from './board/logic.ts'\nimport type { InjectPanelTarget } from './inject/InjectPanel.tsx'\nimport type {\n ApiErrorLike,\n InjectResultView,\n PanelExecuteRequest,\n PanelPrepareRequest,\n PrepareSuccess,\n} from './inject/logic.ts'\n\n// ---------------------------------------------------------------------------\n// Wire bodies (canonical mirror lives in api.ts; re-exported for consumers).\n// ---------------------------------------------------------------------------\n\nexport type { ExecuteActionBody, PrepareActionBody } from './api.ts'\n\n/**\n * Execute-path HTTP deadline. Path two's server-side budget is the 30s CLI\n * timeout + 5s hard-kill buffer (host: send-cli.ts `DEFAULT_SEND_TIMEOUT_MS`\n * + `HARD_TIMEOUT_BUFFER_MS` = 35s worst case); a client deadline below\n * that fabricates a terminal 'unknown' out of every slow-but-honest\n * delivery receipt (M2 review F-3). 45s = server worst case + 10s margin;\n * the mirror relation is pinned by test against the host constants.\n */\nexport const EXECUTE_TIMEOUT_MS = 45_000\n\n/** Panel prepare request → the host prepare wire body. */\nexport function prepareEnvelope(req: PanelPrepareRequest): PrepareActionBody {\n return {\n type: 'inject.prepare',\n target: { agent: req.target.agent, sessionId: req.target.sessionId },\n mode: req.mode,\n message: req.message,\n }\n}\n\n/** Panel execute request → the host execute wire body. */\nexport function executeEnvelope(req: PanelExecuteRequest): ExecuteActionBody {\n return {\n type: 'inject.execute',\n requestId: req.requestId,\n confirmToken: req.confirmToken,\n message: req.message,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Action callbacks.\n// ---------------------------------------------------------------------------\n\n/** Transport seam: POST one action body, resolve the parsed JSON reply. */\nexport type PostActionFn = (\n body: PrepareActionBody | ExecuteActionBody,\n opts?: RequestOptions,\n) => Promise<unknown>\n\n/** Default transport: api.ts postAction (typed directly; the wire bodies\n * are ActionEnvelope union members, so no cast stands between the panel\n * and the dispatcher). */\nconst defaultPost: PostActionFn = (body, opts) => postAction(body, opts)\n\n/** The two integration callbacks the InjectPanel consumes. */\nexport interface InjectActions {\n onPrepare(req: PanelPrepareRequest): Promise<PrepareSuccess | ApiErrorLike>\n onExecute(req: PanelExecuteRequest): Promise<InjectResultView | ApiErrorLike>\n}\n\nexport interface InjectActionDeps {\n /** Transport override (tests); defaults to api.ts postAction. */\n post?: PostActionFn\n /** Fired once per delivered execute (e.g. a controller snapshot refresh). */\n onDelivered?: () => void\n}\n\n/**\n * Build the panel's `onPrepare`/`onExecute` callbacks over the action\n * transport. Error posture per the panel contract: ApiError resolves as a\n * value (the panel classifies http-kind as a server vocabulary verdict and\n * everything else as a transport failure); non-ApiError throws propagate.\n * @param deps - transport and delivery-hook seams.\n * @returns the callbacks, ready to spread onto the panel props.\n */\nexport function createInjectActions(deps: InjectActionDeps = {}): InjectActions {\n const post = deps.post ?? defaultPost\n return {\n async onPrepare(req) {\n try {\n return (await post(prepareEnvelope(req))) as PrepareSuccess\n } catch (err) {\n if (isApiError(err)) return err\n throw err\n }\n },\n async onExecute(req) {\n let result: InjectResultView\n try {\n // Longer deadline than prepare: the send-cli path legitimately runs\n // up to 35s server-side before an honest receipt arrives (F-3).\n result = (await post(executeEnvelope(req), {\n timeoutMs: EXECUTE_TIMEOUT_MS,\n })) as InjectResultView\n } catch (err) {\n if (isApiError(err)) return err\n throw err\n }\n // Only a positive delivery refreshes; 'unknown' stays terminal and\n // untouched (S6), 'failed' changes nothing worth re-pulling.\n if (result.outcome === 'delivered') deps.onDelivered?.()\n return result\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Target mapping.\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the selected board card into the panel's injection target.\n * @param sessions - current card VMs from the controller view state.\n * @param sessionId - the selected session, or null when nothing is selected.\n * @returns the target, or null when unselected or gone from the snapshot.\n */\nexport function findInjectTarget(\n sessions: readonly SessionCardVM[],\n sessionId: string | null,\n): InjectPanelTarget | null {\n if (sessionId === null) return null\n const card = sessions.find((session) => session.sessionId === sessionId)\n if (card === undefined) return null\n const title = card.title.trim()\n return {\n agent: card.agent,\n sessionId: card.sessionId,\n ...(title !== '' ? { title } : {}),\n }\n}\n","/**\n * Cross-agent session board (design §5.1 view 1).\n *\n * Presentation-only: no data fetching, no api/sse imports. Everything\n * arrives through props already shaped as the view models of `logic.ts`;\n * the integration layer (T2.4) owns state, transport, and the mapping\n * from the host wire types (epoch-seconds → epoch-ms conversion included).\n *\n * Interaction surface handed back to the owner:\n * - `onFiltersChange` — time-window select / show-dead checkbox (controlled);\n * - `onRefresh` — manual snapshot pull button;\n * - `onSelectSession` — card click, pass-through for the M3 detail view.\n */\n\nimport type { ReactElement } from 'react'\nimport {\n buildBoardViewModel,\n timeWindowLabel,\n formatTemplate,\n type BoardFilterState,\n type DaemonStateToken,\n type DerivedSessionCardVM,\n type ProjectGroupVM,\n type SessionCardVM,\n type StreamHealthToken,\n} from './logic.ts'\nimport { BOARD_STRINGS } from './strings.ts'\nimport styles from './board.module.css'\n\n/** Time-window choices offered by the top bar (hours). */\nexport const TIME_WINDOW_OPTIONS: readonly number[] = [6, 12, 24, 48, 168]\n\nexport interface BoardProps {\n daemonState: DaemonStateToken\n /** Optional raw detail for the daemon badge hover (e.g. \"pid 123 · v0.6.0\"). */\n daemonDetail?: string\n streamHealth: StreamHealthToken\n /** Epoch ms of the last authoritative snapshot reconcile, or null. */\n lastReconcileAtMs: number | null\n sessions: SessionCardVM[]\n /** Controlled filter state (owner persists it to the settings namespace). */\n filters: BoardFilterState\n onFiltersChange: (next: BoardFilterState) => void\n onRefresh: () => void\n onSelectSession: (sessionId: string) => void\n /** Clock injection for deterministic rendering; defaults to Date.now(). */\n nowMs?: number\n}\n\nfunction SessionCard(props: {\n card: DerivedSessionCardVM\n onSelect: (sessionId: string) => void\n}): ReactElement {\n const { card, onSelect } = props\n return (\n <button\n type=\"button\"\n className={styles['card']}\n onClick={() => onSelect(card.sessionId)}\n data-testid=\"agent-sidecar-card\"\n >\n <div className={styles['cardHead']}>\n <span className={styles['agent']}>\n <span className={styles['glyph']} aria-hidden>\n {card.glyph}\n </span>\n {card.agent}\n </span>\n <span className={styles['badge']} data-tone={card.badge.tone} title={card.hoverTitle}>\n <span className={styles['dot']} data-tone={card.badge.tone} />\n {card.badge.label}\n {card.badge.attention !== null && (\n <span className={styles['attention']} data-kind={card.badge.attention}>\n {card.badge.attentionLabel}\n </span>\n )}\n </span>\n </div>\n <div className={styles['cardTitle']} title={card.title}>\n {card.title.trim() === '' ? BOARD_STRINGS.card.untitled : card.title}\n </div>\n <div className={styles['cardId']} title={card.sessionId}>\n {card.shortId}\n </div>\n <div className={styles['cardEvent']}>\n {card.lastEvent === null\n ? BOARD_STRINGS.card.noEvent\n : `${card.lastEvent.kind} · ${card.lastEvent.text}`}\n </div>\n <div className={styles['cardTime']}>{card.relativeTime}</div>\n </button>\n )\n}\n\nfunction ProjectGroup(props: {\n group: ProjectGroupVM<DerivedSessionCardVM>\n onSelect: (sessionId: string) => void\n}): ReactElement {\n const { group, onSelect } = props\n return (\n <section className={styles['group']}>\n <div className={styles['groupHead']}>\n <span\n className={styles['groupName']}\n title={group.fullPath === '' ? undefined : group.fullPath}\n >\n {group.label}\n </span>\n <span className={styles['groupCount']}>\n {formatTemplate(BOARD_STRINGS.groupCount, { n: group.cards.length })}\n </span>\n </div>\n <div className={styles['grid']}>\n {group.cards.map((card) => (\n <SessionCard key={`${card.agent}:${card.sessionId}`} card={card} onSelect={onSelect} />\n ))}\n </div>\n </section>\n )\n}\n\n/** The board view. Pure render of `buildBoardViewModel` over the props. */\nexport function Board(props: BoardProps): ReactElement {\n const nowMs = props.nowMs ?? Date.now()\n const vm = buildBoardViewModel({\n sessions: props.sessions,\n filters: props.filters,\n daemonState: props.daemonState,\n streamHealth: props.streamHealth,\n lastReconcileAtMs: props.lastReconcileAtMs,\n nowMs,\n })\n // Keep the select renderable even when the persisted setting is not one\n // of the stock options (e.g. a hand-edited settings.yaml value).\n const windowOptions = TIME_WINDOW_OPTIONS.includes(props.filters.timeWindowHours)\n ? TIME_WINDOW_OPTIONS\n : [...TIME_WINDOW_OPTIONS, props.filters.timeWindowHours].sort((a, b) => a - b)\n\n return (\n <div className={styles['root']} data-testid=\"agent-sidecar-board\">\n <header className={styles['topbar']}>\n <span className={styles['title']}>{BOARD_STRINGS.topbar.title}</span>\n <span className={styles['badge']} data-tone={vm.daemonBadge.tone} title={props.daemonDetail}>\n <span className={styles['dot']} data-tone={vm.daemonBadge.tone} />\n {vm.daemonBadge.label}\n </span>\n <span className={styles['badge']} data-tone={vm.streamTone}>\n <span className={styles['dot']} data-tone={vm.streamTone} />\n {vm.streamLabel}\n </span>\n <span className={styles['spacer']} />\n <label className={styles['control']}>\n {BOARD_STRINGS.topbar.timeWindow}\n <select\n className={styles['select']}\n value={String(props.filters.timeWindowHours)}\n onChange={(ev) =>\n props.onFiltersChange({\n ...props.filters,\n timeWindowHours: Number(ev.target.value),\n })\n }\n >\n {windowOptions.map((hours) => (\n <option key={hours} value={String(hours)}>\n {timeWindowLabel(hours)}\n </option>\n ))}\n </select>\n </label>\n <label className={styles['control']}>\n <input\n type=\"checkbox\"\n className={styles['checkbox']}\n checked={props.filters.showDead}\n onChange={(ev) =>\n props.onFiltersChange({ ...props.filters, showDead: ev.target.checked })\n }\n />\n {BOARD_STRINGS.topbar.showDead}\n </label>\n <button\n type=\"button\"\n className={styles['refresh']}\n title={BOARD_STRINGS.topbar.refreshTitle}\n onClick={props.onRefresh}\n >\n {BOARD_STRINGS.topbar.refresh}\n </button>\n </header>\n\n {vm.banner !== null && (\n <div className={styles['banner']} data-tone={vm.banner.tone} role=\"status\">\n {vm.banner.text}\n </div>\n )}\n\n {vm.emptyState !== null ? (\n <div className={styles['empty']} data-kind={vm.emptyState.kind}>\n <div className={styles['emptyTitle']}>{vm.emptyState.title}</div>\n <div className={styles['emptyHint']}>{vm.emptyState.hint}</div>\n </div>\n ) : (\n vm.groups.map((group) => (\n <ProjectGroup\n key={group.key === '' ? '\\u0000unknown' : group.key}\n group={group}\n onSelect={props.onSelectSession}\n />\n ))\n )}\n </div>\n )\n}\n","/**\n * Cross-agent project correlation view (design §4.e.2 / §5.1 M3, T5.5).\n *\n * Presentation-only and fully controlled: no data fetching, no api/sse\n * imports. The owner fetches `GET <prefix>/projects`, hands the wire\n * groups in as props (the wire shape IS the input view model — camelCase,\n * epoch-ms `lastActivityAt`), and receives session clicks back through\n * `onSelectSession` (pass-through to the detail view, same as Board).\n *\n * Render precedence when there is nothing to show: error > loading >\n * empty state. When groups ARE available, an error renders as a banner\n * above the (possibly stale) content instead of replacing it, and\n * `loading` shows as a quiet header chip — honest degradation without\n * blanking data the user already has.\n */\n\nimport type { ReactElement } from 'react'\nimport {\n buildProjectViewModel,\n PROJECT_VIEW_STRINGS,\n type AgentLaneVM,\n type DerivedProjectGroupVM,\n type DerivedProjectSessionVM,\n type ProjectGroupVM,\n} from './project-view-logic.ts'\nimport styles from './project-view.module.css'\n\nexport interface ProjectViewProps {\n /** Wire groups of `GET projects` (owner passes the response through). */\n groups: ProjectGroupVM[]\n loading: boolean\n /** Human-readable fetch error, or null. */\n error: string | null\n onSelectSession: (sessionId: string) => void\n /** Clock injection for deterministic rendering; defaults to Date.now(). */\n nowMs?: number\n}\n\nfunction SessionRow(props: {\n session: DerivedProjectSessionVM\n onSelect: (sessionId: string) => void\n}): ReactElement {\n const { session, onSelect } = props\n return (\n <button\n type=\"button\"\n className={styles['session']}\n onClick={() => onSelect(session.sessionId)}\n data-testid=\"agent-sidecar-project-session\"\n >\n <span className={styles['badge']} data-tone={session.badge.tone}>\n <span className={styles['dot']} data-tone={session.badge.tone} />\n {session.badge.label}\n {session.badge.attention !== null && (\n <span className={styles['attention']} data-kind={session.badge.attention}>\n {session.badge.attentionLabel}\n </span>\n )}\n </span>\n <span className={styles['sessionTitle']} title={session.title}>\n {session.displayTitle}\n </span>\n {session.live && <span className={styles['liveChip']}>{PROJECT_VIEW_STRINGS.liveChip}</span>}\n <span className={styles['sessionId']} title={session.sessionId}>\n {session.shortId}\n </span>\n <span className={styles['sessionTime']}>{session.relativeTime}</span>\n </button>\n )\n}\n\nfunction AgentLane(props: {\n lane: AgentLaneVM\n onSelect: (sessionId: string) => void\n}): ReactElement {\n const { lane, onSelect } = props\n return (\n <div className={styles['lane']}>\n <div className={styles['laneHead']}>\n <span className={styles['glyph']} aria-hidden>\n {lane.glyph}\n </span>\n {lane.agent}\n </div>\n <div className={styles['laneSessions']}>\n {lane.sessions.map((session) => (\n <SessionRow\n key={`${session.agent}:${session.sessionId}`}\n session={session}\n onSelect={onSelect}\n />\n ))}\n </div>\n </div>\n )\n}\n\nfunction ProjectSection(props: {\n group: DerivedProjectGroupVM\n onSelect: (sessionId: string) => void\n}): ReactElement {\n const { group, onSelect } = props\n return (\n <section className={styles['group']} data-testid=\"agent-sidecar-project-group\">\n <div className={styles['groupHead']}>\n <span\n className={styles['groupName']}\n title={group.fullPath === '' ? undefined : group.fullPath}\n >\n {group.label}\n </span>\n <span className={styles['agentBadges']}>\n {group.agentBadges.map((badge) => (\n <span key={badge.agent} className={styles['agentBadge']} title={badge.agent}>\n <span className={styles['glyph']} aria-hidden>\n {badge.glyph}\n </span>\n {badge.agent}\n </span>\n ))}\n {group.crossAgentLabel !== null && (\n <span className={styles['crossBadge']}>{group.crossAgentLabel}</span>\n )}\n </span>\n <span className={styles['spacer']} />\n <span className={styles['groupMeta']}>{group.sessionCountLabel}</span>\n <span className={styles['groupMeta']}>{group.lastActiveLabel}</span>\n </div>\n <div className={styles['lanes']}>\n {group.lanes.map((lane) => (\n <AgentLane key={lane.agent} lane={lane} onSelect={onSelect} />\n ))}\n </div>\n </section>\n )\n}\n\n/** The project correlation view. Pure render of `buildProjectViewModel`. */\nexport function ProjectView(props: ProjectViewProps): ReactElement {\n const nowMs = props.nowMs ?? Date.now()\n const vm = buildProjectViewModel({ groups: props.groups, nowMs })\n const hasContent = vm.groups.length > 0\n\n return (\n <div className={styles['root']} data-testid=\"agent-sidecar-project-view\">\n <header className={styles['topbar']}>\n <span className={styles['title']}>{PROJECT_VIEW_STRINGS.title}</span>\n <span className={styles['summary']}>{vm.summaryLabel}</span>\n {props.loading && (\n <span className={styles['loadingChip']} role=\"status\">\n {PROJECT_VIEW_STRINGS.loading}\n </span>\n )}\n </header>\n\n {props.error !== null && hasContent && (\n <div className={styles['banner']} data-tone=\"danger\" role=\"alert\">\n {PROJECT_VIEW_STRINGS.errorTitle}: {props.error}\n </div>\n )}\n\n {hasContent ? (\n vm.groups.map((group) => (\n <ProjectSection\n key={group.key === '' ? '\\u0000unknown' : group.key}\n group={group}\n onSelect={props.onSelectSession}\n />\n ))\n ) : props.error !== null ? (\n <div className={styles['empty']} data-kind=\"error\" role=\"alert\">\n <div className={styles['emptyTitle']}>{PROJECT_VIEW_STRINGS.errorTitle}</div>\n <div className={styles['emptyHint']}>{props.error}</div>\n </div>\n ) : props.loading ? (\n <div className={styles['empty']} data-kind=\"loading\" role=\"status\">\n <div className={styles['emptyHint']}>{PROJECT_VIEW_STRINGS.loading}</div>\n </div>\n ) : (\n vm.emptyState !== null && (\n <div className={styles['empty']} data-kind=\"no-projects\">\n <div className={styles['emptyTitle']}>{vm.emptyState.title}</div>\n <div className={styles['emptyHint']}>{vm.emptyState.hint}</div>\n </div>\n )\n )}\n </div>\n )\n}\n","/**\n * Footer status widget (design §5.1 view 4, `sidebar.footer.action` slot).\n *\n * Presentation-only: connection state and working count arrive as props\n * (derive them with `deriveWidgetConnection` / `countWorking` from\n * `board/logic.ts`); the click callback is wired to the board tab by the\n * integration layer.\n *\n * Quiet by design: at zero working sessions only the connection dot shows,\n * no counter, no animation — the DOM stays stable so the footer never\n * flickers or shifts (task spec: 零数据时安静).\n */\n\nimport type { CSSProperties, ReactElement } from 'react'\nimport { widgetTitle, type WidgetConnection } from './board/logic.ts'\n\nexport interface SidecarWidgetProps {\n connection: WidgetConnection\n workingCount: number\n /** Opens the board tab; wired by the integration layer. */\n onOpen?: () => void\n}\n\nconst DOT_COLORS: Record<WidgetConnection, string> = {\n ok: 'var(--dsw-alias-state-success-primary, #1a7f37)',\n degraded: 'var(--dsw-alias-state-warn-primary, #9a6700)',\n off: 'var(--dsw-alias-label-dimmed, #8c959f)',\n}\n\nconst rootStyle: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n padding: '2px 6px',\n border: 'none',\n borderRadius: 6,\n background: 'transparent',\n cursor: 'pointer',\n font: 'inherit',\n color: 'var(--dsw-alias-label-secondary, #57606a)',\n}\n\nconst countStyle: CSSProperties = {\n fontSize: 11,\n fontVariantNumeric: 'tabular-nums',\n lineHeight: '14px',\n whiteSpace: 'nowrap',\n}\n\n/** Connection dot + working-session counter (e.g. `▸2`) for the footer. */\nexport function SidecarWidget(props: SidecarWidgetProps): ReactElement {\n const title = widgetTitle(props.connection, props.workingCount)\n return (\n <button\n type=\"button\"\n style={rootStyle}\n title={title}\n aria-label={title}\n onClick={props.onOpen}\n data-testid=\"agent-sidecar-widget\"\n data-connection={props.connection}\n >\n <span\n aria-hidden\n style={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n flex: 'none',\n background: DOT_COLORS[props.connection],\n }}\n />\n {props.workingCount > 0 && (\n <span style={countStyle} data-testid=\"agent-sidecar-widget-count\">\n {`▸${props.workingCount}`}\n </span>\n )}\n </button>\n )\n}\n","/**\n * Agent Sidecar settings card (browser half, T2.3).\n *\n * MECHANISM (source-audit conclusion): the dsh settings pane does NOT\n * auto-render a form from the host Config schema. The plugin-configuration\n * tab dispatches the keyed `settings.plugin.item` slot per Host-served\n * settings namespace and \"a card draws its own internals; the tab only\n * decides which namespaces to dispatch and stacks what comes back\"\n * (harness `packages/client/ui-settings-plugins/src/client/slot-contract.ts`;\n * unclaimed namespaces render nothing per the adding-a-settings-card\n * cookbook). This card therefore carries the form UI for the key items of\n * the host Config (src/config.ts): daemon.policy/backoffLimit,\n * sidecar.command/runtimeDir, stream.reconcileActiveMs/IdleMs,\n * inject.enabled/defaultMode, analysis.enabled, ui.timeWindowHours/showDead,\n * skill.provide — plus the daemon status/retry row and the injection safety\n * note the design doc (§4.a/§5.3/§6) puts on the settings surface.\n *\n * WIRING CONTRACT (T2.4): the card is fully controlled and presentational.\n * `values` is the staged draft owned by the wiring controller; every edit\n * reports through `onChange(field, value)` and the controller must echo the\n * staged value back through `values` (the shipped ui-settings-plugins cards\n * follow the same staged-edit split). Save/discard/retry are plain\n * callbacks. Registration into `settings.plugin.item` (keyed by the Host\n * settings namespace) also belongs to the wiring half — this module exports\n * the component and its props contract only.\n *\n * The card renders as an `<li>` because the plugin-configuration tab stacks\n * cards in a list (shipped PluginCard precedent). Chrome (disclosure\n * header, unsaved pill, save/discard footer) mirrors the shipped card so\n * the sidecar card reads native next to first-party ones.\n */\n\nimport { useEffect, useId, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { t as defaultT } from './locales/index.ts'\nimport type { SidecarLocaleKey } from './locales/index.ts'\nimport css from './settings-card.module.css'\n\n/**\n * Daemon supervisor state as surfaced to the browser. Mirrors\n * `SupervisorState` in src/supervisor.ts verbatim; restated here because\n * host and client are separate TS programs (tsconfig.host excludes\n * src/client and vice versa) and the client bundle must not import host\n * modules.\n */\nexport type SidecarDaemonState =\n | 'probe'\n | 'adopted'\n | 'defer'\n | 'reprobe'\n | 'hosting'\n | 'hosted'\n | 'backoff'\n | 'failed'\n\n/** Daemon status block input (shape matches the supervisor's ping-derived view). */\nexport interface SidecarDaemonStatus {\n state: SidecarDaemonState\n /** Daemon pid, when a ping succeeded. */\n pid?: number\n /** Daemon version, when a ping succeeded. */\n version?: string\n}\n\n/**\n * The staged form values — one flat field per key item of the host Config\n * schema (grouping mirrors src/config.ts).\n */\nexport interface SettingsCardValues {\n /** daemon.policy */\n daemonPolicy: 'adopt-or-host' | 'adopt-only' | 'off'\n /** daemon.backoffLimit (integer ≥ 1) */\n daemonBackoffLimit: number\n /**\n * sidecar.command, displayed and edited as one whitespace-joined line;\n * argv splitting/joining is the wiring controller's concern.\n */\n sidecarCommand: string\n /** sidecar.runtimeDir ('' = default ~/.agent_sidecar) */\n sidecarRuntimeDir: string\n /** stream.reconcileActiveMs (integer ≥ 100) */\n streamReconcileActiveMs: number\n /** stream.reconcileIdleMs (integer ≥ 100) */\n streamReconcileIdleMs: number\n /** inject.enabled */\n injectEnabled: boolean\n /** inject.defaultMode */\n injectDefaultMode: 'queue' | 'steer'\n /** analysis.enabled */\n analysisEnabled: boolean\n /**\n * analysis.provider / analysis.model ('' = reuse the host default model).\n * Carried through the staged values for save round-trip fidelity (a group\n * write is a COMPLETE analysis object — dropping them here would wipe an\n * explicit configuration); not yet rendered as form fields.\n */\n analysisProvider: string\n analysisModel: string\n /** ui.timeWindowHours (integer ≥ 1) */\n uiTimeWindowHours: number\n /** ui.showDead */\n uiShowDead: boolean\n /** skill.provide */\n skillProvide: boolean\n}\n\n/**\n * Translate signature the card consumes: the module-local `t` by default,\n * or (same shape) a slot-injected / ctx.locale-bound seat supplied by the\n * wiring half.\n */\nexport type SettingsTranslate = (\n key: SidecarLocaleKey,\n params?: Record<string, unknown>,\n) => string\n\nexport interface SettingsCardProps {\n /** Staged form values (owned and echoed back by the wiring controller). */\n values: SettingsCardValues\n /** Report one staged edit. */\n onChange: <K extends keyof SettingsCardValues>(\n field: K,\n value: SettingsCardValues[K],\n ) => void\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /** False disables every control and shows the read-only note. */\n writable: boolean\n /** Whether unsaved edits stand. */\n dirty: boolean\n /** Whether a save is in flight. */\n saving: boolean\n /** Whether the last save failed. */\n saveFailed?: boolean\n /** Daemon status block; omitted hides the row. */\n daemon?: SidecarDaemonStatus\n /** Retry hosting after the supervisor tripped `failed`. */\n onDaemonRetry?: () => void\n /** Documentation link target; omitted hides the link. */\n docsUrl?: string\n /** Locale seat override; defaults to the module-local table. */\n t?: SettingsTranslate\n}\n\nconst DAEMON_STATE_KEY: Record<SidecarDaemonState, SidecarLocaleKey> = {\n 'probe': 'settings.daemonStateProbe',\n 'adopted': 'settings.daemonStateAdopted',\n 'defer': 'settings.daemonStateDefer',\n 'reprobe': 'settings.daemonStateReprobe',\n 'hosting': 'settings.daemonStateHosting',\n 'hosted': 'settings.daemonStateHosted',\n 'backoff': 'settings.daemonStateBackoff',\n 'failed': 'settings.daemonStateFailed',\n}\n\n/** Dot classes per daemon state: healthy / transitional / tripped. */\nfunction statusDotClass(state: SidecarDaemonState): string {\n if (state === 'adopted' || state === 'hosted') return `${css['statusDot']} ${css['statusOk']}`\n if (state === 'failed') return `${css['statusDot']} ${css['statusError']}`\n return css['statusDot'] ?? ''\n}\n\ninterface SectionProps {\n title: string\n children: ReactNode\n}\n\nfunction Section(props: SectionProps): ReactNode {\n return (\n <section className={css['section']}>\n <h3 className={css['sectionTitle']}>{props.title}</h3>\n {props.children}\n </section>\n )\n}\n\ninterface SelectFieldProps {\n label: string\n hint: string\n value: string\n options: readonly { value: string, label: string }[]\n disabled: boolean\n onCommit: (value: string) => void\n}\n\nfunction SelectField(props: SelectFieldProps): ReactNode {\n const id = useId()\n return (\n <div className={css['field']}>\n <label className={css['label']} htmlFor={id}>{props.label}</label>\n <select\n id={id}\n className={css['select']}\n value={props.value}\n disabled={props.disabled}\n onChange={(event) => { props.onCommit(event.target.value) }}\n >\n {props.options.map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n <p className={css['hint']}>{props.hint}</p>\n </div>\n )\n}\n\ninterface ToggleFieldProps {\n label: string\n hint: string\n checked: boolean\n disabled: boolean\n onCommit: (checked: boolean) => void\n}\n\nfunction ToggleField(props: ToggleFieldProps): ReactNode {\n return (\n <div className={css['field']}>\n <label className={css['toggleRow']}>\n <input\n type=\"checkbox\"\n checked={props.checked}\n disabled={props.disabled}\n onChange={(event) => { props.onCommit(event.target.checked) }}\n />\n <span className={css['label']}>{props.label}</span>\n </label>\n <p className={css['hint']}>{props.hint}</p>\n </div>\n )\n}\n\ninterface TextFieldProps {\n label: string\n hint: string\n value: string\n placeholder?: string\n disabled: boolean\n onCommit: (value: string) => void\n}\n\n/** Text field with a local draft so typing survives a non-echoing beat. */\nfunction TextField(props: TextFieldProps): ReactNode {\n const id = useId()\n const [draft, setDraft] = useState(props.value)\n useEffect(() => { setDraft(props.value) }, [props.value])\n return (\n <div className={css['field']}>\n <label className={css['label']} htmlFor={id}>{props.label}</label>\n <input\n id={id}\n className={css['input']}\n type=\"text\"\n value={draft}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => {\n setDraft(event.target.value)\n props.onCommit(event.target.value)\n }}\n />\n <p className={css['hint']}>{props.hint}</p>\n </div>\n )\n}\n\ninterface NumberFieldProps {\n label: string\n hint: string\n /** Copy shown while the draft is not an acceptable integer. */\n invalidHint: string\n /** Schema lower bound (host Config schemastery `.min()`). */\n min: number\n value: number\n disabled: boolean\n onCommit: (value: number) => void\n}\n\n/**\n * Integer field with a local draft: invalid intermediate text (empty,\n * non-numeric, below the schema minimum) shows the invalid hint and commits\n * nothing, so the staged value can never leave the schema's domain.\n */\nfunction NumberField(props: NumberFieldProps): ReactNode {\n const id = useId()\n const [draft, setDraft] = useState(String(props.value))\n const [invalid, setInvalid] = useState(false)\n useEffect(() => {\n setDraft(String(props.value))\n setInvalid(false)\n }, [props.value])\n return (\n <div className={css['field']}>\n <label className={css['label']} htmlFor={id}>{props.label}</label>\n <input\n id={id}\n className={invalid ? `${css['input']} ${css['inputInvalid']}` : css['input']}\n type=\"text\"\n inputMode=\"numeric\"\n value={draft}\n disabled={props.disabled}\n {...invalid ? { 'aria-invalid': true } : {}}\n onChange={(event) => {\n const text = event.target.value\n setDraft(text)\n const parsed = Number(text)\n const acceptable = text.trim() !== '' && Number.isInteger(parsed) && parsed >= props.min\n setInvalid(!acceptable)\n if (acceptable && parsed !== props.value) props.onCommit(parsed)\n }}\n />\n {invalid\n ? <p className={css['invalidHint']} role=\"alert\">{props.invalidHint}</p>\n : <p className={css['hint']}>{props.hint}</p>}\n </div>\n )\n}\n\n/**\n * Render the Agent Sidecar settings card.\n * @param props - staged values, form state, and the wiring callbacks.\n * @returns the card.\n */\nexport function SettingsCard(props: SettingsCardProps): ReactNode {\n const [open, setOpen] = useState(false)\n const t = props.t ?? defaultT\n const { values } = props\n const disabled = !props.writable || props.saving\n const title = t('settings.cardTitle')\n\n const daemonNote = props.daemon?.state === 'defer'\n ? t('settings.daemonDeferNote')\n : props.daemon?.state === 'failed'\n ? t('settings.daemonFailedNote')\n : undefined\n\n return (\n <li className={`${css['card']} ${open ? css['cardOpen'] : ''}`}>\n <button\n type=\"button\"\n className={css['header']}\n aria-expanded={open}\n aria-label={`${t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css['headText']}>\n <span className={css['name']}>{title}</span>\n <span className={css['description']}>{t('settings.cardDescription')}</span>\n </span>\n {props.dirty ? <span className={css['pending']}>{t('settings.unsaved')}</span> : null}\n <span className={`${css['chevron']} ${open ? css['chevronOpen'] : ''}`} aria-hidden />\n </button>\n {open\n ? (\n <div className={css['body']}>\n {!props.writable\n ? <p className={css['readOnly']} role=\"status\">{t('settings.readOnly')}</p>\n : null}\n\n <Section title={t('settings.sectionDaemon')}>\n {props.daemon\n ? (\n <div className={css['field']}>\n <span className={css['label']}>{t('settings.daemonStatusLabel')}</span>\n <div className={css['statusRow']}>\n <span className={statusDotClass(props.daemon.state)} aria-hidden />\n <span className={css['statusText']}>\n {t(DAEMON_STATE_KEY[props.daemon.state])}\n </span>\n {props.daemon.pid !== undefined && props.daemon.version !== undefined\n ? (\n <span className={css['statusMeta']}>\n {t('settings.daemonPidVersion', {\n pid: props.daemon.pid,\n version: props.daemon.version,\n })}\n </span>\n )\n : null}\n {props.daemon.state === 'failed' && props.onDaemonRetry !== undefined\n ? (\n <button\n type=\"button\"\n className={css['retry']}\n onClick={props.onDaemonRetry}\n >\n {t('settings.daemonRetry')}\n </button>\n )\n : null}\n </div>\n {daemonNote !== undefined\n ? <p className={css['note']}>{daemonNote}</p>\n : null}\n </div>\n )\n : null}\n <SelectField\n label={t('settings.daemonPolicyLabel')}\n hint={t('settings.daemonPolicyHint')}\n value={values.daemonPolicy}\n disabled={disabled}\n options={[\n { value: 'adopt-or-host', label: t('settings.daemonPolicyAdoptOrHost') },\n { value: 'adopt-only', label: t('settings.daemonPolicyAdoptOnly') },\n { value: 'off', label: t('settings.daemonPolicyOff') },\n ]}\n onCommit={(value) => {\n props.onChange('daemonPolicy', value as SettingsCardValues['daemonPolicy'])\n }}\n />\n <NumberField\n label={t('settings.daemonBackoffLimitLabel')}\n hint={t('settings.daemonBackoffLimitHint')}\n invalidHint={t('settings.invalidNumber', { min: 1 })}\n min={1}\n value={values.daemonBackoffLimit}\n disabled={disabled}\n onCommit={(value) => { props.onChange('daemonBackoffLimit', value) }}\n />\n </Section>\n\n <Section title={t('settings.sectionSidecar')}>\n <TextField\n label={t('settings.sidecarCommandLabel')}\n hint={t('settings.sidecarCommandHint')}\n value={values.sidecarCommand}\n placeholder=\"agent-sidecar\"\n disabled={disabled}\n onCommit={(value) => { props.onChange('sidecarCommand', value) }}\n />\n <TextField\n label={t('settings.sidecarRuntimeDirLabel')}\n hint={t('settings.sidecarRuntimeDirHint')}\n value={values.sidecarRuntimeDir}\n placeholder=\"~/.agent_sidecar\"\n disabled={disabled}\n onCommit={(value) => { props.onChange('sidecarRuntimeDir', value) }}\n />\n </Section>\n\n <Section title={t('settings.sectionStream')}>\n <NumberField\n label={t('settings.streamActiveMsLabel')}\n hint={t('settings.streamActiveMsHint')}\n invalidHint={t('settings.invalidNumber', { min: 100 })}\n min={100}\n value={values.streamReconcileActiveMs}\n disabled={disabled}\n onCommit={(value) => { props.onChange('streamReconcileActiveMs', value) }}\n />\n <NumberField\n label={t('settings.streamIdleMsLabel')}\n hint={t('settings.streamIdleMsHint')}\n invalidHint={t('settings.invalidNumber', { min: 100 })}\n min={100}\n value={values.streamReconcileIdleMs}\n disabled={disabled}\n onCommit={(value) => { props.onChange('streamReconcileIdleMs', value) }}\n />\n </Section>\n\n <Section title={t('settings.sectionInject')}>\n <p className={css['note']}>{t('settings.injectSafetyNote')}</p>\n <ToggleField\n label={t('settings.injectEnabledLabel')}\n hint={t('settings.injectEnabledHint')}\n checked={values.injectEnabled}\n disabled={disabled}\n onCommit={(checked) => { props.onChange('injectEnabled', checked) }}\n />\n <SelectField\n label={t('settings.injectDefaultModeLabel')}\n hint={t('settings.injectDefaultModeHint')}\n value={values.injectDefaultMode}\n disabled={disabled}\n options={[\n { value: 'queue', label: t('settings.injectModeQueue') },\n { value: 'steer', label: t('settings.injectModeSteer') },\n ]}\n onCommit={(value) => {\n props.onChange('injectDefaultMode', value as SettingsCardValues['injectDefaultMode'])\n }}\n />\n </Section>\n\n <Section title={t('settings.sectionAnalysis')}>\n <ToggleField\n label={t('settings.analysisEnabledLabel')}\n hint={t('settings.analysisEnabledHint')}\n checked={values.analysisEnabled}\n disabled={disabled}\n onCommit={(checked) => { props.onChange('analysisEnabled', checked) }}\n />\n </Section>\n\n <Section title={t('settings.sectionUi')}>\n <NumberField\n label={t('settings.uiTimeWindowHoursLabel')}\n hint={t('settings.uiTimeWindowHoursHint')}\n invalidHint={t('settings.invalidNumber', { min: 1 })}\n min={1}\n value={values.uiTimeWindowHours}\n disabled={disabled}\n onCommit={(value) => { props.onChange('uiTimeWindowHours', value) }}\n />\n <ToggleField\n label={t('settings.uiShowDeadLabel')}\n hint={t('settings.uiShowDeadHint')}\n checked={values.uiShowDead}\n disabled={disabled}\n onCommit={(checked) => { props.onChange('uiShowDead', checked) }}\n />\n </Section>\n\n <Section title={t('settings.sectionSkill')}>\n <ToggleField\n label={t('settings.skillProvideLabel')}\n hint={t('settings.skillProvideHint')}\n checked={values.skillProvide}\n disabled={disabled}\n onCommit={(checked) => { props.onChange('skillProvide', checked) }}\n />\n </Section>\n\n <div className={css['footer']}>\n {props.docsUrl !== undefined\n ? (\n <a\n className={css['docs']}\n href={props.docsUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {t('settings.docsLink')}\n </a>\n )\n : null}\n {props.saveFailed === true\n ? <p className={css['failed']} role=\"status\">{t('settings.saveFailed')}</p>\n : <span className={css['spacer']} />}\n <button\n type=\"button\"\n className={css['discard']}\n disabled={!props.dirty || props.saving}\n onClick={props.onDiscard}\n >\n {t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css['save']}\n disabled={!props.dirty || props.saving || !props.writable}\n onClick={props.onSave}\n >\n {t(props.saving ? 'settings.saving' : 'settings.save')}\n </button>\n </div>\n </div>\n )\n : null}\n </li>\n )\n}\n","/**\n * Pure view-model logic for the session-detail timeline view (design §5.1\n * view 2, §5.3 honest presentation). No React, no I/O, no imports from the\n * data layer — everything arrives and leaves as plain values, so this whole\n * module is unit-testable in a bare node environment (same posture as\n * board/logic.ts and inject/logic.ts).\n *\n * Decoupling contract (T5.3 ↔ S7 integration): the `*Wire` types below are\n * this module's OWN hand-written mirrors of the host M3 response shapes\n * (src/routes.ts `timelineBody` over src/fusion.ts `TimelinePage`) — the\n * client TS program cannot import host modules. The integration layer owns\n * transport and feeds pages in; this module owns accumulation, dedup,\n * ordering, gap detection and presentation derivation.\n *\n * Honesty invariants implemented here (design §4.b.3 / §5.3):\n * - a seq discontinuity between consecutive seq-carrying entries inserts a\n * visible gap marker row (「缺口:可能有 N 条事件未捕获」); seq-less\n * entries are skipped by the detector and can never fake or mask a gap;\n * - page provenance (`sources`) is surfaced as badges, never dropped;\n * - listen-mode merges only ever append facts (dedup by entry identity),\n * and newly appended entries are reported for highlight, not invented.\n *\n * @module\n */\n\nimport { DETAIL_STRINGS } from './strings.ts'\n\n// ---------------------------------------------------------------------------\n// Wire mirrors (host source of truth: routes.ts timelineBody / fusion.ts).\n// ---------------------------------------------------------------------------\n\n/** One merged timeline entry as serialized by the host (fusion.ts `TimelineEntry`). */\nexport interface TimelineEntryWire {\n origin: 'dsh' | 'sidecar'\n /** dsh log seq (native or mirrored via sidecar `extra.seq`); null when unknown. */\n seq: number | null\n /** Unix epoch milliseconds. */\n ts: number\n /** dsh native event type for dsh entries, normalized kind for sidecar entries. */\n kind: string\n /** Normalized sidecar text; `''` for dsh entries without a sidecar twin. */\n text: string\n /** Raw dsh event data; JSON drops it entirely for sidecar-only entries. */\n data?: unknown\n extra: Record<string, unknown> | null\n}\n\n/** Decoded cursor object echoed next to the opaque token (routes.ts). */\nexport interface TimelineCursorWire {\n seq: number | null\n ts: number\n}\n\n/** Which sources contributed to a page (fusion.ts `TimelineSources`). */\nexport interface TimelineSourcesWire {\n dshLive: boolean\n dshCold: boolean\n sidecarReplay: boolean\n sidecarBuffer: boolean\n}\n\n/**\n * One timeline page as answered by `GET session/<id>` (nested) and\n * `GET session/<id>/timeline` (routes.ts `timelineBody`). `nextCursor` is\n * the opaque pagination token and must be round-tripped verbatim.\n */\nexport interface TimelinePageWire {\n sessionId: string\n entries: TimelineEntryWire[]\n cursor: TimelineCursorWire | null\n nextCursor: string | null\n sources: TimelineSourcesWire\n}\n\n// ---------------------------------------------------------------------------\n// Small shared helpers.\n// ---------------------------------------------------------------------------\n\nconst MINUTE_MS = 60_000\nconst HOUR_MS = 3_600_000\nconst DAY_MS = 86_400_000\nconst KEY_SEP = '\\u0000'\n\n/** Resolve `{name}` placeholders in a message template. */\nexport function formatTemplate(\n template: string,\n params: Record<string, string | number>,\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n const value = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n/**\n * Coarse relative time: <60s (including clock skew into the future) is\n * 刚刚, then whole minutes/hours/days. Non-finite input renders empty.\n */\nexport function formatRelativeTime(thenMs: number, nowMs: number): string {\n if (!Number.isFinite(thenMs)) return ''\n const delta = nowMs - thenMs\n if (delta < MINUTE_MS) return DETAIL_STRINGS.time.justNow\n if (delta < HOUR_MS) {\n return formatTemplate(DETAIL_STRINGS.time.minutesAgo, { n: Math.floor(delta / MINUTE_MS) })\n }\n if (delta < DAY_MS) {\n return formatTemplate(DETAIL_STRINGS.time.hoursAgo, { n: Math.floor(delta / HOUR_MS) })\n }\n return formatTemplate(DETAIL_STRINGS.time.daysAgo, { n: Math.floor(delta / DAY_MS) })\n}\n\n// ---------------------------------------------------------------------------\n// Event-kind classification (icon + label).\n// ---------------------------------------------------------------------------\n\n/** Normalized event-kind vocabulary; anything unrecognized maps to 'other'. */\nexport type TimelineKindToken =\n | 'user'\n | 'assistant'\n | 'thinking'\n | 'toolCall'\n | 'toolResult'\n | 'turn'\n | 'step'\n | 'error'\n | 'other'\n\nconst KIND_GLYPHS: Record<TimelineKindToken, string> = {\n user: '▷',\n assistant: '◁',\n thinking: '…',\n toolCall: '⚙',\n toolResult: '↩',\n turn: '§',\n step: '·',\n error: '✕',\n other: '•',\n}\n\n/**\n * Map a raw event kind onto the glyph/label vocabulary. Covers both the\n * sidecar normalized kinds — user/assistant/thinking/tool_call/tool_result\n * plus the turn_ and step_ prefixes (sidecar/model.py) — and dsh native\n * slash-path types (`message/user` style); everything else is honestly\n * 'other'.\n */\nexport function classifyKind(kind: string): TimelineKindToken {\n const k = kind.trim().toLowerCase()\n const last = k.includes('/') ? (k.split('/').pop() ?? k) : k\n if (last === 'user') return 'user'\n if (last === 'assistant') return 'assistant'\n if (last === 'thinking' || last === 'reasoning') return 'thinking'\n if (last === 'tool_call' || last === 'tool-call' || last === 'toolcall') return 'toolCall'\n if (last === 'tool_result' || last === 'tool-result' || last === 'toolresult') {\n return 'toolResult'\n }\n if (last.startsWith('turn_') || last === 'turn') return 'turn'\n if (last.startsWith('step_') || last === 'step') return 'step'\n if (last === 'error') return 'error'\n return 'other'\n}\n\n/** Glyph for a kind token. */\nexport function kindGlyph(token: TimelineKindToken): string {\n return KIND_GLYPHS[token]\n}\n\n/**\n * Display label: the Chinese vocabulary for recognized kinds; unknown kinds\n * keep their raw text (the view never invents a category, design §5.3).\n */\nexport function kindLabel(token: TimelineKindToken, rawKind: string): string {\n if (token === 'other') {\n const trimmed = rawKind.trim()\n return trimmed === '' ? DETAIL_STRINGS.kind.other : trimmed\n }\n return DETAIL_STRINGS.kind[token]\n}\n\n// ---------------------------------------------------------------------------\n// Entry normalization.\n// ---------------------------------------------------------------------------\n\n/** One-line summary cap (characters, not bytes — display concern only). */\nexport const SUMMARY_MAX_CHARS = 120\n/** Expanded-body cap so a single pathological entry cannot freeze the tab. */\nexport const BODY_MAX_CHARS = 4_000\n\n/** One normalized, render-ready timeline entry. */\nexport interface TimelineEntryVM {\n /** Stable identity for dedup and React keys (see {@link entryKey}). */\n key: string\n origin: 'dsh' | 'sidecar'\n seq: number | null\n /** Unix epoch ms. */\n ts: number\n /** Raw wire kind (hover/debug). */\n kindRaw: string\n kind: TimelineKindToken\n glyph: string\n label: string\n /** First line of the body, truncated to {@link SUMMARY_MAX_CHARS}. */\n summary: string\n /** Expanded body (full text or pretty-printed dsh data); null when the summary says it all. */\n body: string | null\n expandable: boolean\n}\n\n/**\n * Dedup identity of one wire entry within a session timeline. Seq-carrying\n * entries collapse on seq+kind+text — NOT seq alone: one dsh record can\n * normalize into several sibling events sharing one seq (reasoning+text\n * blocks, multi-block messages), and a seq-only key would silently fold\n * them away (F1). Same rule as fusion.ts `sidecarEventKey`, so a client\n * multi-page merge matches the host's single-page merge. Seq-less entries\n * fall back to ts+kind+text.\n */\nexport function entryKey(entry: {\n seq: number | null\n ts: number\n kind: string\n text: string\n}): string {\n return entry.seq !== null\n ? `s:${entry.seq}${KEY_SEP}${entry.kind}${KEY_SEP}${entry.text}`\n : `t:${entry.ts}${KEY_SEP}${entry.kind}${KEY_SEP}${entry.text}`\n}\n\nconst ELLIPSIS = '…'\n\nfunction truncateChars(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max)}${ELLIPSIS}`\n}\n\n/** Best-effort primary text of an entry: normalized text, else common data fields. */\nfunction extractBaseText(entry: TimelineEntryWire): string {\n if (entry.text !== '') return entry.text\n const data = entry.data\n if (typeof data === 'string') return data\n if (typeof data === 'object' && data !== null && !Array.isArray(data)) {\n const record = data as Record<string, unknown>\n for (const field of ['text', 'title', 'content', 'message']) {\n const value = record[field]\n if (typeof value === 'string' && value !== '') return value\n }\n }\n return ''\n}\n\nfunction safePrettyJson(value: unknown): string | null {\n try {\n const json = JSON.stringify(value, null, 2)\n return typeof json === 'string' ? json : null\n } catch {\n return null\n }\n}\n\n/**\n * Wire entry → render-ready view model. Summary is the first line of the\n * best-effort text; the expandable body is the full text when the summary\n * truncated it, else the pretty-printed dsh `data` payload when one exists\n * (both bounded by {@link BODY_MAX_CHARS}).\n */\nexport function normalizeTimelineEntry(entry: TimelineEntryWire): TimelineEntryVM {\n const token = classifyKind(entry.kind)\n const baseText = extractBaseText(entry)\n const firstLine = baseText.split('\\n', 1)[0] ?? ''\n const summary = truncateChars(firstLine.trim(), SUMMARY_MAX_CHARS)\n\n let body: string | null = null\n if (baseText.trim() !== '' && baseText.trim() !== summary) {\n body = truncateChars(baseText, BODY_MAX_CHARS)\n } else if (entry.data !== undefined) {\n const json = safePrettyJson(entry.data)\n if (json !== null && json !== summary) body = truncateChars(json, BODY_MAX_CHARS)\n }\n\n return {\n key: entryKey(entry),\n origin: entry.origin,\n seq: entry.seq,\n ts: entry.ts,\n kindRaw: entry.kind,\n kind: token,\n glyph: kindGlyph(token),\n label: kindLabel(token, entry.kind),\n summary,\n body,\n expandable: body !== null,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Ordering (mirrors fusion.ts mergeTimeline so client order == host order).\n// ---------------------------------------------------------------------------\n\n/**\n * Sort entries the way the host merges them: seq-carrying entries keep\n * exact seq order among themselves, seq-less entries sort by ts and are\n * interleaved by ts (tie: seq domain first). This keeps a client-side\n * multi-page merge byte-identical to what one giant host page would be.\n * Within one seq (sibling block events of one dsh record) the dsh entry\n * sorts first — mirroring the host merge — and sidecar siblings keep\n * their arrival order (the sort is stable).\n */\nexport function sortTimelineEntries(entries: readonly TimelineEntryVM[]): TimelineEntryVM[] {\n const seqDomain = entries.filter((e) => e.seq !== null)\n const unseqed = entries.filter((e) => e.seq === null)\n seqDomain.sort(\n (a, b) =>\n (a.seq ?? 0) - (b.seq ?? 0) ||\n (a.origin === b.origin ? 0 : a.origin === 'dsh' ? -1 : 1),\n )\n unseqed.sort((a, b) => a.ts - b.ts || a.key.localeCompare(b.key))\n\n const out: TimelineEntryVM[] = []\n let i = 0\n let j = 0\n for (;;) {\n const a = seqDomain[i]\n const b = unseqed[j]\n if (a === undefined && b === undefined) break\n if (b === undefined || (a !== undefined && a.ts <= b.ts)) {\n if (a !== undefined) {\n out.push(a)\n i += 1\n }\n } else {\n out.push(b)\n j += 1\n }\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Timeline accumulation state (owned by the integration layer, fed to the\n// component as the `timeline` prop).\n// ---------------------------------------------------------------------------\n\n/**\n * Accumulated timeline state across pages and listen updates. Plain data:\n * every mutation helper returns a fresh object (React-state friendly).\n */\nexport interface TimelineVM {\n sessionId: string\n /** Ascending (host merge order), deduplicated, normalized. */\n entries: TimelineEntryVM[]\n /** Union of the provenance flags of every merged page. */\n sources: TimelineSourcesWire\n /**\n * Opaque pagination token of the oldest merged history page; null when\n * the log start was reached (or nothing was loaded yet). Round-trip it\n * verbatim to `GET session/<id>/timeline?cursor=`.\n */\n nextCursor: string | null\n /** True once the log start was reached (nextCursor exhausted). */\n reachedStart: boolean\n /** Keys appended by the most recent listen-mode merge (highlight set). */\n newKeys: readonly string[]\n}\n\n/** Fresh empty state for a session. */\nexport function createTimelineVM(sessionId: string): TimelineVM {\n return {\n sessionId,\n entries: [],\n sources: { dshLive: false, dshCold: false, sidecarReplay: false, sidecarBuffer: false },\n nextCursor: null,\n reachedStart: false,\n newKeys: [],\n }\n}\n\nfunction unionSources(a: TimelineSourcesWire, b: TimelineSourcesWire): TimelineSourcesWire {\n return {\n dshLive: a.dshLive || b.dshLive,\n dshCold: a.dshCold || b.dshCold,\n sidecarReplay: a.sidecarReplay || b.sidecarReplay,\n sidecarBuffer: a.sidecarBuffer || b.sidecarBuffer,\n }\n}\n\ninterface MergeOutcome {\n entries: TimelineEntryVM[]\n appendedKeys: string[]\n}\n\n/** (seq, kind) twin-slot sub-key of the convergence maps in mergeEntries. */\nfunction seqKindKey(seq: number, kind: string): string {\n return `${seq}${KEY_SEP}${kind}`\n}\n\n/** The key an un-supplemented dsh entry (empty wire text) carries. */\nfunction emptyTextKey(seq: number, kind: string): string {\n return `s:${seq}${KEY_SEP}${kind}${KEY_SEP}`\n}\n\n/**\n * Dedup-by-key merge, then host-order sort.\n *\n * Cross-page convergence: the host folds the first sidecar twin's text\n * into the matching dsh entry, so the SAME event can arrive with\n * `text: ''` in one page (twin not yet observed) and with the folded\n * text in a later one — two different keys for one event. A text-carrying\n * arrival therefore upgrades the empty-text (seq, kind) slot in place,\n * and an empty-text arrival for an already-supplemented (seq, kind) is\n * dropped as stale. This keeps the accumulated client merge equal to the\n * host's latest single-page merge instead of duplicating the entry.\n * Genuine same-seq siblings always carry distinct kind/text and are\n * untouched by the rule.\n */\nfunction mergeEntries(\n existing: readonly TimelineEntryVM[],\n incoming: readonly TimelineEntryWire[],\n): MergeOutcome {\n const entries = [...existing]\n const seen = new Set(entries.map((e) => e.key))\n const emptySlot = new Map<string, number>()\n const filled = new Set<string>()\n for (let i = 0; i < entries.length; i += 1) {\n const e = entries[i]\n if (e === undefined || e.seq === null) continue\n const sk = seqKindKey(e.seq, e.kindRaw)\n if (e.key === emptyTextKey(e.seq, e.kindRaw)) emptySlot.set(sk, i)\n else filled.add(sk)\n }\n\n const appendedKeys: string[] = []\n let changed = false\n for (const wire of incoming) {\n const vm = normalizeTimelineEntry(wire)\n if (seen.has(vm.key)) continue\n if (vm.seq !== null) {\n const sk = seqKindKey(vm.seq, vm.kindRaw)\n if (wire.text === '') {\n if (filled.has(sk)) continue\n emptySlot.set(sk, entries.length)\n } else {\n filled.add(sk)\n const slot = emptySlot.get(sk)\n if (slot !== undefined) {\n entries[slot] = vm\n emptySlot.delete(sk)\n seen.add(vm.key)\n appendedKeys.push(vm.key)\n changed = true\n continue\n }\n }\n }\n seen.add(vm.key)\n entries.push(vm)\n appendedKeys.push(vm.key)\n changed = true\n }\n if (!changed) return { entries, appendedKeys: [] }\n return { entries: sortTimelineEntries(entries), appendedKeys }\n}\n\n/**\n * Merge one HISTORY page (the initial newest page, or an older page fetched\n * via `nextCursor`) into the state. Advances the pagination token to the\n * page's own `nextCursor` and never marks entries as new (paging back is\n * not fresh activity). Duplicate entries across overlapping pages dedup on\n * {@link entryKey}.\n */\nexport function applyTimelinePage(vm: TimelineVM, page: TimelinePageWire): TimelineVM {\n const merged = mergeEntries(vm.entries, page.entries)\n return {\n ...vm,\n entries: merged.entries,\n sources: unionSources(vm.sources, page.sources),\n nextCursor: page.nextCursor,\n reachedStart: page.nextCursor === null,\n newKeys: vm.newKeys,\n }\n}\n\n/**\n * Merge one LISTEN update (the newest page refetched after an SSE `state`\n * signal — stream snapshots carry no per-session events, so listen mode is\n * snapshot-trigger + timeline-refetch, ADR-2/ADR-3). Appended entries are\n * reported in `newKeys` for highlight; replays of already-known entries\n * dedup silently and do NOT re-highlight. The pagination token is left\n * alone: a newest-window page must never reset the older-history cursor.\n */\nexport function applyListenPage(vm: TimelineVM, page: TimelinePageWire): TimelineVM {\n const merged = mergeEntries(vm.entries, page.entries)\n return {\n ...vm,\n entries: merged.entries,\n sources: unionSources(vm.sources, page.sources),\n newKeys: merged.appendedKeys,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Gap detection + row derivation.\n// ---------------------------------------------------------------------------\n\n/** One rendered timeline row: a real event or an honesty gap marker. */\nexport type TimelineRowVM =\n | {\n type: 'event'\n key: string\n entry: TimelineEntryVM\n relativeTime: string\n /** Hover title: ISO timestamp + raw kind + origin (+ seq). */\n hoverTitle: string\n /** True when this entry arrived in the latest listen merge. */\n isNew: boolean\n }\n | {\n type: 'gap'\n key: string\n /** Lower bound of dropped events implied by the seq break. */\n missingCount: number\n label: string\n }\n\nfunction isoOrEmpty(ts: number): string {\n if (!Number.isFinite(ts)) return ''\n try {\n return new Date(ts).toISOString()\n } catch {\n return ''\n }\n}\n\nfunction eventHoverTitle(entry: TimelineEntryVM): string {\n const parts = [isoOrEmpty(entry.ts), entry.kindRaw, entry.origin]\n if (entry.seq !== null) {\n parts.push(formatTemplate(DETAIL_STRINGS.timeline.seq, { n: entry.seq }))\n }\n return parts.filter((p) => p !== '').join(' · ')\n}\n\n/** Gap marker text: 「缺口:可能有 N 条事件未捕获(256 队列上限或未持久化)」. */\nexport function gapLabel(missingCount: number): string {\n return formatTemplate(DETAIL_STRINGS.gap.label, { n: missingCount })\n}\n\n/**\n * Derive render rows from the accumulated state, inserting a gap marker\n * wherever consecutive seq-carrying entries jump by more than 1 (honest\n * presentation of the 256-slot subscribe queue drop / unpersisted events,\n * design §4.b.3). Seq-less entries are transparent to the detector: they\n * neither trigger a gap nor reset the last-seen seq, so mixed timelines\n * cannot produce false positives. Nothing is inserted before the first\n * seq entry — unloaded older history is pagination, not a gap.\n */\nexport function buildTimelineRows(vm: TimelineVM, nowMs: number): TimelineRowVM[] {\n const newKeys = new Set(vm.newKeys)\n const rows: TimelineRowVM[] = []\n let lastSeq: number | null = null\n for (const entry of vm.entries) {\n if (entry.seq !== null) {\n if (lastSeq !== null && entry.seq > lastSeq + 1) {\n const missing = entry.seq - lastSeq - 1\n rows.push({\n type: 'gap',\n key: `gap:${lastSeq}-${entry.seq}`,\n missingCount: missing,\n label: gapLabel(missing),\n })\n }\n lastSeq = entry.seq\n }\n rows.push({\n type: 'event',\n key: entry.key,\n entry,\n relativeTime: formatRelativeTime(entry.ts, nowMs),\n hoverTitle: eventHoverTitle(entry),\n isNew: newKeys.has(entry.key),\n })\n }\n return rows\n}\n\n// ---------------------------------------------------------------------------\n// Segmented rendering bound (perf stopgap; no full virtualization, see task\n// report — page sizes already bound growth, this bounds pathological cases).\n// ---------------------------------------------------------------------------\n\n/** Default cap of rendered rows; older rows collapse behind a notice. */\nexport const DEFAULT_MAX_RENDER_ROWS = 400\n\nexport interface LimitedRows {\n rows: TimelineRowVM[]\n /** Rows hidden from the top (oldest side); 0 when nothing was cut. */\n hiddenCount: number\n /** Collapse notice text; null when nothing was cut. */\n notice: string | null\n}\n\n/**\n * Keep only the newest `max` rows (the tail — listen mode appends there).\n * The data stays in the VM; this is purely a render bound the component\n * can lift via its「全部显示」toggle.\n */\nexport function limitTimelineRows(\n rows: readonly TimelineRowVM[],\n max: number = DEFAULT_MAX_RENDER_ROWS,\n): LimitedRows {\n if (!Number.isFinite(max) || max <= 0 || rows.length <= max) {\n return { rows: [...rows], hiddenCount: 0, notice: null }\n }\n const hiddenCount = rows.length - max\n return {\n rows: rows.slice(hiddenCount),\n hiddenCount,\n notice: formatTemplate(DETAIL_STRINGS.timeline.hiddenNotice, { n: hiddenCount }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Source badges.\n// ---------------------------------------------------------------------------\n\n/** Color-token buckets the CSS maps to `--dsw-alias-*` variables. */\nexport type BadgeTone = 'success' | 'warn' | 'neutral' | 'muted' | 'danger'\n\nexport interface SourceBadgeVM {\n id: keyof TimelineSourcesWire\n label: string\n tone: BadgeTone\n}\n\nconst SOURCE_ORDER: ReadonlyArray<{ id: keyof TimelineSourcesWire; tone: BadgeTone }> = [\n { id: 'dshLive', tone: 'success' },\n { id: 'dshCold', tone: 'neutral' },\n { id: 'sidecarReplay', tone: 'neutral' },\n { id: 'sidecarBuffer', tone: 'muted' },\n]\n\n/**\n * Provenance badges for the header, stable order: dsh 实时 → dsh 冷读 →\n * sidecar 重放 → sidecar 缓冲. Only contributing sources appear; an\n * all-false set yields an empty list (the empty state explains itself).\n */\nexport function deriveSourceBadges(sources: TimelineSourcesWire): SourceBadgeVM[] {\n const out: SourceBadgeVM[] = []\n for (const { id, tone } of SOURCE_ORDER) {\n if (sources[id]) out.push({ id, label: DETAIL_STRINGS.sources[id], tone })\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Header status badge.\n// ---------------------------------------------------------------------------\n\n/** Normalized session status vocabulary; anything else maps to 'unknown'. */\nexport type SessionStatusToken = 'working' | 'waiting' | 'idle' | 'dead' | 'unknown'\n\nconst KNOWN_STATUSES: readonly SessionStatusToken[] = ['working', 'waiting', 'idle', 'dead']\n\nconst STATUS_TONE: Record<SessionStatusToken, BadgeTone> = {\n working: 'success',\n waiting: 'warn',\n idle: 'neutral',\n unknown: 'neutral',\n dead: 'muted',\n}\n\nexport interface DetailStatusVM {\n status: SessionStatusToken\n tone: BadgeTone\n label: string\n}\n\n/**\n * Raw observed status → badge. Unknown raw statuses keep their raw text as\n * the label (the view never invents a state); empty raw text reads 未知.\n */\nexport function deriveDetailStatus(rawStatus: string): DetailStatusVM {\n const cleaned = rawStatus.trim().toLowerCase()\n const status = (KNOWN_STATUSES as readonly string[]).includes(cleaned)\n ? (cleaned as SessionStatusToken)\n : 'unknown'\n const trimmed = rawStatus.trim()\n const label =\n status === 'unknown'\n ? trimmed === ''\n ? DETAIL_STRINGS.status.unknown\n : trimmed\n : DETAIL_STRINGS.status[status]\n return { status, tone: STATUS_TONE[status], label }\n}\n\n/** Single-character agent marker (board vocabulary; unknown → neutral dot). */\nconst AGENT_GLYPHS: Record<string, string> = {\n dsh: '◆',\n claude: '✳',\n codex: '▣',\n cursor: '▮',\n 'cursor-cli': '▮',\n 'cursor-ide': '▮',\n copilot: '◉',\n kimi: '◐',\n}\n\nexport function agentGlyph(agent: string): string {\n return AGENT_GLYPHS[agent.trim().toLowerCase()] ?? '●'\n}\n\n// ---------------------------------------------------------------------------\n// Loading / empty / error body states.\n// ---------------------------------------------------------------------------\n\n/**\n * Friendly text for a machine error reason (ApiError.reason / server\n * `{reason}` codes). Unknown codes fall back to an honest 错误码 template.\n */\nexport function detailErrorText(reason: string): string {\n const table: Record<string, string> = DETAIL_STRINGS.states.errors\n const mapped = table[reason]\n return mapped ?? formatTemplate(DETAIL_STRINGS.states.errorFallback, { reason })\n}\n\nexport type DetailBodyKind = 'loading' | 'error' | 'empty' | 'list'\n\nexport interface DetailBodyStateVM {\n kind: DetailBodyKind\n /** Full-body title/hint for the non-list states; null for 'list'. */\n title: string | null\n hint: string | null\n /** Non-null when stale entries stay visible while a refresh/page failed. */\n errorBanner: string | null\n}\n\n/**\n * Body-state resolution, honest-by-priority:\n * - entries present → always 'list' (never hide data already shown); a\n * concurrent error surfaces as an inline banner instead;\n * - no entries + error → 'error' (mapped text);\n * - no entries + loading → 'loading';\n * - otherwise → 'empty'.\n */\nexport function deriveDetailBodyState(input: {\n loading: boolean\n /** Machine reason code, or null when the last load succeeded. */\n error: string | null\n entryCount: number\n}): DetailBodyStateVM {\n if (input.entryCount > 0) {\n return {\n kind: 'list',\n title: null,\n hint: null,\n errorBanner: input.error === null ? null : detailErrorText(input.error),\n }\n }\n if (input.error !== null) {\n return {\n kind: 'error',\n title: DETAIL_STRINGS.states.errorTitle,\n hint: detailErrorText(input.error),\n errorBanner: null,\n }\n }\n if (input.loading) {\n return {\n kind: 'loading',\n title: DETAIL_STRINGS.states.loadingTitle,\n hint: null,\n errorBanner: null,\n }\n }\n return {\n kind: 'empty',\n title: DETAIL_STRINGS.states.emptyTitle,\n hint: DETAIL_STRINGS.states.emptyHint,\n errorBanner: null,\n }\n}\n","/**\n * Session-detail view: header + merged event timeline (design §5.1 view 2).\n *\n * Presentation-only and fully controlled: no data fetching, no api/sse\n * imports. The integration layer (S7) owns transport and accumulation —\n * it feeds the {@link TimelineVM} built via logic.ts (`applyTimelinePage`\n * for history pages, `applyListenPage` for listen-mode refetches) and\n * handles `onLoadMore` / `onToggleListen`.\n *\n * Long-list posture (task report): no full virtualization — history only\n * grows page-by-page on explicit 加载更多, and rendering is additionally\n * capped at {@link DEFAULT_MAX_RENDER_ROWS} newest rows behind a collapse\n * notice with a 全部显示 escape hatch. View-local concerns (expanded\n * bodies, the lift-cap flag, auto-scroll) are component state; everything\n * else comes through props.\n */\n\nimport { useEffect, useRef, useState, type ReactElement } from 'react'\nimport {\n buildTimelineRows,\n deriveDetailBodyState,\n deriveDetailStatus,\n deriveSourceBadges,\n agentGlyph,\n limitTimelineRows,\n DEFAULT_MAX_RENDER_ROWS,\n type TimelineRowVM,\n type TimelineVM,\n} from './logic.ts'\nimport { DETAIL_STRINGS } from './strings.ts'\nimport styles from './detail.module.css'\n\nexport interface SessionDetailHeaderVM {\n agent: string\n title: string\n project: string\n /** Raw observed status string (open vocabulary, normalized in logic.ts). */\n status: string\n}\n\nexport interface SessionDetailProps {\n sessionId: string\n header: SessionDetailHeaderVM\n /** Accumulated timeline state (logic.ts createTimelineVM/apply* output). */\n timeline: TimelineVM\n /** True while the owner has a fetch in flight (initial or older page). */\n loading: boolean\n /** Machine reason code of the last failure, or null. */\n error: string | null\n /** True when an older history page can still be fetched. */\n hasMore: boolean\n /** Listen mode (SSE-triggered newest-page refetch) currently on. */\n listening: boolean\n onLoadMore: () => void\n onToggleListen: () => void\n onClose?: () => void\n /** Clock injection for deterministic rendering; defaults to Date.now(). */\n nowMs?: number\n /** Render cap override (segmented rendering); mostly for tests/tuning. */\n maxRenderRows?: number\n}\n\nfunction EventRow(props: {\n row: Extract<TimelineRowVM, { type: 'event' }>\n expanded: boolean\n onToggleExpand: (key: string) => void\n}): ReactElement {\n const { row, expanded, onToggleExpand } = props\n const entry = row.entry\n return (\n <li\n className={styles['event']}\n data-kind={entry.kind}\n data-new={row.isNew || undefined}\n data-testid=\"agent-sidecar-detail-event\"\n >\n <div className={styles['eventHead']} title={row.hoverTitle}>\n <span className={styles['eventGlyph']} aria-hidden>\n {entry.glyph}\n </span>\n <span className={styles['eventLabel']}>{entry.label}</span>\n {entry.seq !== null && (\n <span className={styles['eventSeq']}>\n {DETAIL_STRINGS.timeline.seq.replace('{n}', String(entry.seq))}\n </span>\n )}\n {row.isNew && <span className={styles['eventNew']}>{DETAIL_STRINGS.timeline.newBadge}</span>}\n <span className={styles['eventSpacer']} />\n <span className={styles['eventTime']}>{row.relativeTime}</span>\n </div>\n {entry.summary !== '' && <div className={styles['eventSummary']}>{entry.summary}</div>}\n {entry.expandable && (\n <button\n type=\"button\"\n className={styles['expandButton']}\n onClick={() => onToggleExpand(entry.key)}\n >\n {expanded ? DETAIL_STRINGS.timeline.collapse : DETAIL_STRINGS.timeline.expand}\n </button>\n )}\n {entry.expandable && expanded && entry.body !== null && (\n <pre className={styles['eventBody']}>{entry.body}</pre>\n )}\n </li>\n )\n}\n\n/** The session-detail view. Pure render of the logic.ts pipelines over props. */\nexport function SessionDetail(props: SessionDetailProps): ReactElement {\n const nowMs = props.nowMs ?? Date.now()\n const [expandedKeys, setExpandedKeys] = useState<ReadonlySet<string>>(new Set())\n const [renderAll, setRenderAll] = useState(false)\n const listRef = useRef<HTMLOListElement | null>(null)\n\n const status = deriveDetailStatus(props.header.status)\n const sourceBadges = deriveSourceBadges(props.timeline.sources)\n const bodyState = deriveDetailBodyState({\n loading: props.loading,\n error: props.error,\n entryCount: props.timeline.entries.length,\n })\n\n const allRows = buildTimelineRows(props.timeline, nowMs)\n const limited = renderAll\n ? { rows: allRows, hiddenCount: 0, notice: null }\n : limitTimelineRows(allRows, props.maxRenderRows ?? DEFAULT_MAX_RENDER_ROWS)\n\n const entryCount = props.timeline.entries.length\n const listening = props.listening\n useEffect(() => {\n // Listen mode appends at the tail: keep the newest events in view.\n if (!listening) return\n const list = listRef.current\n if (list !== null) list.scrollTop = list.scrollHeight\n }, [listening, entryCount])\n\n const toggleExpand = (key: string): void => {\n setExpandedKeys((prev) => {\n const next = new Set(prev)\n if (next.has(key)) next.delete(key)\n else next.add(key)\n return next\n })\n }\n\n return (\n <div className={styles['root']} data-testid=\"agent-sidecar-detail\">\n <header className={styles['header']}>\n <div className={styles['headerTop']}>\n {props.onClose !== undefined && (\n <button type=\"button\" className={styles['closeButton']} onClick={props.onClose}>\n {DETAIL_STRINGS.header.close}\n </button>\n )}\n <span className={styles['agent']}>\n <span className={styles['agentGlyph']} aria-hidden>\n {agentGlyph(props.header.agent)}\n </span>\n {props.header.agent}\n </span>\n <span className={styles['badge']} data-tone={status.tone} title={DETAIL_STRINGS.header.observedDisclaimer}>\n <span className={styles['dot']} data-tone={status.tone} />\n {status.label}\n </span>\n <span className={styles['spacer']} />\n <button\n type=\"button\"\n className={styles['listenButton']}\n aria-pressed={props.listening}\n data-active={props.listening || undefined}\n title={DETAIL_STRINGS.header.listenHint}\n onClick={props.onToggleListen}\n >\n {props.listening ? DETAIL_STRINGS.header.listenOn : DETAIL_STRINGS.header.listenOff}\n </button>\n </div>\n <div className={styles['title']} title={props.header.title}>\n {props.header.title.trim() === '' ? DETAIL_STRINGS.header.untitled : props.header.title}\n </div>\n <div className={styles['meta']}>\n <span className={styles['project']} title={props.header.project}>\n {props.header.project.trim() === ''\n ? DETAIL_STRINGS.header.unknownProject\n : props.header.project}\n </span>\n <span className={styles['sessionId']} title={props.sessionId}>\n {props.sessionId}\n </span>\n </div>\n <div className={styles['metaRow']}>\n <span className={styles['disclaimer']}>{DETAIL_STRINGS.header.observedDisclaimer}</span>\n {sourceBadges.length > 0 && (\n <span className={styles['sourceList']} title={DETAIL_STRINGS.sources.title}>\n {sourceBadges.map((badge) => (\n <span key={badge.id} className={styles['sourceBadge']} data-tone={badge.tone}>\n {badge.label}\n </span>\n ))}\n </span>\n )}\n </div>\n </header>\n\n {bodyState.errorBanner !== null && (\n <div className={styles['banner']} role=\"status\">\n {bodyState.errorBanner}\n </div>\n )}\n\n {bodyState.kind !== 'list' ? (\n <div className={styles['bodyState']} data-kind={bodyState.kind}>\n <div className={styles['bodyStateTitle']}>{bodyState.title}</div>\n {bodyState.hint !== null && <div className={styles['bodyStateHint']}>{bodyState.hint}</div>}\n </div>\n ) : (\n <>\n <div className={styles['pager']}>\n {props.hasMore ? (\n <button\n type=\"button\"\n className={styles['loadMoreButton']}\n disabled={props.loading}\n onClick={props.onLoadMore}\n >\n {props.loading\n ? DETAIL_STRINGS.timeline.loadingMore\n : DETAIL_STRINGS.timeline.loadMore}\n </button>\n ) : (\n <span className={styles['pagerNote']}>{DETAIL_STRINGS.timeline.noMore}</span>\n )}\n </div>\n {limited.notice !== null && (\n <div className={styles['hiddenNotice']}>\n {limited.notice}\n <button\n type=\"button\"\n className={styles['showAllButton']}\n onClick={() => setRenderAll(true)}\n >\n {DETAIL_STRINGS.timeline.showAll}\n </button>\n </div>\n )}\n <ol className={styles['timeline']} ref={listRef}>\n {limited.rows.map((row) =>\n row.type === 'gap' ? (\n <li\n key={row.key}\n className={styles['gap']}\n role=\"note\"\n data-testid=\"agent-sidecar-detail-gap\"\n >\n {row.label}\n </li>\n ) : (\n <EventRow\n key={row.key}\n row={row}\n expanded={expandedKeys.has(row.key)}\n onToggleExpand={toggleExpand}\n />\n ),\n )}\n </ol>\n </>\n )}\n </div>\n )\n}\n","/**\n * Pure view-model logic for the dsh deep-query tools (design §5.1 view 2\n * dsh 会话专属区 / M3): lineage trace → tree view model, provenance-jump\n * resolution, search-result normalization (snippet highlighting), and the\n * honest-degradation models (§5.3: degradation is presented, never faked).\n *\n * No React, no I/O, no imports from the data layer — everything arrives\n * and leaves as plain values, so this module is unit-testable in a bare\n * node environment (same posture as board/logic.ts and inject/logic.ts).\n *\n * Decoupling contract: the wire-facing types below are this module's OWN\n * hand-written mirrors of the host read contract (task constraint —\n * components never import host code):\n * - {@link LineageResponseVM} mirrors `GET <prefix>/lineage/<id>` — always\n * 200 `{available, trace, reason, detail?}` (routes.ts handleLineage over\n * fusion.ts LineageResult); `available:false` is DATA, not an error.\n * - {@link LineageTraceVM} mirrors fusion.ts DshLineageTraceFace (the\n * `SessionLineageTrace` subset from dsh-session-query traceSession).\n * - {@link SearchResponseVM} mirrors `GET <prefix>/search?q=&project=&limit=`\n * (routes.ts handleSearch over fusion.ts searchSessions): `mode:\n * 'filter-only'` means the sessionQuery full-text engine is unavailable\n * and the backend degraded to title/project filtering.\n *\n * @module\n */\n\nimport { DSH_TOOLS_STRINGS } from './strings.ts'\n\n// ---------------------------------------------------------------------------\n// Small shared helpers.\n// ---------------------------------------------------------------------------\n\n/** Resolve `{name}` placeholders in a message template (board convention). */\nexport function formatTemplate(\n template: string,\n params: Record<string, string | number>,\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n const value = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n/** Head…tail abbreviation for long session ids (same rule as the board). */\nexport function abbreviateId(id: string, max = 20): string {\n if (id.length <= max) return id\n return `${id.slice(0, 12)}…${id.slice(-6)}`\n}\n\n// ---------------------------------------------------------------------------\n// Lineage wire mirrors (host source of truth noted per type).\n// ---------------------------------------------------------------------------\n\n/** `SessionRecord` subset inside a trace (host: fusion.ts DshLineageRecordFace). */\nexport interface LineageRecordVM {\n header: {\n id: string\n createdAt: number\n cwd?: string\n parentSession?: string\n }\n /** True when the session is live in the dsh process. */\n live: boolean\n /** True when dsh has persisted the session log. */\n persisted: boolean\n}\n\n/** One descendant branch (host: fusion.ts DshLineageNodeFace). */\nexport interface LineageBranchVM {\n session: LineageRecordVM\n descendants: readonly LineageBranchVM[]\n}\n\n/** The trace body (host: fusion.ts DshLineageTraceFace ← traceSession). */\nexport interface LineageTraceVM {\n target: LineageRecordVM\n ancestors: readonly LineageRecordVM[]\n descendants: readonly LineageBranchVM[]\n complete: boolean\n root?: LineageRecordVM\n unresolvedParentId?: string\n}\n\n/** Backend degradation vocabulary (host: fusion.ts LineageResult.reason). */\nexport type LineageBackendReason = 'session_query_unavailable' | 'trace_failed'\n\n/**\n * Client-side degradation vocabulary: the backend reasons plus\n * `not_dsh_session`, minted locally by {@link externalLineageFallback} for\n * sessions of non-dsh agents (lineage is a dsh-only capability, so the\n * integration never even calls the backend for them).\n */\nexport type LineageDegradeReason = LineageBackendReason | 'not_dsh_session'\n\n/** Body of `GET lineage/<id>` — always HTTP 200 (host: routes.ts). */\nexport interface LineageResponseVM {\n available: boolean\n trace: LineageTraceVM | null\n reason: LineageBackendReason | null\n detail?: string\n}\n\n// ---------------------------------------------------------------------------\n// Lineage trace → tree view model.\n// ---------------------------------------------------------------------------\n\n/** Position of a node relative to the traced session. */\nexport type LineageRole = 'ancestor' | 'target' | 'descendant'\n\n/** One flattened tree row, ready to render (indent by `depth`). */\nexport interface LineageNodeVM {\n id: string\n role: LineageRole\n roleLabel: string\n /** Indentation level; the outermost ancestor (or the target) is 0. */\n depth: number\n /** Structural tree parent (render edge), null for the outermost row. */\n parentId: string | null\n /** True when deeper rows directly under this one exist (collapse handle). */\n hasChildren: boolean\n /** True when this node IS the session the user is inspecting (highlight). */\n isCurrent: boolean\n isTarget: boolean\n live: boolean\n persisted: boolean\n shortId: string\n cwd: string | null\n createdAt: number\n}\n\n/** The whole lineage tree render model. */\nexport interface LineageTreeVM {\n /** Rows in render order: ancestors root-first, target, descendants DFS. */\n nodes: LineageNodeVM[]\n targetId: string\n complete: boolean\n unresolvedParentId: string | null\n /** Honest incompleteness banner; null when the trace is complete. */\n incompleteNotice: string | null\n nodeCount: number\n maxDepth: number\n}\n\nfunction toNode(\n record: LineageRecordVM,\n role: LineageRole,\n depth: number,\n parentId: string | null,\n currentSessionId: string | null,\n): LineageNodeVM {\n return {\n id: record.header.id,\n role,\n roleLabel: DSH_TOOLS_STRINGS.lineage.role[role],\n depth,\n parentId,\n hasChildren: false,\n isCurrent: currentSessionId !== null && record.header.id === currentSessionId,\n isTarget: role === 'target',\n live: record.live,\n persisted: record.persisted,\n shortId: abbreviateId(record.header.id),\n cwd: record.header.cwd ?? null,\n createdAt: record.header.createdAt,\n }\n}\n\n/**\n * Order the ancestors root-first by walking `header.parentSession` links\n * up from the target. Ancestors the links cannot reach (broken chain —\n * the trace then also carries `complete:false`) are still shown: they sit\n * above the resolved chain in their reported order, since everything the\n * backend lists in `ancestors` IS an ancestor even when the intermediate\n * links are unresolved.\n */\nfunction orderAncestors(trace: LineageTraceVM): LineageRecordVM[] {\n const byId = new Map<string, LineageRecordVM>()\n for (const record of trace.ancestors) byId.set(record.header.id, record)\n const chain: LineageRecordVM[] = [] // nearest-first\n const visited = new Set<string>()\n let cursor = trace.target.header.parentSession\n while (cursor !== undefined && !visited.has(cursor)) {\n const record = byId.get(cursor)\n if (record === undefined) break\n visited.add(cursor)\n chain.push(record)\n cursor = record.header.parentSession\n }\n const leftovers = trace.ancestors.filter((r) => !visited.has(r.header.id))\n return [...leftovers, ...chain.reverse()]\n}\n\n/**\n * Lineage trace → flattened tree view model. Ancestors form the spine\n * (root-most at depth 0), the target follows, and the descendant branches\n * are appended depth-first in reported order. `currentSessionId` marks the\n * highlight (usually the target, but a lineage panel opened from another\n * node keeps whatever the owner is inspecting).\n */\nexport function buildLineageTree(\n trace: LineageTraceVM,\n currentSessionId: string | null,\n): LineageTreeVM {\n const nodes: LineageNodeVM[] = []\n\n const ancestors = orderAncestors(trace)\n let parentId: string | null = null\n let depth = 0\n for (const record of ancestors) {\n nodes.push(toNode(record, 'ancestor', depth, parentId, currentSessionId))\n parentId = record.header.id\n depth += 1\n }\n\n nodes.push(toNode(trace.target, 'target', depth, parentId, currentSessionId))\n const targetDepth = depth\n\n const walk = (branch: LineageBranchVM, branchDepth: number, branchParent: string): void => {\n nodes.push(\n toNode(branch.session, 'descendant', branchDepth, branchParent, currentSessionId),\n )\n for (const child of branch.descendants) {\n walk(child, branchDepth + 1, branch.session.header.id)\n }\n }\n for (const branch of trace.descendants) {\n walk(branch, targetDepth + 1, trace.target.header.id)\n }\n\n // hasChildren over the flattened DFS order: a row has children exactly\n // when the next row is one level deeper.\n let maxDepth = 0\n for (let i = 0; i < nodes.length; i += 1) {\n const node = nodes[i]\n if (node === undefined) continue\n const next = nodes[i + 1]\n node.hasChildren = next !== undefined && next.depth > node.depth\n if (node.depth > maxDepth) maxDepth = node.depth\n }\n\n const unresolvedParentId = trace.unresolvedParentId ?? null\n const incompleteNotice = trace.complete\n ? null\n : unresolvedParentId !== null\n ? formatTemplate(DSH_TOOLS_STRINGS.lineage.incompleteWithId, {\n id: abbreviateId(unresolvedParentId),\n })\n : DSH_TOOLS_STRINGS.lineage.incomplete\n\n return {\n nodes,\n targetId: trace.target.header.id,\n complete: trace.complete,\n unresolvedParentId,\n incompleteNotice,\n nodeCount: nodes.length,\n maxDepth,\n }\n}\n\n/**\n * Apply component-owned collapse state over the flattened rows: a\n * collapsed row keeps itself but hides every deeper row until the DFS\n * order returns to its level.\n */\nexport function visibleLineageNodes(\n nodes: readonly LineageNodeVM[],\n collapsedIds: ReadonlySet<string>,\n): LineageNodeVM[] {\n const out: LineageNodeVM[] = []\n let hideDeeperThan: number | null = null\n for (const node of nodes) {\n if (hideDeeperThan !== null) {\n if (node.depth > hideDeeperThan) continue\n hideDeeperThan = null\n }\n out.push(node)\n if (node.hasChildren && collapsedIds.has(node.id)) {\n hideDeeperThan = node.depth\n }\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Provenance jump (点击谱系节点 → onSelectSession(id)).\n// ---------------------------------------------------------------------------\n\n/** What a click on a lineage node should do. */\nexport type LineageJump =\n | { kind: 'select'; sessionId: string }\n /** The node is the session already on screen — highlight, no navigation. */\n | { kind: 'current' }\n\n/** Resolve the jump target of a lineage-node click. */\nexport function resolveJumpTarget(\n nodeId: string,\n currentSessionId: string | null,\n): LineageJump {\n if (currentSessionId !== null && nodeId === currentSessionId) {\n return { kind: 'current' }\n }\n return { kind: 'select', sessionId: nodeId }\n}\n\n// ---------------------------------------------------------------------------\n// External-agent degradation (lineage is dsh-exclusive).\n// ---------------------------------------------------------------------------\n\nconst DSH_AGENT = 'dsh'\n\n/**\n * Client-side pre-check for the lineage panel: sessions of non-dsh agents\n * get a synthetic degraded response (reason `not_dsh_session`) and the\n * integration never calls the backend for them; dsh sessions return null\n * (proceed to fetch). Honest presentation, not an error (§5.3).\n */\nexport function externalLineageFallback(\n agent: string,\n): { available: false; trace: null; reason: 'not_dsh_session' } | null {\n if (agent.trim().toLowerCase() === DSH_AGENT) return null\n return { available: false, trace: null, reason: 'not_dsh_session' }\n}\n\n// ---------------------------------------------------------------------------\n// Degradation cards and the lineage view resolver.\n// ---------------------------------------------------------------------------\n\n/** Render model of one degradation card (available:false presentations). */\nexport interface LineageDegradeCardVM {\n reason: LineageDegradeReason | 'unknown'\n title: string\n body: string\n /** Backend `detail` passthrough (trace_failed carries the engine error). */\n detail: string | null\n}\n\n/**\n * Degradation reason → card copy. Reasons outside the known vocabulary\n * (a newer backend) fall back to the generic card carrying the raw reason\n * — the panel never invents a state.\n */\nexport function lineageDegradeCard(\n reason: string | null | undefined,\n detail?: string | null,\n): LineageDegradeCardVM {\n const strings = DSH_TOOLS_STRINGS.lineage.degrade\n const detailOut = detail ?? null\n switch (reason) {\n case 'not_dsh_session':\n return { reason, title: strings.notDshTitle, body: strings.notDshBody, detail: detailOut }\n case 'session_query_unavailable':\n return {\n reason,\n title: strings.queryUnavailableTitle,\n body: strings.queryUnavailableBody,\n detail: detailOut,\n }\n case 'trace_failed':\n return {\n reason,\n title: strings.traceFailedTitle,\n body: strings.traceFailedBody,\n detail: detailOut,\n }\n default:\n return {\n reason: 'unknown',\n title: strings.unknownTitle,\n body: formatTemplate(strings.unknownBody, { reason: reason ?? '?' }),\n detail: detailOut,\n }\n }\n}\n\n/** Discriminated render state of the lineage panel. */\nexport type LineageViewState =\n | { kind: 'loading'; text: string }\n /** Transport/HTTP failure (unlike available:false, this IS an error). */\n | { kind: 'error'; text: string; detail: string | null }\n | { kind: 'degraded'; card: LineageDegradeCardVM }\n /** available:true but no trace body — honest empty state. */\n | { kind: 'empty'; text: string }\n | { kind: 'tree'; tree: LineageTreeVM }\n\n/** Everything the lineage panel receives (mirrors LineageTree props). */\nexport interface LineageViewInput {\n loading: boolean\n /** Transport-level failure text from the integration, or null. */\n error: string | null\n available: boolean\n /** Open vocabulary on purpose: newer backend reasons degrade gracefully. */\n reason?: string | null\n detail?: string | null\n trace: LineageTraceVM | null\n currentSessionId: string | null\n}\n\n/**\n * Resolve what the lineage panel shows, in priority order:\n * loading > transport error > degraded (available:false, per-reason card)\n * > empty (no trace body) > the tree.\n */\nexport function deriveLineageView(input: LineageViewInput): LineageViewState {\n if (input.loading) {\n return { kind: 'loading', text: DSH_TOOLS_STRINGS.lineage.loading }\n }\n if (input.error !== null) {\n return { kind: 'error', text: DSH_TOOLS_STRINGS.lineage.error, detail: input.error }\n }\n if (!input.available) {\n return { kind: 'degraded', card: lineageDegradeCard(input.reason, input.detail) }\n }\n if (input.trace === null) {\n return { kind: 'empty', text: DSH_TOOLS_STRINGS.lineage.empty }\n }\n return { kind: 'tree', tree: buildLineageTree(input.trace, input.currentSessionId) }\n}\n\n// ---------------------------------------------------------------------------\n// Search wire mirrors.\n// ---------------------------------------------------------------------------\n\n/** Search mode: 'filter-only' is the sessionQuery-absent degradation. */\nexport type SearchMode = 'full-text' | 'filter-only'\n\n/** UnifiedSession subset the search rows render (host: fusion.ts). */\nexport interface SearchSessionVM {\n agent: string\n sessionId: string\n title: string\n project: string\n status: string\n}\n\n/** One raw search hit (host: routes.ts handleSearch item). */\nexport interface SearchHitVM {\n session: SearchSessionVM\n /** 'full-text' | 'title' | 'project' today; open for newer backends. */\n matchedBy: string\n /** Best-match excerpt; full-text hits only, null otherwise. */\n snippet: string | null\n}\n\n/** Body of `GET search?q=&project=&limit=` (host: routes.ts). */\nexport interface SearchResponseVM {\n mode: SearchMode\n query: string\n project: string | null\n items: readonly SearchHitVM[]\n}\n\n// ---------------------------------------------------------------------------\n// Snippet highlighting (React-safe segments, no HTML injection).\n// ---------------------------------------------------------------------------\n\n/** One run of snippet text; `highlight` runs matched the query. */\nexport interface SnippetSegment {\n text: string\n highlight: boolean\n}\n\n/**\n * Split a snippet into plain/highlight segments around case-insensitive\n * occurrences of the (trimmed) query. Best-effort: the engine does not\n * mark its match, so a query the snippet spells differently simply yields\n * one plain segment. Empty snippet → no segments.\n */\nexport function highlightSnippet(snippet: string, query: string): SnippetSegment[] {\n if (snippet === '') return []\n const needle = query.trim().toLowerCase()\n if (needle === '') return [{ text: snippet, highlight: false }]\n const haystack = snippet.toLowerCase()\n const segments: SnippetSegment[] = []\n let pos = 0\n for (;;) {\n const hit = haystack.indexOf(needle, pos)\n if (hit === -1) break\n if (hit > pos) segments.push({ text: snippet.slice(pos, hit), highlight: false })\n segments.push({ text: snippet.slice(hit, hit + needle.length), highlight: true })\n pos = hit + needle.length\n }\n if (pos < snippet.length) segments.push({ text: snippet.slice(pos), highlight: false })\n return segments\n}\n\n// ---------------------------------------------------------------------------\n// Search-result normalization.\n// ---------------------------------------------------------------------------\n\n/** Known matchedBy vocabulary plus the honest catch-all. */\nexport type MatchedByToken = 'full-text' | 'title' | 'project' | 'other'\n\nconst MATCHED_BY_TOKENS: readonly MatchedByToken[] = ['full-text', 'title', 'project']\n\n/** One render-ready search result row. */\nexport interface SearchItemVM {\n sessionId: string\n agent: string\n /** Raw title (may be empty). */\n title: string\n /** Title with the untitled fallback applied. */\n titleLabel: string\n project: string\n status: string\n matchedBy: MatchedByToken\n matchedByLabel: string\n /** Pre-split highlight segments; null for non-full-text matches. */\n snippet: SnippetSegment[] | null\n shortId: string\n}\n\n/**\n * Wire hits → render rows. Snippets are split against the response's own\n * echoed query; matchedBy values outside the known vocabulary map to the\n * 'other' tag (label keeps a stable word, never invents a match kind).\n */\nexport function normalizeSearchItems(response: SearchResponseVM): SearchItemVM[] {\n return response.items.map((hit) => {\n const matchedBy = (MATCHED_BY_TOKENS as readonly string[]).includes(hit.matchedBy)\n ? (hit.matchedBy as MatchedByToken)\n : 'other'\n const title = hit.session.title\n return {\n sessionId: hit.session.sessionId,\n agent: hit.session.agent,\n title,\n titleLabel: title.trim() === '' ? DSH_TOOLS_STRINGS.search.untitled : title,\n project: hit.session.project,\n status: hit.session.status,\n matchedBy,\n matchedByLabel: DSH_TOOLS_STRINGS.search.matchedBy[matchedBy],\n snippet:\n hit.snippet === null || hit.snippet === ''\n ? null\n : highlightSnippet(hit.snippet, response.query),\n shortId: abbreviateId(hit.session.sessionId),\n }\n })\n}\n\n/**\n * The filter-only degradation bar text; null when full-text search is up.\n * Shown whenever the mode says so — the degradation is a capability fact,\n * independent of whether the current query has results.\n */\nexport function searchDegradeNotice(mode: SearchMode): string | null {\n return mode === 'filter-only' ? DSH_TOOLS_STRINGS.search.filterOnlyNotice : null\n}\n\n// ---------------------------------------------------------------------------\n// Search panel view resolver.\n// ---------------------------------------------------------------------------\n\n/** Discriminated body state of the search panel. */\nexport type SearchBodyKind = 'loading' | 'error' | 'results' | 'empty' | 'idle'\n\nexport interface SearchViewState {\n body: SearchBodyKind\n /** Degradation bar text (independent of the body state), or null. */\n notice: string | null\n /** Localized text for loading/error/empty bodies, null otherwise. */\n text: string | null\n /** Transport error detail for the error body, null otherwise. */\n detail: string | null\n}\n\n/**\n * Resolve the search panel body: loading > error > results > empty (a\n * submitted non-blank query with zero hits) > idle (nothing asked yet).\n */\nexport function deriveSearchView(input: {\n loading: boolean\n error: string | null\n mode: SearchMode\n itemCount: number\n query: string\n}): SearchViewState {\n const notice = searchDegradeNotice(input.mode)\n if (input.loading) {\n return { body: 'loading', notice, text: DSH_TOOLS_STRINGS.search.loading, detail: null }\n }\n if (input.error !== null) {\n return {\n body: 'error',\n notice,\n text: DSH_TOOLS_STRINGS.search.error,\n detail: input.error,\n }\n }\n if (input.itemCount > 0) return { body: 'results', notice, text: null, detail: null }\n if (input.query.trim() !== '') {\n return { body: 'empty', notice, text: DSH_TOOLS_STRINGS.search.empty, detail: null }\n }\n return { body: 'idle', notice, text: null, detail: null }\n}\n","/**\n * dsh lineage tree (design §5.1 view 2 dsh 会话专属区: 谱系树 + 溯源跳转).\n *\n * Fully controlled and presentational — the component does NO data\n * fetching; the integration layer calls `GET <prefix>/lineage/<id>` (or\n * short-circuits with `externalLineageFallback` for non-dsh sessions) and\n * feeds the always-200 body straight into the props. `available:false` is\n * rendered as an honest degradation card per reason (§5.3), never as an\n * error and never faked into an empty tree.\n *\n * The only component-owned state is the collapse set of the tree rows; it\n * resets whenever the traced target changes. Every derivation lives in\n * ./logic.ts and is unit-tested there.\n */\n\nimport { useState } from 'react'\nimport type { ReactElement } from 'react'\nimport css from './dsh-tools.module.css'\nimport {\n deriveLineageView,\n formatTemplate,\n resolveJumpTarget,\n visibleLineageNodes,\n type LineageNodeVM,\n type LineageTraceVM,\n type LineageTreeVM,\n} from './logic.ts'\nimport { DSH_TOOLS_STRINGS } from './strings.ts'\n\nexport interface LineageTreeProps {\n /** Wire trace of `GET lineage/<id>` (null while degraded/empty). */\n trace: LineageTraceVM | null\n /** Wire `available` bit; false renders a degradation card, not an error. */\n available: boolean\n /** Wire degradation reason, or the client-side `not_dsh_session`. */\n reason?: string | null\n /** Wire degradation detail (trace_failed carries the engine error). */\n detail?: string | null\n /** The session the user is inspecting — highlighted, click is a no-op. */\n currentSessionId: string | null\n /** Provenance jump: click on any other node navigates to that session. */\n onSelectSession: (sessionId: string) => void\n loading: boolean\n /** Transport/HTTP failure text from the integration, or null. */\n error: string | null\n}\n\nconst S = DSH_TOOLS_STRINGS.lineage\n\nfunction TreeRow(props: {\n node: LineageNodeVM\n collapsed: boolean\n currentSessionId: string | null\n onToggle: (id: string) => void\n onSelectSession: (sessionId: string) => void\n}): ReactElement {\n const { node, collapsed } = props\n const jump = resolveJumpTarget(node.id, props.currentSessionId)\n return (\n <div\n className={css['treeRow']}\n style={{ paddingLeft: node.depth * 16 }}\n data-testid=\"agent-sidecar-lineage-row\"\n data-role={node.role}\n >\n {node.hasChildren ? (\n <button\n type=\"button\"\n className={css['toggle']}\n aria-expanded={!collapsed}\n aria-label={collapsed ? S.expand : S.collapse}\n onClick={() => props.onToggle(node.id)}\n >\n {collapsed ? '▸' : '▾'}\n </button>\n ) : (\n <span className={css['toggleSpacer']} aria-hidden />\n )}\n <button\n type=\"button\"\n className={css['node']}\n data-current={node.isCurrent ? 'true' : 'false'}\n aria-current={node.isCurrent ? 'true' : undefined}\n title={jump.kind === 'current' ? S.currentTitle : S.jumpTitle}\n onClick={() => {\n if (jump.kind === 'select') props.onSelectSession(jump.sessionId)\n }}\n >\n <span className={css['nodeRole']}>{node.roleLabel}</span>\n <span className={css['nodeId']} title={node.id}>\n {node.shortId}\n </span>\n {node.isCurrent && (\n <span className={css['nodeBadge']} data-kind=\"current\">\n {S.currentBadge}\n </span>\n )}\n {node.live && (\n <span className={css['nodeBadge']} data-kind=\"live\">\n {S.liveBadge}\n </span>\n )}\n {!node.persisted && <span className={css['nodeBadge']}>{S.notPersistedBadge}</span>}\n </button>\n </div>\n )\n}\n\nfunction Tree(props: {\n tree: LineageTreeVM\n currentSessionId: string | null\n onSelectSession: (sessionId: string) => void\n}): ReactElement {\n const { tree } = props\n const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(new Set())\n // Collapse state belongs to one trace; a new target starts expanded.\n const [seenTarget, setSeenTarget] = useState(tree.targetId)\n if (seenTarget !== tree.targetId) {\n setSeenTarget(tree.targetId)\n setCollapsed(new Set())\n }\n const toggle = (id: string): void => {\n setCollapsed((prev) => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n return (\n <>\n {tree.incompleteNotice !== null && (\n <div className={css['noticeBar']} role=\"status\">\n {tree.incompleteNotice}\n </div>\n )}\n <div className={css['tree']} role=\"tree\" data-testid=\"agent-sidecar-lineage-tree\">\n {visibleLineageNodes(tree.nodes, collapsed).map((node) => (\n <TreeRow\n key={node.id}\n node={node}\n collapsed={collapsed.has(node.id)}\n currentSessionId={props.currentSessionId}\n onToggle={toggle}\n onSelectSession={props.onSelectSession}\n />\n ))}\n </div>\n </>\n )\n}\n\n/** The lineage panel. Pure render of `deriveLineageView` over the props. */\nexport function LineageTree(props: LineageTreeProps): ReactElement {\n const view = deriveLineageView({\n loading: props.loading,\n error: props.error,\n available: props.available,\n reason: props.reason ?? null,\n detail: props.detail ?? null,\n trace: props.trace,\n currentSessionId: props.currentSessionId,\n })\n\n return (\n <section className={css['panel']} data-testid=\"agent-sidecar-lineage\">\n <div className={css['panelHead']}>\n <span className={css['panelTitle']}>{S.title}</span>\n {view.kind === 'tree' && (\n <span className={css['panelCount']}>\n {formatTemplate(S.nodeCount, { n: view.tree.nodeCount })}\n </span>\n )}\n </div>\n\n {view.kind === 'loading' && <div className={css['mutedLine']}>{view.text}</div>}\n\n {view.kind === 'error' && (\n <div className={css['errorCard']} role=\"alert\">\n {view.text}\n {view.detail !== null && <span className={css['errorDetail']}>{view.detail}</span>}\n </div>\n )}\n\n {view.kind === 'degraded' && (\n <div\n className={css['degradeCard']}\n data-reason={view.card.reason}\n data-testid=\"agent-sidecar-lineage-degraded\"\n >\n <span className={css['degradeTitle']}>{view.card.title}</span>\n <span className={css['degradeBody']}>{view.card.body}</span>\n {view.card.detail !== null && (\n <span className={css['degradeDetail']}>{view.card.detail}</span>\n )}\n </div>\n )}\n\n {view.kind === 'empty' && <div className={css['mutedLine']}>{view.text}</div>}\n\n {view.kind === 'tree' && (\n <Tree\n tree={view.tree}\n currentSessionId={props.currentSessionId}\n onSelectSession={props.onSelectSession}\n />\n )}\n </section>\n )\n}\n","/**\n * Cross-agent session search panel (design §4.e.4 / §5.1: dsh 全文检索,\n * 外部会话标题/项目过滤降级).\n *\n * Fully controlled and presentational — the component does NO data\n * fetching; the integration layer owns the query state, calls\n * `GET <prefix>/search?q=&project=&limit=`, maps the response through\n * `normalizeSearchItems` (./logic.ts) and feeds the rows plus the wire\n * `mode` back in. `mode: 'filter-only'` renders the honest degradation\n * bar (§5.3) — the panel keeps working as a title/project filter and\n * never pretends full-text ran.\n *\n * Snippets arrive pre-split into highlight segments (React-safe, no HTML\n * injection); full-text hits carry them, filter hits render without.\n */\n\nimport type { ReactElement } from 'react'\nimport css from './dsh-tools.module.css'\nimport {\n deriveSearchView,\n formatTemplate,\n type SearchItemVM,\n type SearchMode,\n} from './logic.ts'\nimport { DSH_TOOLS_STRINGS } from './strings.ts'\n\nexport interface SearchPanelProps {\n /** Controlled query text (owner persists/submits it). */\n query: string\n /** Active project filter echoed by the backend, if any. */\n project?: string | null\n /** Wire search mode; 'filter-only' shows the degradation bar. */\n mode: SearchMode\n /** Normalized result rows (owner maps the wire body via logic.ts). */\n items: SearchItemVM[]\n loading: boolean\n /** Transport/HTTP failure text from the integration, or null. */\n error: string | null\n onQueryChange: (query: string) => void\n onSubmit: () => void\n /** Result click → open that session (same seam as the board cards). */\n onSelectSession: (sessionId: string) => void\n}\n\nconst S = DSH_TOOLS_STRINGS.search\n\nfunction ResultItem(props: {\n item: SearchItemVM\n onSelectSession: (sessionId: string) => void\n}): ReactElement {\n const { item } = props\n return (\n <button\n type=\"button\"\n className={css['resultItem']}\n onClick={() => props.onSelectSession(item.sessionId)}\n data-testid=\"agent-sidecar-search-item\"\n >\n <span className={css['resultHead']}>\n <span className={css['resultAgent']}>{item.agent}</span>\n <span className={css['resultTitle']} title={item.title}>\n {item.titleLabel}\n </span>\n <span className={css['matchTag']} data-kind={item.matchedBy}>\n {item.matchedByLabel}\n </span>\n </span>\n <span className={css['resultMeta']}>\n <span className={css['resultProject']} title={item.project}>\n {item.project}\n </span>\n <span className={css['resultId']} title={item.sessionId}>\n {item.shortId}\n </span>\n </span>\n {item.snippet !== null && (\n <span className={css['snippet']}>\n {item.snippet.map((segment, index) =>\n segment.highlight ? (\n <mark key={index} className={css['snippetMark']}>\n {segment.text}\n </mark>\n ) : (\n <span key={index}>{segment.text}</span>\n ),\n )}\n </span>\n )}\n </button>\n )\n}\n\n/** The search panel. Pure render of `deriveSearchView` over the props. */\nexport function SearchPanel(props: SearchPanelProps): ReactElement {\n const view = deriveSearchView({\n loading: props.loading,\n error: props.error,\n mode: props.mode,\n itemCount: props.items.length,\n query: props.query,\n })\n const project = props.project ?? null\n\n return (\n <section className={css['panel']} data-testid=\"agent-sidecar-search\">\n <div className={css['panelHead']}>\n <span className={css['panelTitle']}>{S.title}</span>\n </div>\n\n <form\n className={css['searchForm']}\n onSubmit={(ev) => {\n ev.preventDefault()\n props.onSubmit()\n }}\n >\n <input\n type=\"search\"\n className={css['searchInput']}\n value={props.query}\n placeholder={S.placeholder}\n onChange={(ev) => props.onQueryChange(ev.target.value)}\n />\n <button type=\"submit\" className={css['searchSubmit']}>\n {S.submit}\n </button>\n </form>\n\n {project !== null && project !== '' && (\n <span className={css['projectChip']} title={project}>\n {formatTemplate(S.projectFilter, { project })}\n </span>\n )}\n\n {view.notice !== null && (\n <div\n className={css['noticeBar']}\n role=\"status\"\n data-testid=\"agent-sidecar-search-degraded\"\n >\n {view.notice}\n </div>\n )}\n\n {view.body === 'loading' && <div className={css['mutedLine']}>{view.text}</div>}\n\n {view.body === 'error' && (\n <div className={css['errorCard']} role=\"alert\">\n {view.text}\n {view.detail !== null && <span className={css['errorDetail']}>{view.detail}</span>}\n </div>\n )}\n\n {view.body === 'empty' && <div className={css['mutedLine']}>{view.text}</div>}\n\n {view.body === 'results' && (\n <div className={css['resultList']}>\n {props.items.map((item) => (\n <ResultItem\n key={`${item.agent}:${item.sessionId}`}\n item={item}\n onSelectSession={props.onSelectSession}\n />\n ))}\n </div>\n )}\n </section>\n )\n}\n","/**\n * AI bypass-analysis panel (T5.10b, design §4.e.3 / §5.1 view 2): fully\n * controlled presentation over an {@link AnalysisGlueState} — the owner\n * (detail view) holds the AnalysisStore and passes state + intents down.\n *\n * Honesty posture (§5.3): the engine disclaimer (fallback copy when a\n * settle never happened) renders with every result, truncation is called\n * out per exchange, terminal errors carry the vocabulary code, and the\n * capability-off state explains where to enable analysis instead of\n * hiding the entry. Strings ride the main locale table (`analysis.*`).\n *\n * @module\n */\n\nimport { useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { t } from '../locales/index.ts'\nimport type { AnalysisExchange, AnalysisGlueState } from '../analysis-glue.ts'\nimport css from './analysis.module.css'\n\nexport interface AnalysisPanelProps {\n /** Live `analysis.enabled` bit (settings scope); false disables intents. */\n enabled: boolean\n state: AnalysisGlueState\n onStart: () => void\n onFollowup: (question: string) => void\n onStop: () => void\n onClose: () => void\n}\n\n/** Terminal failure code → locale key ('' falls back to the generic). */\nconst ERROR_KEYS: Record<string, Parameters<typeof t>[0]> = {\n analysis_disabled: 'analysis.errDisabled',\n analysis_unavailable: 'analysis.errUnavailable',\n target_not_found: 'analysis.errTargetNotFound',\n too_many_active: 'analysis.errTooManyActive',\n timeout: 'analysis.errTimeout',\n create_failed: 'analysis.errCreateFailed',\n cancelled: 'analysis.errCancelled',\n network_error: 'analysis.errNetwork',\n request_timeout: 'analysis.errNetwork',\n}\n\nfunction errorText(code: string): string {\n const key = ERROR_KEYS[code]\n return key !== undefined ? t(key) : t('analysis.errGeneric', { code })\n}\n\n/** Retryable notice code → copy. */\nfunction noticeText(code: string): string {\n if (code === 'timeout') return t('analysis.noticeTimeout')\n if (code === 'cancel_failed') return t('analysis.noticeCancelFailed')\n return t('analysis.noticeNetwork')\n}\n\nfunction Exchange(props: { exchange: AnalysisExchange }): ReactElement {\n const { exchange } = props\n return (\n <div className={css['exchange']} data-testid=\"agent-sidecar-analysis-exchange\">\n <span className={css['exchangeLabel']}>\n {exchange.question === null ? t('analysis.exchangeInitial') : t('analysis.followupLabel')}\n </span>\n {exchange.question !== null && <div className={css['question']}>{exchange.question}</div>}\n <pre className={css['summary']}>\n {exchange.summary === '' ? t('analysis.emptySummary') : exchange.summary}\n </pre>\n {exchange.truncated && (\n <span className={css['truncated']}>{t('analysis.truncatedNotice')}</span>\n )}\n </div>\n )\n}\n\n/** The analysis panel. Pure render over props (state machine lives in glue). */\nexport function AnalysisPanel(props: AnalysisPanelProps): ReactElement {\n const { enabled, state } = props\n const [question, setQuestion] = useState('')\n\n const conversationLive = state.phase === 'ready' || state.phase === 'answering'\n const showStart = state.phase === 'idle' || state.phase === 'failed' || state.phase === 'stopped'\n\n const submitFollowup = (): void => {\n const q = question.trim()\n if (q === '' || state.phase !== 'ready') return\n setQuestion('')\n props.onFollowup(q)\n }\n\n return (\n <section className={css['panel']} data-testid=\"agent-sidecar-analysis\">\n <div className={css['head']}>\n <span className={css['title']}>{t('analysis.title')}</span>\n <span className={css['spacer']} />\n {conversationLive && (\n <button\n type=\"button\"\n className={css['textButton']}\n disabled={!enabled}\n onClick={props.onStop}\n data-testid=\"agent-sidecar-analysis-stop\"\n >\n {t('analysis.stop')}\n </button>\n )}\n <button type=\"button\" className={css['closeButton']} onClick={props.onClose}>\n {t('analysis.close')}\n </button>\n </div>\n\n {!enabled && (\n <div className={css['noteCard']} data-testid=\"agent-sidecar-analysis-disabled\">\n {t('analysis.disabledNote')}\n </div>\n )}\n\n {state.exchanges.map((exchange, index) => (\n <Exchange key={index} exchange={exchange} />\n ))}\n\n {state.phase === 'failed' && state.errorCode !== null && (\n <div className={css['errorCard']} data-testid=\"agent-sidecar-analysis-error\">\n {errorText(state.errorCode)}\n </div>\n )}\n {state.phase === 'stopped' && (\n <div className={css['noteCard']}>{t('analysis.stopped')}</div>\n )}\n {state.noticeCode !== null && (\n <div className={css['noticeBar']} data-testid=\"agent-sidecar-analysis-notice\">\n {noticeText(state.noticeCode)}\n </div>\n )}\n\n {enabled && showStart && (\n <>\n {state.phase === 'idle' && (\n <div className={css['mutedLine']}>{t('analysis.idleHint')}</div>\n )}\n <button\n type=\"button\"\n className={css['primaryButton']}\n onClick={props.onStart}\n data-testid=\"agent-sidecar-analysis-start\"\n >\n {state.phase === 'idle' ? t('analysis.start') : t('analysis.restart')}\n </button>\n </>\n )}\n\n {state.phase === 'requesting' && (\n <div className={css['mutedLine']}>{t('analysis.requesting')}</div>\n )}\n {state.phase === 'answering' && (\n <div className={css['mutedLine']}>{t('analysis.answering')}</div>\n )}\n\n {conversationLive && (\n <div className={css['followupForm']}>\n <input\n className={css['followupInput']}\n value={question}\n placeholder={t('analysis.followupPlaceholder')}\n disabled={!enabled || state.phase !== 'ready'}\n onChange={(event) => {\n setQuestion(event.currentTarget.value)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') submitFollowup()\n }}\n data-testid=\"agent-sidecar-analysis-question\"\n />\n <button\n type=\"button\"\n className={css['textButton']}\n disabled={!enabled || state.phase !== 'ready' || question.trim() === ''}\n onClick={submitFollowup}\n data-testid=\"agent-sidecar-analysis-followup\"\n >\n {t('analysis.followupSubmit')}\n </button>\n </div>\n )}\n\n {(conversationLive || state.exchanges.length > 0) && (\n <div className={css['disclaimer']} data-testid=\"agent-sidecar-analysis-disclaimer\">\n {state.disclaimer ?? t('analysis.disclaimerFallback')}\n </div>\n )}\n </section>\n )\n}\n","/**\n * Pure logic for the inject panel (design §5.1 view 3, §5.3 confirmation\n * boundary, §8 S5-S7 wording). No React, no I/O, no imports from the data\n * layer — everything arrives and leaves as plain values, so this module is\n * unit-testable in a bare node environment (same posture as board/logic.ts).\n *\n * Decoupling contract (T4.5 ↔ S5 integration): the wire-facing types below\n * (PrepareSuccess / InjectResultView / InjectPlanView / ApiErrorLike) are\n * this module's OWN mirrors of the host action contract (src/routes.ts +\n * src/inject-gateway.ts) and of the data layer's normalized error\n * (client/api.ts ApiError, matched structurally so real instances satisfy\n * {@link ApiErrorLike} without an import). The integration layer feeds the\n * panel through the `onPrepare`/`onExecute` props; this module classifies\n * whatever comes back.\n *\n * Two-phase state machine (§4.f.5):\n *\n * idle ──PREPARE_START──▶ preparing ──PREPARE_OK──▶ confirm\n * ▲ │ │\n * │◀──PREPARE_REJECTED/ERROR─┘ TICK(expired) / CANCEL\n * │◀──────────────────────────────────────────────────┤\n * │ EXECUTE_START\n * │ ▼\n * │◀──RESET (delivered/failed only)── result ◀── executing\n *\n * `outcome: 'unknown'` is terminal: the reducer refuses RESET out of an\n * unknown result, so there is NO machine path that could re-drive an\n * execute after an unknown delivery (S6).\n *\n * @module\n */\n\nimport type { SidecarLocaleKey } from '../locales/index.ts'\n\n// ---------------------------------------------------------------------------\n// Wire-facing mirrors (host source of truth noted per type).\n// ---------------------------------------------------------------------------\n\n/** Injection mode, aligned with dsh `session.prompt` semantics (host: inject-gateway.ts). */\nexport type InjectMode = 'queue' | 'steer'\n\n/** Terminal outcome vocabulary (host: inject-gateway.ts). */\nexport type InjectOutcome = 'delivered' | 'failed' | 'unknown'\n\n/** Injection target reference as sent in the prepare envelope. */\nexport interface InjectTargetRef {\n agent: string\n sessionId: string\n}\n\n/** Live target snapshot captured by the server-side prepare re-check. */\nexport interface TargetStatusView {\n agent: string\n sessionId: string\n status: string\n title?: string\n project?: string\n}\n\n/** Confirmation plan echoed by `inject.prepare` (host: inject-gateway.ts). */\nexport interface InjectPlanView {\n target: InjectTargetRef\n mode: InjectMode\n targetStatus: TargetStatusView\n messagePreview: { bytes: number; head: string }\n}\n\n/** Success body of `POST action {type:'inject.prepare'}` (host: routes.ts). */\nexport interface PrepareSuccess {\n requestId: string\n confirmToken: string\n plan: InjectPlanView\n /** Epoch ms after which the confirmToken is dead. */\n expiresAt: number\n}\n\n/** Body of `POST action {type:'inject.execute'}` answers (host: routes.ts). */\nexport interface InjectResultView {\n outcome: InjectOutcome\n errorCode?: string\n detail?: string\n /** True when the gateway replayed a cached first result (idempotency). */\n replayed?: boolean\n}\n\n/**\n * Structural mirror of the data layer's ApiError (client/api.ts): real\n * instances satisfy this shape, so the panel can classify them without\n * importing the data layer.\n */\nexport interface ApiErrorLike {\n kind: 'timeout' | 'aborted' | 'network' | 'http' | 'parse'\n /** Server `{reason}` code for kind 'http', a stable local code otherwise. */\n reason: string\n status: number | null\n}\n\n/** What the panel hands the integration's `onPrepare` callback. */\nexport interface PanelPrepareRequest {\n target: InjectTargetRef\n mode: InjectMode\n message: string\n}\n\n/** What the panel hands the integration's `onExecute` callback. */\nexport interface PanelExecuteRequest {\n requestId: string\n confirmToken: string\n message: string\n}\n\n// ---------------------------------------------------------------------------\n// Byte counting and message validation (mirrors the host gate so the UI\n// refuses locally exactly what the server would refuse remotely).\n// ---------------------------------------------------------------------------\n\n/** Message byte cap; must mirror inject-gateway.ts (pinned by test). */\nexport const MAX_MESSAGE_BYTES = 16 * 1024\n\nconst utf8 = new TextEncoder()\n\n/**\n * UTF-8 byte size of a message. TextEncoder agrees with the host's\n * `Buffer.byteLength(message, 'utf8')` for every well-formed string, so the\n * 16 KiB verdict is identical on both sides.\n */\nexport function messageBytes(message: string): number {\n return utf8.encode(message).length\n}\n\nexport type MessageInvalidCode = 'empty' | 'nul' | 'too_large'\n\nexport type MessageValidation =\n | { ok: true; bytes: number }\n | { ok: false; code: MessageInvalidCode; bytes: number }\n\n/** Local pre-validation, same checks and order as the host gateway. */\nexport function validateMessage(message: string): MessageValidation {\n const bytes = messageBytes(message)\n if (bytes === 0) return { ok: false, code: 'empty', bytes }\n if (message.includes('\\u0000')) return { ok: false, code: 'nul', bytes }\n if (bytes > MAX_MESSAGE_BYTES) return { ok: false, code: 'too_large', bytes }\n return { ok: true, bytes }\n}\n\n/** Render model for the live byte counter / limit progress bar. */\nexport interface ByteUsage {\n bytes: number\n limit: number\n /** Fill fraction for the progress bar, clamped to [0, 1]. */\n ratio: number\n over: boolean\n}\n\n/** Derive the byte counter view from a byte count. */\nexport function byteUsage(bytes: number): ByteUsage {\n return {\n bytes,\n limit: MAX_MESSAGE_BYTES,\n ratio: Math.min(1, Math.max(0, bytes / MAX_MESSAGE_BYTES)),\n over: bytes > MAX_MESSAGE_BYTES,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Target visibility warning (design §4.d / S7).\n// ---------------------------------------------------------------------------\n\n/**\n * Whether the process-list visibility warning applies. Only cursor-cli\n * qualifies: its upstream contract puts the prompt on the native\n * subprocess argv. claude / codex / dsh do not warn — after S4a the\n * sidecar's own argv (send --message-stdin) and the dsh in-process path\n * both keep the body off the process list.\n */\nexport function showsProcessListWarning(agent: string): boolean {\n return agent.trim().toLowerCase() === 'cursor-cli'\n}\n\n// ---------------------------------------------------------------------------\n// Token countdown (60s TTL is server-owned; the UI only reads expiresAt).\n// ---------------------------------------------------------------------------\n\nexport interface TokenCountdown {\n remainingMs: number\n /** Whole seconds for display, rounded up so \"1s\" never shows as \"0s\". */\n seconds: number\n expired: boolean\n}\n\n/** Countdown view for a server-issued expiresAt (epoch ms). */\nexport function tokenCountdown(expiresAt: number, nowMs: number): TokenCountdown {\n const remainingMs = Math.max(0, expiresAt - nowMs)\n return {\n remainingMs,\n seconds: Math.ceil(remainingMs / 1000),\n expired: nowMs >= expiresAt,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Two-phase state machine.\n// ---------------------------------------------------------------------------\n\n/** Non-blocking notice shown on the editor after a failed/expired attempt. */\nexport type PanelNotice =\n | { kind: 'token_expired' }\n /** Server vocabulary rejection (prepare answered with an error status). */\n | { kind: 'prepare_rejected'; code: string; detail?: string }\n /** Transport-level prepare failure (timeout/network/…); safe to retry. */\n | { kind: 'prepare_error'; code: string }\n\nexport type PanelState =\n | { phase: 'idle'; notice: PanelNotice | null }\n | { phase: 'preparing'; message: string; mode: InjectMode }\n | {\n phase: 'confirm'\n message: string\n mode: InjectMode\n requestId: string\n confirmToken: string\n plan: InjectPlanView\n expiresAt: number\n }\n | {\n phase: 'executing'\n message: string\n mode: InjectMode\n requestId: string\n confirmToken: string\n plan: InjectPlanView\n }\n | { phase: 'result'; result: InjectResultView; plan: InjectPlanView | null }\n\nexport type InjectPhase = PanelState['phase']\n\nexport type PanelEvent =\n | { type: 'PREPARE_START'; message: string; mode: InjectMode }\n | { type: 'PREPARE_OK'; response: PrepareSuccess }\n | { type: 'PREPARE_REJECTED'; code: string; detail?: string }\n | { type: 'PREPARE_ERROR'; code: string }\n | { type: 'TICK'; nowMs: number }\n | { type: 'CANCEL' }\n | { type: 'EXECUTE_START' }\n | { type: 'EXECUTE_RESULT'; result: InjectResultView }\n | { type: 'RESET' }\n\n/** Fresh machine state (factory, so callers can never share a mutable seed). */\nexport function initialPanelState(): PanelState {\n return { phase: 'idle', notice: null }\n}\n\n/**\n * Pure transition function. Events that do not apply to the current phase\n * return the state unchanged (same reference), which both makes stale\n * async callbacks harmless and gives React a free bail-out.\n */\nexport function reducePanel(state: PanelState, event: PanelEvent): PanelState {\n switch (event.type) {\n case 'PREPARE_START':\n if (state.phase !== 'idle') return state\n return { phase: 'preparing', message: event.message, mode: event.mode }\n\n case 'PREPARE_OK':\n if (state.phase !== 'preparing') return state\n return {\n phase: 'confirm',\n message: state.message,\n mode: state.mode,\n requestId: event.response.requestId,\n confirmToken: event.response.confirmToken,\n plan: event.response.plan,\n expiresAt: event.response.expiresAt,\n }\n\n case 'PREPARE_REJECTED':\n if (state.phase !== 'preparing') return state\n return {\n phase: 'idle',\n notice: {\n kind: 'prepare_rejected',\n code: event.code,\n ...(event.detail !== undefined ? { detail: event.detail } : {}),\n },\n }\n\n case 'PREPARE_ERROR':\n if (state.phase !== 'preparing') return state\n return { phase: 'idle', notice: { kind: 'prepare_error', code: event.code } }\n\n case 'TICK':\n if (state.phase !== 'confirm') return state\n if (event.nowMs < state.expiresAt) return state\n // Token TTL elapsed while waiting for the click: back to the editor\n // with a \"prepare again\" notice (§4.f.5, 60s server TTL).\n return { phase: 'idle', notice: { kind: 'token_expired' } }\n\n case 'CANCEL':\n if (state.phase !== 'confirm') return state\n return { phase: 'idle', notice: null }\n\n case 'EXECUTE_START':\n if (state.phase !== 'confirm') return state\n return {\n phase: 'executing',\n message: state.message,\n mode: state.mode,\n requestId: state.requestId,\n confirmToken: state.confirmToken,\n plan: state.plan,\n }\n\n case 'EXECUTE_RESULT':\n if (state.phase !== 'executing') return state\n return { phase: 'result', result: event.result, plan: state.plan }\n\n case 'RESET':\n if (state.phase !== 'result') return state\n // 'unknown' is terminal (S6): no machine path leads back to the\n // editor, so no UI built on this reducer can offer a retry.\n if (state.result.outcome === 'unknown') return state\n return { phase: 'idle', notice: null }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Response classification (PrepareSuccess/InjectResult vs normalized error).\n// ---------------------------------------------------------------------------\n\nconst API_ERROR_KINDS: ReadonlySet<string> = new Set([\n 'timeout',\n 'aborted',\n 'network',\n 'http',\n 'parse',\n])\n\n/** Structural guard matching client/api.ts ApiError instances. */\nexport function isApiErrorLike(value: unknown): value is ApiErrorLike {\n if (typeof value !== 'object' || value === null) return false\n const v = value as Record<string, unknown>\n return (\n typeof v['kind'] === 'string' &&\n API_ERROR_KINDS.has(v['kind']) &&\n typeof v['reason'] === 'string' &&\n !('outcome' in v)\n )\n}\n\n/**\n * Classify what `onPrepare` resolved with. An HTTP-kind error is a server\n * vocabulary rejection (the reason carries the errorCode routes mapped to\n * the status); any other kind is a transport failure — harmless for\n * prepare, which is side-effect-free beyond a token that expires on its\n * own, so the editor may simply offer another attempt.\n */\nexport function classifyPrepareResponse(\n value: PrepareSuccess | ApiErrorLike,\n): PanelEvent {\n if (isApiErrorLike(value)) {\n return value.kind === 'http'\n ? { type: 'PREPARE_REJECTED', code: value.reason }\n : { type: 'PREPARE_ERROR', code: value.reason }\n }\n return { type: 'PREPARE_OK', response: value }\n}\n\n/**\n * Classify what `onExecute` resolved with. An HTTP-kind error is the\n * routes-mapped failed outcome. Everything else (timeout/network/parse/\n * aborted) happened AFTER the execute may already have been dispatched, so\n * the honest verdict is `outcome: 'unknown'` — terminal, no retry (S6);\n * the user is pointed at the target session to verify.\n */\nexport function classifyExecuteResponse(\n value: InjectResultView | ApiErrorLike,\n): PanelEvent {\n if (isApiErrorLike(value)) {\n if (value.kind === 'http') {\n return {\n type: 'EXECUTE_RESULT',\n result: { outcome: 'failed', errorCode: value.reason },\n }\n }\n return {\n type: 'EXECUTE_RESULT',\n result: { outcome: 'unknown', errorCode: value.reason },\n }\n }\n return { type: 'EXECUTE_RESULT', result: value }\n}\n\n// ---------------------------------------------------------------------------\n// Enable/disable gate for the editor (design: 置灰规则).\n// ---------------------------------------------------------------------------\n\nexport type EditorBlock = 'inject_off' | 'no_target' | 'busy' | 'invalid_message'\n\nexport interface EditorGate {\n canPrepare: boolean\n block: EditorBlock | null\n}\n\n/**\n * Why (if at all) the prepare action is unavailable, in priority order:\n * capability off > no target > a phase already in flight > invalid message.\n */\nexport function deriveEditorGate(input: {\n injectEnabled: boolean\n hasTarget: boolean\n phase: InjectPhase\n validation: MessageValidation\n}): EditorGate {\n if (!input.injectEnabled) return { canPrepare: false, block: 'inject_off' }\n if (!input.hasTarget) return { canPrepare: false, block: 'no_target' }\n if (input.phase !== 'idle') return { canPrepare: false, block: 'busy' }\n if (!input.validation.ok) return { canPrepare: false, block: 'invalid_message' }\n return { canPrepare: true, block: null }\n}\n\n// ---------------------------------------------------------------------------\n// Result actions (S6: unknown is terminal).\n// ---------------------------------------------------------------------------\n\nexport interface ResultActions {\n /** True only for 'failed': a fresh prepare (fresh confirmation) is offered. */\n canReprepare: boolean\n /** True only for 'unknown': point the user at the session to verify. */\n showCheckSessionHint: boolean\n}\n\n/** Which follow-up affordances a terminal outcome earns. */\nexport function resultActions(outcome: InjectOutcome): ResultActions {\n return {\n canReprepare: outcome === 'failed',\n showCheckSessionHint: outcome === 'unknown',\n }\n}\n\n// ---------------------------------------------------------------------------\n// Copy mapping (keys live in the T2.3 locale table, inject.* domain).\n// ---------------------------------------------------------------------------\n\n/** A locale key plus its `{name}` template params, ready for `t()`. */\nexport interface CopyRef {\n key: SidecarLocaleKey\n params?: Record<string, unknown>\n}\n\n/** Mode radio copy (label + semantics hint) per injection mode. */\nexport const MODE_COPY = {\n queue: { label: 'inject.modeQueue', hint: 'inject.modeQueueHint' },\n steer: { label: 'inject.modeSteer', hint: 'inject.modeSteerHint' },\n} as const satisfies Record<InjectMode, { label: SidecarLocaleKey; hint: SidecarLocaleKey }>\n\n/** Local validation verdict → copy key. */\nexport const MESSAGE_INVALID_COPY = {\n empty: 'inject.msgEmpty',\n nul: 'inject.msgNul',\n too_large: 'inject.msgTooLarge',\n} as const satisfies Record<MessageInvalidCode, SidecarLocaleKey>\n\n/** Validation verdict → renderable copy (too_large carries bytes/limit). */\nexport function messageInvalidCopy(\n validation: Extract<MessageValidation, { ok: false }>,\n): CopyRef {\n if (validation.code === 'too_large') {\n return {\n key: MESSAGE_INVALID_COPY.too_large,\n params: { bytes: validation.bytes, limit: MAX_MESSAGE_BYTES },\n }\n }\n return { key: MESSAGE_INVALID_COPY[validation.code] }\n}\n\n/** Terminal outcome → headline copy key. */\nexport const RESULT_COPY = {\n delivered: 'inject.resultDelivered',\n failed: 'inject.resultFailed',\n unknown: 'inject.resultUnknown',\n} as const satisfies Record<InjectOutcome, SidecarLocaleKey>\n\n/**\n * Error vocabulary → copy key: the gateway codes (inject-gateway.ts), plus\n * the data layer's transport reasons (api.ts). Unlisted codes fall back to\n * the generic template via {@link errorCopy}.\n */\nexport const ERROR_COPY: Readonly<Record<string, SidecarLocaleKey>> = {\n inject_disabled: 'inject.errInjectDisabled',\n invalid_message: 'inject.errInvalidMessage',\n target_not_found: 'inject.errTargetNotFound',\n target_dead: 'inject.errTargetDead',\n too_many_pending: 'inject.errTooManyPending',\n token_missing: 'inject.errTokenMissing',\n token_expired: 'inject.errTokenExpired',\n token_reused: 'inject.errTokenReused',\n token_mismatch: 'inject.errTokenMismatch',\n unsupported_agent: 'inject.errUnsupportedAgent',\n executor_error: 'inject.errExecutorError',\n request_timeout: 'inject.errTimeout',\n request_aborted: 'inject.errAborted',\n network_error: 'inject.errNetwork',\n invalid_json: 'inject.errParse',\n}\n\n/** Error code → renderable copy; unknown codes get the generic template. */\nexport function errorCopy(code: string): CopyRef {\n const key = ERROR_COPY[code]\n return key === undefined ? { key: 'inject.errGeneric', params: { code } } : { key }\n}\n\n/** Editor notice → renderable copy. */\nexport function noticeCopy(notice: PanelNotice): CopyRef {\n if (notice.kind === 'token_expired') return { key: 'inject.tokenExpired' }\n return errorCopy(notice.code)\n}\n","/**\n * Inject panel (design §5.1 view 3): the two-phase confirmation UI for\n * message injection. Fully controlled and presentational — the component\n * does NO data fetching; the integration layer supplies `onPrepare` /\n * `onExecute` callbacks that speak to `POST <prefix>/action` and resolve\n * with either the wire success shape or the data layer's normalized\n * ApiError (matched structurally, never imported).\n *\n * Three zones, driven by the pure reducer in ./logic.ts:\n *\n * 1. editor — message textarea + live UTF-8 byte counter, queue/steer mode\n * radios, target row, cursor-cli process-list warning (§4.d/S7) and the\n * audit-fingerprint note (§5.3);\n * 2. confirm — the prepare plan (live target-status snapshot + message\n * digest), the 60s confirmToken countdown, and the deliberately\n * restrained danger button;\n * 3. result — delivered / failed (re-prepare offered) / unknown (terminal:\n * the reducer itself refuses a reset, so no retry affordance can exist —\n * S6 — and the copy points at the session to verify).\n *\n * `capability.inject === false` disables the whole panel with the\n * \"enable injection in Settings\" note.\n */\n\nimport { useEffect, useId, useReducer, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { t as defaultT } from '../locales/index.ts'\nimport type { SidecarLocaleKey } from '../locales/index.ts'\nimport css from './inject.module.css'\nimport {\n byteUsage,\n classifyExecuteResponse,\n classifyPrepareResponse,\n deriveEditorGate,\n errorCopy,\n initialPanelState,\n messageInvalidCopy,\n MODE_COPY,\n noticeCopy,\n reducePanel,\n RESULT_COPY,\n resultActions,\n showsProcessListWarning,\n tokenCountdown,\n validateMessage,\n} from './logic.ts'\nimport type {\n ApiErrorLike,\n CopyRef,\n InjectMode,\n InjectPlanView,\n InjectResultView,\n PanelEvent,\n PanelExecuteRequest,\n PanelPrepareRequest,\n PrepareSuccess,\n} from './logic.ts'\n\n/** Translate seat the panel consumes (module-local t by default). */\nexport type InjectTranslate = (\n key: SidecarLocaleKey,\n params?: Record<string, unknown>,\n) => string\n\n/** The session the panel injects into, as picked on the board/detail view. */\nexport interface InjectPanelTarget {\n agent: string\n sessionId: string\n title?: string\n}\n\nexport interface InjectPanelProps {\n /** Server capability bits (from the state snapshot). */\n capability: { inject: boolean }\n /** Injection target; null renders the editor disabled with a hint. */\n target: InjectPanelTarget | null\n /** Mode preselected on mount (the inject.default-mode setting). */\n defaultMode: InjectMode\n /** Phase one: POST inject.prepare (resolve errors as values, don't throw). */\n onPrepare(req: PanelPrepareRequest): Promise<PrepareSuccess | ApiErrorLike>\n /** Phase two: POST inject.execute (resolve errors as values, don't throw). */\n onExecute(req: PanelExecuteRequest): Promise<InjectResultView | ApiErrorLike>\n /** Close/dismiss the panel (the hosting modal owns the lifecycle). */\n onClose?(): void\n /** Clock injection for the token countdown; defaults to Date.now. */\n nowMs?: () => number\n /** Locale seat override; defaults to the module-local table. */\n t?: InjectTranslate\n}\n\n/** Countdown re-render cadence while a confirm token is live. */\nconst TICK_MS = 500\n\nfunction renderCopy(t: InjectTranslate, copy: CopyRef): string {\n return t(copy.key, copy.params)\n}\n\n// ---------------------------------------------------------------------------\n// Zone building blocks.\n// ---------------------------------------------------------------------------\n\ninterface WarningsProps {\n t: InjectTranslate\n agent: string | null\n}\n\n/** cursor-cli process-list warning (gated) + the always-on audit note. */\nfunction Warnings(props: WarningsProps): ReactNode {\n return (\n <>\n {props.agent !== null && showsProcessListWarning(props.agent)\n ? <p className={css['warnBar']} role=\"alert\">{props.t('inject.argvWarning')}</p>\n : null}\n <p className={css['auditNote']}>{props.t('inject.auditNote')}</p>\n </>\n )\n}\n\ninterface PlanBoxProps {\n t: InjectTranslate\n plan: InjectPlanView\n}\n\n/** Confirm-phase plan: live target snapshot + message digest. */\nfunction PlanBox(props: PlanBoxProps): ReactNode {\n const { t, plan } = props\n const status = plan.targetStatus\n const targetName = status.title !== undefined && status.title !== ''\n ? status.title\n : plan.target.sessionId\n return (\n <div className={css['planBox']}>\n <div className={css['planRow']}>\n <span className={css['planKey']}>{t('inject.planTargetLabel')}</span>\n <span className={css['planValue']}>\n <span className={css['agentTag']}>{plan.target.agent}</span>\n <span className={css['planTitle']} title={plan.target.sessionId}>{targetName}</span>\n </span>\n </div>\n <div className={css['planRow']}>\n <span className={css['planKey']}>\n {t('inject.planStatus', { status: status.status })}\n </span>\n </div>\n <p className={css['observedNote']}>{t('inject.statusObservedNote')}</p>\n <div className={css['planRow']}>\n <span className={css['planKey']}>{t('inject.planModeLabel')}</span>\n <span className={css['planValue']}>{t(MODE_COPY[plan.mode].label)}</span>\n </div>\n <div className={css['planPreview']}>\n <span className={css['planKey']}>\n {t('inject.planPreviewLabel', { bytes: plan.messagePreview.bytes })}\n </span>\n <pre className={css['preview']}>{plan.messagePreview.head}</pre>\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Panel.\n// ---------------------------------------------------------------------------\n\n/**\n * Render the two-phase inject panel.\n * @param props - capability, target, and the integration callbacks.\n * @returns the panel.\n */\nexport function InjectPanel(props: InjectPanelProps): ReactNode {\n const t = props.t ?? defaultT\n const now = props.nowMs ?? Date.now\n const [state, dispatch] = useReducer(reducePanel, undefined, initialPanelState)\n const [draft, setDraft] = useState('')\n const [mode, setMode] = useState<InjectMode>(props.defaultMode)\n const [clock, setClock] = useState(() => now())\n const textareaId = useId()\n const modeGroup = useId()\n\n // Token countdown: refresh the display clock and let the reducer decide\n // expiry, only while a confirm token is live.\n const inConfirm = state.phase === 'confirm'\n useEffect(() => {\n if (!inConfirm) return\n setClock(now())\n const id = setInterval(() => {\n const nowValue = now()\n setClock(nowValue)\n dispatch({ type: 'TICK', nowMs: nowValue })\n }, TICK_MS)\n return () => { clearInterval(id) }\n // `now` is a stable prop (or Date.now); phase entry is the real trigger.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [inConfirm])\n\n const validation = validateMessage(draft)\n const gate = deriveEditorGate({\n injectEnabled: props.capability.inject,\n hasTarget: props.target !== null,\n phase: state.phase,\n validation,\n })\n\n const handlePrepare = async (): Promise<void> => {\n const target = props.target\n if (target === null || !gate.canPrepare) return\n const message = draft\n dispatch({ type: 'PREPARE_START', message, mode })\n let event: PanelEvent\n try {\n const response = await props.onPrepare({\n target: { agent: target.agent, sessionId: target.sessionId },\n mode,\n message,\n })\n event = classifyPrepareResponse(response)\n } catch {\n // Contract says resolve errors as values; a throw is still mapped to\n // a retryable transport notice rather than a crashed panel.\n event = { type: 'PREPARE_ERROR', code: 'unexpected_error' }\n }\n dispatch(event)\n }\n\n const handleExecute = async (): Promise<void> => {\n if (state.phase !== 'confirm') return\n const { requestId, confirmToken, message } = state\n dispatch({ type: 'EXECUTE_START' })\n let event: PanelEvent\n try {\n const response = await props.onExecute({ requestId, confirmToken, message })\n event = classifyExecuteResponse(response)\n } catch {\n // The execute may already have been dispatched server-side: honest\n // verdict is unknown — terminal, no retry (S6).\n event = {\n type: 'EXECUTE_RESULT',\n result: { outcome: 'unknown', errorCode: 'unexpected_error' },\n }\n }\n dispatch(event)\n }\n\n const closeButton = props.onClose !== undefined\n ? (\n <button type=\"button\" className={css['btn']} onClick={props.onClose}>\n {t('inject.close')}\n </button>\n )\n : null\n\n // ── whole-panel gate: injection capability off ─────────────────────────\n if (!props.capability.inject) {\n return (\n <section className={css['panel']} aria-label={t('inject.title')}>\n <header className={css['header']}>\n <h3 className={css['title']}>{t('inject.title')}</h3>\n </header>\n <div className={css['body']}>\n <p className={css['capabilityOff']} role=\"status\">{t('inject.capabilityOff')}</p>\n {closeButton !== null\n ? <div className={css['footer']}>{closeButton}</div>\n : null}\n </div>\n </section>\n )\n }\n\n // ── zone 3: result ──────────────────────────────────────────────────────\n if (state.phase === 'result') {\n const { result } = state\n const actions = resultActions(result.outcome)\n const toneClass = result.outcome === 'delivered'\n ? css['resultOk']\n : result.outcome === 'failed'\n ? css['resultFail']\n : css['resultUnknown']\n return (\n <section className={css['panel']} aria-label={t('inject.title')}>\n <header className={css['header']}>\n <h3 className={css['title']}>{t('inject.title')}</h3>\n </header>\n <div className={css['body']}>\n <p className={toneClass} role=\"status\">{t(RESULT_COPY[result.outcome])}</p>\n {result.outcome === 'failed' && result.errorCode !== undefined\n ? (\n <p className={css['resultDetail']}>\n {renderCopy(t, errorCopy(result.errorCode))}\n </p>\n )\n : null}\n {result.replayed === true\n ? <p className={css['resultDetail']}>{t('inject.resultReplayed')}</p>\n : null}\n <div className={css['footer']}>\n {closeButton}\n {actions.canReprepare\n ? (\n <button\n type=\"button\"\n className={css['btnPrimary']}\n onClick={() => { dispatch({ type: 'RESET' }) }}\n >\n {t('inject.reprepare')}\n </button>\n )\n : null}\n {result.outcome === 'delivered' && props.onClose === undefined\n ? (\n <button\n type=\"button\"\n className={css['btnPrimary']}\n onClick={() => { dispatch({ type: 'RESET' }) }}\n >\n {t('inject.done')}\n </button>\n )\n : null}\n </div>\n </div>\n </section>\n )\n }\n\n // ── zone 2: confirm / executing ─────────────────────────────────────────\n if (state.phase === 'confirm' || state.phase === 'executing') {\n const executing = state.phase === 'executing'\n const countdown = state.phase === 'confirm'\n ? tokenCountdown(state.expiresAt, clock)\n : null\n return (\n <section className={css['panel']} aria-label={t('inject.confirmTitle')}>\n <header className={css['header']}>\n <h3 className={css['title']}>{t('inject.confirmTitle')}</h3>\n </header>\n <div className={css['body']}>\n <PlanBox t={t} plan={state.plan} />\n <Warnings t={t} agent={state.plan.target.agent} />\n {countdown !== null\n ? (\n <p className={css['countdown']} role=\"timer\">\n {t('inject.countdown', { seconds: countdown.seconds })}\n </p>\n )\n : null}\n <div className={css['footer']}>\n <button\n type=\"button\"\n className={css['btn']}\n disabled={executing}\n onClick={() => { dispatch({ type: 'CANCEL' }) }}\n >\n {t('inject.cancel')}\n </button>\n <button\n type=\"button\"\n className={css['btnDanger']}\n disabled={executing}\n onClick={() => { void handleExecute() }}\n >\n {t(executing ? 'inject.executing' : 'inject.confirmExecute')}\n </button>\n </div>\n </div>\n </section>\n )\n }\n\n // ── zone 1: editor (idle / preparing) ───────────────────────────────────\n const preparing = state.phase === 'preparing'\n const canEdit = props.target !== null && !preparing\n const usage = byteUsage(validation.bytes)\n const notice = state.phase === 'idle' ? state.notice : null\n const showInvalid = !validation.ok && validation.code !== 'empty'\n\n return (\n <section className={css['panel']} aria-label={t('inject.title')}>\n <header className={css['header']}>\n <h3 className={css['title']}>{t('inject.title')}</h3>\n </header>\n <div className={css['body']}>\n {notice !== null\n ? (\n <p\n className={notice.kind === 'token_expired' ? css['noticeWarn'] : css['noticeError']}\n role=\"alert\"\n >\n {renderCopy(t, noticeCopy(notice))}\n {notice.kind === 'prepare_rejected' && notice.detail !== undefined\n ? <span className={css['noticeDetail']}> {notice.detail}</span>\n : null}\n </p>\n )\n : null}\n\n <div className={css['targetRow']}>\n <span className={css['planKey']}>{t('inject.targetLabel')}</span>\n {props.target !== null\n ? (\n <span className={css['planValue']}>\n <span className={css['agentTag']}>{props.target.agent}</span>\n <span className={css['planTitle']} title={props.target.sessionId}>\n {props.target.title !== undefined && props.target.title !== ''\n ? props.target.title\n : props.target.sessionId}\n </span>\n </span>\n )\n : <span className={css['noTarget']}>{t('inject.noTarget')}</span>}\n </div>\n\n <div className={css['field']}>\n <label className={css['label']} htmlFor={textareaId}>\n {t('inject.messageLabel')}\n </label>\n <textarea\n id={textareaId}\n className={css['textarea']}\n value={draft}\n placeholder={t('inject.messagePlaceholder')}\n disabled={!canEdit}\n rows={5}\n onChange={(event) => { setDraft(event.target.value) }}\n />\n <div className={css['byteRow']}>\n <div className={css['byteBar']} aria-hidden>\n <div\n className={usage.over\n ? `${css['byteFill']} ${css['byteFillOver']}`\n : css['byteFill']}\n style={{ width: `${usage.ratio * 100}%` }}\n />\n </div>\n <span className={usage.over ? css['byteTextOver'] : css['byteText']}>\n {t('inject.byteCount', { bytes: usage.bytes, limit: usage.limit })}\n </span>\n </div>\n {showInvalid && !validation.ok\n ? (\n <p className={css['invalid']} role=\"alert\">\n {renderCopy(t, messageInvalidCopy(validation))}\n </p>\n )\n : null}\n </div>\n\n <fieldset className={css['modes']}>\n <legend className={css['label']}>{t('inject.modeLabel')}</legend>\n {(['queue', 'steer'] as const).map(option => (\n <label key={option} className={css['modeOption']}>\n <input\n type=\"radio\"\n name={modeGroup}\n value={option}\n checked={mode === option}\n disabled={!canEdit}\n onChange={() => { setMode(option) }}\n />\n <span className={css['modeText']}>\n <span className={css['modeLabel']}>{t(MODE_COPY[option].label)}</span>\n <span className={css['modeHint']}>{t(MODE_COPY[option].hint)}</span>\n </span>\n </label>\n ))}\n </fieldset>\n\n <Warnings t={t} agent={props.target?.agent ?? null} />\n\n <div className={css['footer']}>\n {closeButton}\n <button\n type=\"button\"\n className={css['btnPrimary']}\n disabled={!gate.canPrepare}\n onClick={() => { void handlePrepare() }}\n >\n {t(preparing ? 'inject.preparing' : 'inject.prepare')}\n </button>\n </div>\n </div>\n </section>\n )\n}\n","/**\n * Transport helpers for the detail view's M3 read endpoints. api.ts (T2.1)\n * predates M3: its `fetchSession` is typed for the M1 placeholder response\n * and it has no timeline-pagination call — and per the task boundary its\n * existing exports must not change (the S7 integration wave unifies the\n * data layer). So this module carries the missing calls with the same\n * posture as api.ts (same-origin relative paths, bounded timeout,\n * normalized ApiError, injectable primitives), reusing api.ts's exported\n * building blocks instead of redefining them.\n *\n * Read-only surface, no retry policy here (transport, not policy).\n *\n * @module\n */\n\nimport {\n API_PREFIX,\n ApiError,\n DEFAULT_TIMEOUT_MS,\n type AbortControllerLike,\n type FetchLike,\n type RequestOptions,\n type ResponseLike,\n type SessionView,\n type TimerHandle,\n} from '../api.ts'\nimport type { TimelinePageWire } from './logic.ts'\n\n// ---------------------------------------------------------------------------\n// Wire mirrors of the M3 `GET session/<id>` body (host: routes.ts\n// handleSession with fusion wired; unified row: fusion.ts UnifiedSession).\n// ---------------------------------------------------------------------------\n\n/** One deduplicated cross-agent session row (host: fusion.ts). */\nexport interface UnifiedSessionWire {\n agent: string\n sessionId: string\n origin: 'dsh-live' | 'sidecar' | 'merged'\n live: boolean\n status: string\n title: string\n project: string\n /** Unix epoch ms of the freshest signal across both sources. */\n lastActivityAt: number\n lastEvent: { ts: string; kind: string; text: string } | null\n lastSeq: number | null\n gap: boolean\n parentId: string | null\n extra: Record<string, unknown>\n}\n\n/** Body of `GET session/<id>` on an M3 host (fusion wired). */\nexport interface SessionDetailWire {\n /** Sidecar board row; null for dsh-live sessions the sidecar has not seen. */\n session: SessionView | null\n /** Fused row; null when only the board knows the session. */\n unified: UnifiedSessionWire | null\n /** Newest timeline page; null only on a pre-M3 host (placeholder contract). */\n timeline: TimelinePageWire | null\n}\n\n// ---------------------------------------------------------------------------\n// Bounded same-origin GET (api.ts `request` is module-private; this is the\n// same discipline in miniature: timeout, external abort, ApiError taxonomy).\n// ---------------------------------------------------------------------------\n\nconst defaultSetTimeout = (fn: () => void, ms: number): TimerHandle =>\n globalThis.setTimeout(fn, ms)\n\nconst defaultClearTimeout = (handle: TimerHandle): void => {\n globalThis.clearTimeout(handle as ReturnType<typeof globalThis.setTimeout>)\n}\n\nconst defaultCreateAbortController = (): AbortControllerLike => new AbortController()\n\nfunction resolveFetch(opts: RequestOptions): FetchLike {\n if (opts.fetch !== undefined) return opts.fetch\n return globalThis.fetch as unknown as FetchLike\n}\n\nasync function getJson(path: string, opts: RequestOptions): Promise<unknown> {\n const doFetch = resolveFetch(opts)\n const controller = (opts.createAbortController ?? defaultCreateAbortController)()\n const setT = opts.setTimeout ?? defaultSetTimeout\n const clearT = opts.clearTimeout ?? defaultClearTimeout\n\n let timedOut = false\n let externallyAborted = false\n const timer = setT(() => {\n timedOut = true\n controller.abort()\n }, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n\n const external = opts.signal\n const onExternalAbort = (): void => {\n externallyAborted = true\n controller.abort()\n }\n if (external !== undefined) {\n if (external.aborted) onExternalAbort()\n else external.addEventListener('abort', onExternalAbort)\n }\n\n try {\n let res: ResponseLike\n try {\n res = await doFetch(path, { method: 'GET', signal: controller.signal })\n } catch (err) {\n if (timedOut) throw new ApiError('timeout', 'request_timeout', null, err)\n if (externallyAborted) throw new ApiError('aborted', 'request_aborted', null, err)\n throw new ApiError('network', 'network_error', null, err)\n }\n if (!res.ok) {\n let reason = `http_${res.status}`\n try {\n const body = await res.json()\n if (typeof body === 'object' && body !== null) {\n const value = (body as Record<string, unknown>)['reason']\n if (typeof value === 'string' && value !== '') reason = value\n }\n } catch {\n // Non-JSON error body: the status-derived reason stands.\n }\n throw new ApiError('http', reason, res.status)\n }\n try {\n return await res.json()\n } catch (err) {\n if (timedOut) throw new ApiError('timeout', 'request_timeout', null, err)\n throw new ApiError('parse', 'invalid_json', res.status, err)\n }\n } finally {\n clearT(timer)\n if (external !== undefined) external.removeEventListener('abort', onExternalAbort)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public surface.\n// ---------------------------------------------------------------------------\n\n/**\n * `GET <prefix>/session/<id>` typed for the M3 body. Unknown ids reject\n * with an ApiError carrying the server's `session_not_found` reason; a\n * pre-M3 host answers `timeline: null` (the caller degrades honestly).\n */\nexport async function fetchSessionDetail(\n sessionId: string,\n opts: RequestOptions = {},\n): Promise<SessionDetailWire> {\n const path = `${API_PREFIX}/session/${encodeURIComponent(sessionId)}`\n return (await getJson(path, opts)) as SessionDetailWire\n}\n\n/**\n * `GET <prefix>/session/<id>/timeline?cursor=&limit=` — one older history\n * page. `cursor` is the opaque `nextCursor` token from a previous page,\n * passed through verbatim (the server rejects tampered tokens with 400\n * `invalid_cursor`); omit it for the newest window (listen-mode refetch).\n */\nexport async function fetchTimelinePage(\n sessionId: string,\n opts: RequestOptions & { cursor?: string | null; limit?: number } = {},\n): Promise<TimelinePageWire> {\n const params = new URLSearchParams()\n if (opts.cursor !== undefined && opts.cursor !== null && opts.cursor !== '') {\n params.set('cursor', opts.cursor)\n }\n if (opts.limit !== undefined) params.set('limit', String(opts.limit))\n const query = params.toString()\n const path = `${API_PREFIX}/session/${encodeURIComponent(sessionId)}/timeline${\n query === '' ? '' : `?${query}`\n }`\n return (await getJson(path, opts)) as TimelinePageWire\n}\n","/**\n * Transport helpers for the M3 deep-query read endpoints (lineage /\n * search / projects), completing what detail/transport.ts started for\n * `session/<id>` + timeline. Same posture as api.ts: same-origin relative\n * paths under {@link API_PREFIX}, bounded timeout, normalized ApiError,\n * injectable browser primitives so node tests run without a DOM.\n *\n * Response typing reuses the components' own wire mirrors\n * (dsh-tools/logic.ts, board/project-view-logic.ts) so the transport and\n * the render pipelines can never disagree about a shape.\n *\n * Read-only surface, no retry policy here (transport, not policy).\n *\n * @module\n */\n\nimport {\n API_PREFIX,\n ApiError,\n DEFAULT_TIMEOUT_MS,\n type AbortControllerLike,\n type FetchLike,\n type RequestOptions,\n type ResponseLike,\n type TimerHandle,\n} from './api.ts'\nimport type { LineageResponseVM, SearchResponseVM } from './dsh-tools/logic.ts'\nimport type { ProjectGroupVM } from './board/project-view-logic.ts'\n\n/** Body of `GET <prefix>/projects` (host: routes.ts handleProjects). */\nexport interface ProjectsResponseVM {\n groups: ProjectGroupVM[]\n}\n\n// ---------------------------------------------------------------------------\n// Bounded same-origin GET (api.ts `request` is module-private; this is the\n// same discipline detail/transport.ts uses: timeout, external abort,\n// ApiError taxonomy).\n// ---------------------------------------------------------------------------\n\nconst defaultSetTimeout = (fn: () => void, ms: number): TimerHandle =>\n globalThis.setTimeout(fn, ms)\n\nconst defaultClearTimeout = (handle: TimerHandle): void => {\n globalThis.clearTimeout(handle as ReturnType<typeof globalThis.setTimeout>)\n}\n\nconst defaultCreateAbortController = (): AbortControllerLike => new AbortController()\n\nfunction resolveFetch(opts: RequestOptions): FetchLike {\n if (opts.fetch !== undefined) return opts.fetch\n return globalThis.fetch as unknown as FetchLike\n}\n\nasync function getJson(path: string, opts: RequestOptions): Promise<unknown> {\n const doFetch = resolveFetch(opts)\n const controller = (opts.createAbortController ?? defaultCreateAbortController)()\n const setT = opts.setTimeout ?? defaultSetTimeout\n const clearT = opts.clearTimeout ?? defaultClearTimeout\n\n let timedOut = false\n let externallyAborted = false\n const timer = setT(() => {\n timedOut = true\n controller.abort()\n }, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n\n const external = opts.signal\n const onExternalAbort = (): void => {\n externallyAborted = true\n controller.abort()\n }\n if (external !== undefined) {\n if (external.aborted) onExternalAbort()\n else external.addEventListener('abort', onExternalAbort)\n }\n\n try {\n let res: ResponseLike\n try {\n res = await doFetch(path, { method: 'GET', signal: controller.signal })\n } catch (err) {\n if (timedOut) throw new ApiError('timeout', 'request_timeout', null, err)\n if (externallyAborted) throw new ApiError('aborted', 'request_aborted', null, err)\n throw new ApiError('network', 'network_error', null, err)\n }\n if (!res.ok) {\n let reason = `http_${res.status}`\n try {\n const body = await res.json()\n if (typeof body === 'object' && body !== null) {\n const value = (body as Record<string, unknown>)['reason']\n if (typeof value === 'string' && value !== '') reason = value\n }\n } catch {\n // Non-JSON error body: the status-derived reason stands.\n }\n throw new ApiError('http', reason, res.status)\n }\n try {\n return await res.json()\n } catch (err) {\n if (timedOut) throw new ApiError('timeout', 'request_timeout', null, err)\n throw new ApiError('parse', 'invalid_json', res.status, err)\n }\n } finally {\n clearT(timer)\n if (external !== undefined) external.removeEventListener('abort', onExternalAbort)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public surface.\n// ---------------------------------------------------------------------------\n\n/**\n * `GET <prefix>/lineage/<id>` — dsh lineage trace. Degradation\n * (sessionQuery absent / trace failed) is DATA: the host answers 200 with\n * `{available:false, reason}`, so only transport/HTTP failures reject\n * (e.g. 501 `fusion_not_wired` on a pre-M3 host).\n */\nexport async function fetchLineage(\n sessionId: string,\n opts: RequestOptions = {},\n): Promise<LineageResponseVM> {\n const path = `${API_PREFIX}/lineage/${encodeURIComponent(sessionId)}`\n return (await getJson(path, opts)) as LineageResponseVM\n}\n\n/**\n * `GET <prefix>/search?q=&project=&limit=` — cross-agent session search.\n * At least one of `q` / `project` must be non-blank (the host answers 400\n * `invalid_request` otherwise — callers gate before dialing). The response\n * echoes the mode; `filter-only` is the honest degradation, not an error.\n */\nexport async function fetchSearch(\n opts: RequestOptions & { q?: string; project?: string | null; limit?: number } = {},\n): Promise<SearchResponseVM> {\n const params = new URLSearchParams()\n if (opts.q !== undefined && opts.q.trim() !== '') params.set('q', opts.q)\n if (opts.project !== undefined && opts.project !== null && opts.project.trim() !== '') {\n params.set('project', opts.project)\n }\n if (opts.limit !== undefined) params.set('limit', String(opts.limit))\n return (await getJson(`${API_PREFIX}/search?${params.toString()}`, opts)) as SearchResponseVM\n}\n\n/** `GET <prefix>/projects` — cross-agent project groups. */\nexport async function fetchProjects(opts: RequestOptions = {}): Promise<ProjectsResponseVM> {\n return (await getJson(`${API_PREFIX}/projects`, opts)) as ProjectsResponseVM\n}\n","/**\n * Session-detail data glue (T5.10b): one framework-free store per opened\n * detail view, feeding the controlled SessionDetail / LineageTree\n * components. Owns transport orchestration only — accumulation and\n * presentation stay in detail/logic.ts and dsh-tools/logic.ts:\n *\n * - initial load via detail/transport.ts `fetchSessionDetail` (header +\n * newest timeline page in one round-trip);\n * - older-history pagination via `fetchTimelinePage(cursor)`;\n * - listen mode: the controller's SSE `state` frames carry no per-session\n * events (ADR-2/ADR-3), so the integration calls {@link\n * DetailStore.notifySnapshot} on every frame and the store refetches the\n * newest window — coalesced so at most one refetch is in flight and at\n * most one more is queued;\n * - dsh-exclusive lineage: fetched only for dsh sessions; non-dsh agents\n * get the client-minted `not_dsh_session` degradation without dialing\n * (dsh-tools/logic.ts `externalLineageFallback`).\n *\n * Same store discipline as controller.ts: subscribe/getState for\n * `useSyncExternalStore`, immutable state snapshots, `dispose()` makes\n * late settlements no-ops. All transport is injectable for node tests.\n *\n * @module\n */\n\nimport { isApiError, type RequestOptions } from './api.ts'\nimport {\n fetchSessionDetail,\n fetchTimelinePage,\n type SessionDetailWire,\n} from './detail/transport.ts'\nimport { fetchLineage } from './m3-transport.ts'\nimport {\n applyListenPage,\n applyTimelinePage,\n createTimelineVM,\n type TimelinePageWire,\n type TimelineVM,\n} from './detail/logic.ts'\nimport {\n externalLineageFallback,\n type LineageResponseVM,\n type LineageTraceVM,\n} from './dsh-tools/logic.ts'\n\n// ---------------------------------------------------------------------------\n// State shapes.\n// ---------------------------------------------------------------------------\n\n/**\n * Header seed carried from the surface that opened the detail view (board\n * card / project row); structurally SessionDetailHeaderVM. The\n * authoritative header comes from the fetched detail body — the hint just\n * paints the first frame and covers degraded loads.\n */\nexport interface DetailHeaderHint {\n agent: string\n title: string\n project: string\n status: string\n}\n\n/** Lineage panel slice (mirrors LineageTree props). */\nexport interface LineageSliceState {\n /** True until the first resolution (fetch settle or client degrade). */\n loading: boolean\n /** Transport/HTTP failure reason code, or null. */\n error: string | null\n available: boolean\n reason: string | null\n detail: string | null\n trace: LineageTraceVM | null\n}\n\n/** Everything the detail view renders (mirrors SessionDetail props). */\nexport interface DetailGlueState {\n sessionId: string\n header: DetailHeaderHint\n timeline: TimelineVM\n /** True while the initial load or an older-page fetch is in flight. */\n loading: boolean\n /** Machine reason code of the last failure, or null. */\n error: string | null\n hasMore: boolean\n listening: boolean\n /** True once the initial load succeeded (timeline usable). */\n ready: boolean\n lineage: LineageSliceState\n}\n\nconst EMPTY_HEADER: DetailHeaderHint = { agent: '', title: '', project: '', status: '' }\n\nconst INITIAL_LINEAGE: LineageSliceState = {\n loading: true,\n error: null,\n available: false,\n reason: null,\n detail: null,\n trace: null,\n}\n\n/** Map any settlement failure to a stable machine reason code. */\nfunction reasonOf(err: unknown): string {\n return isApiError(err) ? err.reason : 'network_error'\n}\n\n// ---------------------------------------------------------------------------\n// Injectable transport (defaults are the real calls).\n// ---------------------------------------------------------------------------\n\nexport interface DetailStoreOptions {\n /** Header seed from the opening surface; null paints an empty header. */\n hint?: DetailHeaderHint | null\n /** Newest-window size for listen-mode refetches (server default if unset). */\n listenLimit?: number\n fetchDetailFn?: (sessionId: string, opts?: RequestOptions) => Promise<SessionDetailWire>\n fetchPageFn?: (\n sessionId: string,\n opts?: RequestOptions & { cursor?: string | null; limit?: number },\n ) => Promise<TimelinePageWire>\n fetchLineageFn?: (sessionId: string, opts?: RequestOptions) => Promise<LineageResponseVM>\n}\n\n// ---------------------------------------------------------------------------\n// The store.\n// ---------------------------------------------------------------------------\n\nexport class DetailStore {\n private state: DetailGlueState\n private readonly listeners = new Set<() => void>()\n private readonly fetchDetailFn: NonNullable<DetailStoreOptions['fetchDetailFn']>\n private readonly fetchPageFn: NonNullable<DetailStoreOptions['fetchPageFn']>\n private readonly fetchLineageFn: NonNullable<DetailStoreOptions['fetchLineageFn']>\n private readonly listenLimit: number | undefined\n private disposed = false\n private opened = false\n private paging = false\n private listenInFlight = false\n private listenQueued = false\n private lineageStarted = false\n\n constructor(sessionId: string, options: DetailStoreOptions = {}) {\n this.fetchDetailFn = options.fetchDetailFn ?? fetchSessionDetail\n this.fetchPageFn = options.fetchPageFn ?? fetchTimelinePage\n this.fetchLineageFn = options.fetchLineageFn ?? fetchLineage\n this.listenLimit = options.listenLimit\n this.state = {\n sessionId,\n header: options.hint ?? EMPTY_HEADER,\n timeline: createTimelineVM(sessionId),\n loading: false,\n error: null,\n hasMore: false,\n listening: false,\n ready: false,\n lineage: INITIAL_LINEAGE,\n }\n }\n\n subscribe = (fn: () => void): (() => void) => {\n this.listeners.add(fn)\n return () => { this.listeners.delete(fn) }\n }\n\n getState = (): DetailGlueState => this.state\n\n private setState(patch: Partial<DetailGlueState>): void {\n if (this.disposed) return\n this.state = { ...this.state, ...patch }\n for (const fn of [...this.listeners]) fn()\n }\n\n /** Initial load: header + newest timeline page, then lineage. Idempotent. */\n async open(): Promise<void> {\n if (this.opened || this.disposed) return\n this.opened = true\n this.setState({ loading: true, error: null })\n try {\n const wire = await this.fetchDetailFn(this.state.sessionId)\n if (this.disposed) return\n const header = headerFromDetailWire(wire) ?? this.state.header\n if (wire.timeline === null) {\n // Pre-M3 host placeholder contract: card data only, no timeline.\n this.setState({ header, loading: false, error: 'fusion_not_wired' })\n } else {\n const timeline = applyTimelinePage(\n createTimelineVM(this.state.sessionId),\n wire.timeline,\n )\n this.setState({\n header,\n timeline,\n loading: false,\n error: null,\n hasMore: timeline.nextCursor !== null,\n ready: true,\n })\n }\n void this.loadLineage(header.agent)\n } catch (err) {\n if (this.disposed) return\n this.setState({ loading: false, error: reasonOf(err) })\n // The board hint still tells the agent kind; resolve the lineage\n // slice anyway so the panel degrades instead of spinning forever.\n void this.loadLineage(this.state.header.agent)\n }\n }\n\n /** Fetch one older history page via the accumulated cursor. */\n async loadMore(): Promise<void> {\n const { timeline } = this.state\n if (this.disposed || this.paging || !this.state.ready) return\n if (timeline.nextCursor === null) return\n this.paging = true\n this.setState({ loading: true, error: null })\n try {\n const page = await this.fetchPageFn(this.state.sessionId, {\n cursor: timeline.nextCursor,\n })\n if (this.disposed) return\n const next = applyTimelinePage(this.state.timeline, page)\n this.setState({\n timeline: next,\n loading: false,\n hasMore: next.nextCursor !== null,\n })\n } catch (err) {\n if (this.disposed) return\n // Entries already shown stay visible; the view renders the reason\n // as an inline banner (deriveDetailBodyState 'list' arm).\n this.setState({ loading: false, error: reasonOf(err) })\n } finally {\n this.paging = false\n }\n }\n\n /** Flip listen mode; turning it on refetches the newest window at once. */\n toggleListen(): void {\n const listening = !this.state.listening\n this.setState({ listening })\n if (listening) this.scheduleListenRefetch()\n }\n\n /**\n * SSE `state` frame hook (one call per controller notification). Refreshes\n * the header from the live board card when given, and in listen mode\n * triggers a coalesced newest-window refetch.\n */\n notifySnapshot(card: DetailHeaderHint | null): void {\n if (this.disposed) return\n if (card !== null) {\n const h = this.state.header\n if (\n card.agent !== h.agent ||\n card.title !== h.title ||\n card.project !== h.project ||\n card.status !== h.status\n ) {\n this.setState({ header: card })\n }\n }\n if (this.state.listening && this.state.ready) this.scheduleListenRefetch()\n }\n\n /** At most one refetch in flight; at most one more queued (idempotence). */\n private scheduleListenRefetch(): void {\n if (this.disposed || !this.state.ready) return\n if (this.listenInFlight) {\n this.listenQueued = true\n return\n }\n this.listenInFlight = true\n void this.runListenRefetch()\n }\n\n private async runListenRefetch(): Promise<void> {\n try {\n const page = await this.fetchPageFn(this.state.sessionId, {\n ...(this.listenLimit !== undefined ? { limit: this.listenLimit } : {}),\n })\n if (this.disposed) return\n this.setState({ timeline: applyListenPage(this.state.timeline, page) })\n } catch {\n // Listen refetch is best-effort: the next SSE frame retries; the\n // already-rendered timeline must not degrade into an error state.\n } finally {\n this.listenInFlight = false\n if (this.listenQueued && !this.disposed) {\n this.listenQueued = false\n this.scheduleListenRefetch()\n }\n }\n }\n\n /** Resolve the lineage slice once (dsh-only capability; see module doc). */\n private async loadLineage(agent: string): Promise<void> {\n if (this.lineageStarted || this.disposed) return\n if (agent.trim() === '') {\n // Agent unknown (load failed before the header resolved): degrade\n // as non-dsh rather than dialing a lineage endpoint blind.\n this.lineageStarted = true\n this.setState({\n lineage: {\n loading: false, error: null, available: false,\n reason: 'not_dsh_session', detail: null, trace: null,\n },\n })\n return\n }\n this.lineageStarted = true\n const fallback = externalLineageFallback(agent)\n if (fallback !== null) {\n this.setState({\n lineage: {\n loading: false,\n error: null,\n available: fallback.available,\n reason: fallback.reason,\n detail: null,\n trace: fallback.trace,\n },\n })\n return\n }\n try {\n const body = await this.fetchLineageFn(this.state.sessionId)\n if (this.disposed) return\n this.setState({\n lineage: {\n loading: false,\n error: null,\n available: body.available,\n reason: body.reason,\n detail: body.detail ?? null,\n trace: body.trace,\n },\n })\n } catch (err) {\n if (this.disposed) return\n this.setState({ lineage: { ...INITIAL_LINEAGE, loading: false, error: reasonOf(err) } })\n }\n }\n\n /** Late settlements become no-ops; subscribers are dropped. Idempotent. */\n dispose(): void {\n this.disposed = true\n this.listeners.clear()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers (exported for tests and for the mount integration).\n// ---------------------------------------------------------------------------\n\n/**\n * Authoritative header from the M3 detail body: the fused row wins (it\n * merges both sources), the sidecar board row covers fusion-less hosts;\n * null when the body carries neither (caller keeps its hint).\n */\nexport function headerFromDetailWire(wire: SessionDetailWire): DetailHeaderHint | null {\n if (wire.unified !== null) {\n return {\n agent: wire.unified.agent,\n title: wire.unified.title,\n project: wire.unified.project,\n status: wire.unified.status,\n }\n }\n if (wire.session !== null) {\n return {\n agent: wire.session.agent,\n title: wire.session.title,\n project: wire.session.project,\n status: wire.session.status,\n }\n }\n return null\n}\n\n/**\n * Find the live board card of a session (controller SessionCardVM rows) →\n * header hint, or null when off-board.\n */\nexport function findCardHint(\n sessions: ReadonlyArray<{\n agent: string\n sessionId: string\n title: string\n project: string\n status: string\n }>,\n sessionId: string,\n): DetailHeaderHint | null {\n const card = sessions.find((s) => s.sessionId === sessionId)\n if (card === undefined) return null\n return { agent: card.agent, title: card.title, project: card.project, status: card.status }\n}\n","/**\n * Cross-agent search data glue (T5.10b): a framework-free store feeding\n * the controlled SearchPanel. Normalization (matchedBy vocabulary, snippet\n * highlighting) stays in dsh-tools/logic.ts — this store owns the query\n * box state and transport orchestration only.\n *\n * Submit model: explicit user submit (no as-you-type dialing). A blank\n * query with no project filter clears the results locally — the host\n * answers 400 `invalid_request` for it, so the store never dials that.\n * Stale results are replaced per settle; a failed submit keeps the last\n * results visible with an error banner (SearchPanel renders error-first).\n *\n * Same store discipline as controller.ts: subscribe/getState for\n * `useSyncExternalStore`, immutable snapshots, dispose() = late no-ops.\n * Out-of-order settles are ignored via a submit ticket.\n *\n * @module\n */\n\nimport { isApiError, type RequestOptions } from './api.ts'\nimport { fetchSearch } from './m3-transport.ts'\nimport {\n normalizeSearchItems,\n type SearchItemVM,\n type SearchMode,\n type SearchResponseVM,\n} from './dsh-tools/logic.ts'\n\nexport interface SearchGlueState {\n /** Controlled query box value. */\n query: string\n /** Query echoed by the last settled response ('' before the first). */\n submittedQuery: string\n /** Mode of the last settled response; 'full-text' until told otherwise. */\n mode: SearchMode\n items: SearchItemVM[]\n loading: boolean\n /** Machine reason code of the last failure, or null. */\n error: string | null\n /** Active project filter sent with submits and echoed back, if any. */\n project: string | null\n}\n\nexport interface SearchStoreOptions {\n fetchSearchFn?: (\n opts?: RequestOptions & { q?: string; project?: string | null; limit?: number },\n ) => Promise<SearchResponseVM>\n /** Result cap passed to the endpoint (server default if unset). */\n limit?: number\n /** Fixed project filter (e.g. a search scoped from the project view). */\n project?: string | null\n}\n\nconst INITIAL_STATE: SearchGlueState = {\n query: '',\n submittedQuery: '',\n mode: 'full-text',\n items: [],\n loading: false,\n error: null,\n project: null,\n}\n\nexport class SearchStore {\n private state: SearchGlueState\n private readonly listeners = new Set<() => void>()\n private readonly fetchSearchFn: NonNullable<SearchStoreOptions['fetchSearchFn']>\n private readonly limit: number | undefined\n private disposed = false\n private ticket = 0\n\n constructor(options: SearchStoreOptions = {}) {\n this.fetchSearchFn = options.fetchSearchFn ?? fetchSearch\n this.limit = options.limit\n this.state = { ...INITIAL_STATE, project: options.project ?? null }\n }\n\n subscribe = (fn: () => void): (() => void) => {\n this.listeners.add(fn)\n return () => { this.listeners.delete(fn) }\n }\n\n getState = (): SearchGlueState => this.state\n\n private setState(patch: Partial<SearchGlueState>): void {\n if (this.disposed) return\n this.state = { ...this.state, ...patch }\n for (const fn of [...this.listeners]) fn()\n }\n\n /** Controlled input change (no dialing). */\n setQuery(query: string): void {\n this.setState({ query })\n }\n\n /** Submit the current query; blank + no project filter clears locally. */\n async submit(): Promise<void> {\n if (this.disposed) return\n const q = this.state.query.trim()\n const project = this.state.project\n if (q === '' && (project === null || project.trim() === '')) {\n this.ticket += 1 // outrun any in-flight settle\n this.setState({\n items: [], submittedQuery: '', loading: false, error: null,\n })\n return\n }\n const ticket = (this.ticket += 1)\n this.setState({ loading: true, error: null })\n try {\n const response = await this.fetchSearchFn({\n q,\n project,\n ...(this.limit !== undefined ? { limit: this.limit } : {}),\n })\n if (this.disposed || ticket !== this.ticket) return\n this.adoptResponse(response)\n } catch (err) {\n if (this.disposed || ticket !== this.ticket) return\n this.setState({\n loading: false,\n error: isApiError(err) ? err.reason : 'network_error',\n })\n }\n }\n\n /** Apply one settled wire response (public seam for tests/materialize). */\n adoptResponse(response: SearchResponseVM): void {\n this.setState({\n items: normalizeSearchItems(response),\n mode: response.mode,\n submittedQuery: response.query,\n project: response.project,\n loading: false,\n error: null,\n })\n }\n\n dispose(): void {\n this.disposed = true\n this.listeners.clear()\n }\n}\n","/**\n * AI bypass-analysis data glue (T5.10b, design §4.e.3 / §5.1): a\n * framework-free store per analysis conversation, driving the controlled\n * AnalysisPanel over the `POST action` `analysis.*` trio (host contract:\n * routes.ts handleAnalysis* over analysis.ts AnalysisEngine).\n *\n * Wire semantics this store encodes:\n * - a settled engine result comes back with the HTTP status derived from\n * its `errorCode` (timeout→504, too_many_active→429, create_failed→502,\n * cancelled→200); api.ts postAction folds non-200 bodies into ApiError\n * with `reason` = that code — so failures arrive here as ApiError and\n * the cancelled-terminal arrives as a 200 body;\n * - the write gate answers 403 `analysis_disabled` (fail-closed), an\n * agents-less composition answers 501 `analysis_unavailable`, and a\n * pre-analysis host answers 400 `unknown_action` (mapped to the same\n * honest \"unavailable\" terminal);\n * - a request timeout DISPOSES the engine session (terminal), a followup\n * timeout KEEPS it (non-terminal notice; analysis.ts contract).\n *\n * Token honesty: this store dials analysis turns only on explicit user\n * intent (start / followup / stop) — never automatically. The one\n * automatic call is dispose()'s fire-and-forget cancel, which spends no\n * tokens and only releases the engine session slot (F2).\n *\n * @module\n */\n\nimport {\n isApiError,\n postAction,\n type AnalysisTargetKind,\n type RequestOptions,\n} from './api.ts'\n\n// ---------------------------------------------------------------------------\n// Wire mirror (host: analysis.ts AnalysisResult; hand-written, client\n// program cannot import host modules).\n// ---------------------------------------------------------------------------\n\n/** Terminal fact about one analysis turn (host: analysis.ts). */\nexport type AnalysisOutcomeWire = 'completed' | 'failed' | 'cancelled' | 'timeout'\n\n/** Body of a settled `analysis.request` / `analysis.followup` action. */\nexport interface AnalysisResultWire {\n outcome: AnalysisOutcomeWire\n analysisSessionId?: string\n summary?: string\n truncated: boolean\n tokensHint?: number\n errorCode?: string\n detail?: string\n disclaimer: string\n}\n\n/** Analysis target selector (mirrors routes.ts AnalysisTargetRequest). */\nexport interface AnalysisTarget {\n targetKind: AnalysisTargetKind\n targetId?: string\n question?: string\n}\n\n// ---------------------------------------------------------------------------\n// Envelope builders (exported pure for tests).\n// ---------------------------------------------------------------------------\n\nexport function analysisRequestEnvelope(target: AnalysisTarget): {\n type: 'analysis.request'\n targetKind: AnalysisTargetKind\n targetId?: string\n question?: string\n} {\n return {\n type: 'analysis.request',\n targetKind: target.targetKind,\n ...(target.targetId !== undefined ? { targetId: target.targetId } : {}),\n ...(target.question !== undefined && target.question.trim() !== ''\n ? { question: target.question }\n : {}),\n }\n}\n\nexport function analysisFollowupEnvelope(analysisSessionId: string, question: string): {\n type: 'analysis.followup'\n analysisSessionId: string\n question: string\n} {\n return { type: 'analysis.followup', analysisSessionId, question }\n}\n\nexport function analysisCancelEnvelope(analysisSessionId: string): {\n type: 'analysis.cancel'\n analysisSessionId: string\n} {\n return { type: 'analysis.cancel', analysisSessionId }\n}\n\n// ---------------------------------------------------------------------------\n// State shapes.\n// ---------------------------------------------------------------------------\n\n/**\n * Conversation phases. `failed` is terminal for the conversation (restart\n * mints a new engine session); `stopped` is the user-cancelled terminal.\n */\nexport type AnalysisPhase =\n | 'idle'\n | 'requesting'\n | 'ready'\n | 'answering'\n | 'stopped'\n | 'failed'\n\n/** One settled turn: the initial summary (question null) or a follow-up. */\nexport interface AnalysisExchange {\n question: string | null\n summary: string\n truncated: boolean\n tokensHint: number | null\n}\n\nexport interface AnalysisGlueState {\n phase: AnalysisPhase\n analysisSessionId: string | null\n exchanges: AnalysisExchange[]\n /** Engine disclaimer verbatim; null before the first settle. */\n disclaimer: string | null\n /** Terminal failure code (phase 'failed'), else null. */\n errorCode: string | null\n /** Non-terminal notice code (kept session; retryable), else null. */\n noticeCode: string | null\n}\n\nconst INITIAL_STATE: AnalysisGlueState = {\n phase: 'idle',\n analysisSessionId: null,\n exchanges: [],\n disclaimer: null,\n errorCode: null,\n noticeCode: null,\n}\n\n/**\n * The action deadline: the engine bounds one turn at ~60s (analysis.ts\n * `turnTimeoutMs`), so the transport must outlive it to receive the\n * engine's own timeout verdict instead of racing it.\n */\nexport const ANALYSIS_POST_TIMEOUT_MS = 75_000\n\n/** ApiError reason codes that keep a live followup session usable. */\nconst RETRYABLE_FOLLOWUP_CODES = new Set([\n 'timeout', // engine turn timeout: session kept by contract\n 'request_timeout',\n 'network_error',\n])\n\nfunction failureCode(err: unknown): string {\n if (isApiError(err)) {\n // A pre-analysis host rejects the envelope as unknown_action — same\n // honest terminal as an assembled-but-agents-less host.\n return err.reason === 'unknown_action' ? 'analysis_unavailable' : err.reason\n }\n return 'network_error'\n}\n\n// ---------------------------------------------------------------------------\n// The store.\n// ---------------------------------------------------------------------------\n\nexport interface AnalysisStoreOptions {\n postActionFn?: typeof postAction\n timeoutMs?: number\n}\n\nexport class AnalysisStore {\n private state: AnalysisGlueState = INITIAL_STATE\n private readonly listeners = new Set<() => void>()\n private readonly postActionFn: typeof postAction\n private readonly timeoutMs: number\n private disposed = false\n\n constructor(options: AnalysisStoreOptions = {}) {\n this.postActionFn = options.postActionFn ?? postAction\n this.timeoutMs = options.timeoutMs ?? ANALYSIS_POST_TIMEOUT_MS\n }\n\n subscribe = (fn: () => void): (() => void) => {\n this.listeners.add(fn)\n return () => { this.listeners.delete(fn) }\n }\n\n getState = (): AnalysisGlueState => this.state\n\n private setState(patch: Partial<AnalysisGlueState>): void {\n if (this.disposed) return\n this.state = { ...this.state, ...patch }\n for (const fn of [...this.listeners]) fn()\n }\n\n private post(body: Parameters<typeof postAction>[0]): Promise<unknown> {\n const opts: RequestOptions = { timeoutMs: this.timeoutMs }\n return this.postActionFn(body, opts)\n }\n\n /** Start one analysis (allowed from idle and from the terminal phases). */\n async start(target: AnalysisTarget): Promise<void> {\n const { phase } = this.state\n if (this.disposed || phase === 'requesting' || phase === 'ready' || phase === 'answering') {\n return\n }\n this.setState({ ...INITIAL_STATE, phase: 'requesting' })\n try {\n const result = (await this.post(analysisRequestEnvelope(target))) as AnalysisResultWire\n if (this.disposed) {\n // The view unmounted while the request was in flight: the engine\n // session it just created has no owner left — release it so it\n // cannot strand one of the engine's bounded session slots (F2).\n const id = result?.analysisSessionId\n if (result?.outcome === 'completed' && typeof id === 'string' && id !== '') {\n this.fireCancel(id)\n }\n return\n }\n if (this.state.phase !== 'requesting') return\n this.adoptResult(null, result)\n } catch (err) {\n if (this.disposed || this.state.phase !== 'requesting') return\n // Request-path failures are all terminal: either the engine session\n // was never created, or (engine timeout) it was already disposed.\n this.setState({ phase: 'failed', errorCode: failureCode(err) })\n }\n }\n\n /** Ask a follow-up in the live analysis session. */\n async followup(question: string): Promise<void> {\n const id = this.state.analysisSessionId\n const q = question.trim()\n if (this.disposed || this.state.phase !== 'ready' || id === null || q === '') return\n this.setState({ phase: 'answering', noticeCode: null })\n // Widened re-reads: setState/stop() move the phase between awaits, and\n // TS's property narrowing from the entry guard would misjudge that.\n const phaseNow = (): AnalysisPhase => this.state.phase\n try {\n const result = (await this.post(analysisFollowupEnvelope(id, q))) as AnalysisResultWire\n if (this.disposed || phaseNow() !== 'answering') return\n this.adoptResult(q, result)\n } catch (err) {\n if (this.disposed || phaseNow() !== 'answering') return\n const code = failureCode(err)\n if (RETRYABLE_FOLLOWUP_CODES.has(code)) {\n // The engine keeps the session on a turn timeout; transport-level\n // failures are unknowable, so stay usable and let the user retry.\n this.setState({ phase: 'ready', noticeCode: code })\n } else {\n this.setState({ phase: 'failed', errorCode: code })\n }\n }\n }\n\n /** Release the analysis session (idempotent on the engine side). */\n async stop(): Promise<void> {\n const id = this.state.analysisSessionId\n const { phase } = this.state\n if (this.disposed || id === null || (phase !== 'ready' && phase !== 'answering')) return\n try {\n await this.post(analysisCancelEnvelope(id))\n if (this.disposed) return\n // An in-flight followup now settles against a stopped phase and is\n // dropped by the phase recheck above.\n this.setState({ phase: 'stopped', noticeCode: null })\n } catch {\n if (this.disposed) return\n this.setState({ noticeCode: 'cancel_failed' })\n }\n }\n\n /** Fold one settled 200 result into the conversation. */\n private adoptResult(question: string | null, result: AnalysisResultWire): void {\n if (result.outcome === 'completed') {\n this.setState({\n phase: 'ready',\n analysisSessionId: result.analysisSessionId ?? this.state.analysisSessionId,\n exchanges: [\n ...this.state.exchanges,\n {\n question,\n summary: result.summary ?? '',\n truncated: result.truncated,\n tokensHint: result.tokensHint ?? null,\n },\n ],\n disclaimer: result.disclaimer,\n errorCode: null,\n noticeCode: null,\n })\n return\n }\n // A 200 non-completed result is the engine's `cancelled` verdict (all\n // other failure codes ride non-200 statuses); treat unknown shapes as\n // terminal too — never invent a usable session.\n this.setState({\n phase: result.errorCode === 'cancelled' || result.outcome === 'cancelled'\n ? 'stopped'\n : 'failed',\n errorCode: result.errorCode ?? result.outcome,\n disclaimer: result.disclaimer,\n })\n }\n\n /**\n * Late settlements become no-ops; subscribers are dropped. Idempotent.\n * A live engine session is released with a fire-and-forget cancel so\n * unmount / session-switch remounts cannot strand `maxActiveSessions`\n * slots until plugin unload (F2). Cancel is idempotent on the engine\n * side; a transport failure is silently ignored (nothing to surface —\n * the store is gone).\n */\n dispose(): void {\n if (this.disposed) return\n const { analysisSessionId, phase } = this.state\n this.disposed = true\n this.listeners.clear()\n if (analysisSessionId !== null && (phase === 'ready' || phase === 'answering')) {\n this.fireCancel(analysisSessionId)\n }\n }\n\n /** Fire-and-forget analysis.cancel (failures are deliberately silent). */\n private fireCancel(analysisSessionId: string): void {\n void this.post(analysisCancelEnvelope(analysisSessionId)).catch(() => {})\n }\n}\n","/**\n * Project-correlation data glue (T5.10b): a framework-free store feeding\n * the controlled ProjectView with `GET projects` wire groups. Derivation\n * (normalize/merge/sort) stays in board/project-view-logic.ts — this store\n * owns transport orchestration and refresh throttling only.\n *\n * Refresh model: an explicit `refresh()` on view entry, plus SSE-driven\n * `notifySnapshot()` refreshes throttled to {@link DEFAULT_MIN_REFRESH_MS}\n * (state frames arrive per board mutation; the projects endpoint recomputes\n * groups per call, so hammering it buys nothing). Stale groups stay\n * rendered through failed refreshes (honest banner, never a blank).\n *\n * Same store discipline as controller.ts: subscribe/getState for\n * `useSyncExternalStore`, immutable snapshots, dispose() = late no-ops.\n *\n * @module\n */\n\nimport { isApiError, type RequestOptions } from './api.ts'\nimport { fetchProjects, type ProjectsResponseVM } from './m3-transport.ts'\nimport type { ProjectGroupVM } from './board/project-view-logic.ts'\nimport type { DetailHeaderHint } from './detail-glue.ts'\n\n/** Minimum spacing between SSE-triggered refreshes. */\nexport const DEFAULT_MIN_REFRESH_MS = 5_000\n\nexport interface ProjectsGlueState {\n groups: ProjectGroupVM[]\n /** True while a fetch is in flight AND nothing was loaded yet. */\n loading: boolean\n /** Machine reason code of the last failure, or null. */\n error: string | null\n /** Epoch ms of the last successful load; null before the first one. */\n loadedAt: number | null\n}\n\nexport interface ProjectsStoreOptions {\n fetchProjectsFn?: (opts?: RequestOptions) => Promise<ProjectsResponseVM>\n minRefreshMs?: number\n /** Clock injection for throttling tests. */\n now?: () => number\n}\n\nexport class ProjectsStore {\n private state: ProjectsGlueState = { groups: [], loading: false, error: null, loadedAt: null }\n private readonly listeners = new Set<() => void>()\n private readonly fetchProjectsFn: NonNullable<ProjectsStoreOptions['fetchProjectsFn']>\n private readonly minRefreshMs: number\n private readonly now: () => number\n private disposed = false\n private inFlight = false\n private lastAttemptAt: number | null = null\n\n constructor(options: ProjectsStoreOptions = {}) {\n this.fetchProjectsFn = options.fetchProjectsFn ?? fetchProjects\n this.minRefreshMs = options.minRefreshMs ?? DEFAULT_MIN_REFRESH_MS\n this.now = options.now ?? Date.now\n }\n\n subscribe = (fn: () => void): (() => void) => {\n this.listeners.add(fn)\n return () => { this.listeners.delete(fn) }\n }\n\n getState = (): ProjectsGlueState => this.state\n\n private setState(patch: Partial<ProjectsGlueState>): void {\n if (this.disposed) return\n this.state = { ...this.state, ...patch }\n for (const fn of [...this.listeners]) fn()\n }\n\n /** Fetch the groups now (deduped while one call is in flight). */\n async refresh(): Promise<void> {\n if (this.disposed || this.inFlight) return\n this.inFlight = true\n this.lastAttemptAt = this.now()\n if (this.state.loadedAt === null) this.setState({ loading: true, error: null })\n try {\n const body = await this.fetchProjectsFn()\n if (this.disposed) return\n this.setState({\n groups: body.groups,\n loading: false,\n error: null,\n loadedAt: this.now(),\n })\n } catch (err) {\n if (this.disposed) return\n // Previously loaded groups stay on screen; the view banners the code.\n this.setState({\n loading: false,\n error: isApiError(err) ? err.reason : 'network_error',\n })\n } finally {\n this.inFlight = false\n }\n }\n\n /** SSE `state` frame hook: throttled refresh. */\n notifySnapshot(): void {\n if (this.disposed || this.inFlight) return\n if (this.lastAttemptAt !== null && this.now() - this.lastAttemptAt < this.minRefreshMs) return\n void this.refresh()\n }\n\n dispose(): void {\n this.disposed = true\n this.listeners.clear()\n }\n}\n\n/**\n * Find a session inside loaded project groups → header hint for the\n * detail view (project rows carry no board card), or null when unknown.\n */\nexport function findProjectSessionHint(\n groups: readonly ProjectGroupVM[],\n sessionId: string,\n): DetailHeaderHint | null {\n for (const group of groups) {\n for (const session of group.sessions) {\n if (session.sessionId === sessionId) {\n return {\n agent: session.agent,\n title: session.title,\n project: group.project,\n status: session.status,\n }\n }\n }\n }\n return null\n}\n","/**\n * Session-detail container (T5.10b, design §5.1 view 2): the full-tab\n * detail surface opened by clicking a session card on the board or in the\n * project view. Composes the M3 presentational components over their glue\n * stores:\n *\n * - SessionDetail (timeline) ← DetailStore (fetchSessionDetail +\n * fetchTimelinePage pagination + SSE-triggered listen refetch);\n * - action row: 注入 (M2 InjectPanel as a modal, reused verbatim) and\n * AI 分析 (AnalysisPanel over AnalysisStore; disabled with an honest\n * hint while `analysis.enabled` is off);\n * - dsh 会话专属区: LineageTree ← the DetailStore lineage slice (non-dsh\n * sessions degrade client-side, no dialing) and SearchPanel ←\n * SearchStore (full-text or filter-only degradation; a result click\n * navigates the detail view to that session).\n *\n * The stores live in component state (one set per opened session id — the\n * owner keys this component by session id) and are disposed on unmount.\n * SSE coupling: the controller's subscribe seam notifies the DetailStore\n * on every state frame (header refresh + listen-mode refetch trigger).\n *\n * @module\n */\n\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport type { ReactElement } from 'react'\nimport { SessionDetail } from './detail/SessionDetail.tsx'\nimport { LineageTree } from './dsh-tools/LineageTree.tsx'\nimport { SearchPanel } from './dsh-tools/SearchPanel.tsx'\nimport { AnalysisPanel } from './analysis/AnalysisPanel.tsx'\nimport { InjectPanel } from './inject/InjectPanel.tsx'\nimport { DetailStore, findCardHint, type DetailHeaderHint } from './detail-glue.ts'\nimport { SearchStore } from './search-glue.ts'\nimport { AnalysisStore } from './analysis-glue.ts'\nimport { ProjectsStore } from './project-glue.ts'\nimport type { SidecarController } from './controller.ts'\nimport type { InjectMode } from './inject/logic.ts'\nimport type { InjectActions } from './inject-glue.ts'\nimport { t } from './locales/index.ts'\nimport css from './detail-view.module.css'\nimport overlay from './inject/overlay.module.css'\n\n/**\n * Everything the integrated M3 surfaces need from the entry (index.ts\n * builds one; mount.tsx and this container share it). Store factories are\n * seams so tests/materialization can substitute injected transports.\n */\nexport interface SidecarUiIntegration {\n /** M2 injection wiring; absent = injection not assembled (entry hidden). */\n inject?: {\n actions: InjectActions\n getDefaultMode: () => InjectMode\n }\n /** Live `analysis.enabled` reader (settings scope box; default false). */\n getAnalysisEnabled: () => boolean\n createDetailStore: (sessionId: string, hint: DetailHeaderHint | null) => DetailStore\n createSearchStore: () => SearchStore\n createAnalysisStore: () => AnalysisStore\n createProjectsStore: () => ProjectsStore\n}\n\n/** Default integration over the real transports (analysis off until read). */\nexport function createDefaultIntegration(\n base: Omit<\n SidecarUiIntegration,\n 'createDetailStore' | 'createSearchStore' | 'createAnalysisStore' | 'createProjectsStore'\n >,\n): SidecarUiIntegration {\n return {\n ...base,\n createDetailStore: (sessionId, hint) => new DetailStore(sessionId, { hint }),\n createSearchStore: () => new SearchStore(),\n createAnalysisStore: () => new AnalysisStore(),\n createProjectsStore: () => new ProjectsStore(),\n }\n}\n\nexport interface SidecarDetailViewProps {\n sessionId: string\n /** Header seed from the opening surface (board card / project row). */\n hint: DetailHeaderHint | null\n controller: SidecarController\n integration: SidecarUiIntegration\n onClose: () => void\n /** Provenance/search jump: navigate the detail view to another session. */\n onSelectSession: (sessionId: string) => void\n}\n\n/**\n * The detail view. Owner remounts it per session id (`key={sessionId}`),\n * so every store below is scoped to exactly one session.\n */\nexport function SidecarDetailView(props: SidecarDetailViewProps): ReactElement {\n const { controller, integration, sessionId } = props\n const [detailStore] = useState(() => integration.createDetailStore(sessionId, props.hint))\n const [searchStore] = useState(() => integration.createSearchStore())\n const [analysisStore] = useState(() => integration.createAnalysisStore())\n const [injectOpen, setInjectOpen] = useState(false)\n const [analysisOpen, setAnalysisOpen] = useState(false)\n\n useEffect(() => {\n void detailStore.open()\n // One notification per SSE state frame / poll settle: header refresh\n // from the live board card + listen-mode newest-window refetch.\n const unsubscribe = controller.subscribe(() => {\n detailStore.notifySnapshot(findCardHint(controller.getState().sessions, sessionId))\n })\n return () => {\n unsubscribe()\n detailStore.dispose()\n searchStore.dispose()\n analysisStore.dispose()\n }\n }, [controller, detailStore, searchStore, analysisStore, sessionId])\n\n const detail = useSyncExternalStore(\n detailStore.subscribe, detailStore.getState, detailStore.getState)\n const search = useSyncExternalStore(\n searchStore.subscribe, searchStore.getState, searchStore.getState)\n const analysis = useSyncExternalStore(\n analysisStore.subscribe, analysisStore.getState, analysisStore.getState)\n const view = useSyncExternalStore(\n (cb) => controller.subscribe(cb),\n () => controller.getState(),\n () => controller.getState(),\n )\n\n const analysisEnabled = integration.getAnalysisEnabled()\n const injectIntegration = props.integration.inject\n const closeInject = (): void => { setInjectOpen(false) }\n const title = detail.header.title.trim()\n\n return (\n <div className={css['detailRoot']} data-testid=\"agent-sidecar-detail-view\">\n <div className={css['actionsRow']}>\n {injectIntegration !== undefined && (\n <button\n type=\"button\"\n className={css['actionButton']}\n onClick={() => { setInjectOpen(true) }}\n data-testid=\"agent-sidecar-detail-inject\"\n >\n {t('detail.actions.inject')}\n </button>\n )}\n <button\n type=\"button\"\n className={css['actionButton']}\n disabled={!analysisEnabled}\n title={analysisEnabled ? undefined : t('detail.actions.analyzeDisabledHint')}\n onClick={() => { setAnalysisOpen(true) }}\n data-testid=\"agent-sidecar-detail-analyze\"\n >\n {t('detail.actions.analyze')}\n </button>\n </div>\n\n <SessionDetail\n sessionId={sessionId}\n header={detail.header}\n timeline={detail.timeline}\n loading={detail.loading}\n error={detail.error}\n hasMore={detail.hasMore}\n listening={detail.listening}\n onLoadMore={() => { void detailStore.loadMore() }}\n onToggleListen={() => { detailStore.toggleListen() }}\n onClose={props.onClose}\n />\n\n {analysisOpen && (\n <AnalysisPanel\n enabled={analysisEnabled}\n state={analysis}\n onStart={() => {\n void analysisStore.start({ targetKind: 'session', targetId: sessionId })\n }}\n onFollowup={(question) => { void analysisStore.followup(question) }}\n onStop={() => { void analysisStore.stop() }}\n onClose={() => { setAnalysisOpen(false) }}\n />\n )}\n\n <div className={css['toolsSection']} data-testid=\"agent-sidecar-detail-tools\">\n <LineageTree\n trace={detail.lineage.trace}\n available={detail.lineage.available}\n reason={detail.lineage.reason}\n detail={detail.lineage.detail}\n currentSessionId={sessionId}\n onSelectSession={props.onSelectSession}\n loading={detail.lineage.loading}\n error={detail.lineage.error}\n />\n <SearchPanel\n query={search.query}\n project={search.project}\n mode={search.mode}\n items={search.items}\n loading={search.loading}\n error={search.error}\n onQueryChange={(query) => { searchStore.setQuery(query) }}\n onSubmit={() => { void searchStore.submit() }}\n onSelectSession={props.onSelectSession}\n />\n </div>\n\n {injectIntegration !== undefined && injectOpen && (\n <div className={overlay['backdrop']} role=\"presentation\" onClick={closeInject}>\n <div\n className={overlay['dialog']}\n role=\"dialog\"\n aria-modal=\"true\"\n onClick={(event) => { event.stopPropagation() }}\n >\n <InjectPanel\n capability={{ inject: view.injectCapability }}\n target={{\n agent: detail.header.agent,\n sessionId,\n ...(title !== '' ? { title } : {}),\n }}\n defaultMode={injectIntegration.getDefaultMode()}\n onPrepare={injectIntegration.actions.onPrepare}\n onExecute={injectIntegration.actions.onExecute}\n onClose={closeInject}\n />\n </div>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Pure conversion layer between the host Config wire shape and the settings\n * card's flat form values (T2.4 wiring glue; no React, no I/O, node-testable).\n *\n * `SidecarConfigView` hand-mirrors the host `Config` interface\n * (src/config.ts) the same way api.ts mirrors the route wire types: host and\n * client are separate TS programs, so the client restates the shape it reads\n * off the settings scope (`ctx.settingsScope.bind({namespace}).getSnapshot()\n * .value` carries the schema-resolved section, which IS this shape).\n *\n * Write granularity: the browser settings scope's `set(field, value)` writes\n * exactly one top-level path segment (`{op:'set', path:[field]}` — verified\n * against harness ui-settings settings-scope.ts), so saving works per\n * top-level GROUP: {@link diffGroups} emits one complete group object per\n * group whose staged values differ from the resolved ones. Writing a whole\n * group marks all its fields user-overridden — the settings service treats\n * presence in the user layer as the override marker regardless of value,\n * which is its documented semantics, not a distortion.\n *\n * @module\n */\n\nimport type { SettingsCardValues } from './settings-card.tsx'\n\n/** Mirror of the host `Config` (src/config.ts); see the module doc. */\nexport interface SidecarConfigView {\n daemon: { policy: 'adopt-or-host' | 'adopt-only' | 'off'; backoffLimit: number }\n sidecar: { command: string[]; runtimeDir: string }\n stream: { reconcileActiveMs: number; reconcileIdleMs: number }\n inject: { enabled: boolean; defaultMode: 'queue' | 'steer' }\n analysis: { enabled: boolean; provider: string; model: string }\n ui: { timeWindowHours: number; showDead: boolean }\n skill: { provide: boolean }\n}\n\n/** Schema defaults of src/config.ts (fallback while the scope is not ready). */\nexport const DEFAULT_CONFIG_VIEW: SidecarConfigView = {\n daemon: { policy: 'adopt-or-host', backoffLimit: 5 },\n sidecar: { command: ['agent-sidecar'], runtimeDir: '' },\n stream: { reconcileActiveMs: 2000, reconcileIdleMs: 10000 },\n inject: { enabled: false, defaultMode: 'queue' },\n analysis: { enabled: false, provider: '', model: '' },\n ui: { timeWindowHours: 24, showDead: false },\n skill: { provide: true },\n}\n\n/** Whitespace-join an argv for the card's single-line command field. */\nexport function joinCommand(argv: readonly string[]): string {\n return argv.join(' ')\n}\n\n/** Split the card's command line back into argv (whitespace, empty dropped). */\nexport function splitCommand(line: string): string[] {\n return line.split(/\\s+/).filter((part) => part !== '')\n}\n\n/** Config wire shape → the card's flat staged-form values. */\nexport function configToValues(config: SidecarConfigView): SettingsCardValues {\n return {\n daemonPolicy: config.daemon.policy,\n daemonBackoffLimit: config.daemon.backoffLimit,\n sidecarCommand: joinCommand(config.sidecar.command),\n sidecarRuntimeDir: config.sidecar.runtimeDir,\n streamReconcileActiveMs: config.stream.reconcileActiveMs,\n streamReconcileIdleMs: config.stream.reconcileIdleMs,\n injectEnabled: config.inject.enabled,\n injectDefaultMode: config.inject.defaultMode,\n analysisEnabled: config.analysis.enabled,\n analysisProvider: config.analysis.provider,\n analysisModel: config.analysis.model,\n uiTimeWindowHours: config.ui.timeWindowHours,\n uiShowDead: config.ui.showDead,\n skillProvide: config.skill.provide,\n }\n}\n\n/** Flat card values → the grouped config wire shape. */\nexport function valuesToConfigView(values: SettingsCardValues): SidecarConfigView {\n return {\n daemon: { policy: values.daemonPolicy, backoffLimit: values.daemonBackoffLimit },\n sidecar: {\n command: splitCommand(values.sidecarCommand),\n runtimeDir: values.sidecarRuntimeDir,\n },\n stream: {\n reconcileActiveMs: values.streamReconcileActiveMs,\n reconcileIdleMs: values.streamReconcileIdleMs,\n },\n inject: { enabled: values.injectEnabled, defaultMode: values.injectDefaultMode },\n analysis: {\n enabled: values.analysisEnabled,\n provider: values.analysisProvider,\n model: values.analysisModel,\n },\n ui: { timeWindowHours: values.uiTimeWindowHours, showDead: values.uiShowDead },\n skill: { provide: values.skillProvide },\n }\n}\n\n/** Field-wise equality over the flat card values (argv compared as joined text). */\nexport function cardValuesEqual(a: SettingsCardValues, b: SettingsCardValues): boolean {\n return (Object.keys(a) as Array<keyof SettingsCardValues>).every(\n (key) => a[key] === b[key],\n )\n}\n\n/** One pending settings write: a complete top-level group object. */\nexport interface GroupPatch {\n group: keyof SidecarConfigView\n patch: Record<string, unknown>\n}\n\n/**\n * Diff two card-value sets into per-group writes (see module doc for why the\n * write unit is a whole group). Order is the Config declaration order.\n */\nexport function diffGroups(\n current: SettingsCardValues,\n target: SettingsCardValues,\n): GroupPatch[] {\n const from = valuesToConfigView(current)\n const to = valuesToConfigView(target)\n const patches: GroupPatch[] = []\n for (const group of Object.keys(to) as Array<keyof SidecarConfigView>) {\n if (JSON.stringify(from[group]) !== JSON.stringify(to[group])) {\n patches.push({ group, patch: { ...to[group] } })\n }\n }\n return patches\n}\n","/**\n * Slot-facing React glue (T2.4): binds the {@link SidecarController} stores\n * to the three presentational modules via `useSyncExternalStore` and hands\n * back zero-prop components ready for slot registration. Factories close\n * over the controller so subscribe/getSnapshot identities stay stable\n * across renders (uSES resubscribes on identity change).\n *\n * The settings card entry additionally owns the staged-edit lifecycle over\n * a bound `SettingsScope` (browser mirror of the host settings namespace):\n * resolved values come from the scope snapshot, edits stage locally, save\n * writes one complete top-level group per changed group (see\n * settings-glue.ts for the write-granularity rationale), and success is\n * judged by comparing the post-write snapshot against the staged target —\n * `scope.set` settles without rejecting even when the host declines the\n * write (it recovers by reloading host state instead).\n */\n\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport type { ReactElement } from 'react'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\nimport { Board } from './board/Board.tsx'\nimport { ProjectView } from './board/project-view.tsx'\nimport { SidecarWidget } from './widget.tsx'\nimport { SettingsCard, type SettingsCardValues, type SidecarDaemonStatus } from './settings-card.tsx'\nimport { countWorking, deriveWidgetConnection } from './board/logic.ts'\nimport type { BoardFilterState } from './board/logic.ts'\nimport type { SidecarController, SidecarViewState } from './controller.ts'\nimport type { InjectMode } from './inject/logic.ts'\nimport type { InjectActions } from './inject-glue.ts'\nimport { SidecarDetailView, type SidecarUiIntegration } from './detail-view.tsx'\nimport { findCardHint, type DetailHeaderHint } from './detail-glue.ts'\nimport { findProjectSessionHint, type ProjectsStore } from './project-glue.ts'\nimport { detailErrorText } from './detail/logic.ts'\nimport { t } from './locales/index.ts'\nimport css from './detail-view.module.css'\nimport {\n DEFAULT_CONFIG_VIEW,\n cardValuesEqual,\n configToValues,\n diffGroups,\n type SidecarConfigView,\n} from './settings-glue.ts'\n\n/**\n * What the board tab needs to host the inject panel (S5 wiring, T4.9).\n * Since T5.10b the panel opens from the detail view's 注入 button; this\n * shape rides {@link SidecarUiIntegration.inject} unchanged.\n */\nexport interface BoardInjectIntegration {\n /** onPrepare/onExecute over the action transport (inject-glue.ts). */\n actions: InjectActions\n /**\n * Late-bound `inject.default-mode` reader: the settings scope resolves\n * after the tab mounts, so the value is read at panel-open time.\n */\n getDefaultMode: () => InjectMode\n}\n\n/** Board-tab main views (detail is an overlay route on top of either). */\ntype MainView = 'board' | 'projects'\n\n/**\n * Project-correlation view bound to its store: refresh on entry, then\n * throttled SSE-driven refreshes for as long as the view is on screen.\n */\nfunction ProjectsContainer(props: {\n controller: SidecarController\n store: ProjectsStore\n onSelectSession: (sessionId: string) => void\n}): ReactElement {\n const { controller, store } = props\n useEffect(() => {\n void store.refresh()\n return controller.subscribe(() => { store.notifySnapshot() })\n }, [controller, store])\n const state = useSyncExternalStore(store.subscribe, store.getState, store.getState)\n return (\n <ProjectView\n groups={state.groups}\n loading={state.loading}\n error={state.error === null ? null : detailErrorText(state.error)}\n onSelectSession={props.onSelectSession}\n />\n )\n}\n\n/**\n * Cross-agent board tab (the \"Sidecar\" conversation tab), since T5.10b the\n * shell of the whole M3 information architecture (design §5.1):\n *\n * - view 1: the session board, with a 「会话看板 / 项目视图」 switcher\n * (ProjectView over `GET projects`);\n * - view 2: clicking a session card in EITHER view routes to the full-tab\n * session-detail view (timeline + 注入 + AI 分析 + dsh 谱系/检索);\n * detail-internal jumps (lineage nodes, search hits) re-route in place;\n * - view 3: the M2 inject panel opens as a modal from the detail view.\n *\n * Without an integration the board renders read-only and inert (no detail\n * routing) — the M1 degradation posture.\n */\nexport function createBoardTab(\n controller: SidecarController,\n integration?: SidecarUiIntegration,\n): () => ReactElement {\n const subscribe = (cb: () => void): (() => void) => controller.subscribe(cb)\n const getState = (): SidecarViewState => controller.getState()\n const getFilters = (): BoardFilterState => controller.getFilters()\n\n return function SidecarBoardTab(): ReactElement {\n // Third argument (server snapshot) keeps the components renderable under\n // react-dom/server (DOM-level verification harness); same source.\n const state = useSyncExternalStore(subscribe, getState, getState)\n const filters = useSyncExternalStore(subscribe, getFilters, getFilters)\n const [mainView, setMainView] = useState<MainView>('board')\n const [detail, setDetail] = useState<{ id: string; hint: DetailHeaderHint | null } | null>(\n null,\n )\n // One ProjectsStore per tab mount, created lazily with the integration\n // seam; state survives board↔projects↔detail switches within the tab.\n const [projectsStore] = useState<ProjectsStore | null>(\n () => integration?.createProjectsStore() ?? null,\n )\n useEffect(() => () => { projectsStore?.dispose() }, [projectsStore])\n\n const openDetail = (sessionId: string): void => {\n if (integration === undefined) return\n const hint =\n findCardHint(state.sessions, sessionId) ??\n (projectsStore !== null\n ? findProjectSessionHint(projectsStore.getState().groups, sessionId)\n : null)\n setDetail({ id: sessionId, hint })\n }\n\n if (integration !== undefined && detail !== null) {\n return (\n <SidecarDetailView\n // key remounts per session: fresh stores, no state bleed on jumps.\n key={detail.id}\n sessionId={detail.id}\n hint={detail.hint}\n controller={controller}\n integration={integration}\n onClose={() => { setDetail(null) }}\n onSelectSession={openDetail}\n />\n )\n }\n\n return (\n <>\n <div className={css['switcherBar']} data-testid=\"agent-sidecar-view-switcher\">\n <button\n type=\"button\"\n className={css['switcherButton']}\n data-active={mainView === 'board' || undefined}\n onClick={() => { setMainView('board') }}\n >\n {t('board.viewBoard')}\n </button>\n <button\n type=\"button\"\n className={css['switcherButton']}\n data-active={mainView === 'projects' || undefined}\n onClick={() => { setMainView('projects') }}\n >\n {t('board.viewProjects')}\n </button>\n </div>\n {mainView === 'projects' && projectsStore !== null\n ? (\n <ProjectsContainer\n controller={controller}\n store={projectsStore}\n onSelectSession={openDetail}\n />\n )\n : (\n <Board\n daemonState={state.daemonState}\n {...state.daemonDetail !== undefined ? { daemonDetail: state.daemonDetail } : {}}\n streamHealth={state.streamHealth}\n lastReconcileAtMs={state.lastReconcileAtMs}\n sessions={state.sessions}\n filters={filters}\n onFiltersChange={(next) => {\n controller.setFilters(next)\n }}\n onRefresh={() => {\n void controller.refresh()\n }}\n onSelectSession={openDetail}\n />\n )}\n </>\n )\n }\n}\n\n/** Footer connection dot + working counter bound to the controller. */\nexport function createFooterWidget(controller: SidecarController): () => ReactElement {\n const subscribe = (cb: () => void): (() => void) => controller.subscribe(cb)\n const getState = (): SidecarViewState => controller.getState()\n return function SidecarFooterWidget(): ReactElement {\n const state = useSyncExternalStore(subscribe, getState, getState)\n return (\n <SidecarWidget\n connection={deriveWidgetConnection(state.daemonState, state.streamHealth)}\n workingCount={countWorking(state.sessions)}\n />\n )\n }\n}\n\n/** Fallback card values while the scope snapshot is not ready. */\nconst FALLBACK_VALUES: SettingsCardValues = configToValues(DEFAULT_CONFIG_VIEW)\n\n/**\n * Settings card bound to the controller (daemon status row) and to the\n * namespace scope (values + persistence). See the module doc for the\n * staged-edit / save-verification contract.\n */\nexport function createSettingsCardEntry(\n controller: SidecarController,\n scope: SettingsScope<SidecarConfigView>,\n): () => ReactElement {\n const subscribeState = (cb: () => void): (() => void) => controller.subscribe(cb)\n const getState = (): SidecarViewState => controller.getState()\n const subscribeScope = (cb: () => void): (() => void) => scope.subscribe(cb)\n const getSnapshot = (): ReturnType<typeof scope.getSnapshot> => scope.getSnapshot()\n\n return function SidecarSettingsCardEntry(): ReactElement {\n const snapshot = useSyncExternalStore(subscribeScope, getSnapshot, getSnapshot)\n const state = useSyncExternalStore(subscribeState, getState, getState)\n const [staged, setStaged] = useState<Partial<SettingsCardValues>>({})\n const [saving, setSaving] = useState(false)\n const [saveFailed, setSaveFailed] = useState(false)\n\n const resolved =\n snapshot.value !== undefined ? configToValues(snapshot.value) : FALLBACK_VALUES\n const values: SettingsCardValues = { ...resolved, ...staged }\n const writable =\n snapshot.status === 'ready' && snapshot.writable && snapshot.mode === 'host'\n const dirty = !cardValuesEqual(values, resolved)\n\n const onChange = <K extends keyof SettingsCardValues>(\n field: K,\n value: SettingsCardValues[K],\n ): void => {\n setSaveFailed(false)\n setStaged((prev) => {\n const next = { ...prev }\n if (resolved[field] === value) delete next[field]\n else next[field] = value\n return next\n })\n }\n\n const onSave = (): void => {\n const target = { ...resolved, ...staged }\n setSaving(true)\n setSaveFailed(false)\n void (async () => {\n try {\n for (const { group, patch } of diffGroups(resolved, target)) {\n await scope.set(group, patch)\n }\n const after = scope.getSnapshot().value\n if (after !== undefined && cardValuesEqual(configToValues(after), target)) {\n setStaged({})\n } else {\n setSaveFailed(true)\n }\n } catch (err) {\n console.error('agent-sidecar: settings save failed', err)\n setSaveFailed(true)\n } finally {\n setSaving(false)\n }\n })()\n }\n\n const daemon: SidecarDaemonStatus = {\n state: state.daemonState,\n ...state.lastPing !== null\n ? { pid: state.lastPing.pid, version: state.lastPing.version }\n : {},\n }\n\n return (\n <SettingsCard\n values={values}\n onChange={onChange}\n onSave={onSave}\n onDiscard={() => {\n setStaged({})\n setSaveFailed(false)\n }}\n writable={writable}\n dirty={dirty}\n saving={saving}\n saveFailed={saveFailed}\n daemon={daemon}\n />\n )\n }\n}\n","/**\n * Optional better-sidebar mini tab (T6.3, design §5.2 / ADR-5 option C).\n *\n * dsh-better-sidebar (npm, ≥0.4.0) turns the sidebar into a registry\n * service: its client half runs `ctx.provide('betterSidebar', service)` and\n * consumers call `service.registerTab(descriptor)` (source of record:\n * better-sidebar docs/external-plugin-guide.md + src/client/service.ts).\n * This module integrates as a SOFT dependency, dsh-sentinel style:\n *\n * - NO value import and NO type import of `dsh-better-sidebar` — the client\n * bundle purity gate stays react-only. The minimal service contract is\n * restated locally ({@link BetterSidebarServiceFace}) and the live service\n * is duck-typed at runtime ({@link probeBetterSidebar}).\n * - Absent → silent skip: {@link mountSidebarTab} parks the registration in\n * `ctx.inject(['betterSidebar'], …)` — a cordis fiber that stays PENDING\n * until some plugin provides the service (zero timers, zero polling, zero\n * resources), plus one debug-level log line at mount time. If\n * better-sidebar is installed later (any load order), the fiber activates\n * and the tab registers; if never, nothing ever runs.\n * - Present → one compact \"Sidecar\" tab (`agent-sidecar:monitor`): daemon\n * connection dot, working/waiting counts, and the most recently active\n * sessions, all read from the SHARED {@link SidecarController} stores —\n * no second poller, no second SSE.\n * - `visible === false` releases resources: better-sidebar hands tab\n * components a `visible` prop (false while the panel is collapsed or\n * another tab is active). The shared controller has no pause/refcount\n * surface (its stream is plugin-lifetime, `stop()` is terminal), so the\n * gate lives in this view: {@link VisibleGatedStore} unsubscribes from the\n * controller while hidden (re-render churn stops; the view triggers no\n * fetches of its own) and resubscribes + catches up on show.\n */\n\nimport { createElement as h, useEffect, useState, useSyncExternalStore } from 'react'\nimport type { CSSProperties, ReactElement, ReactNode } from 'react'\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SidecarController, SidecarViewState } from './controller.ts'\nimport {\n agentGlyph,\n countWorking,\n deriveWidgetConnection,\n formatRelativeTime,\n normalizeStatus,\n projectDisplayName,\n widgetTitle,\n type SessionCardVM,\n type SessionStatusToken,\n type WidgetConnection,\n} from './board/logic.ts'\nimport { t, type SidecarLocaleKey } from './locales/index.ts'\n\n/** Registered tab type id (design §5.2 names it verbatim). */\nexport const SIDEBAR_TAB_ID = 'agent-sidecar:monitor'\n\n/** `+`-menu order: after the built-ins (explorer=10 … browser=50). */\nexport const SIDEBAR_TAB_ORDER = 60\n\n/** Compact view caps the session list at this many rows (task spec: 3-5). */\nexport const MAX_RECENT_SESSIONS = 5\n\n// ---------------------------------------------------------------------------\n// Duck-typed better-sidebar contract (locally restated, never imported).\n// ---------------------------------------------------------------------------\n\n/** Props better-sidebar hands every tab component; only `visible` is used. */\nexport interface SidebarTabComponentProps {\n /** False while the panel is collapsed or another tab is active. */\n visible: boolean\n}\n\n/** The one descriptor shape this module registers (subset of TabDescriptor). */\nexport interface SidebarTabDescriptor {\n id: string\n title: string | (() => string)\n icon?: (size: number) => ReactNode\n order?: number\n /** Single-instance sugar: opening again focuses the existing tab. */\n single?: boolean\n component: (props: SidebarTabComponentProps) => ReactNode\n}\n\n/** Minimal face of `ctx.betterSidebar` this module relies on. */\nexport interface BetterSidebarServiceFace {\n /** Registers a tab type; returns the disposer. Throws on duplicate id. */\n registerTab(descriptor: SidebarTabDescriptor): () => void\n}\n\n/**\n * Duck-type a candidate service value: any object exposing a callable\n * `registerTab` qualifies (the registry contract is stable since\n * better-sidebar v0.4.0). Anything else — absent service, or a foreign\n * object squatting on the service name — reads as \"not installed\".\n */\nexport function probeBetterSidebar(candidate: unknown): BetterSidebarServiceFace | null {\n if (typeof candidate !== 'object' || candidate === null) return null\n const face = candidate as { registerTab?: unknown }\n return typeof face.registerTab === 'function'\n ? (candidate as BetterSidebarServiceFace)\n : null\n}\n\n// ---------------------------------------------------------------------------\n// Visibility gate over the shared controller.\n// ---------------------------------------------------------------------------\n\n/** The slice of {@link SidecarController} this view consumes (tests fake it). */\nexport type SidecarStateSource = Pick<SidecarController, 'subscribe' | 'getState'>\n\n/**\n * A pausable read-through store between the shared controller and one tab\n * instance. While visible it mirrors the controller (subscribed, snapshots\n * flow, listeners notified); while hidden it holds NO controller\n * subscription — upstream notifications cost this view nothing — and serves\n * the last seen snapshot. Turning visible again resubscribes and catches up\n * once. Methods are bound fields so uSES sees stable identities.\n */\nexport class VisibleGatedStore {\n private readonly source: SidecarStateSource\n private readonly listeners = new Set<() => void>()\n private unsubscribe: (() => void) | null = null\n private snapshot: SidecarViewState\n private disposed = false\n\n constructor(source: SidecarStateSource) {\n this.source = source\n this.snapshot = source.getState()\n }\n\n /** Whether the upstream controller subscription is currently held. */\n get subscribed(): boolean {\n return this.unsubscribe !== null\n }\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n readonly getState = (): SidecarViewState => this.snapshot\n\n /** Idempotent visibility switch: subscribe + catch up, or unsubscribe. */\n setVisible(visible: boolean): void {\n if (this.disposed || visible === this.subscribed) return\n if (visible) {\n this.unsubscribe = this.source.subscribe(() => {\n this.pull()\n })\n this.pull()\n } else {\n this.unsubscribe?.()\n this.unsubscribe = null\n }\n }\n\n /** Terminal teardown (component unmount): drop upstream and listeners. */\n dispose(): void {\n this.disposed = true\n this.unsubscribe?.()\n this.unsubscribe = null\n this.listeners.clear()\n }\n\n private pull(): void {\n const next = this.source.getState()\n if (next === this.snapshot) return\n this.snapshot = next\n for (const fn of [...this.listeners]) fn()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pure view model (exported for tests).\n// ---------------------------------------------------------------------------\n\n/** Count of sessions currently observed as waiting. */\nexport function countWaiting(sessions: ReadonlyArray<{ status: string }>): number {\n let count = 0\n for (const session of sessions) {\n if (normalizeStatus(session.status) === 'waiting') count += 1\n }\n return count\n}\n\n/**\n * The compact list: non-dead sessions, most recently updated first, capped\n * at {@link MAX_RECENT_SESSIONS} (ties break by session id for stability).\n */\nexport function recentActiveSessions<T extends SessionCardVM>(\n sessions: readonly T[],\n limit: number = MAX_RECENT_SESSIONS,\n): T[] {\n return sessions\n .filter((session) => normalizeStatus(session.status) !== 'dead')\n .sort((a, b) =>\n b.updatedAtMs !== a.updatedAtMs\n ? b.updatedAtMs - a.updatedAtMs\n : a.sessionId.localeCompare(b.sessionId))\n .slice(0, limit)\n}\n\n/** Everything the mini tab renders. */\nexport interface SidebarMiniVM {\n connection: WidgetConnection\n /** Hover text for the header dot (connection + working count). */\n connectionTitle: string\n workingCount: number\n waitingCount: number\n recent: SessionCardVM[]\n hasSnapshot: boolean\n}\n\n/** Fold the shared view state into the mini tab's view model (pure). */\nexport function deriveMiniVM(state: SidecarViewState): SidebarMiniVM {\n const connection = deriveWidgetConnection(state.daemonState, state.streamHealth)\n const workingCount = countWorking(state.sessions)\n return {\n connection,\n connectionTitle: widgetTitle(connection, workingCount),\n workingCount,\n waitingCount: countWaiting(state.sessions),\n recent: recentActiveSessions(state.sessions),\n hasSnapshot: state.hasSnapshot,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Presentation.\n// ---------------------------------------------------------------------------\n\nconst STATUS_LABEL_KEY: Record<SessionStatusToken, SidecarLocaleKey> = {\n working: 'detail.status.working',\n waiting: 'detail.status.waiting',\n idle: 'detail.status.idle',\n dead: 'detail.status.dead',\n unknown: 'detail.status.unknown',\n}\n\nconst DOT_COLORS: Record<WidgetConnection, string> = {\n ok: 'var(--dsw-alias-state-success-primary, #1a7f37)',\n degraded: 'var(--dsw-alias-state-warn-primary, #9a6700)',\n off: 'var(--dsw-alias-label-dimmed, #8c959f)',\n}\n\nconst rootStyle: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: '10px 12px',\n fontSize: 12,\n color: 'var(--dsw-alias-label-primary, #1f2328)',\n}\n\nconst headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n}\n\nconst dotStyle = (connection: WidgetConnection): CSSProperties => ({\n width: 8,\n height: 8,\n borderRadius: '50%',\n flex: 'none',\n background: DOT_COLORS[connection],\n})\n\nconst countsStyle: CSSProperties = {\n fontVariantNumeric: 'tabular-nums',\n color: 'var(--dsw-alias-label-secondary, #57606a)',\n}\n\nconst sectionTitleStyle: CSSProperties = {\n fontSize: 11,\n fontWeight: 600,\n color: 'var(--dsw-alias-label-secondary, #57606a)',\n}\n\nconst rowStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n width: '100%',\n padding: '4px 6px',\n border: 'none',\n borderRadius: 6,\n background: 'transparent',\n cursor: 'pointer',\n font: 'inherit',\n textAlign: 'left',\n color: 'inherit',\n}\n\nconst rowTitleStyle: CSSProperties = {\n flex: 1,\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n}\n\nconst rowMetaStyle: CSSProperties = {\n flex: 'none',\n fontSize: 11,\n color: 'var(--dsw-alias-label-dimmed, #8c959f)',\n whiteSpace: 'nowrap',\n}\n\nconst detailStyle: CSSProperties = {\n margin: '0 6px 4px 20px',\n fontSize: 11,\n lineHeight: '16px',\n color: 'var(--dsw-alias-label-secondary, #57606a)',\n overflowWrap: 'anywhere',\n}\n\nconst mutedStyle: CSSProperties = {\n color: 'var(--dsw-alias-label-dimmed, #8c959f)',\n}\n\nconst hintStyle: CSSProperties = {\n fontSize: 11,\n color: 'var(--dsw-alias-label-dimmed, #8c959f)',\n}\n\n/** One session row + its inline expansion (last event, project, id). */\nfunction SessionRow(props: {\n session: SessionCardVM\n nowMs: number\n expanded: boolean\n onToggle: () => void\n}): ReactElement {\n const { session, nowMs, expanded } = props\n const status = normalizeStatus(session.status)\n const title = session.title.trim() === '' ? t('sidebar.untitled') : session.title\n const rowChildren: ReactNode[] = [\n h('span', { key: 'glyph', 'aria-hidden': true }, agentGlyph(session.agent)),\n h('span', { key: 'title', style: rowTitleStyle, title: session.sessionId }, title),\n h(\n 'span',\n { key: 'meta', style: rowMetaStyle },\n `${t(STATUS_LABEL_KEY[status])} · ${formatRelativeTime(session.updatedAtMs, nowMs)}`,\n ),\n ]\n const children: ReactNode[] = [\n h(\n 'button',\n {\n key: 'row',\n type: 'button',\n style: rowStyle,\n onClick: props.onToggle,\n 'data-testid': 'agent-sidecar-sidebar-session',\n 'data-session-id': session.sessionId,\n 'data-status': status,\n 'aria-expanded': expanded,\n },\n ...rowChildren,\n ),\n ]\n if (expanded) {\n const lastEvent =\n session.lastEvent === null\n ? h('span', { style: mutedStyle }, t('sidebar.noEvent'))\n : `${session.lastEvent.kind}: ${session.lastEvent.text}`\n children.push(\n h(\n 'div',\n { key: 'detail', style: detailStyle, 'data-testid': 'agent-sidecar-sidebar-detail' },\n h('div', { key: 'project' }, projectDisplayName(session.project)),\n h('div', { key: 'event' }, lastEvent),\n ),\n )\n }\n return h('li', { style: { listStyle: 'none' } }, ...children)\n}\n\n/**\n * Build the tab component bound to the shared controller. One\n * {@link VisibleGatedStore} per mounted tab instance: the `visible` prop is\n * synced into it by effect, unmount disposes it.\n */\nexport function createSidebarTabComponent(\n controller: SidecarStateSource,\n): (props: SidebarTabComponentProps) => ReactElement {\n return function SidecarSidebarTab({ visible }: SidebarTabComponentProps): ReactElement {\n const [gate] = useState(() => new VisibleGatedStore(controller))\n useEffect(() => () => {\n gate.dispose()\n }, [gate])\n useEffect(() => {\n gate.setVisible(visible)\n }, [gate, visible])\n const state = useSyncExternalStore(gate.subscribe, gate.getState, gate.getState)\n const [expandedId, setExpandedId] = useState<string | null>(null)\n\n const vm = deriveMiniVM(state)\n const nowMs = Date.now()\n\n let body: ReactNode\n if (!vm.hasSnapshot) {\n body = h('div', { style: mutedStyle }, t('sidebar.connecting'))\n } else if (vm.recent.length === 0) {\n body = h('div', { style: mutedStyle }, t('sidebar.noSessions'))\n } else {\n body = h(\n 'ul',\n { style: { margin: 0, padding: 0 } },\n ...vm.recent.map((session) =>\n h(SessionRow, {\n key: session.sessionId,\n session,\n nowMs,\n expanded: expandedId === session.sessionId,\n onToggle: () => {\n setExpandedId((prev) => (prev === session.sessionId ? null : session.sessionId))\n },\n })),\n )\n }\n\n return h(\n 'div',\n { style: rootStyle, 'data-testid': 'agent-sidecar-sidebar-tab', 'data-visible': visible },\n h(\n 'div',\n { style: headerStyle, title: vm.connectionTitle },\n h('span', { 'aria-hidden': true, style: dotStyle(vm.connection) }),\n h(\n 'span',\n { style: countsStyle, 'data-testid': 'agent-sidecar-sidebar-counts' },\n t('sidebar.countsRow', { working: vm.workingCount, waiting: vm.waitingCount }),\n ),\n ),\n h('div', { style: sectionTitleStyle }, t('sidebar.recentTitle')),\n body,\n h('div', { style: hintStyle }, t('sidebar.boardHint')),\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mount.\n// ---------------------------------------------------------------------------\n\n/** Context slice {@link mountSidebarTab} consumes (tests hand in a fake). */\nexport interface SidebarMountContext {\n get(name: string): unknown\n inject(deps: string[], callback: (ctx: unknown) => void): unknown\n}\n\n/**\n * Park the better-sidebar integration on the optional service. Not\n * installed → the inject fiber never activates (silent skip, one debug\n * line, zero resources). Installed (before or after this plugin, order\n * does not matter) → duck-type the service and register the mini tab\n * inside `ctx.effect`, so plugin unload / HMR unregisters it. A duplicate\n * registration (double apply) or a service throw degrades to a logged\n * no-op — never past this seat.\n */\nexport function mountSidebarTab(ctx: ClientContext, controller: SidecarStateSource): void {\n const mount = ctx as unknown as SidebarMountContext\n if (probeBetterSidebar(mount.get('betterSidebar')) === null) {\n console.debug(\n 'agent-sidecar: better-sidebar not detected; the optional sidebar tab stays idle',\n )\n }\n mount.inject(['betterSidebar'], (injected) => {\n const bctx = injected as ClientContext & SidebarMountContext\n const service = probeBetterSidebar(bctx.get('betterSidebar'))\n if (service === null) {\n // Provided but not duck-compatible (e.g. a future breaking rewrite).\n console.debug('agent-sidecar: betterSidebar service lacks registerTab; skipping tab')\n return\n }\n const component = createSidebarTabComponent(controller)\n bctx.effect(() => {\n try {\n return service.registerTab({\n id: SIDEBAR_TAB_ID,\n title: () => t('sidebar.tabTitle'),\n icon: (size: number) =>\n h('span', { 'aria-hidden': true, style: { fontSize: Math.round(size * 0.75) } }, '◈'),\n order: SIDEBAR_TAB_ORDER,\n single: true,\n component,\n })\n } catch (err) {\n console.error('agent-sidecar: better-sidebar tab registration failed', err)\n return () => {}\n }\n }, 'agent-sidecar: better-sidebar tab')\n })\n}\n","/**\n * Agent Sidecar — browser client half (T2.4 full integration + S5/T4.9\n * injection & command wiring).\n *\n * Mounts the completed modules into dsh Web on three slot seats plus one\n * service contribution:\n *\n * - `conversation.view` (list slot, session scope): the cross-agent board\n * as a \"Sidecar\" tab, order 30 — the official \"multiple views per\n * session\" seat (registration shape follows the S0 skeleton and the\n * dsh-project-kanban precedent). Selecting a session card opens the\n * inject panel (T4.5) as a modal overlay: onPrepare/onExecute ride the\n * action transport via inject-glue.ts, the capability bit comes from the\n * state snapshot, and `inject.default-mode` is adopted late from the\n * settings scope (seat 3) — 'queue', the schema default, until then;\n * - `sidebar.footer.action` (list slot, root scope): the connection-dot\n * footer widget (slot declared by dsh-client-ui-sidebar's footer rail;\n * type merge imported from its installed d.ts);\n * - `settings.plugin.item` (keyed slot, root scope): the settings card,\n * keyed by the host-side settings namespace 'agent-sidecar' (the\n * configurable-plugins tab dispatches one card per HOST-SERVED\n * namespace — the host half registers it via `ctx.settings`);\n * - `/sidecar` slash command (T4.6): registered lazily on the `commandUi`\n * service via registerSidecarCommand — a composition without the\n * slash-menu runtime simply never gains the command, and a duplicate\n * registration (double apply) degrades to a logged no-op;\n * - optional better-sidebar mini tab (T6.3): parked on the lazily-injected\n * `betterSidebar` service via mountSidebarTab (optional peer, runtime\n * duck-typed probe — never imported); not installed = silent skip.\n *\n * Lifecycle contract:\n * - every resource rides `ctx.effect`: the data stream + visibilitychange\n * listener, the injected `<style data-plugin>` tags (tsdown's CSS-module\n * loader injects them at factory execution — once per materialization —\n * so a keeper effect caches their text and restores them when a\n * re-apply of the same materialized module follows an unload);\n * - apply-guard idempotency: each mount checks the slot ledger for an\n * entry with this plugin's id/key before registering, so a duplicate\n * apply never double-registers (same cell + same priority would throw);\n * - graceful degradation: every mount is individually try/catch-ed —\n * a failing seat logs and is skipped, never taking the GUI down;\n * - the settings card waits on the optional `settingsScope` service via\n * `ctx.inject` (provided by dsh-client-ui-settings in the web app\n * composition); board and widget mount without it.\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only slot-contract merges into SlotMap (erased at runtime):\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client'\nimport { registerSidecarCommand } from './commands.ts'\nimport { PLUGIN_ID, SidecarController } from './controller.ts'\nimport { createInjectActions } from './inject-glue.ts'\nimport type { InjectMode } from './inject/logic.ts'\nimport { createBoardTab, createFooterWidget, createSettingsCardEntry } from './mount.tsx'\nimport { mountSidebarTab } from './sidebar-tab.tsx'\nimport { createDefaultIntegration, type SidecarUiIntegration } from './detail-view.tsx'\nimport type { SidecarConfigView } from './settings-glue.ts'\n\nexport const name = 'agent-sidecar'\n\n/** The slot registry is the only hard dependency; settingsScope is lazy. */\nexport const inject = ['slots']\n\n/** Entry id (list slots) and cell key (settings keyed slot) in one. */\nconst ENTRY_ID = 'agent-sidecar'\n\n/** Host-side settings namespace (host half registers it via ctx.settings). */\nconst SETTINGS_NAMESPACE = 'agent-sidecar'\n\n/**\n * Structural face of the `settingsScope` binder service provided by\n * dsh-client-ui-settings (not a published type surface for plugins in this\n * dsh version; shape verified against harness\n * packages/client/ui-settings/src/client/settings-scope.ts).\n */\ninterface SettingsScopeBinderFace {\n bind<T>(spec: SettingsScopeSpec<T>): SettingsScope<T>\n}\n\n/** The three slot seats this plugin occupies (all merged into SlotMap). */\ntype SidecarSlot = 'conversation.view' | 'sidebar.footer.action' | 'settings.plugin.item'\n\n/**\n * Apply-guard: whether the slot ledger already holds this plugin's entry\n * (list slots match by `id`, the keyed settings slot by `key`; both use\n * the same 'agent-sidecar' token). `entries()` answers [] for undeclared\n * slots, and the check runs inside the deferred `slots.inject` callback —\n * i.e. at actual registration time, when the ledger is authoritative.\n */\nfunction hasOwnEntry(ctx: ClientContext, slot: SidecarSlot): boolean {\n return ctx.slots\n .entries(slot)\n .some((entry) => entry.options.id === ENTRY_ID || entry.options.key === SETTINGS_NAMESPACE)\n}\n\n// ---------------------------------------------------------------------------\n// Style lifecycle.\n// ---------------------------------------------------------------------------\n\n/**\n * Style text per data-plugin-css tag id, cached at module scope so it\n * survives unload → re-apply cycles of one materialized module (the CSS\n * factory body only runs once per materialization).\n */\nconst styleTextCache = new Map<string, string>()\n\n/**\n * Keeper effect for the tsdown-injected `<style data-plugin>` tags: cache\n * their text, restore any tag a previous unload removed, and remove all of\n * this plugin's tags on dispose.\n */\nfunction keepStylesAlive(): () => void {\n if (typeof document === 'undefined') return () => {}\n const ownTags = `style[data-plugin=${JSON.stringify(PLUGIN_ID)}]`\n for (const el of Array.from(document.querySelectorAll(ownTags))) {\n const key = (el as HTMLStyleElement).dataset['pluginCss']\n if (key !== undefined) styleTextCache.set(key, el.textContent ?? '')\n }\n for (const [key, cssText] of styleTextCache) {\n if (document.querySelector(`style[data-plugin-css=${JSON.stringify(key)}]`) === null) {\n const tag = document.createElement('style')\n tag.dataset['plugin'] = PLUGIN_ID\n tag.dataset['pluginCss'] = key\n tag.textContent = cssText\n document.head.appendChild(tag)\n }\n }\n return () => {\n for (const el of Array.from(document.querySelectorAll(ownTags))) el.remove()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin entry.\n// ---------------------------------------------------------------------------\n\n/**\n * Mount the board tab, footer widget, and settings card, and run the data\n * controller for as long as the plugin fiber lives.\n * @param ctx - browser plugin context handed by the client loader.\n */\nexport function apply(ctx: ClientContext): void {\n const controller = new SidecarController()\n\n // Inject integration (S5/T4.9): the detail view hosts the panel; a\n // delivered execute pulls one fresh snapshot so the board reflects the\n // injection promptly. The default mode is a mutable box because the\n // settings scope (seat 3) resolves after the board mounts — the reader\n // is late-bound and the box holds the schema default until then.\n const injectPrefs: { defaultMode: InjectMode } = { defaultMode: 'queue' }\n\n // Analysis capability (T5.10b): the state snapshot carries no analysis\n // bit, so the UI gate mirrors the live `analysis.enabled` setting the\n // same late-bound way (schema default false = fail-closed until the\n // scope resolves); the server 403 gate stays authoritative regardless.\n const analysisPrefs: { enabled: boolean } = { enabled: false }\n\n // M3 integration handed to the board tab (design §5.1): detail routing,\n // project view, dsh deep-query tools, and the analysis panel, each over\n // its glue store on the real transports.\n const uiIntegration: SidecarUiIntegration = createDefaultIntegration({\n inject: {\n actions: createInjectActions({\n onDelivered: () => {\n void controller.refresh()\n },\n }),\n getDefaultMode: () => injectPrefs.defaultMode,\n },\n getAnalysisEnabled: () => analysisPrefs.enabled,\n })\n\n // Data feed + visibility resume (design §5.3: visibilitychange → pollNow).\n ctx.effect(() => {\n try {\n controller.start()\n } catch (err) {\n // e.g. an environment without EventSource: the board renders its\n // empty/unknown state instead of taking the plugin fiber down.\n console.error('agent-sidecar: data stream start failed', err)\n }\n if (typeof document === 'undefined') {\n return () => {\n controller.stop()\n }\n }\n const onVisibility = (): void => {\n if (!document.hidden) controller.pollNow()\n }\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n document.removeEventListener('visibilitychange', onVisibility)\n controller.stop()\n }\n }, 'agent-sidecar: client data feed')\n\n ctx.effect(keepStylesAlive, 'agent-sidecar: injected styles')\n\n // Seat 1: cross-agent board as the \"Sidecar\" conversation tab — since\n // T5.10b the shell of the M3 information architecture: board/project\n // switcher, card-click detail routing (timeline + inject + analysis +\n // dsh lineage/search), all riding the integration seams above.\n try {\n const SidecarBoardTab = createBoardTab(controller, uiIntegration)\n ctx.slots.inject('conversation.view', () => {\n if (hasOwnEntry(ctx, 'conversation.view')) return () => {}\n try {\n return ctx.slots.register({\n name: 'conversation.view',\n id: ENTRY_ID,\n order: 30,\n label: 'Sidecar',\n }, SidecarBoardTab)\n } catch (err) {\n console.error('agent-sidecar: board tab registration failed', err)\n return () => {}\n }\n })\n } catch (err) {\n console.error('agent-sidecar: board tab mount failed', err)\n }\n\n // Seat 2: footer status widget.\n try {\n const SidecarFooterWidget = createFooterWidget(controller)\n ctx.slots.inject('sidebar.footer.action', () => {\n if (hasOwnEntry(ctx, 'sidebar.footer.action')) return () => {}\n try {\n return ctx.slots.register({\n name: 'sidebar.footer.action',\n id: ENTRY_ID,\n order: 30,\n }, SidecarFooterWidget)\n } catch (err) {\n console.error('agent-sidecar: footer widget registration failed', err)\n return () => {}\n }\n })\n } catch (err) {\n console.error('agent-sidecar: footer widget mount failed', err)\n }\n\n // Seat 3: settings card, once the optional settingsScope service resolves.\n try {\n // The inject callback receives the base cordis Context; narrow once\n // (slots/effect are inherited, settingsScope is the resolved service).\n ctx.inject(['settingsScope'], (injected) => {\n const sctx = injected as ClientContext & { settingsScope: SettingsScopeBinderFace }\n try {\n const scope = sctx.settingsScope.bind<SidecarConfigView>({\n namespace: SETTINGS_NAMESPACE,\n })\n\n // ui.* settings defaults seed the board filters until the user\n // touches them (localStorage value or filter gesture wins);\n // inject.default-mode feeds the panel's late-bound mode reader;\n // analysis.enabled feeds the analysis entry's late-bound gate.\n const adoptDefaults = (): void => {\n const value = scope.getSnapshot().value\n if (value?.ui !== undefined) controller.adoptConfigDefaults(value.ui)\n if (value?.inject !== undefined) injectPrefs.defaultMode = value.inject.defaultMode\n if (value?.analysis !== undefined) analysisPrefs.enabled = value.analysis.enabled\n }\n sctx.effect(\n () => scope.subscribe(adoptDefaults),\n 'agent-sidecar: settings→filter defaults',\n )\n adoptDefaults()\n\n const SidecarSettingsCardEntry = createSettingsCardEntry(controller, scope)\n sctx.slots.inject('settings.plugin.item', () => {\n if (hasOwnEntry(sctx, 'settings.plugin.item')) return () => {}\n try {\n return sctx.slots.register({\n name: 'settings.plugin.item',\n key: SETTINGS_NAMESPACE,\n }, SidecarSettingsCardEntry)\n } catch (err) {\n console.error('agent-sidecar: settings card registration failed', err)\n return () => {}\n }\n })\n } catch (err) {\n console.error('agent-sidecar: settings card mount failed', err)\n }\n })\n } catch (err) {\n console.error('agent-sidecar: settings scope injection failed', err)\n }\n\n // Seat 4: the `/sidecar` slash command, lazy on the commandUi service\n // (T4.6). registerSidecarCommand catches its own registration failures\n // (duplicate name on a double apply logs and no-ops); this try/catch\n // keeps the degradation posture uniform with the slot seats. Disposal\n // rides the injected fiber — unloading the plugin unregisters it.\n try {\n registerSidecarCommand(ctx)\n } catch (err) {\n console.error('agent-sidecar: /sidecar command mount failed', err)\n }\n\n // Seat 5 (optional, T6.3): the better-sidebar mini tab, parked on the\n // lazily-injected `betterSidebar` service (optional peer; runtime\n // duck-typed probe, never imported). Not installed → the fiber stays\n // pending forever: silent skip, zero resources. A probe/registration\n // failure degrades to a log and never touches the other seats.\n try {\n mountSidebarTab(ctx, controller)\n } catch (err) {\n console.error('agent-sidecar: better-sidebar tab mount failed', err)\n }\n}\n"],"mappings":"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"}
|