fix(web): recover from quiet or stalled connections - #370
Conversation
tt-a1i
left a comment
There was a problem hiding this comment.
Exact-head review: 72b334d
[P1] Timing out prompt admission creates an uncertain mutation that the UI presents as a safe retry. web/ui/app.js:803-825 aborts POST /api/prompt after 30 seconds, removes the optimistic row, restores the draft, and reports failure. The server path at web/host/web-host.ts:492-527 has already generated a new command id and may still complete runtime.sendPrompt(); it neither binds client disconnect to cancellation nor accepts a client idempotency key. Retrying the restored draft therefore creates another command id and can execute the same model/tool turn twice.
Please make admission retry-safe: use a stable client-generated idempotency key with server-side deduplication and a queryable receipt, or expose timeout as unknown/pending and reconcile canonical state before allowing retry. Add a test where the client times out just before the first admission completes, retries, and proves only one turn is admitted.
Verification boundary: exact-head cross-layer Standards + Spec review. Existing liveness tests cover bounded waiting, not the timeout/late-success/retry race.
|
已按 P1 review 修复并推送到 Real UI verification截图来自本地直接启动的 Web Host(commit |
# Conflicts: # tests/web/web-host.test.ts
# Conflicts: # web/ui/app.js

When a browser loses a prompt response, the server may already have accepted the request. Retrying now preserves the original admission ID and replays the first result instead of dispatching another model/tool execution.
Related to #363.
The Host stores bounded admission receipts, including the original follow-up queue snapshot and cursor. In-flight records are not evicted. Explicit retries with missing records, including after a Host restart, return
COMMAND_ADMISSION_UNKNOWNwithout dispatch. Different content under one ID is rejected. Known preflight or capacity rejection permits a new attempt; timeout, connection loss, and response-body failures preserve the original ID and the existing execution state.SSE heartbeats and bounded reads recover quiet/stalled connections. Non-cached page departure cancels the reader and stops reconnection; bfcache preserves the single connection loop. Tests clean up their readers, sockets, and timers and exit normally. The integration preserves canonical themes, execution cancellation, and admission-time queue feedback.
Validation: final integrated Node 22 focused tests passed 104/104;
bun run checkpassed; full Node suite passed 1423 tests with one skip, plus Vitest 30/30. Coverage includes lost response and retry with exactly one dispatch, first-receipt replay after the native queue changes, bounded pending capacity, unknown retries, and SSE/page lifecycle cleanup. CI will validate the final published head. No new live-provider or release acceptance is claimed.