Commit Graph

270 Commits

Author SHA1 Message Date
Lyon 0fdd659826 fix(v02): retry trace snapshot before failure persist (#873)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 23:32:08 +08:00
Lyon 5107160786 fix(v02): persist trace for failed chat submits (#872)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 23:10:10 +08:00
Lyon 99d0604861 fix(v02): preserve trace for terminal chat responses (#868)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 22:53:31 +08:00
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 ffae1c12e0 fix(v02): retry historical trace replay (#864)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 22:17:58 +08:00
Lyon ec4ab46411 fix(v02): collapse duplicate assistant trace snapshots (#863)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 22:10:23 +08:00
Lyon 03f604d3fe fix(v02): persist terminal trace before refresh (#861)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 22:05:59 +08:00
Lyon c4ff24f60a fix(v02): dedupe replayed trace tool rows (#860)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 21:49:57 +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
Lyon be557c09bc fix(v02): repair final response status (#837)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 17:45:25 +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 10c03e6c32 fix(web): v0.2 unify Code Agent trace polling path + persist fail (HWLAB #802) (#806)
Follow-up to PR #798 (#795 final). PR #798 closed the total-timeout /
hard-cap issue, but left two residual code-path inconsistencies that
#802 captures:

- Submit path and refresh path used different trace-polling code:
  submitMessage called `pollRunnerTrace` + `waitForAgentResult` (a
  dual function pair from pre-#798), while hydrate() never subscribed
  to the running trace at all. After a page refresh, the user saw
  the cloud-api "running" state but no live trace events.
- The submit fail branch only dispatched a local "Code Agent 超时"
  message; it never called `persistConversation`, so the failure was
  invisible after F5 — the cloud-api conversation would show
  "running" forever from the user's perspective.

This PR collapses both into a single `subscribeToTrace` entry point
and wires the refresh path into the same active subscription:

- `state/runner-trace.ts` — replace `waitForAgentResult` +
  `pollRunnerTrace` with a single `subscribeToTrace(config)` that
  handles both the result endpoint and the trace endpoint in one
  `for(;;)` loop. Calls `onActivity` on EVERY successful poll (not
  just on new events) so the per-poll inactivity window in
  `fetchJson` does not fire on a healthy polling loop. Terminal
  status -> `onComplete`; 5xx -> keep polling, no activity; 4xx ->
  `onInfrastructureError`; `signal.aborted` -> silent return. The
  hard-cap / 4x / `TRACE_HARD_CAP_ATTEMPTS` machinery is gone (was
  already removed by #798).
- `state/trace-reattach.ts` — new `useTraceReattach` hook that
  subscribes to the running trace on hydrate (same `subscribeToTrace`
  call as submitMessage). Reuses an existing agent message slot for
  the traceId if one exists; otherwise creates a placeholder. Persists
  on both onComplete and onInfrastructureError. Path unification
  achieved: one entry, two callers.
- `state/workbench.ts`:
  - `submitMessage` calls `subscribeToTrace` directly with a
    single-line `onActivity: () => updateActivity()` plus
    `onComplete` / `onInfrastructureError` callbacks. The
    `onInfrastructureError` branch now ALSO calls
    `persistConversation`, persisting the fail state to cloud-api.
  - The re-attach useEffect is replaced with a one-liner
    `useTraceReattach({...})` call (workbench.ts stays under the
    400-line guard).
- `scripts/fetchJson-inactivity.test.ts` — third case tightened:
  200ms-cadence activity for 10s with `timeoutMs: 3000`; the
  request must live the full 10s and abort only after activity
  stops. Asserts `elapsed >= 10_000`.
- `docs/reference/spec-v02-hwlab-cloud-web.md` — the "Code Agent
  Timeout Model" section is renamed "Code Agent Timeout Model 与
  Path Unification" and now also pins: (a) `subscribeToTrace` is
  the only entry; `waitForAgentResult` / `pollRunnerTrace` /
  `TRACE_HARD_CAP_ATTEMPTS` MUST NOT exist; (b) `useTraceReattach`
  MUST be wired in; (c) `persistConversation` is required in both
  onComplete and onInfrastructureError; (d) `state.workspace.activeTraceId`
  changes MUST re-attach. Includes a distilled history of
  #775 / #777 / #791 / #795 / #797 / #798 / #802.

Verification
- `bun run scripts/tsc-check.ts` → strict React TSX, 0 explicit any
- `bun run check` → 5 pass / 0 fail (3 inactivity + 2 dist-contract)
- `bun run build` → 9 dist files verified fresh; `app.js`
  238460 → 240990 B
- `state/trace-reattach.ts` is now wired in; `state/runner-trace.ts`
  exports only `subscribeToTrace` (verified by tsc + grep)
- `workbench.ts` line count 400 (under the 400-line guard)
- New 200ms-cadence 10s test passes; old 100ms-cadence 2s test
  and total-timeout 200ms test still pass.

Refs: HWLAB #802, #795, #777, #791, #798

Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 11:08:20 +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 f7731d1421 fix(web): v0.2 remove all total-timeout / hard cap on Code Agent turn (HWLAB #795 final) (#798)
Follow-up to PR #797: drop every wall-clock or attempt-count ceiling from
`state/runner-trace.ts` so the Web UI's Code Agent inactivity-timeout
contract is total-cap-free. The previous PR kept a
`max(totalTimeoutMs * 4, totalTimeoutMs + 60s)` outer-loop safety net
plus a `TRACE_HARD_CAP_ATTEMPTS = 120` poll-count cap, which would
re-introduce the same regression class that #795 was filed against: a
long-running operation that keeps emitting trace events could still be
killed by the outer ceiling even with an active `activityRef`. The
"Code Agent Timeout Model" section of
`docs/reference/spec-v02-hwlab-cloud-web.md` now pins the contract:

- The sole abort signal is per-poll `fetchJson` inactivity-timeout,
  driven by `activityRef`.
- The outer `waitForAgentResult` / `pollRunnerTrace` loops are
  `for (;;)`. They only exit when the upstream returns a terminal
  status, the request fails with a non-5xx, or the inactivity window
  fires inside `fetchJson`.
- The per-poll `getAgentChatResult` call passes the full
  `totalTimeoutMs`; the inactivity window is **not** shrunk by
  wall-clock elapsed time.
- `TRACE_HARD_CAP_ATTEMPTS` and the 4x / +60s outer cap are deleted.
- Runaway protection is the caller's responsibility: Web users have
  cancel / steer / close-tab; CLI has `--timeout-ms`. The browser does
  not introduce an implicit cap.

Changes

- `web/hwlab-cloud-web/src/state/runner-trace.ts`:
  - Drop `const hardCapMs = Math.max(totalTimeoutMs * 4, totalTimeoutMs + 60_000)`
    and the `if (Date.now() - startedAt >= hardCapMs) break;` checks in
    both `waitForAgentResult` and `pollRunnerTrace`.
  - Replace `while (Date.now() - startedAt < hardCapMs)` with
    `for (;;)`. Drop the now-unused `startedAt` locals and the
    `let attempt = 0; attempt > TRACE_HARD_CAP_ATTEMPTS` guard.
  - Pass `totalTimeoutMs` directly to `api.getAgentChatResult` instead
    of `totalTimeoutMs - (Date.now() - startedAt)` so the per-poll
    inactivity window is stable.
  - Delete `const TRACE_HARD_CAP_ATTEMPTS = 120;` (no longer referenced).

- `web/hwlab-cloud-web/scripts/fetchJson-inactivity.test.ts`:
  - Replace the "1.5s cadence 6.5s" case with a stricter "200ms cadence
    10s" case: `timeoutMs=3000` inactivity window, continuous 200ms
    activity for 10s, then stop. Assert `elapsed >= 10_000` and that
    the abort is the inactivity-timeout error. This pins the
    `elapsed >= 10000` invariant in the spec.

- `docs/reference/spec-v02-hwlab-cloud-web.md`:
  - Strengthen the existing bullet in "在系统中的职责划分" to
    explicitly forbid total-timeout, hard cap, `codeAgentTimeoutMs * N`,
    poll-count cap, and wall-clock-shrunk per-poll windows.
  - Add a dedicated "Code Agent Timeout Model" section after "## 内部架构"
    that distils the contract, lists the three invariants, and records
    the history of #775 / #777 / #791 / #795 leading to the final state.

Verification

- `bun run scripts/tsc-check.ts` → strict React TSX, 0 explicit any
- `bun run check` → 5 pass / 0 fail (3 inactivity + 2 dist-contract)
- `bun run build` → 9 dist files verified fresh; `app.js` 241802 → 238460 B
  (delta reflects the deleted hardCap / TRACE_HARD_CAP_ATTEMPTS code)
- New 200ms-cadence 10s inactivity test runs for ~13s and asserts the
  request lives the full window — proving the outer cap is gone.

Refs: HWLAB #795 (final), #777, #791, #775

Co-authored-by: Codex <codex@local>
2026-06-04 09:36:29 +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 3ac4cf8d2e fix(web): v0.2 round-5 workbench: poll-based runner trace streaming for Code Agent real-time events (HWLAB #775 round 2) (#781)
Continues HWLAB #775 round 2 by adding live runner-trace
streaming on top of the round-1 inactivity-timeout + 23-row
Code Agent status + rich trace panel foundation. Cloud-api v0.2
removed the pre-React `/v1/agent/chat/trace/<id>/stream` SSE
endpoint (404 today), so this round uses the existing
`/v1/agent/chat/trace/<id>` JSON endpoint as a polling source.

## 修复对照(迁移前 → 迁移后)

| 类别 | 迁移前 (`893f46bc`) | 迁移后 (round 2) | 状态 |
|---|---|---|---|
| **Trace 事件实时滚动** | `app-device-pod.ts:708-756` `subscribeRunnerTrace` EventSource on `/v1/agent/chat/trace/<id>/stream`,`TRACE_STREAM_FALLBACK_MS` 后降级到 `pollRunnerTrace` (3s 间隔) | `state/runner-trace.ts:pollRunnerTrace` 直接走 `/v1/agent/chat/trace/<id>` 1.5s 间隔(EventSource 端点 404),每次新 eventCount / status / updatedAt 触发 `onSnapshot(snapshot)` |  对齐(polling 模式) |
| **Trace 事件合并** | `app-trace.ts:80-110` `appendTraceEvents` 把新 events 追加到 message.runnerTrace.events | `state/runner-trace.ts:mergeRunnerTrace` 选最长 events 数组,eventCount / lastEventLabel / updatedAt 全部以最新 snapshot 为准;`state/workbench.ts:reducer.message:trace` 派发并替换 |  对齐 |
| **Trace 终态** | `app-device-pod.ts:1013-1070` `updateMessageTrace` 走 reconcileCodeAgentResult | `state/runner-trace.ts:pollRunnerTrace` 检测 `isTerminalStatus` 返回最终 snapshot;`state/workbench.ts:submitMessage` 拿到 `mergeTraceResults(terminal, traceTerminal)` 作为 `messageFromAgentResponse` 入参 |  对齐 |
| **Trace 全量回放** | `app-conv.ts:1133-1170` `replayFullTrace(messageId)` 调 `/v1/agent/chat/trace/<id>` | `state/runner-trace.ts:replayFullTrace(traceId, totalTimeoutMs)` 同样路径;`message:trace` reducer 自动接收新 snapshot |  对齐(client 暴露) |
| **TRACE_POLL_INTERVAL_MS** | `TRACE_POLL_INTERVAL_MS = 1500`(推断,从 `setTimeout(tick, ...)`) | `state/runner-trace.ts:TRACE_POLL_INTERVAL_MS = 1500` |  对齐 |

## 新文件 / 改动

- 新增 `web/hwlab-cloud-web/src/state/runner-trace.ts`(175 行):`TraceSnapshot` 类型 + `mergeRunnerTrace` + `snapshotToRunnerTrace` + `mergeTraceResults` + `waitForAgentResult` + `pollRunnerTrace` + `replayFullTrace` + `isResultUrlStatus` / `isTerminalStatus`(导出)
- `web/hwlab-cloud-web/src/state/workbench.ts`:删除内置的 `waitForAgentResult` / `pollRunnerTrace` / `mergeTrace` / `snapshotToRunnerTrace` / `mergeTraceResults` / `isResultUrlStatus` / `isTerminalStatus` / `TraceSnapshot`,全部 import 自 `./runner-trace`;新增 reducer `message:trace` 处理 partial trace 推送;`submitMessage` 并行启动 `pollRunnerTrace` 与 `waitForAgentResult`,最终用 `mergeTraceResults` 合并

## 验收

- `cd web/hwlab-cloud-web && 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` 213154 → 216030 bytes
- `workbench.ts` 371 行(< 400 前端 guard);`runner-trace.ts` 175 行
- dist `app.js` 含 `/v1/agent/chat/trace`、`eventsCompacted`、`agentRun` 关键字

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

Co-authored-by: HWLAB <ci@hwlab.local>
2026-06-03 22:22:32 +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 d11c91d36e fix(web): avoid live 404 probes 2026-06-03 18:47:42 +08:00
Codex deb99ec1c9 fix(web): defer workbench fetches until auth ready 2026-06-03 18:33:50 +08:00
Codex 58b06fcd17 fix(web): enforce React TypeScript single path 2026-06-03 17:47:27 +08:00
Lyon 6ecfe015cf fix(web): ensure web deps installed before vite build in runtime (#768)
Co-authored-by: Codex <codex@local>
2026-06-03 17:28:30 +08:00
Lyon a78a4199a8 fix(web): use local node_modules/.bin/vite in dist-contract (#767)
Co-authored-by: Codex <codex@local>
2026-06-03 17:15:54 +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
Lyon 893f46bc03 fix(issue751): tighten DOM iterator + payloadError + WorkbenchSummary types (#763)
#751 第三轮推进到更紧的类型:
- app.ts 局部 const el 标 ElMap,让 el.gateStatusFilter / el.codeAgentTimeout
  等 HTMLSelectElement / HTMLButtonElement 子类型在本地 el 上也生效
- web-types.d.ts 加 WorkbenchRuntimePathShape / WorkbenchRuntimePathRow /
  WorkbenchCompactDetailsOptions / WorkbenchGateDiagnosticsRow,让
  codeAgentRuntimePathFromMessage / messageCompactDetails / gateDiagnostics.rows
  不再回退到 WorkbenchUnknown
- 7 个高发点 sed 批量加 <HTMLElement> 类型参数到 querySelectorAll /
  querySelector<HTMLElement>,让 list.dataset / tile.dataset 等访问合法
- event.target.closest 改 (event.target as HTMLElement).closest<HTMLElement>(...)
- ElMap.skillRefresh / skillUploadButton / commandClear 收紧 HTMLButtonElement
- agentErrorFromHttpResponse / failureMessage / classifyCodeAgentCompletion
  等返回类型精化(WorkbenchSummaryShape 加 title/status/replied/sourceKind 等)
- app-conversation.ts 内部 6 个函数(sessionSummaryTone / agentStatusLabel /
  agentStatusTone / sourceTitle / sourceFixtureTitle / textFallbackTitle / 等)
  加显式形参/返回类型
- 把 source 里的 : unknown / : unknown[] 全部改成 : WorkbenchUnknown(带索引
  签名),让 property access 在 tsc 下合法

web:check 55 pass / 0 fail;web:build 12 dist;tsc-check 仍守住 0 显式 any
硬 gate。residual 541 残错继续按 #751 后续 issue 跟踪。

Refs: pikasTech/HWLAB#751

Co-authored-by: Codex <codex@noreply.local>
2026-06-03 16:35:29 +08:00
Codex 898206fd30 fix(web): bind login submit before session probe 2026-06-03 16:32:57 +08:00
Lyon ceea621ce9 Merge pull request #755 from pikasTech/fix/issue753-webui-broken
fix(web): restore v02 workbench layout
2026-06-03 15:58:50 +08:00
Lyon 39980bea34 fix(issue751): add WorkbenchAugmentedError + 0 any hard gate (#757)
#751 第二轮基线(#754)继续往下推:
- web-types.d.ts 新增 WorkbenchAugmentedError / WorkbenchAugmentedErrorFields
  / WorkbenchScrollOptions / WorkbenchLoadOptions / WorkbenchActionResultOptions
  / WorkbenchSummaryShape / WorkbenchComposerShape 七个精确接口;前两个
  让 custom Error 增广(agentErrorFromHttpResponse 那一串 error.traceId/code/
  layer/blocker/retryable/userMessage/missingConfig/route/toolName/timeoutMs/
  hardTimeoutMs/idleMs/lastActivityAt/waitingFor/providerStatus/category)有
  显式类型,runtime 不需要 any。WorkbenchScrollOptions 把 deferred/force/
  behavior/silent 收齐,scrollTraceToBottom 等的调用方不再绕开类型。
- ElMap.commandSend/commandClear 从 HTMLElement 收紧为 HTMLButtonElement,
  配合 lib 加 DOM.Iterable 让 NodeListOf<Element> 的 for...of 工作。
- WorkbenchState.conversationScrollPosition / traceScrollPositions /
  traceBodyScrollPositions 加上 bottomGap 字段,scrlTopForPosition 形参同步
  升级。
- app-conversation.ts:captureConversationScrollPosition 形参
  captureTraceScrollPositions / captureTraceBodyScrollPositions / restoreTraceScrollPositions
  / scrollTraceToBottom / agentErrorFromHttpResponse / agentFailurePresentation
  / classifyCodeAgentCompletion / scrollTopForPosition 加精确参数与返回类型;
  7 处 typeof payloadError === "object" ? payloadError.X 改为 as { X?: T } 强转。
- tsconfig.json:加 DOM.Iterable。

本次提交不破 web:check(54/0)与 web:build(12 dist);tsc-check 仍以
0 explicit untyped 为硬 gate,residual 残错按 #751 后续 issue 跟踪。
每类残错在 closeout comment 列出。

Refs: pikasTech/HWLAB#751

Co-authored-by: Codex <codex@noreply.local>
2026-06-03 15:57:03 +08:00
Codex 2fa23bd71d fix(web): restore v02 workbench layout 2026-06-03 15:49:52 +08:00
Lyon 10906ec726 fix(issue751): add web-types.d.ts + tsconfig + tsc-check baseline (#754)
为 issue #751 的 TypeScript 收紧建基线:
- 新增 web/hwlab-cloud-web/tsconfig.json(strict family 全部开启、noImplicitAny
  默认关、allowImportingTsExtensions 打开、types=[bun-types, node])
- 新增 web/hwlab-cloud-web/web-types.d.ts(ElMap + WorkbenchState + 跨文件
  function/lifecycle/constant 的 ambient declarations;用 WorkbenchUnknown
  接口避开 any 字面量,同时保留索引签名允许任意属性访问)
- 新增 web/hwlab-cloud-web/scripts/tsc-check.ts:web:check 的并行 type gate,
  显式 untyped 注解(项目源码 + web-types.d.ts)直接 exit 2;residual type
  错误按 issue #751 follow-up 处理
- web/hwlab-cloud-web/package.json:加 typescript@5.6.3 / @types/node@22.7.5
  / bun-types@1.1.33;check / check:tsc / check:tsc-strict 入口
- byId/query 加显式 : HTMLElement 返回类型(不再返回 HTMLElement | null)
- app-conversation.ts 中 captureConversationScrollPosition 加 : void 注解

web:check(54 tests / 0 fail)+ web:build(12 dist files)通过;tsc-check
报告 0 explicit untyped,residual type 错误(property access / strict null
等)作为 issue #751 后续轮次跟踪,不阻塞 build。

Refs: pikasTech/HWLAB#751

Co-authored-by: Codex <codex@noreply.local>
2026-06-03 15:21:03 +08:00
Lyon 9d71bf5f6b fix(web): HWLAB #748 initCommandBar orphan el.commandNewSession listener (#752)
PR #746 (#744) removed the duplicate `command-new-session` button from index.html and the `el.commandNewSession` field from the `el` literal, but left `el.commandNewSession.addEventListener("click", ...)` inside `initCommandBar()`. The resulting undefined reference crashed every Cloud Workbench init with

  app.js:5414 Uncaught TypeError: Cannot read properties of undefined (reading 'addEventListener')
      at initCommandBar (app.js:5414:24)
      at app.js:3538:1

The new session entry point is `#session-create` in the session sidebar (added in #740/#743), so this listener is no longer needed. Removal is the minimal fix; all other command-bar bindings (`commandInput`, `commandForm`, `commandClear`) are untouched.

Verified locally:
  - web:check (54 tests pass, all static contracts pass)
  - web:build (12 dist files fresh)
  - dist/app.js: 0 references to commandNewSession

Refs: pikasTech/HWLAB#748

Co-authored-by: Codex <codex@local>
2026-06-03 14:45:26 +08:00
Codex d39e322bf0 fix(web/cli): do not truncate the terminal final-response body in trace rows
- HWLAB v0.2 Final Response was being truncated to 5000 characters in
  traceAssistantSummaryRow and then again to 1200 characters in the CLI
  compactTraceRenderRow, even when the response was the assistant’s
  terminal (last) message. The same Final Response is what
  /v1/agent/chat/result returns as reply.content, so the trace view
  should show the full text and stay consistent with the CLI
  assistantText path.
- Mark each rendered row with a terminal flag (true only for the
  terminal Final Response) and lift the body cap to
  Number.POSITIVE_INFINITY for terminal rows. The CLI Web-render path
  now passes the full body through when row.terminal is true and keeps
  the 1200 char preview for non-terminal streaming rows.
- Add unit tests in web/hwlab-cloud-web/app-trace.test.ts and
  tools/hwlab-cli/client.test.ts to lock the behavior in for the
  terminal/no-truncate path and the streaming/2200 cap path.

Refs: pikasTech/HWLAB#722
2026-06-03 13:26:19 +08:00
Lyon 2168ad2ad7 fix(web): HWLAB #744 WebUI v0.2 改进 (#746)
- 问题1: 修复重复 session - 按 threadId 去重 conversation
- 问题2: session sidebar 默认宽度改为150% (330px),移除上限
- 问题3: 删除重复的命令栏「新建 Session」按钮
- 问题4: session 报错(status=failed/error/canceled/timeout)后允许继续重试
- 问题5: session tabs 上方显示当前模型通道
- 问题6: 所有时间统一使用北京时间(UTC+8),移除「北京时间」字样
- 问题7: 注销按钮移入 activity-rail 设置标签

Closes #744

Co-authored-by: Codex <codex@local>
2026-06-03 13:13:23 +08:00
Codex 6dbc72deac fix(issue740): HWLAB v0.2 WEBUI session management improvements
1. 在 panel-title-row 增加"复制 session id"按钮,把当前会话
   conversation id 复制到剪贴板,便于在 HWLAB CLI 复现;
2. 侧边栏标签改为显示最近更新时间(相对时间),排序仍然按
   lastTimestampMs 倒序,新会话靠前;
3. messageCount 与 firstUserMessagePreview 由 backend 持久化,
   不再被 slice(-50) 窗口掩盖,CLI/Web 看到的条数一致;
4. 把 left-sidebar-resize 移到 session-sidebar 右侧,
   activity-rail 固定 92px,拖拽控制 session-sidebar 宽度;
5. 侧边栏标签默认显示用户摘要(首条 user message 截断),
   长文本用省略号收尾。

新增 `hwlab-cli client session inspect <conversationId>` 子命令
与 `GET /v1/agent/conversations/{id}` 端点对齐,便于在 CLI 复现
WEB 看到的 session 详情(包含 messageCount / trace / thread)。

Backend 改动:access-control.ts 的 normalizeConversationSnapshot
和 mergeAgentSessionOwnerEvidence 现在按 max 累加 messageCount,
并保留 firstUserMessagePreview;mergeTerminalConversationMessages
返回 { messages, messageCount, firstUserMessagePreview }。
2026-06-03 11:41:02 +08:00
Codex 94b9df822c feat: 手动化 Code Agent session 管理 2026-06-03 10:40:50 +08:00
Codex da25cb4c02 feat: align session sidebar and cli management 2026-06-03 09:47:52 +08:00
Codex 5b1c9eb5dd feat: add session tabs to cloud web 2026-06-03 08:37:07 +08:00