Merge pull request #1117 from pikasTech/feat/1115-workbench-store-trace-artificer

docs: 定义 Workbench Vue 迁移语义合同
This commit is contained in:
Lyon
2026-06-12 02:12:03 +08:00
committed by GitHub
2 changed files with 78 additions and 0 deletions
+1
View File
@@ -97,6 +97,7 @@ HWLAB 是硬件实验室运行面和控制面项目。本文是 agent、指挥
- v0.2 CI/CD 加法 lane、`v0.2-gitops``hwlab-v02``19666/19667` 规格:[docs/reference/spec-v02-cicd.md](docs/reference/spec-v02-cicd.md)。
- v0.2 `hwlab-cloud-api` API 核心服务规格:[docs/reference/spec-v02-hwlab-cloud-api.md](docs/reference/spec-v02-hwlab-cloud-api.md)。
- v0.2 `hwlab-cloud-web` 浏览器工作台规格:[docs/reference/spec-v02-hwlab-cloud-web.md](docs/reference/spec-v02-hwlab-cloud-web.md)。
- v0.3 Workbench Vue 迁移 API/store/trace 语义合同:[docs/reference/spec-v03-workbench-vue-migration.md](docs/reference/spec-v03-workbench-vue-migration.md)。
- v0.2 Provider API Key 管理页、HWLAB 鉴权后委托 AgentRun 后端和 DeepSeek 官方链路验证规格:[docs/reference/spec-v02-provider-management.md](docs/reference/spec-v02-provider-management.md)。
- v0.2 Code Agent 由 `hwlab-cloud-api` 接入 AgentRun v0.1 共享执行基础设施,不再保留 HWLAB 自有 agent manager/worker 控制面:[docs/reference/agentrun-code-agent-dispatch.md](docs/reference/agentrun-code-agent-dispatch.md)。
- v0.2 Code Agent trace/result/final response 的权威数据源、派生缓存边界和 Web/CLI trace 展示路径:[docs/reference/spec-v02-code-agent-trace.md](docs/reference/spec-v02-code-agent-trace.md)。
@@ -0,0 +1,77 @@
# v0.3 Workbench Vue 迁移语义合同
本文定义 Cloud Workbench 从 React `useWorkbenchStore` 迁移到 Vue/Pinia 时必须保留的 API、store 和 trace 语义。Vue 实现可以复用 Sub2API frontend 的 api/store/composable 分层形态,但不能照搬 Sub2API 的 bearer token 鉴权和通用刷新模型。
## 基线来源
| 来源 | 基线文件 | 可迁移结论 |
|---|---|---|
| HWLAB React Workbench | `web/hwlab-cloud-web/src/state/workbench.ts``runner-trace.ts``trace-reattach.ts``services/api/client.ts``types/domain.ts` | 当前语义权威。Vue 迁移必须保持显式 session、inactivity-timeout、trace reattach、result/trace 同源 API 和 HWPOD evidence label。 |
| HWLAB Web 规格 | `docs/reference/spec-v02-hwlab-cloud-web.md` | Code Agent 没有浏览器侧 total-timeout / attempt cap;唯一 abort 信号是 `activityRef` 驱动的 inactivity-timeout。 |
| Sub2API frontend `v0.1.136` | `src/api/client.ts``src/stores/auth.ts``src/stores/app.ts``src/composables/useAutoRefresh.ts``src/composables/useClipboard.ts` | 可复用 Composition API Pinia store、独立 api module、toast/clipboard/auto-refresh composable 的组织方式;不可复用 localStorage bearer token 鉴权。 |
## React Workbench 语义提取
| React 模块 | 当前语义 | Vue 迁移归属 |
|---|---|---|
| `services/api/client.ts::fetchJson` / `fetchText` | 同源 `fetch``credentials=same-origin`、统一 `ApiResult<T>``recordApiTiming``activityRef` 每秒重算 idle window,超时错误必须说明 `waitingFor``lastEventLabel`。 | `src/api/client.ts` 保持 fetch client,不换成 Sub2API axios bearer client。 |
| `services/api/client.ts::api` | 同源代理覆盖 auth bootstrap、live probe、workspace、conversation、Code Agent send/steer/result/cancel、HWPOD node-ops、skills 等端点。 | 拆成 `src/api/workbench.ts``src/api/auth.ts`;只拆模块,不改 endpoint path 和 timeout contract。 |
| `state/workbench.ts::initialState` | `providerProfile``codeAgentTimeoutMs``gatewayShellTimeoutMs` 从 localStorage 读取并 clamp;默认 Code Agent inactivity budget 为 `1_800_000ms`。 | `stores/workbench` state 初始化;持久化 key 保持 `hwlab.workbench.*.v1`。 |
| `state/workbench.ts::hydrate` | 并发读取 `/v1/workbench/workspace``/v1/agent/conversations`,用 workspace selected conversation 生成 message 列表并记住 projectId。 | `stores/workbench.hydrate()`;首屏可由 `useWorkspaceBootstrap` 提供 workspace 快照,后续再补 conversation list。 |
| `state/workbench.ts::refreshLive` | 每 30s 读取 health/live、health、REST index、adapter、HWPOD specs、HWPOD node-ops、live-builds,并推导 Code Agent availability。 | `stores/workbench.refreshLive()` + `useAutoRefresh`;只能在 workspace 入口启用,性能页不得初始化。 |
| `state/workbench.ts::createSession` / `selectConversation` / `deleteCurrentSession` | session 由用户显式创建、选择、删除;普通 send 不隐式创建 sessionselect payload 带 `updatedByClient`。 | `stores/workbench` actionsVue UI 必须先暴露 session 创建/选择入口,再允许普通 turn。 |
| `state/workbench-reducer.ts::composerFromState` | 通过当前 request、running agent message、workspace active trace 推导 composerrunning turn 时 submit mode 为 `steer` 且 route 为 `/v1/agent/chat/steer`。 | `stores/workbench` getter `composer`;不要在组件中重复推导。 |
| `state/workbench.ts::submitMessage` | 生成 user/pending message,先 `persistConversation`,再 POST send/steersubmit kickoff 调 `recordActivity()`POST 使用同一个 `activityRef`terminal 后 merge trace/result 并再次持久化。 | `stores/workbench.submitMessage()``useTraceSubscription`message mutation 和 persistence 保持 store 内聚。 |
| `state/runner-trace.ts::subscribeToTrace` | result 和 trace 两路同源 poll;每次成功 poll 调 `onActivity`5xx 不刷新 activity 且继续 poll;非 5xx 失败走 infrastructure error;外层 `for(;;)` 没有 total timeout。 | `composables/useTraceSubscription.ts` 的唯一 active polling 入口。 |
| `state/runner-trace.ts::mergeTraceResults` | terminal result 与 trace snapshot 合并;优先使用 finalResponse、terminal assistant event、result text,保留 `agentRun`、events、traceSummary、terminalEvidence。 | `api/workbench``composables/useTraceSubscription` 附近的纯函数;必须有单元测试。 |
| `state/trace-reattach.ts` | hydrate 后发现 `activeTraceId` 且当前没有 pending,就创建 placeholder 并主动订阅到 terminal;完成/失败都写回 conversation。 | `composables/useTraceReattach.ts` watch `store.activeTraceId``store.chatPending`。 |
| `components/hwpod/HwpodNodeOpsSidebar.tsx` | evidence label 为 `Contract``Specs``Spec``Node``Ops`、per-op label 和 `probe`status/freshness/blocker 来源必须是同源 `/v1/hwpod-node-ops``/v1/hwpod/specs?probe=1`。 | Vue HWPOD panel 保留同名 label、`data-hwpod-detail` 语义和 blocker/status 规则。 |
## Sub2API 分层对照
| Sub2API 形态 | 可吸收部分 | HWLAB 必须改写部分 |
|---|---|---|
| `api/client.ts` axios instance、request/response interceptor、统一 unwrap `{ code, data }` | api client 是独立模块,业务 endpoint 放在 `src/api/*.ts`;请求取消保持原错误,便于调用方忽略。 | HWLAB 不使用 localStorage `auth_token``refresh_token` 或 401 token refresh queue;保留 same-origin cookie session、`ApiResult<T>``activityRef` inactivity-timeout。 |
| `stores/auth.ts` Composition API Pinia`ref` state、`computed` getter、actions、定时器 cleanup | auth store 的组织方式、启动时 `checkAuth/bootstrap`、loading/error state 可复用。 | HWLAB `stores/auth` 只消费 `/auth/bootstrap``/auth/session``/auth/login``/auth/logout`;不得持久化 bearer token 或引入 Sub2API refresh token。 |
| `stores/app.ts` toast、global loading、cached settings | toast/loading 与 `useClipboard` 依赖 app store 的方式可复用。 | Workbench live refresh、workspace hydrate 不放进 app storeapp store 只保留 UI 横切状态。 |
| `composables/useAutoRefresh.ts` localStorage 保存 enabled/interval/countdown/fetching,组件卸载清 timer | 可作为 Workbench live refresh 和 HWPOD panel refresh 控件基础。 | 不得用 auto-refresh 替代 Code Agent trace subscriptiontrace 仍由 `useTraceSubscription` 无限 poll 并使用 `activityRef`。 |
| `composables/useClipboard.ts` Clipboard API + textarea fallback + toast | 可直接迁移为工作台复制 trace/result/session id 的通用 composable。 | 成功/失败文案走 HWLAB app store/i18n;不要把复制行为和业务状态 mutation 耦合。 |
## Vue Pinia / Composable 目标映射
| 目标文件 | 责任边界 | 必须暴露的接口 |
|---|---|---|
| `src/api/client.ts` | fetch 基础设施、`ApiResult<T>``ActivityRef`、inactivity-timeout、RUM timing。 | `fetchJson<T>(path, options)``fetchText(path, options)``type ActivityRef``type ActivityRefSource`。 |
| `src/api/workbench.ts` | Workbench 业务 endpoint wrapper;只表达 HTTP path 和 timeout,不持有 UI state。 | `workspace``updateWorkspace``selectConversation``conversations``saveConversation``deleteConversation``sendAgentMessage``steerAgentMessage``getAgentChatResult``cancelAgentMessage``liveSurface``hwpodSpecs``hwpodNodeOpsHealth`。 |
| `src/api/auth.ts` | Web session bootstrap/login/logout wrapper。 | `bootstrap()``session()``login(username,password)``logout()`;返回同源 session 状态。 |
| `src/stores/auth.ts` | Web session 状态。 | state: `authenticated``user``actor``expiresAt``loading``error`; actions: `bootstrap``login``logout`; getter: `isAuthenticated`。 |
| `src/stores/workbench.ts` | Workbench durable state、derived composer、session lifecycle、message mutation、conversation persistence。 | state: `workspace``conversations``messages``providerProfile`、timeouts、`live``codeAgentAvailability``chatPending``currentRequest``lastActivityAt`; getters: `activeProjectId``activeConversationId``sessionTabs``composer``activeTraceId`; actions: `hydrate``refreshLive``createSession``selectConversation``deleteCurrentSession``submitMessage``cancelAgentMessage``retryAgentMessage``replayAgentTrace``recordActivity`、timeout/profile setters。 |
| `src/composables/useTraceSubscription.ts` | active trace polling lifecycle;替代 React `subscribeToTrace`。 | `start(config)``stop()``running`; config 包含 `traceId``projectId``initial``inactivityTimeoutMs``onActivity``onSnapshot``onComplete``onInfrastructureError`。 |
| `src/composables/useTraceReattach.ts` | watch store active trace and reattach after hydrate/refresh。 | 自动 watch `enabled``store.activeTraceId``store.chatPending`; 创建 placeholder、订阅、完成/失败后 persist。 |
| `src/composables/useWorkspaceBootstrap.ts` | 首屏 bootstrap 合并 auth/workspace,避免重复请求。 | `bootstrapWorkspace(projectId)` 返回 auth state + workspace snapshot;必须消费 `/auth/workspace-bootstrap?projectId=...`。 |
| `src/composables/useAutoRefresh.ts` | UI refresh control for live surface only。 | 复用 Sub2API `enabled``intervalSeconds``countdown``fetching``setEnabled``setInterval``shouldPause` 默认在 hidden/performance route 时暂停。 |
| `src/composables/useClipboard.ts` | 文本复制和 toast。 | `copyToClipboard(text, successMessage?)``copied`。 |
## Trace / Result 不变量
- `submitMessage``useTraceReattach` 必须共享 `useTraceSubscription`,不得拆成 submit-only 和 hydrate-only 两套 polling。
- `getAgentChatResult(resultUrl, inactivityTimeoutMs, activityRef)` 的 timeout 入参不随 wall-clock 缩小;外层循环没有累计耗时上限、attempt cap 或 `timeoutMs * N` 兜底。
- 每一次成功 result poll 或 trace poll 都调用 `store.recordActivity()`5xx retry 不调用,非 5xx 业务失败进入 infrastructure error。
- terminal 完成前至少尝试合并 trace snapshotfinal assistant text 优先级保持 `finalResponse`、terminal assistant event、result text、普通 assistant event。
- steer POST 如果 timeout/ECONNRESET 但目标 trace 仍 runningVue store 必须释放本次 submit lock、保留原 agent message running/steerable,不得把原 turn 标 failed。
- `session_failed``thread-resume-failed`、provider continuation 失效、运行面中断或用户取消后,不得自动滚动新 session;下一步必须由用户显式创建或选择 session。
## 迁移顺序
1. 先迁 `types/domain.ts`、trace merge 纯函数和 `api/client.ts`,用无浏览器单测锁住 `activityRef` inactivity 行为。
2. 再迁 `api/workbench.ts``stores/auth.ts`,确认同源 session contract 未引入 bearer token。
3.`useTraceSubscription``useTraceReattach`,用 fake API 断言无 total-timeout、成功 poll 刷新 activity、5xx 不刷新 activity、terminal result 合并 trace。
4.`stores/workbench.ts`,把 React reducer 的 action 语义变成 Pinia action/getter;组件只读 getter 和调 action。
5. 最后迁 UI 组件,HWPOD、trace panel、session sidebar 和 composer 只消费 store/composable,不内联业务推导。
## 验证要求
- 源码层:Vue 迁移 PR 至少覆盖 `api/client` inactivity、`useTraceSubscription` polling、`stores/workbench.composer``useTraceReattach` placeholder/terminal persistence、HWPOD label mapping。
- 静态检查:保留 `bun run --cwd web/hwlab-cloud-web check` 或 Vue 等价入口;不得恢复旧 quick prompt、旧 layout smoke 或与当前契约无关的门禁。
- 同路径验收:显式创建或选择 Code Agent session 后,通过 Web 同源 path 提交 turn`result/trace --render web` 使用同一 trace row;未选择 session 的普通 send 必须返回 `session_required`
- HWPOD 验收:Vue 面板必须从 `/v1/hwpod-node-ops``/v1/hwpod/specs?probe=1` 显示 status/freshness/blocker,不得用 fixture 或 blocked fallback 声称真实硬件 DEV-LIVE。