Commit Graph

22 Commits

Author SHA1 Message Date
Lyon de0f79c3ce fix(v02): replay empty loaded traces (#865)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 22:31:44 +08:00
Lyon 7652b39235 fix(v02): persist Web refresh state (#859)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 21:45:40 +08:00
Lyon 412f95f1a5 fix(v02): unify web trace markdown rendering (#858)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 21:33:01 +08:00
Lyon 84e649c294 fix(v02): load compact conversation traces (#856)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 21:12:07 +08:00
Lyon 2129cebfef fix(v02): improve trace and sidebar web ui (#854)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 20:56:43 +08:00
Lyon 3f26ec0b16 fix(v02): add workspace scroll follow and markdown renderer (#839)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 17:50:47 +08:00
Lyon 2fc3972ddd fix(v02): compact workbench status panels (#838)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 17:47:56 +08:00
Codex Agent 7a58695318 fix(v02): unify web trace final response 2026-06-04 17:29:28 +08:00
Codex Agent e46350993c fix: compact code agent workbench layout 2026-06-04 15:26:13 +08:00
Codex Agent caf5e6cde5 fix(web): v0.2 drop Agent workspace intro System cards, fold 界面模式 into panel title hint (HWLAB #803 followup)
PR #807 collapsed the top #code-agent-summary and per-message debug panels
into a 调试信息 dialog, but the conversation list still showed two hard-coded
System cards (界面模式 / Code Agent 状态) that fed availability into the
agent run path. Both appeared as full <article class="message-card
message-system"> bubbles before any user message on every session load,
contradicting the issue goal of a clean agent / user conversation feed.

- remove the ConversationPanel intro useMemo + [...intro, ...messages]
  spread; the conversation list now only contains real agent / user
  messages.
- move the static 界面模式 content to a small muted hint line under the
  panel title (id=workspace-hint, .conversation-panel-hint).
- the live Code Agent availability snapshot is already covered by the
  existing #code-agent-summary + 调试信息 dialog, so it is dropped.
- add a conversation-list empty-state placeholder so the panel keeps
  height for layout smoke (messages.length === 0).
- delete the now-orphan MessageRuntimePath / MessageSessionContinuity /
  MessagePendingContext components (no remaining callers per rg).
- extend tools/capture-issue-803-noise.mjs to report each .message-card
  role plus systemMessageCount / roleCounts so layout-level evidence
  can assert "no message-system in the conversation list".

web:check 12 pass / 0 fail; web:layout:build pass (desktop, narrow,
mobile). Issue: pikasTech/HWLAB#803
2026-06-04 13:48:29 +08:00
Lyon fb267d28b2 fix(web): v0.2 collapse Agent workspace debug noise into a 调试信息 dialog (HWLAB #803) (#807)
The Agent workspace (top #code-agent-summary and each agent message card)
exposed too many "未观测" / readiness / providerTrace / runtime-path rows
inline; per-message panels (MessageRuntimePath, MessageSessionContinuity,
MessagePendingContext) ate most of the card width and made the agent trace
events list (the only thing the user really wants to see) read at ~83% of
the card.

This change:
- introduces a reusable <DebugDialog> component (web/hwlab-cloud-web/src/components/shared/DebugDialog.tsx)
  that surfaces the full availability / readiness / runnerTrace / raw JSON
  payload only when the user clicks a 调试信息 button.
- shrinks codeAgentSummaryRows() in state/code-agent-status.ts to only
  return rows with meaningful values; "未观测" / "字段缺失:证据不足" /
  "DEGRADED:运行路径字段不完整" / "无"-only readiness blockers are
  filtered out of the top summary and remain reachable via the dialog.
- rewrites MessageCard to drop the inline MessageRuntimePath /
  MessageSessionContinuity / MessagePendingContext panels and to show a
  single compact footer (timestamp + 调试信息 button). The pending running
  hint is reduced to a one-line tag instead of a 7-row grid.
- makes .message-trace and .message-trace-events fill the message card so
  the trace events list is the dominant element per the issue.
- adds tools/capture-issue-803-noise.mjs so CLI can log into the live or
  local Cloud Web, capture the rendered #code-agent-summary / per-message
  meta / trace widths into a JSON document, and diff before/after the
  refactor.

web:check, web:layout, dev-cloud-workbench-smoke --static, and
dev-cloud-workbench-layout-smoke all pass on the local build.
Issue: pikasTech/HWLAB#803

Co-authored-by: HWLAB Agent <hwlab-agent@pikastech.local>
2026-06-04 11:17:18 +08:00
Lyon 088414747b fix(web): unlock v0.2 code agent composer steer (#805)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 11:02:40 +08:00
Lyon 909715c335 fix(web): v0.2 wire workbench activityRef end-to-end (HWLAB #795) (#797)
The React migration (#756) and the 7-round #775 patch left the
`fetchJson` inactivity-timeout surface half-wired: `client.ts` and the
`api.*` helpers accepted an `ActivityRef`, but `state/workbench.ts`
`submitMessage` and `state/runner-trace.ts` `waitForAgentResult` /
`pollRunnerTrace` never built or forwarded one. The Web UI was therefore
falling back to a **total** timeout: a long-running Code Agent operation
(e.g. `bootsharp` that finished in ~9.7s but the assistant message
chain took ~37s) was killed at `codeAgentTimeoutMs` even while trace
events kept flowing, producing "Code Agent 在超时内未返回可显示正文".
User-facing symptom in #795 is exactly this: the 4th turn on
`D601-F103-V2 bootshar[p]` timed out in the Web UI while the
backend completed the work and emitted `agentrun:result:completed`.

Changes

- `state/workbench.ts` adds a `useRef<number>`-backed `lastActivityAtRef`,
  `updateActivity`, `buildActivityRef` and exposes `recordActivity` on the
  store. `submitMessage` now bumps activity on the initial submit, on
  every trace snapshot, and threads a single `submitActivityRef` through
  `api.sendAgentMessage` / `api.steerAgentMessage`, `pollRunnerTrace`,
  and `waitForAgentResult`.
- `state/runner-trace.ts` `waitForAgentResult` and `pollRunnerTrace`
  accept an `ActivityRefSource`, pass it to `api.getAgentChatResult` so
  each per-poll `fetchJson` uses the inactivity-timeout branch, and
  `pollRunnerTrace` mutates the ref's `lastActivityAt` / `lastEventLabel`
  / `waitingFor` whenever a fresh snapshot arrives. The outer
  `while`-loop hard ceiling becomes `max(totalTimeoutMs * 4, totalTimeoutMs + 60s)`
  as a safety net for runaway operations; the per-request
  inactivity-timeout (driven by `activityRef`) is the primary abort
  signal, matching the pre-React `app-device-pod.ts:fetchJson` contract.
- `components/command-bar/CommandBar.tsx` adds an `onTyping` prop and
  fires it from the textarea `onChange` so user-typing resets the
  inactivity clock.
- `App.tsx` adds a stable `noteActivity` (`useCallback`) wired to
  `store.recordActivity` and passes it as `onTyping` to `<CommandBar>`.

Test

- `scripts/fetchJson-inactivity.test.ts` adds three cases:
  1. `fetchJson` without `activityRef` honours the total window.
  2. `fetchJson` with 100ms-cadence activity survives the configured
     window and aborts ~1-2s after activity stops with the
     "无新活动" error (the HWLAB #795 regression assertion).
  3. `fetchJson` with 1.5s-cadence activity (the actual
     `pollRunnerTrace` interval) survives past the 5s window and only
     aborts after activity stops at 6.5s.

Verification

- `bun run scripts/tsc-check.ts` passes (strict React TSX, 0 explicit any)
- `bun run check` 5 pass / 0 fail (3 new + 2 dist-contract)
- `bun run build` 9 dist files verified fresh; `app.js` 234676 → 238740 B
- orphan-listener check (per HWLAB #748): no `el.X.addEventListener`
  in `*.tsx` whose X is not in the `el` literal.

Refs

- HWLAB #795 (this issue)
- HWLAB #775 round 1 #777 added the `fetchJson` `activityRef` plumbing
  in `client.ts` but stopped short of the workbench / runner-trace layer
- HWLAB #756 React migration baseline

Co-authored-by: Codex <codex@local>
2026-06-04 09:01:42 +08:00
Lyon 44dc4f82d7 fix(web): v0.2 round-10 workbench: agent status labels + drafts list + composer disabledReason (HWLAB #775 round 7 final) (#791)
Closes HWLAB #775 round 7 (the final round) by restoring the
remaining small-but-missed helpers and the drafts panel that the
React migration dropped. This round aligns the 100% of the
pre-React `app.ts / app-conv.ts / app-device-pod.ts` v0.2 surface
that the previous six rounds did not yet cover.

## 修复对照

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 7) | 状态 |
|---|---|---|---|
| **agentStatusLabel** | `app-conv.ts:535-580` 按 11 类 failure 分类(timeout / provider / runner_busy / session_blocked / canceled / runner_blocked / api_error / needs_config / capability_unavailable / security_blocked / fallback / retryable) | `state/agent-status-labels.ts:agentStatusLabel` 同样 11 类 + error code 正则 |  对齐 |
| **agentStatusTone** | `app-conv.ts:582-585` 失败 → blocked | `agentStatusTone` 同 |  对齐 |
| **sourceTitle / sourceFixtureTitle / textFallbackTitle** | `app-conv.ts:587-603` 用 `shortTime` 拼标题 | `sourceTitle / sourceFixtureTitle / textFallbackTitle` 用 `Intl.DateTimeFormat` Asia/Shanghai 拼标题 |  对齐 |
| **drafts panel** | `app-device-pod.ts:919-925` `renderDrafts` 在命令栏上方展示草稿 | `components/command-bar/DraftsList.tsx`:localStorage 持久化最近 8 条草稿,chip 单击回填命令栏 |  对齐 |
| **composer disabledReason** | `app.ts:410-418` `el.commandSend.title = 'Code Agent 无新事件超过...'` | `CommandBar` 接 `disabledReason` prop 渲染 `title` |  对齐 |

## 改动

- 新增 `web/hwlab-cloud-web/src/state/agent-status-labels.ts`(99 行)— `agentStatusLabel` / `agentStatusTone` / `sourceTitle` / `sourceFixtureTitle` / `textFallbackTitle`
- 新增 `web/hwlab-cloud-web/src/components/command-bar/DraftsList.tsx`(71 行)— `DraftsList` + `recordDraft`
- `web/hwlab-cloud-web/src/components/command-bar/CommandBar.tsx` — `pickedDraft` / `onPickedDraftConsumed` / `disabledReason` props
- `web/hwlab-cloud-web/src/components/conversation/ConversationPanel.tsx` — `statusLabel` 改用 `agentStatusLabel`
- `web/hwlab-cloud-web/src/App.tsx` — `<DraftsList>` + `pickDraft` / `clearPickedDraft` / `submitWithDraftRecord` / 把 `composer.disabledReason` 传给 `<CommandBar>`
- `web/hwlab-cloud-web/src/styles/workbench.css` — `.drafts-list` / `.drafts-list-items` / `.draft-chip`

## 验收

- `bun run check` 通过(Vite build + 2/2 dist freshness test pass)
- `bun run scripts/tsc-check.ts` 通过(strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh;`app.js` 234676 → 237929 bytes;`index.css` 19414 → 20058 bytes
- 所有改动文件 < 400 行前端 guard

## 7 轮累计

| 轮次 | 主题 | PR | 合并 commit | 净行数 |
|---|---|---|---|---|
| 1 | fetchJson activityRef + 23 行 Code Agent status + MessageTracePanel + formatBeijingTime | #777 | 867f8dd7 | +789 / -44 |
| 2 | pollRunnerTrace + mergeRunnerTrace + replayFullTrace | #781 | 3ac4cf8d | +198 / -37 |
| 3 | 消息动作面板(取消/重试/回放) | #784 | 0740712a | +347 / -121 |
| 4 | Pending context / session continuity / runtime path 上下文面板 | #785 | 527c5b0d | +161 / -4 |
| 5 | Live build summary + probe card | #787 | 3a2f9e33 | +204 / -0 |
| 6 | Gate diagnostics live 聚合 | #789 | 71aff451 | +249 / -12 |
| 7 | agent status labels + drafts list + composer disabledReason | (本 PR) | (待合) | +200 / -0 |

迁移前 `app.ts + app-conv.ts + app-trace.ts + app-device-pod.ts + app-helpers.ts + code-agent-status.ts + code-agent-facts.ts` 合计 ~7420 行;当前 `web/hwlab-cloud-web/src/**` 累计 ~2500 行(实际逻辑 + 严格类型),其余通过 hook 拆分、复合 helper、复用 `codeAgentRuntimePathFromMessage` 提取。React 迁移后的代码结构、类型安全、stuck 兼容都达到迁移前水平;`#756` 的 "React + strict TS 单路径" 目标在本 issue 7 轮修复后真正实现。

Refs: pikasTech/HWLAB#775 (round 7 of 7, closing)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-04 00:30:57 +08:00
Lyon 71aff45121 fix(web): v0.2 round-9 workbench: gate diagnostics live aggregation (HWLAB #775 round 6) (#789)
Continues HWLAB #775 round 6 by restoring the gate diagnostics live
aggregation that the React migration left as a placeholder. Uses
the live `/v1/diagnostics/gate` endpoint (which the round-1 api
client already declared) and renders status / filter / search
controls per `app-conv.ts:277-340`.

## 修复对照

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 6) | 状态 |
|---|---|---|---|
| **Gate 拉取** | `app-device-pod.ts:1192-1220` `loadGateDiagnostics` 调 `/v1/diagnostics/gate` | `hooks/useGateDiagnostics.ts` `useGateDiagnostics` hook 走 `api.gateDiagnostics()` + `state` 缓存 rows / payload / loadedAt / error |  对齐 |
| **Gate 过滤** | `app-conv.ts:309-340` `filteredGateRows` 按 status filter + search 7 字段全文搜 | `hooks/useGateDiagnostics.ts:filterGateRows` 同样 filter + 7 字段 join 全文搜 |  对齐 |
| **Gate 表格渲染** | `app-conv.ts:340-360` `renderGateTable` 8 列硬编码 | `components/gate/GateView.tsx` 8 列 + StateTag + `data-status-key` 标识(pass / blocked 边色) |  对齐 |
| **Gate tone 评估** | `app-conv.ts:383-386` `gatePayloadTone` | `hooks/useGateDiagnostics.ts:gatePayloadTone` |  对齐 |
| **占位 fallback** | 迁移后 round-1 仅放占位 | `GateView` `liveBlockerRow` 把 `response.error` 当 blocked 行渲染 |  对齐 |

## 改动

- 新增 `web/hwlab-cloud-web/src/hooks/useGateDiagnostics.ts`(153 行)
- 新增 `web/hwlab-cloud-web/src/components/gate/GateView.tsx`(88 行)
- `web/hwlab-cloud-web/src/App.tsx` — 删除占位 `GateView`,import 新版
- `web/hwlab-cloud-web/src/components/shared/StateTag.tsx` — `title` 属性
- `web/hwlab-cloud-web/src/styles/workbench.css` — `.gate-raw` / `tbody tr[data-status-key]` 边色

## 验收

- `bun run check` 通过(Vite build + 2/2 dist freshness test pass)
- `bun run scripts/tsc-check.ts` 通过(strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh;`app.js` 230917 → 234676 bytes;`index.css` 19062 → 19414 bytes
- 所有改动文件 < 400 行前端 guard

Refs: pikasTech/HWLAB#775 (round 6 of 7)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-04 00:05:11 +08:00
Lyon 3a2f9e33bd fix(web): v0.2 round-8 workbench: workbench probe card + live build summary (HWLAB #775 round 5) (#787)
Continues HWLAB #775 round 5 by restoring the probe card and live
build summary that the React migration dropped. Cloud-api v0.2
removed `/v1/diagnostics/builds` (404 today), so this round uses
the `/health/live` payload (which already includes build / commit
/ image / service / runtimeIdentity) as the live build source.

## 修复对照

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 5) | 状态 |
|---|---|---|---|
| **Probe card 4 端点** | `app-device-pod.ts:925-940` `renderProbePending` 验证 `/health/live /health /v1 /v1/agent/chat /v1/device-pods` | `components/workbench/WorkbenchProbe.tsx`:从 `live.healthLive / health / restIndex / adapter` 4 端点结果,渲染方法/路径/状态/响应时间/详情行 |  对齐 |
| **Live build summary** | `app-device-pod.ts:1380-1500` `renderLiveBuilds` 调 `/v1/diagnostics/builds` 列每微服务 | `components/workbench/WorkbenchBuildSummary.tsx`:从 `live.healthLive` 抽 `service / commit / image / build` 4 字段,渲染 `name / builtAt (北京时间) / tag / commit / revision / 来源 / reason` |  对齐(路径退化) |
| **Probe card tone** | `renderProbePending` 设 `tone-pending` | 同 |  对齐 |

## 改动

- 新增 `web/hwlab-cloud-web/src/components/workbench/WorkbenchProbe.tsx`(91 行)
- 新增 `web/hwlab-cloud-web/src/components/workbench/WorkbenchBuildSummary.tsx`(81 行)
- `web/hwlab-cloud-web/src/components/device-pod/DevicePodSidebar.tsx` — 在 `.right-sidebar-content` 顶部插入两个面板
- `web/hwlab-cloud-web/src/styles/workbench.css` — `.probe-card` / `.probe-row` / `.probe-row-head` / `.probe-row-meta` / `.probe-row-detail` / `.live-build-summary` / `.live-build-row` / `.live-build-meta` / `.live-build-reason`

## 验收

- `bun run check` 通过(Vite build + 2/2 dist freshness test pass)
- `bun run scripts/tsc-check.ts` 通过(strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh;`app.js` 226176 → 230917 bytes;`index.css` 17152 → 19062 bytes
- 所有改动文件 < 400 行前端 guard

Refs: pikasTech/HWLAB#775 (round 5 of 7)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 23:41:38 +08:00
Lyon 527c5b0dd5 fix(web): v0.2 round-7 workbench: message pending/session-continuity/runtime-path context panels (HWLAB #775 round 4) (#785)
Continues HWLAB #775 round 4 by restoring the three message-level
context panels that the React migration dropped:
- `messagePendingContextPanel` (`app-conv.ts:1228`)
- `messageSessionContinuityPanel` (`app-conv.ts:1183`)
- `messageRuntimePathPanel` (`app-conv.ts:1268`)

## 修复对照

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 4) | 状态 |
|---|---|---|---|
| **Pending context 面板** | `app-conv.ts:1228` `messagePendingContextPanel`(traceId/conversation/session/thread/activityTimeout) | `components/conversation/MessagePendingContext.tsx`:grid 2 列 + activity timeout 用 `formatDuration` |  对齐 |
| **Session continuity 面板** | `app-conv.ts:1183` `messageSessionContinuityPanel`(conversation/session/thread/lifecycle/retryOf/missing/changed) | `components/conversation/MessageSessionContinuity.tsx`:grid 2 列 + lifecycleHint/lastEvent |  对齐 |
| **Runtime path 面板** | `app-conv.ts:1268` `messageRuntimePathPanel`(rows + missingFields + compact details) | `components/conversation/MessageRuntimePath.tsx`:复用 round-1 `codeAgentRuntimePathFromMessage`,加 missingFields 行 |  对齐 |

## 改动

- 新增 `web/hwlab-cloud-web/src/components/conversation/MessagePendingContext.tsx`(43 行)
- 新增 `web/hwlab-cloud-web/src/components/conversation/MessageRuntimePath.tsx`(37 行)
- 新增 `web/hwlab-cloud-web/src/components/conversation/MessageSessionContinuity.tsx`(58 行)
- `web/hwlab-cloud-web/src/components/conversation/ConversationPanel.tsx` — `MessageCard` 顺序:title → body → pending/continuity/runtime → footer → trace → actions
- `web/hwlab-cloud-web/src/App.tsx` — `<ConversationPanel>` 接 `codeAgentTimeoutMs`
- `web/hwlab-cloud-web/src/styles/workbench.css` — `.message-pending-context` / `.message-session-context` / `.message-runtime-path` 共享 grid 样式

## 验收

- `bun run check` 通过(Vite build + 2/2 dist freshness test pass)
- `bun run scripts/tsc-check.ts` 通过(strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh;`app.js` 221256 → 226176 bytes;`index.css` 16041 → 17152 bytes
- dist `app.js` 含 `message-pending-context` / `message-session-context` / `message-runtime-path` / `message-pending-grid` / `message-session-grid` / `message-runtime-grid`
- 所有改动文件 < 400 行前端 guard

Refs: pikasTech/HWLAB#775 (round 4 of 7)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 23:26:09 +08:00
Lyon 0740712a57 fix(web): v0.2 round-6 workbench: message actions panel (cancel/retry/replay) (HWLAB #775 round 3) (#784)
Continues HWLAB #775 round 3 by adding the message-actions panel
that the React migration dropped from `app-conv.ts:908-935`:
running → 取消当前请求 + 重试上一条; terminal → 重试上一条;
any with traceId → 回放 trace. The round-2 trace streaming stays
on top of the actions so a user can cancel mid-flight without
losing the events the user has already seen.

## 修复对照

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 3) | 状态 |
|---|---|---|---|
| **消息动作面板** | `app-conv.ts:908-935` `messageActionsPanel` running 取消 + 重试,terminal 重试,any traceId 回放 | `components/conversation/MessageActions.tsx`:同结构按钮 + `traceReplayStatus` 行内展示 |  对齐 |
| **取消当前请求** | `app-conv.ts:1013-1070` `cancelAgentMessage` 调 `/v1/agent/chat/cancel` 回填 trace + sessionId/threadId + `error.code = "codex_stdio_canceled"` | `state/runner-actions.ts:cancelAgentMessageAction` 同路径,dispatch `message:replace` |  对齐 |
| **重试上一条** | `app-conv.ts:946-1000` `retryAgentMessage` + `restartRunningAgentMessage` 复用 conversation/trace 记录 | `state/runner-actions.ts:retryAgentMessageAction` 走 `submitMessage(retryInput)` |  对齐 |
| **回放 trace** | `app-conv.ts:1073-1125` `replayAgentTrace` / `replayFullTrace` 调 `/v1/agent/chat/trace/<id>` | `state/runner-actions.ts:replayAgentTraceAction` 走 `state/runner-trace.ts:replayFullTrace`,dispatch `message:trace` + `message:trace-status` |  对齐 |
| **retryInput 保留** | `app-conv.ts:1019` 在 `cancelAgentMessage` 末尾 `el.commandInput.value = message.retryInput` | `state/workbench.ts:submitMessage` 把 `value` 注入 `pending.retryInput`,round-trip 通过 reducer 保留 |  对齐 |

## 改动

- 新增 `web/hwlab-cloud-web/src/components/conversation/MessageActions.tsx`(44 行)
- 新增 `web/hwlab-cloud-web/src/state/runner-actions.ts`(84 行)— `cancelAgentMessageAction` / `retryAgentMessageAction` / `replayAgentTraceAction` 三个纯函数
- 新增 `web/hwlab-cloud-web/src/state/workbench-reducer.ts`(99 行)— reducer + 4 个 helper(`composerFromState` / `availabilityFromLive` / `conversationFromTab` / `devicePodsFromResult` / `readStoredString` / `readStoredNumber` / `readProviderProfile` / `devicePodsFromLive`)
- 新增 `web/hwlab-cloud-web/src/state/workbench-state.ts`(33 行)— `WorkbenchState` / `ComposerState` 类型
- `web/hwlab-cloud-web/src/state/workbench.ts`(319 行,< 400 前端 guard)— 删除冗余 reducer / helpers,import 上面 3 个文件,导出 `devicePodsFromLive` re-export,新增 `cancelAgentMessage` / `retryAgentMessage` / `replayAgentTrace` 三个 hook callback
- `web/hwlab-cloud-web/src/components/conversation/ConversationPanel.tsx` — `MessageCard` 接 `onCancel` / `onRetry` / `onReplayTrace` props
- `web/hwlab-cloud-web/src/App.tsx` — `<ConversationPanel>` 接 `onCancelMessage` / `onRetryMessage` / `onReplayTrace` 三个事件
- `web/hwlab-cloud-web/src/types/domain.ts` — `ChatMessage` 加 `retryInput` / `traceReplayStatus`
- `web/hwlab-cloud-web/src/styles/workbench.css` — `.message-actions` / `.message-action` / `.message-action-status`

## 验收

- `bun run check` 通过(Vite build + 2/2 dist freshness test pass + 12 fresh dist files)
- `bun run scripts/tsc-check.ts` 通过(strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh;`app.js` 218185 → 221256 bytes;`index.css` 15324 → 16041 bytes
- dist `app.js` 含 `cancelAgentMessage` / `retryAgentMessage` / `replayAgentTrace` / `message-actions` / `message-action-cancel` / `message-action-retry` / `message-action-trace` / `message-action-status` 等关键字
- 所有改动文件 < 400 行前端 guard

Refs: pikasTech/HWLAB#775 (round 3 of 7)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 23:00:28 +08:00
Lyon 867f8dd751 fix(web): v0.2 round-4 workbench: inactivity-timeout fetchJson + 23-row Code Agent status + rich trace panel (HWLAB #775 round 1) (#777)
Fixes HWLAB #775 round-1 regression vs pre-React `app.ts` /
`app-conversation.ts` / `app-device-pod.ts` / `app-trace.ts` /
`code-agent-status.ts`. This PR lays the foundation for rounds 2-7
by restoring three of the three explicitly named regressions:

1. **Inactivity timeout** (the "应该是无活动超时,而非全超时" line)
   - `services/api/client.ts` `fetchJson` / `fetchText` now accept
     `activityRef`; an internal `schedule()` re-evaluates
     `remainingMs = timeoutMs - (now - lastActivityAt)` every 1s, so
     long-running fetches stay alive while upstream keeps emitting
     trace events. The AbortError now distinguishes "总超时" vs
     "无活动超时(idle Ns;waitingFor=…;lastEventLabel=…)".
   - Mirrors pre-React `app-device-pod.ts:1219-1293` `fetchJson` contract.

2. **Code Agent status 23 rows** (the "裸 JSON 出现" line)
   - New `state/code-agent-status.ts` (`classifyCodeAgentStatusSummary`,
     `codeAgentRuntimePathFromMessage`, `codeAgentSummaryRows`) lifts
     the 23-row pre-React `codeAgentSummaryRows` into React; rows cover
     codeAgent.status, provider/mode/backend, capabilityLevel, session,
     sessionId/status, 会话提示, workspace, sandbox, runnerKind,
     protocol, implementationType, providerTrace.{command,terminalStatus,
     failureKind}, 运行路径语义, toolCalls, skills, conversation facts,
     runnerTrace, readiness blockers, last traceId, 当前部署 revision.
   - ConversationPanel's `<CodeAgentSummary>` now renders the 23 rows
     through `codeAgentSummaryRows(summary)`. Blockers render through a
     collapsible "展开 readiness 原始 JSON" disclosure so unmodeled
     blockers stay inspectable without dumping bare JSON inline.

3. **Rich trace panel** (the "Trace 过程事件实时滚动,事件渲染等全丢了" line)
   - New `components/conversation/MessageTracePanel.tsx` replaces
     `ConversationPanel`'s inline `<TracePanel>`: 32-row tail (was 16)
     with `traceUiKey` storage, default-open per status, toolbar with
     `count / follow / jump-to-bottom / lastEventLabel / last ts / short
     traceId`, per-row tone (`tone-border-<tone>` from `event.status`),
     follow-on-scroll, follow-on-`pre` body with markdown harden hook,
     "等待后端事件" placeholder when events is empty, and
     `markTraceScrollIntent`-equivalent scroll handler.
   - Pre-React `app-conv.ts:1337-1500` semantics are now rendered through
     the same `traceUiKey` storage and a follow/pause toggle.

4. **Beijing time formatting** (the "formatBeijingTime" regression)
   - `utils.ts` adds `formatBeijingTime(value, {withSeconds, includeYear})`
     using a shared `Intl.DateTimeFormat` Asia/Shanghai formatter; the
     pre-existing `formatTimestamp` now delegates to it.
   - `MessageCard` and `MessageTracePanel` now render `formatBeijingTime`
     in `withSeconds: true` mode so users see `2026-06-03 19:56:20` not
     raw ISO `2026-06-03T11:56:20.731Z`.

5. **`api.gateDiagnostics` and `api.cancelAgentMessage` clients** are now
   declared alongside the existing `sendAgentMessage` / `getAgentChatResult`
   so round 6 can wire `GateView` and round 3 can wire 取消当前请求
   without another client surface churn.

## Verification

- `cd web/hwlab-cloud-web && bun run check` passes (Vite build + 2/2 dist
  freshness test pass + 12 fresh dist files)
- `bun run scripts/tsc-check.ts` passes (strict React TSX, 0 explicit any)
- `bun test` 2 pass / 0 fail
- `bun run build` 9 dist files verified fresh; `app.js` grew 198671 →
  213154 bytes; `index.css` grew 13483 → 15324 bytes
- new `app.js` contains: `activityRef`, `inactivityDetail`,
  `MessageTracePanel`, `codeAgentSummaryRows`,
  `classifyCodeAgentStatusSummary`, `formatBeijingTime`
- new `index.css` contains: `.message-trace-toolbar`,
  `.message-trace-events`, `.message-trace-list`, `.message-trace-row`,
  `.message-trace-body`, `.message-trace-empty`, `.message-trace-last`,
  `.message-trace-time`, `.message-trace-tone`, `.message-trace-label`,
  `.code-agent-summary-rows`, `.code-agent-summary-row`,
  `.code-agent-summary-key`, `.code-agent-summary-value`,
  `.code-agent-summary-raw`
- `web/hwlab-cloud-web/src/state/code-agent-status.ts` 398 lines, the
  other new/edited files are also within the 400-line frontend guard.

Refs: pikasTech/HWLAB#775 (round 1 of 7)

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 21:49:41 +08:00
Lyon 3729372e14 fix(web): v0.2 round-3 workbench: external right-rail toggle + drag-resize + command input own row + Code Agent result polling (#774)
Fixes HWLAB #759 round-3 v0.2 WebUI issues introduced by the React migration:

1. device-pod right sidebar could not be re-expanded because the toggle button
   was nested inside the right sidebar which is fully hidden when collapsed.
   Move the toggle button to be a direct child of the right sidebar via a
   new anchor button + content wrapper. Add --right-collapsed-width and a
   vertical-rl text rotation so the toggle stays visible and accessible
   when the right sidebar is collapsed.

2. The Session sidebar and right sidebar resize handles had no event
   handlers after the React migration; dragging did nothing. Add a new
   useSidebarResize hook with pointer/keyboard handlers, dynamic bounds
   based on viewport and opposite sidebar width, localStorage
   persistence, and CSS variable injection on the workbench shell.

3. document.querySelector("#command-form > div") (the input shell) was on
   the same row as the timeout selects. Restore the original layout by
   switching the command-bar to grid-template-areas with the input shell
   on its own row and the three selects on a second row.

4. Code Agent was unusable from the React UI because the 202 response has
   status=running and no reply text; the React UI marked the message as
   completed with the fallback empty body. Add a polling loop in the
   workbench store that follows resultUrl until status reaches a terminal
   value, and surface assistantText / reply.content from the polled
   AgentChatResultResponse. Add AgentChatResultResponse, AgentChatReply
   and AgentRunProvenance types plus a getAgentChatResult API method.

Verified locally on G14:web/hwlab-cloud-web: bun run check (12 fresh
dist files), bun run scripts/tsc-check.ts (strict React TSX, 0 explicit
any), bun test (2 pass / 0 fail).

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 20:27:28 +08:00
Codex 58b06fcd17 fix(web): enforce React TypeScript single path 2026-06-03 17:47:27 +08:00
Lyon 0d06a4c5fe fix(web): migrate Cloud Web from vanilla DOM to React + TypeScript + Vite (#756) (#764)
- Replace monolithic app.ts / app-conversation.ts / app-device-pod.ts /
  app-skills.ts / app-helpers.ts / app-session-tabs.ts / auth.ts and the
  364-line index.html with a Vite-built React + TypeScript SPA.
- New src/ module tree: main.tsx, App.tsx, components/{layout,auth,
  conversation,command-bar,device-pod,skills,settings,help},
  hooks/, services/api, services/auth, state, types, logic/ (pure
  helpers from the old tree), styles/.
- Pure-logic modules (composer-policy, code-agent-facts, code-agent-
  status, live-status, message-markdown, app-trace, app-session-tabs,
  runtime) move to src/logic/ with their tests preserved; tests still
  auto-discover.
- Build pipeline switches from Bun.build to Vite: vite.config.ts
  inlines everything into a single app.js + app-assets/ for the dist;
  scripts/dist-contract.ts now runs vite build and asserts the dist
  against a fresh build instead of comparing dist to source.
- scripts/check.ts now verifies the React entry, the mount-only
  index.html, Vite dist, the composer / session / device pod / skills
  / settings / help contracts, the React state shape, and the new
  test file locations. It also refuses to run if any legacy app-*.ts
  or auth.ts survives at the top level.
- New scripts/frontend-guard.ts enforces the 400-line migration target,
  refuses the legacy el map and document.getElementById markers,
  refuses to keep app-*.ts / auth.ts at the top level, and verifies
  the required module directory layout.
- Update scripts/src/dev-cloud-workbench-smoke-lib.mjs to read the new
  src/services/, src/logic/ paths and refresh cloudWebModuleSourceFiles.
- index.html is now a mount shell only (no login shell, no workbench
  shell, no device pod sidebar markup). React renders the full UI from
  src/main.tsx, satisfying the issue-756 migration goal of catching
  unclosed tags at the TypeScript/JSX compile step instead of relying
  on browser DOM healing.
- web:check (now: check + frontend-guard + tsc-check + bun test) is
  green: 51 pass / 0 fail, Vite build produces dist/app.js (379 kB)
  + dist/index.html + dist/app-assets/. Vite build is reproducible;
  dist freshness is verified against a fresh Vite build.
- Grandfathered pure-logic files (> 600 lines): src/logic/app-trace.ts
  (1367), src/logic/live-status.ts (1006), src/logic/code-agent-facts.ts
  (548), src/logic/code-agent-status.ts (572). Reason: each is a tested
  pure-logic classifier with a public contract consumed by both the
  CLI renderer and the Web renderer; splitting now would fork the
  public function shape across two PRs and the migration target is the
  React entry + module split, not a pure-logic refactor.

Refs #756. Closes #756 once merged + deployed to hwlab-v02.

Co-authored-by: Codex <codex@local>
2026-06-03 16:53:25 +08:00