fix(chat): 修复长回合终态历史落库失败导致对话丢失 - #445
Conversation
Long agent turns could finish in memory while the durable SQLite snapshot stayed on the initial user-only write. Final persist now retries transient failures, catches up multi-segment compaction jumps one append at a time, aligns header/segment counts before write, and only runs memory extraction after history lands.
|
PR governance checks passed. Awaiting human review. |
…ments The prior fix derived header total_segment_count/total_message_count by summing the in-memory segments. After a conversation is reopened from history, state.segments holds only the loaded active segment while sealed segments live solely in SQLite, so that sum undercounts. Rust's verify_chat_history_consistency compares the header against COUNT/SUM over all rows in the same transaction and rejects every persist — reproducing the exact history loss this PR targets, and now deterministically. Anchor final-persist header totals on state.meta (maintained incrementally by appendMessagesToConversation and reconciled by normalizeConversationState, so it already matches the durable segment sum). For intermediate catch-up appends, derive each step's totals from the final header minus the not-yet-appended in-memory segments, and set totalSegmentCount to activeSegmentIndex + 1 so it equals stored_count + 1 as the append precondition demands. Add regression coverage for reopened conversations (active-segment upsert and multi-segment catch-up) whose meta totals exceed the in-memory segment sum; fix the multi-jump assertion to expect the per-step segment count.
|
在原改动基础上追加了一个 commit( 问题 1 — 终态 header 计数对内存段求和(P0)
Rust 端 差分实证(DB 布局 seg0=100/seg1=80/seg2=90/seg3=22,重开后内存只有 seg3):
问题 2 — catch-up 中间步
|
…otals Fork main has no taskState/taskList on StoredChatContextMeta; keep Stack-Cairn#445 catch-up/retry helpers and assert intermediate append totals as activeIndex+1 so tsc and history persist tests pass on headless main.
Agent Dev memory extraction can write durable memory before the final chat snapshot lands, leaving memory ahead of a user-only history when persistence fails. Persist the completed answer first, gate extraction on that success, then persist any render-only extraction status separately.
… and keep headless Bring upstream main (including merged PR Stack-Cairn#445 chat history final-persist) into the fork while preserving the headless runtime, tauriBridge adapters, and desktop/headless command routing. Resolve conflicts by taking upstream feature code paths and re-applying headless-compatible invoke/listen/openUrl bridges, AppendSegment input types, and worktree command registration.
关联
Fixes #440
问题
长 agent 回合结束后,内存里已有完整对话,但 SQLite 可能仍停在开局时的 仅用户消息 快照。
刷新/重开会话后助手回复丢失;与此同时记忆提取仍可能基于内存状态执行,造成“对话丢了、记忆却写了”的不一致。
这是 GUI/WebUI 共用历史链路问题,不是 headless 专属。
上游 issue:#440(用户侧表现为“历史记录保存失败 / 重启后对话丢失”)。
根因
persistConversationRuntime一旦失败会被当成非致命 soft-fail。不支持的历史分段跳变,整次终态落库失败。改动
false与 throw 的既有契约messages.length,并同步中间 append 的contextMetaJsontotals验证
本地(
crates/agent-gui):pnpm exec biome check(本次改动文件)通过pnpm exec tsc --noEmit通过node --test test/chat/chat-history-persist-queue.test.mjs test/chat/chat-stop-timing.test.mjs→ 18/18pnpm lint:全量仅有既有 warn(useWorkspaceProjects等,非本 PR 引入;CI 此前也只因本 PR format error 失败)Screenshots / preview
逻辑层持久化修复,无界面布局变更。以下引用 #440 用户复现截图作为 before 场景说明:
说明
upstream/main,不叠在 headless feat(headless): Headless 服务器运行时与 liveagent-server(无需 Tauri) #409 / feat(docker): Headless 可选部署的 Dev-Tools 镜像与 Compose 配置 #410 上。--- images --- ['https://github.com/user-attachments/assets/f2f04143-acf1-490f-9629-666d3227a9a8']