feat(serve): add HTTP rewind endpoints for daemon/web-shell (issue #4514 T3.2)#4820
Conversation
|
Thanks for the PR, @doudouOUC! Template looks good ✓ — all required sections present, bilingual description, test plan included. On direction: This is T3.2 from the tracked daemon capability gaps (#4514) — On approach: The scope feels right and well-contained. Two HTTP endpoints (list snapshots + execute rewind), structured error classes (409 busy, 400 invalid target), SSE cross-client notification, and SDK plumbing — this mirrors the pattern used by other daemon endpoints ( One observation: the No tests included in the 14 changed files — I'll check whether existing test coverage is sufficient during code review. Moving on to code review. 🔍 中文说明感谢 PR,@doudouOUC! 模板完整 ✓ — 所有章节齐全,双语描述,包含测试计划。 方向: 这是 #4514 追踪的 daemon 能力缺口 T3.2 — 方案: 范围合理且聚焦。两个 HTTP 端点(列出快照 + 执行回退)、结构化错误类(409 busy、400 invalid target)、SSE 跨客户端通知和 SDK 接入 — 与其他 daemon 端点( 一个观察:对最多 100 个快照的 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
📋 Review SummaryThis PR adds HTTP rewind endpoints to the daemon, enabling web-shell and SDK clients to rewind a session's conversation and files to a previous turn—previously only available via TUI interactive dialog. The implementation is well-structured, extending the existing 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
There was a problem hiding this comment.
Pull request overview
This PR adds first-class HTTP + SDK support for rewinding a daemon session to an earlier turn (conversation truncation + best-effort file history restore), enabling web-shell/SDK clients to replace the current TUI-only /rewind dialog workflow.
Changes:
- Add daemon HTTP routes:
GET /session/:id/rewind/snapshotsandPOST /session/:id/rewind, with structured 400/409 error mapping. - Extend ACP bridge + agent extMethods to list rewind snapshots, accept
promptId, rewind session state, and emit asession_rewoundSSE event. - Update TypeScript SDK types/clients/reducers to support new endpoints and event handling.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/sdk-typescript/src/daemon/types.ts | Adds SDK result/snapshot types for rewind APIs. |
| packages/sdk-typescript/src/daemon/index.ts | Re-exports new rewind types + session_rewound event types. |
| packages/sdk-typescript/src/daemon/events.ts | Registers/parses session_rewound event and updates session view-state reducer. |
| packages/sdk-typescript/src/daemon/DaemonSessionClient.ts | Adds per-session convenience wrappers for snapshot listing + rewind. |
| packages/sdk-typescript/src/daemon/DaemonClient.ts | Adds HTTP client methods for rewind snapshot listing + rewind mutation. |
| packages/cli/src/ui/commands/rewindCommand.ts | Marks /rewind as interactive-only (TUI dialog). |
| packages/cli/src/serve/server.ts | Implements new HTTP rewind routes + HTTP mapping for new bridge error classes. |
| packages/cli/src/serve/capabilities.ts | Advertises session_rewind capability. |
| packages/cli/src/serve/acpSessionBridge.ts | Re-exports new rewind error classes for server error mapping. |
| packages/cli/src/acp-integration/acpAgent.ts | Implements extMethods for snapshot listing + promptId-based rewind + errorKind mapping. |
| packages/acp-bridge/src/status.ts | Adds extMethod names for rewind snapshots + rewind control. |
| packages/acp-bridge/src/bridgeTypes.ts | Adds bridge request/response types for rewind APIs. |
| packages/acp-bridge/src/bridgeErrors.ts | Adds SessionBusyError (409) and InvalidRewindTargetError (400). |
| packages/acp-bridge/src/bridge.ts | Adds bridge methods for snapshot listing + rewind mutation + SSE session_rewound publish. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Copilot review response summary
|
wenshao
left a comment
There was a problem hiding this comment.
Additional notes:
POST /session/:id/rewindis missing from theresolveDaemonTelemetryRouteregex atserver.ts:284. Rewind is a destructive mutation — addrewindto the alternation group so it's tracked in daemon telemetry.historyBeforeRewindis computed inacpAgent.tsbut discarded by the bridge'sRewindResponsetype. If daemon clients won't need undo-after-rewind, consider skipping the snapshot capture in the daemon path to avoid unnecessary serialization cost.
wenshao
left a comment
There was a problem hiding this comment.
Additional findings:
- 2 test regressions in
acpAgent.test.ts: "status ext methods expose workspace snapshots without secrets" (line 1132) and "status ext methods return error cells when workspace snapshots fail" (line 1258) — the test mock is missingsettings.forScope()which the new code paths require. CI-blocking regressions. - The nested unbounded
Promise.allin the snapshot listing (acpAgent.ts:2165) firesgetDiffStatsfor up to 100 snapshots × all tracked files with no cap — unlikegetTurnDiffwhich usesMAX_TURN_DIFF_FILES = 500. Worst case risks EMFILE on large sessions.
— qwen3.7-max via Qwen Code /review
wenshao review response summary (8a7a113)
|
Local Test Report — PR #4820Branch: Summary
PR-Caused Issues (2)1. SDK browser bundle size exceeded The new rewind types and client methods ( 2. Capability registry tests (3 failures)
The PR adds Pre-Existing IssuesacpAgent.test.ts — 2 failures (known on
server.test.ts — 9
VerdictNot merge-ready. Two actionable items must be addressed:
Tested by wenshao |
|
Thanks for the thorough local test report. Issue 1 (SDK bundle size): Will rebase onto latest daemon_mode_b_main which includes the 108 KiB bump from #4812. Issue 2 (capability registry tests): Already fixed in commit 5f73d06 — Pre-existing failures: Confirmed — the 2 acpAgent teardown races and 9 runQwenServe afterEach timeouts are known on daemon_mode_b_main and unrelated to this PR. |
T3.2) Expose session rewind as structured HTTP API so web-shell and SDK clients can rewind a session's conversation and files to a previous turn without relying on TUI-only dialog interaction. API surface: - GET /session/:id/rewind/snapshots — list rewindable turns with diff stats - POST /session/:id/rewind — execute file restore + conversation truncation Leverages the existing Session.rewindToTurn() for conversation truncation and FileHistoryService.rewind() for file restore. Extends the existing 'rewindSession' ACP extMethod to also support promptId parameter and file history rewind. Error handling: - 409 SessionBusyError when a prompt is running - 400 InvalidRewindTargetError when the target turn is compressed or does not exist - 404 SessionNotFoundError for unknown sessions Cross-client SSE event 'session_rewound' published on success with originatorClientId for echo suppression. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
Fixes from final audit: - acpAgent.test.ts: add filesChanged/filesFailed to expected response, add newSession call before invalid-turnIndex test - server.test.ts: add session_rewind to expected feature lists - acpAgent.ts: make snapshot turnIndex 0-based (consistent with rewind response targetTurnIndex) - server.ts: restore accidentally deleted comment on RestoreInProgressError handler 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
…to format errors Two Codex review fixes: 1. After a rewind, Session.turn remains monotonic so promptId suffixes no longer correspond to actual turn positions. Use the snapshot's index in FileHistoryService.getSnapshots() instead of parsing the suffix — the array is always in sync with the current conversation. 2. Format validation errors (invalid prefix, non-numeric suffix) now carry errorKind: 'invalid_rewind_target' so the bridge maps them to 400 instead of falling through to 500. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
…acing, telemetry route Fixes from wenshao's CHANGES_REQUESTED review: 1. Add filesFailed to session_rewound SSE event payload so subscribers can detect partial file restoration failures 2. Surface file rewind errors in filesFailed array instead of silently swallowing them 3. Add SESSION_ID_RE validation to sessionRewindSnapshots handler 4. Add 'rewind' to resolveDaemonTelemetryRoute regex 5. Update DaemonSessionRewoundData type and isSessionRewoundData guard to include filesFailed 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
…e extraction wenshao R3 fixes: 1. Add debugLogger.error for file-history rewind failures so oncall has log breadcrumbs for partial-rewind incidents 2. Extract response fields once and reuse in both event + return 3. Fix rewound boolean: false when filesFailed is non-empty 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
5747039 to
bb69139
Compare
✅ Local Verification Report — PR #4820feat(serve): add HTTP rewind endpoints for daemon/web-shell (issue #4514 T3.2) Environment
Build Verification
Test Results
Capability Registry Count Drift (Minor Issue)3 Pre-existing Failures (NOT introduced by this PR)
Review Issues Status
Code Review Summary
Verdict✅ PASS — Core rewind functionality works (4/4 acpAgent tests pass), all Critical review issues addressed, bridge + SDK types consistent. Minor issue: capability-registry test count is 3 behind (needs rebase). Pre-existing failures only in unrelated areas. Verified by wenshao |
chiga0
left a comment
There was a problem hiding this comment.
Review at HEAD bb69139a — APPROVE
PR: feat(serve): add HTTP rewind endpoints for daemon/web-shell (issue #4514 T3.2) — +474/-14, 16 files, 5 commits
Architecture assessment
Clean 3-layer design:
- HTTP routes (
server.ts):GET /session/:id/rewind/snapshots(status) +POST /session/:id/rewind(control,mutate({ strict: true })guard) - Bridge (
bridge.ts): ACP extMethod transport +errorKind-based error mapping + event bus publish - Agent (
acpAgent.ts): Core rewind logic with file-history restoration + structured error propagation
Key correctness checks
-
Error propagation chain — Agent throws
RequestErrorwitherrorKind→ bridge maps to typed Error class (SessionBusyError/InvalidRewindTargetError) → server maps to HTTP status (409/400). Verified:data.errorKindcheck in bridge.ts correctly unwraps the structured error from ACP child. -
turnIndex derivation from promptId — Uses snapshot array position (
findIndex), NOT promptId suffix. Comment explains Session.turn is monotonic and doesn't reset on rewind. Correct design. -
Snapshot isolation —
sessionId + '########'prefix +/^\d+$/.test()ensures per-session snapshot filtering. -
File-history best-effort —
fhs.rewind(promptId, true)wrapped in try/catch, failures captured infilesFailed[]without blocking conversation rewind.debugLogger.erroradded (commit 5, addresses wenshao Critical). -
SDK event integration —
isSessionRewoundDatavalidates withisFiniteNumber(rejects NaN/Infinity), reducer incrementsrewindCountand mergesoriginatorClientId.session_rewoundadded toDaemonControlEventunion. -
Capabilities —
session_rewindregistered in capability registry. Telemetry regex updated.
wenshao review audit (3 CR → APPROVE)
| Round | Critical findings | Resolution |
|---|---|---|
| 1 | filesFailed missing from session_rewound event |
Fixed in commit 4 |
| 2 | Non-atomic rewind + silent error catch | Addressed: debugLogger.error added, errorKind mapping complete |
| 4 | debugLogger in file-history catch block |
Fixed in commit 5 |
All Critical items resolved at HEAD. Remaining Suggestions (concurrent POST race, fragile string matching, rewound boolean collapse, test coverage gaps) are follow-up level — accepted by author and reviewer across rounds.
Follow-up items (non-blocking)
- Test coverage: Copilot flagged missing SDK unit tests for
getRewindSnapshots/rewindSessionand server route tests for the new endpoints.acpAgent.test.tsonly has mock field updates + session existence fix, no dedicated rewind endpoint tests. - Telemetry gap:
GET /session/:id/rewind/snapshots(multi-segment path) doesn't match the existing single-segment regex pattern. - String matching for error classification:
msg.includes('Cannot rewind while a prompt is running')is fragile to upstream message changes.
0 blocking findings. wenshao APPROVED at HEAD.
Co-authored-by: Jacob Richman <jacob314@gmail.com>
What this PR does
Adds HTTP rewind endpoints (
GET /session/:id/rewind/snapshotsandPOST /session/:id/rewind) to the daemon so web-shell and SDK clients can rewind a session's conversation and files to a previous turn — replacing the TUI-only interactive dialog.Extends the existing
rewindSessionACP extMethod to also accept apromptIdparameter and perform file history rewind viaFileHistoryService. AddsSessionBusyError(409) andInvalidRewindTargetError(400) error classes for structured HTTP error responses. Publishes asession_rewoundSSE event for cross-client notification. SDK types, client methods, and event handling are included.Supersedes #4817 (which targeted
maininstead ofdaemon_mode_b_main).Why it's needed
Issue #4514 T3.2:
/rewindis currently interactive-only (opens a TUI dialog). Web-shell and SDK clients have no way to rewind a session. The underlyingFileHistoryServiceis pure filesystem (no git dependency), so this can be exposed over HTTP without a new checkpoint substrate.Reviewer Test Plan
How to verify
npm run dev:daemonGET /session/$SID/rewind/snapshots— verify non-empty list with per-turn diff statsPOST /session/$SID/rewindwith a validpromptIdfrom the list — verify files restored, conversation truncated,session_rewoundevent on SSEPOST /session/$SID/rewindwith an invalid promptId — verify 400POST /session/$SID/rewindwhile a prompt is running — verify 409Evidence (Before & After)
N/A — new HTTP endpoints, no UI changes.
Tested on
Environment (optional)
Local
npm run dev:daemon+curl.Risk & Scope
getDiffStatsfor all snapshots (up to 100) inPromise.all— acceptable for MVP but may need pagination for long sessions./restore(depends on gitService, planned for deprecation). Unrewind/unrevert not implemented per design decision.rewindSessionextMethod callers (VSCode extension) are backward-compatible — oldtargetTurnIndexparam still works whenpromptIdis absent.Linked Issues
Closes T3.2 of #4514.
中文说明
为 daemon 新增 HTTP rewind 端点(
GET /session/:id/rewind/snapshots和POST /session/:id/rewind),使 web-shell 和 SDK 客户端能将会话的对话和文件回退到之前的 turn,替代仅 TUI 可用的交互式对话框。扩展已有的
rewindSessionACP extMethod 以支持promptId参数和通过FileHistoryService进行文件历史回退。新增SessionBusyError(409)和InvalidRewindTargetError(400)错误类用于结构化 HTTP 错误响应。成功后发布session_rewoundSSE 事件用于跨客户端通知。包含 SDK 类型、客户端方法和事件处理。🤖 Generated with Qwen Code