# Knowledge Interaction Surfaces Web Client

> 覆盖 Blade React Web 的启动恢复、Session/任务 Zustand 投影、SSE 重连、聊天输入、 多项目导航、布局、预览、Browser 面板与终端。进入时机：修改 Web 消息流、Session 切换、草稿、错误恢复、Sidebar/Kanban、全屏预览、文件/Browser 活动或前端 API。 不包含：Hono 路由与服务端 run 所有权（见 ../hono-server-api-and-streaming/）、 Browser 自动化内核（见 ../../tool-and-automation-platform/browser-automation/）。 关键词：useSessionStore, sessionService, EventSource, createEventDispatcher, ChatInput, Layout, FilePreview, BrowserPanel, SessionRef。

- Skill: `echovic/knowledge-interaction-surfaces-web-client` (Agent Skill)
- Install (CLI): `npx skillmds@latest add echovic/knowledge-interaction-surfaces-web-client`
- Raw SKILL.md: https://api.skillmd.com/api/skills/echovic/knowledge-interaction-surfaces-web-client/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: echovic (https://skillmd.com/u/echovic)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/echovic/knowledge-interaction-surfaces-web-client

---


## Module Structure

Web Client 将 HTTP 快照、单 Session SSE 与全局任务 SSE 折叠为浏览器状态，并以复合
SessionRef 驱动聊天、看板、Sidebar、预览和终端；它不直接执行 Agent。

### Directory Layout
- `packages/cli/web/src/App.tsx` — URL/本地状态恢复、全局订阅和主视图选择
- `packages/cli/web/src/services/` — HTTP、Session SSE、全局 SSE 与 Browser API 客户端
- `packages/cli/web/src/store/session/` — Session、消息、流式事件、任务与 UI slices
- `packages/cli/web/src/components/chat/` — 对话时间线、输入、交互、Goal、Team 与状态
- `packages/cli/web/src/components/layout/` — Sidebar、导航、响应式壳层与资源入口
- `packages/cli/web/src/components/tasks/` and `packages/cli/web/src/components/kanban/` — 任务创建、切换与多项目看板
- `packages/cli/web/src/components/preview/` — diff、文件、日志和 Browser 预览
- `packages/cli/web/src/components/terminal/` — WebSocket PTY 面板
- `packages/cli/web/src/lib/` — 草稿、Session 导航、身份、附件和快捷键辅助

### Key Entry Points
- `App` in `packages/cli/web/src/App.tsx` — 解析导航意图并选择临时任务、聊天或看板
- `sessionService.openEventSubscription()` in `packages/cli/web/src/services/sessionService.ts` — 单 Session SSE 与 durable cursor
- `createSessionSlice()` in `packages/cli/web/src/store/session/slices/sessionSlice.ts` — Session 导航、发送、恢复和任务动作
- `createEventDispatcher()` in `packages/cli/web/src/store/session/handlers/eventHandlers.ts` — SSE 到 Store/Browser 活动的分发
- `Layout` in `packages/cli/web/src/components/layout/Layout.tsx` — Sidebar、工作区、预览与终端组合
- `ChatInput` in `packages/cli/web/src/components/chat/ChatInput.tsx` — Session 级输入、草稿和能力选择

## Gotchas
- Web 中 Session 身份必须始终是 `{sessionId, projectPath}`；历史上只比 session ID 会让跨项目同名 Session 的消息、错误、预览和任务状态串线 (`packages/cli/web/src/store/session/sessionIdentity.ts`, `git:d20b6ed5`)
- `selectSession()` 必须先打开并缓冲新 SSE，再并行拉取消息/Goal/Session/Team 快照，最后原子提交身份并回放缓冲事件；先替换 Store 再订阅会产生 snapshot/live 空窗 (`packages/cli/web/src/store/session/slices/sessionSlice.ts`, `git:121ea8fc`)
- 新订阅准备失败时旧订阅必须继续存活；`replaceEventSubscription()` 只能在新连接 ready 后关闭旧连接 (`packages/cli/web/src/store/session/slices/streamingSlice.ts`, `git:b3ffab60`)
- `ChatView` 不应在 React effect cleanup 中取消 Store 拥有的 SSE；StrictMode 会重放 effect，组件级 cleanup 曾导致激活后连接被意外关闭 (`packages/cli/web/src/components/chat/ChatView.tsx`, `git:01505f04`)
- 活动 run 的 SSE 不在线时 Composer 必须禁用 steering；HTTP 仍可接收请求不代表客户端能安全观察确认、终态或 follow-up (`packages/cli/web/src/components/chat/ChatView.tsx`)
- 所有终态事件和 `tool.start` 前必须排空 80ms delta 缓冲；否则 prose/reasoning 可能在工具卡或完成状态之后写入 (`packages/cli/web/src/store/session/handlers/eventHandlers.ts`, `packages/cli/web/src/store/session/handlers/streamingBuffer.ts`)
- `session.completed`、`session.error`、idle status 与 abort 会触发权威消息重同步；只依赖 live delta 无法覆盖断线期间的 committed 结果或恢复生成的消息 (`packages/cli/web/src/store/session/handlers/eventHandlers.ts`, `packages/cli/web/src/store/session/slices/sessionSlice.ts`)
- 显式 URL 中的 Session 是权威意图：目标缺失或不可用时必须停在临时页面并保留导航错误，不能静默打开 localStorage 中另一个 Session (`packages/cli/web/src/store/session/sessionNavigation.ts`)
- Composer 草稿按复合 SessionRef 分区；文本与 output schema 写入 sessionStorage，图片只在内存 Map 保存，浏览器刷新后不会恢复图片附件 (`packages/cli/web/src/components/chat/ChatView.tsx`, `packages/cli/web/src/lib/composerDraft.ts`)
- 发送成功后才清草稿；请求拒绝时 Store 删除 optimistic user message，而 ChatInput 保留输入供重试，不能在点击发送时先清空 (`packages/cli/web/src/components/chat/ChatInput.tsx`, `packages/cli/web/src/store/session/slices/sessionSlice.ts`)
- 模型、reasoning、service tier、verbosity 与 permission mode 在活动 run 中被冻结；Composer 可以显示选择器，但发送 steering 时不会携带这些切换，Server 也会拒绝并发切换 (`packages/cli/web/src/components/chat/ChatInput.tsx`, `packages/cli/web/src/store/session/slices/sessionSlice.ts`)
- FilePreview 的请求必须同时校验 request generation 和当前 SessionRef；仅比较路径或 URL 会让 A→B→A 的旧响应覆盖新 A 状态 (`packages/cli/web/src/components/preview/FilePreview.tsx`)
- worktree Task 交付后文件树浏览 source project，但 durable diff 仍从 Task Session artifact 读取；discard 后则明确隐藏所有旧消息 diff (`packages/cli/web/src/components/preview/FilePreview.tsx`)
- Browser tab 使用 `forceMount` 保留 iframe/控制状态；切换到 Files/Logs 时不能卸载 Browser 会话 (`packages/cli/web/src/components/preview/FilePreview.tsx`)
- 全屏预览仍保留同一个 Chat Composer 和状态投影，只把工作区内容置于覆盖层后；不要为全屏模式创建第二套 ChatView/Input (`packages/cli/web/src/components/layout/Layout.tsx`, `packages/cli/web/src/components/chat/ChatView.tsx`, `git:3b786d37`)

## Architecture
- 启动时全局任务 SSE、Session catalog、设置、模型和工作区信息并行加载；没有显式目标时先进入临时 Session，避免阻塞首屏等待完整 catalog (`packages/cli/web/src/App.tsx`)
- Store 由 Session、TaskList、Message、Streaming 和 UI slices 组成；Session slice 负责身份/命令，event dispatcher 负责事件归约，组件只读取投影 (`packages/cli/web/src/store/session/index.ts`)
- Session SSE 使用 committed `seq` 维护内存游标并指数退避重连；connected 帧必须匹配精确 SessionRef 才算 ready，heartbeat 只更新活性 (`packages/cli/web/src/services/sessionService.ts`)
- 高频 content、thinking 和 subagent delta 按 channel 合并；工具边界会把内容从 before 切到 after，使同一个 assistant 时间线保持正文、工具、后续正文的顺序 (`packages/cli/web/src/store/session/handlers/eventHandlers.ts`)
- 全局任务流单独驱动 Sidebar/Kanban 和 unread attention，不依赖当前打开的 Session；单 Session 流只处理当前精确 ref (`packages/cli/web/src/store/session/slices/taskListSlice.ts`, `packages/cli/web/src/store/session/handlers/eventHandlers.ts`)
- Preview 同时支持 message 内嵌 diff 与 durable Task artifact；有 durable artifact 预期时失败会显示错误而不会退回可能过期的 message diff (`packages/cli/web/src/components/preview/FilePreview.tsx`)
- Agent Browser 活动只从不带 seq 的 live tool start/result 投影到 BrowserActivityStore，并自动打开 Browser tab；历史 committed 工具事件不能伪装成当前活动 (`packages/cli/web/src/store/session/handlers/eventHandlers.ts`, `packages/cli/web/src/store/BrowserActivityStore.ts`, `git:ed9505d7`)

## Decisions
- Web 使用共享 TypeBox API schema 的 Vite alias，而不复制请求/事件类型；Vite 还把巨大的 event handler 单独分 chunk 以控制首屏 bundle (`packages/cli/web/vite.config.ts`)
- 导航 URL 同时携带展示 project 与 Session execution workspace；worktree Session 两者不同时使用独立 `workspace` 参数，分享链接仍能精确恢复 (`packages/cli/web/src/store/session/sessionNavigation.ts`)
- Web 权限模式由当前 Session metadata 驱动，新临时任务重置为 `autoEdit`，避免从刚访问的 YOLO Session 泄漏到下一任务 (`packages/cli/web/src/store/session/slices/sessionSlice.ts`, `packages/cli/web/src/store/ConfigStore.ts`)
- 输入能力控件按当前模型 catalog 动态显示，communication style 例外地是全局跨 Session 偏好；不要把它误做成 Composer 私有 override (`packages/cli/web/src/components/chat/ChatInput.tsx`, `packages/cli/web/src/store/SettingsStore.ts`)

## Patterns
- 每次导航、fork、异步预览和 catalog 加载都持有 generation；结果返回时同时验证 generation 与精确 ref，迟到成功和迟到错误都静默丢弃 (`packages/cli/web/src/store/session/slices/sessionSlice.ts`, `packages/cli/web/src/components/preview/FilePreview.tsx`)
- optimistic user message 仅覆盖提交等待窗口，服务端 `message.created` 会按身份替换；提交失败则移除 optimistic 项并设置带 SessionRef 的 errorContext (`packages/cli/web/src/store/session/slices/sessionSlice.ts`, `packages/cli/web/src/store/session/slices/messageSlice.ts`)
- 错误 UI 按 navigation、submission、execution、task_action 分类；另一个 Session 的 task action 错误不得出现在当前 ChatView (`packages/cli/web/src/components/chat/ChatView.tsx`)
- 响应式 Sidebar 与 Preview 在窄屏作为 focus-contained modal，并把所有背景区域设为 inert；关闭后恢复触发按钮焦点 (`packages/cli/web/src/components/layout/Layout.tsx`, `packages/cli/web/src/components/preview/FilePreview.tsx`)
- 本地持久化均 fail open：导航、草稿、Sidebar 模式和 Preview 宽度在 storage 被禁用时仍保留内存行为 (`packages/cli/web/src/lib/composerDraft.ts`, `packages/cli/web/src/store/session/sessionNavigation.ts`, `packages/cli/web/src/store/AppStore.ts`)

## Dependencies
- Web 使用 React 19、Zustand、Vite、Radix primitives、Monaco 与 xterm；运行时 API 路径由 Vite dev proxy 或 BladeServer 同源静态托管提供 (`packages/cli/web/package.json`, `packages/cli/web/vite.config.ts`)

