909715c335
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>