Commit Graph

8 Commits

Author SHA1 Message Date
Codex Agent 946d550056 fix: stabilize v02 workspace first paint 2026-06-05 14:58:37 +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
Codex Agent e46350993c fix: compact code agent workbench layout 2026-06-04 15:26:13 +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
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