Commit Graph

1256 Commits

Author SHA1 Message Date
Lyon f7e4c372ce fix(cloud): restore running session active trace (#811)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 12:18:08 +08:00
Lyon d5690ce2a5 fix(cloud): preserve selected conversation during terminal sync (#809)
Co-authored-by: Codex Agent <codex@hwlab.local>
2026-06-04 12:00:21 +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 f2d4181496 Merge pull request #804 from pikasTech/fix/issue801-evidence-arg
fix(tools): v0.2 device-pod-cli maps build/download evidence to first-class workspace.evidence/debug.evidence (HWLAB #801)
2026-06-04 10:39:07 +08:00
Codex Agent bc059fa49d fix(tools): v0.2 device-pod-cli maps build/download evidence to first-class workspace.evidence/debug.evidence (HWLAB #801)
The CLI was emitting workspace.build/debug.download with action=evidence
for "<pod>:workspace build evidence <jobId>" and "<pod>:debug-probe
download evidence <jobId>". Cloud-api accepted the sub-action form, but
the device-pod executor mapped it to device-host-cli argv as
"workspace build evidence" / "debug-probe download evidence" -- neither
of which exists in the host CLI dispatcher; the host returned
"unsupported command: --profile-json-b64 ..." and the evidence job was
marked failed with blocker.code gateway_dispatch_failed.

Per docs/reference/g14.md (v0.2 device-pod cloud-api architecture),
"The evidence sub-action on workspace.evidence / debug.evidence is a
first-class intent, not a workspace.build sub-action." The executor's
deviceHostArgs already produces the correct argv for the first-class
intent, so the fix lives in the CLI selector parser: when
operation === "build" and rest[1] === "evidence", emit
intent=workspace.evidence with kind=build and the explicit jobId; the
same shape for operation === "download" -> debug.evidence with
kind=download. Non-evidence sub-actions (start, status, wait, output,
cancel) are unchanged.

Live verification on G14 against http://74.48.78.17:19667 (D601-F103-V2,
profileHash sha256:0352cd14...):
  - before: device-host-cli argv = "workspace build evidence"
            -> gateway_dispatch_failed
  - after:  device-host-cli argv = "workspace evidence build <jobId> ..."
            accepted by cloud-api with status=running, blocker=null
  - the executor argv proves the fix; the D601 Windows host
    F:\Work\ConStart\tools\device-host-cli.mjs is still a stale
    D601 ops-side copy and is tracked separately in #779 (out of scope
    for #801).

Adds:
  - device-pod-cli.test.ts: evidence sub-action -> first-class intent
  - device-pod-cli.test.ts: build start / download start regression
    (non-evidence sub-actions keep workspace.build / debug.download)

Out of scope: COM9 -> COM3 UART port drift on the Windows device pod
host (separate issue from #801; no UART/profile port code touched).
2026-06-04 10:36:11 +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 8d4fea5e19 Merge pull request #794 from pikasTech/fix/issue792-concept-boundary
fix(v0.2): freeze HWLAB-AgentRun concept boundary for #792
2026-06-04 02:21:05 +08:00
Codex 19438d6aa4 fix(v0.2): freeze HWLAB-AgentRun concept boundary for #792
P0 freezes the concept boundary between HWLAB (business session/workspace) and
AgentRun (tenant/policy/execution):

- HWLAB adapter now always uses DEFAULT_HWLAB_AGENTRUN_PROJECT_ID /
  DEFAULT_HWLAB_AGENTRUN_PROVIDER_ID / DEFAULT_HWLAB_AGENTRUN_BACKEND_PROFILE on
  AgentRun runPayload, regardless of caller options. Business projectId belongs
  in workspaceRef only, not in AgentRun projectId.

- commandPayload.projectId is now always set to the HWLAB-fixed
  DEFAULT_HWLAB_AGENTRUN_PROJECT_ID, never undefined and never derived from
  caller options.

- client agent send --session-id <SES> --conversation-id <CONV> without
  --provider-profile now looks up the explicit session record to inherit
  providerProfile / threadId / conversationId. Mismatched --conversation-id
  against the session record aborts with session_conversation_mismatch
  before dispatching to AgentRun (prevents stale session state from being
  used as a new conversation).

Contract tests added (all 4 passing):
- internal/agent/agentrun-dispatch.test.mjs: HWLAB AgentRun assembly fixes
  tenantId/projectId/providerId/backendProfile regardless of options; command
  payload does not leak business projectId into AgentRun command projectId.
- tools/hwlab-cli/client.test.ts: send --session-id + --conversation-id
  inherits explicit session providerProfile; fails with
  session_conversation_mismatch when the session points at a different
  conversation.

P2 (CLI/trace result segregation) and P3 (state machine convergence) remain
for follow-up. Tracked via #792 comment chain.

Tracked-by: pikasTech/HWLAB#792
2026-06-04 02:18:34 +08:00
Codex b23ba1cb82 docs: unify v0.2 user api key auth 2026-06-04 01:06:36 +08:00
Codex f81aad7ace docs: clarify AgentRun session resume boundaries 2026-06-04 01:02:12 +08:00
Codex ac0a7e57d8 docs: add v0.2 auth spec 2026-06-04 00:47:15 +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 64ab9a3e77 Merge pull request #790 from pikasTech/fix-v02-cli-session-provider-profile-20260603
修复 v0.2 CLI 显式 session 默认 providerProfile
2026-06-04 00:29:13 +08:00
Codex 1f504f00f9 fix(cli): inherit provider profile from explicit agent session 2026-06-04 00:28:22 +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 dbd09adb8d Merge pull request #786 from pikasTech/fix-v02-agentrun-runner-reensure
fix: reensure AgentRun runner for reused sessions
2026-06-03 23:22:31 +08:00
Codex 32b29bc1dc fix: reensure AgentRun runner for reused sessions 2026-06-03 23:21:05 +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 dcbd5febfb Merge pull request #783 from pikasTech/fix-v02-cli-explicit-session
fix: align v0.2 explicit agent session dispatch
2026-06-03 22:52:49 +08:00
Codex 8f0a2cd213 fix: align v0.2 explicit agent session dispatch
Fixes HWLAB #782.
2026-06-03 22:50:58 +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 5d9182807f Merge pull request #778 from pikasTech/fix/issue773-output-text-evidence-readonly-rerun
fix(v0.2): device-pod output.text evidence read-only selectors for #773 (re-run on v0.2 round-4)
2026-06-03 21:59:41 +08:00
Codex d9d283c314 fix(v0.2): move workspace/debug-probe evidence handler to top-level command in device-host-cli for #773
Live CLI verification at 74.48.78.17:19667 showed that
workspace.evidence dispatched correctly through cloud-api and
the executor (executor returned text with the actual error),
but the device-host-cli threw unsupported command because
the new evidence subcommand was nested under command=build
instead of being its own top-level command.

The args shape for workspace.evidence from deviceHostArgs is
[workspace, evidence, kind, ...options], so the main() match
in device-host-cli should hit
at the workspace group level, not
under . Same fix for debug-probe.

After the move, readJobEvidence is dispatched with the right
kindPrefix (keil-build for workspace.evidence kind=build,
keil-download for debug-probe.evidence kind=download) and
the optional jobId from rest[1]. Tail defaults to 200 lines,
--full for the entire log.

Tracked-by: pikasTech/HWLAB#773
2026-06-03 21:57:53 +08:00
Codex eb5bada0d1 fix(v0.2): device-pod output.text evidence read-only selectors for #773
Tracks pikasTech/HWLAB#773. PR #765 fixed selector confusion but did
not touch cloud-api evidence propagation. This change closes the
real root cause and adds the read-only evidence selectors that #760
follow-up called for.

cloud-api (internal/cloud/access-control.ts):
- DEVICE_JOB_INTENTS adds workspace.evidence and debug.evidence.
- DEVICE_JOB_READ_ONLY_SUB_ACTIONS = { status, output, wait, cancel,
  evidence } and DEVICE_JOB_ACTIONABLE_INTENTS = { workspace.build,
  debug.download } make the mutating-intent / sub-action matrix
  explicit.
- _deviceJobRequiresReason(intent, args, reason) returns false when
  the caller already provided reason OR when the actionable mutating
  intent is paired with a read-only sub-action. debug.reset and other
  non-actionable mutating intents still always require reason.
- executorOutputPayload now also surfaces output.summary,
  nestedOutput.summary, evidence.text, evidence.logTail and
  evidence.summary as body.output.text, so a dispatcher that
  includes the host logTail / buildSummary in its result becomes
  visible to the Code Agent without a separate bootsharp dance.

device-pod executor (cmd/hwlab-device-pod/main.ts):
- gatewayDispatchText also walks result.evidence.{text,logTail,
  summary}, dispatch.message (only when dispatchStatus=completed),
  dispatch.summary, result.summary and dispatch.buildSummary before
  falling back to JSON.stringify.
- deviceHostArgs maps workspace.evidence / debug.evidence to the
  host device-host-cli evidence subcommands.

device-host-cli (skills/device-pod-cli/assets/device-host-cli.mjs):
- new readJobEvidence(kindPrefix, requestedId, { tail, full,
  target }) reads the most recent (or specified) keil-build /
  keil-download job log file and returns it as keil-build.evidence /
  keil-download.evidence. tail defaults to 200, --full for entire
  log. Missing job returns ok=false but does not throw.
- workspace build evidence and debug-probe download evidence now
  wired in main() and help text.

device-pod-cli (tools/src/device-pod-cli-lib.ts):
- selectorCheatSheet adds the evidence row and clarifies that
  build/download status/output/wait/cancel/evidence are read-only
  and do NOT need --reason. build/download start still require
  --reason. usage examples now include the two evidence selectors.
- failed-fast selectors are unchanged; existing 26 tests still pass.

SKILL.md (skills/device-pod-cli/SKILL.md):
- Selector Cheat Sheet adds workspace.evidence and
  debug.evidence rows.
- #773 follow-up note: --reason is now required only for
  mutating sub-actions, not for the read-only ones.

cloud-api tests (internal/cloud/access-control.test.ts):
- workspace.evidence and debug.evidence must be in DEVICE_JOB_INTENTS.
- _deviceJobRequiresReason signature and the read-only / actionable
  branch table.
- executorOutputPayload must look at evidence and summary fields
  in addition to text. 3 new tests; pre-existing 4 workbench
  failures are unrelated to this change and reproduce on the
  unchanged v0.2 base.

Verification (host-side, G14 /root/hwlab-v02, source=0cf9a8c6):
- bun test tools/device-pod-cli.test.ts → 26/26 pass.
- bun test internal/cloud/access-control.test.ts → 23 pass,
  4 pre-existing workbench failures (unchanged on v0.2 base).
- bun test cmd/hwlab-device-pod/main.test.ts → 7/7 pass.
- node --check skills/device-pod-cli/assets/device-host-cli.mjs
  → syntax OK.

Hot probe: live v0.2 cloud-api at 74.48.78.17:19667 confirmed
job_devicepod_804c5db4... (workspace.build) returned
text="", bytes=0, output={} before this change. After the
executor text-extraction update and the new evidence selector,
a follow-up workspace.build start + build evidence will surface
the host logTail / buildSummary in body.output.text without
requiring bootsharp + host file fallback.

Tracked-by: pikasTech/HWLAB#773
2026-06-03 21:57:39 +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
Codex 6341015d3b feat(v0.2): Code Agent 默认 sessionStorage=persistent + eviction reset UX
PR D for #770:HWLAB Cloud Web Workbench 透传 session state 真正持久化

- code-agent-agentrun-adapter.ts:
  * 新 ensureAgentRunSessionPersistent:默认 sessionStorage=persistent,
    调 POST /api/v1/sessions 同步建 session + PVC
  * submitAgentRunChatTurn 改两步尝试:ensureSession 或 runner-jobs POST
    收到 session-store-evicted 时用新 sessionId(<base>-reset-<trace8>)
    + threadId=null 重试,trace 追加 agentrun:session-reset 事件
  * 新 shouldResetSessionAfterEviction / newSessionIdAfterEviction 辅助函数
- code-agent-response-contract.mjs: session-store-evicted 归
  session-blocked category
- code-agent-chat.ts: 新 userMessage
  「Code Agent session 存储已失效(PVC 被回收或 TTL 到期),
   HWLAB 已为你开新 sessionId,可继续发送下一条消息。」
- spec-v02-hwlab-cloud-web.md: 新增 'Session state 持久化与 eviction reset'
  章节,含禁止路径与实现情况表

env 控制:HWLAB_CODE_AGENT_AGENTRUN_SESSION_STORAGE=ephemeral
可退回 metadata-only 模式(PR 前默认行为)。
2026-06-03 21:47:14 +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
Lyon 0cf9a8c607 Merge pull request #765 from pikasTech/fix/v02-device-pod-cli-selector-friction-20260603
fix(device-pod-cli): add selector cheat sheet and fail-fast hints for #760
2026-06-03 19:54:36 +08:00
Lyon 429befb358 Merge pull request #772 from pikasTech/fix/issue756-live-404-cleanup
fix(web): avoid live 404 probes
2026-06-03 18:48:27 +08:00
Codex d11c91d36e fix(web): avoid live 404 probes 2026-06-03 18:47:42 +08:00
Lyon b556f4160d Merge pull request #771 from pikasTech/fix/issue756-playwright-live-errors
fix(web): defer workbench fetches until auth ready
2026-06-03 18:34:28 +08:00
Codex deb99ec1c9 fix(web): defer workbench fetches until auth ready 2026-06-03 18:33:50 +08:00
Lyon 21a64d8ef4 Merge pull request #769 from pikasTech/fix/issue756-react-ts-single-path
fix(web): enforce React TypeScript single path
2026-06-03 17:52:29 +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
Codex Device-Pod Agent 5c35f8349e fix(device-pod-cli): add selector cheat sheet and fail-fast hints
Address the friction in #760 where Code Agent picked <pod>:workspace:/ keil
download/flash and <pod>:debug-probe flash for firmware flashing, all of
which are not SOP. Add to SKILL.md:

- Selector Cheat Sheet: explicit per-surface allowed operations table
- Common selector mistakes table: wrong selector -> error you would hit
  -> correct selector
- Pitfalls Observed in Real Runs: --reason required for build/download
  even on read-only sub-actions, cloud-api job output is empty for
  build/download, workspace rg --pattern shell-pipe collision

In tools/src/device-pod-cli-lib.ts:
- workspace.keil now fail-fasts on any action that is not add-source or
  remove-source, with code unsupported_workspace_keil_action and a
  selector hint pointing to debug-probe download start
- debug-probe flash now has a dedicated error code path with the same
  selector hint instead of the generic unsupported_debug_operation
- catch-all debug-probe error message now includes the allowed list
- help() payload exposes a selectorCheatSheet array so hwpod --help
  callers see the SOP table without reading the SKILL.md file

Tests in tools/device-pod-cli.test.ts: 6 new cases lock in the new
error codes, the selector cheat sheet, and that add-source still
dispatches correctly. 26/26 pass.
2026-06-03 16:59: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
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
Lyon cae2f1e71b Merge pull request #762 from pikasTech/fix/issue753-auth-race
fix(web): bind login submit before session probe
2026-06-03 16:34:08 +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