fix(transcript): lossless transcript restore — full-fidelity cold-boot resume, per-turn thinking hydration, seq envelope#5077
Conversation
…nt trails Restore-fidelity fixes for reopened threads: - keep each past turn's reasoning/narration transcript (turnTranscriptsByThread) and render it via PastTurnInsights, not just tool cards (fix 1) - surface an interrupted turn's persisted partial answer + thinking as a settled, marked-interrupted bubble on restore (fix 2) - remove the dead 'reasoning' TimelineItem union member (fix 3) - render restored sub-agent transcripts for past turns too (fix 4) - order replayed transcripts by persisted seq when present (fix 5)
…ed partials, seq order, subagent fallback
…ity session_raw transcript The cold-boot web-chat resume path seeded LLM context from lossy (sender, content) prose (memory_conversations), dropping every tool call, tool-role result, and reasoning block, so the model 'forgot' all tool interactions after an app restart. Add Agent::seed_resume_from_thread_transcript, which routes thread to its root session_raw transcript via find_root_transcript_for_thread and reuses the existing read_transcript / bound_cached_transcript_messages machinery (same path as try_load_session_transcript) so tool_calls, role:tool messages, and reasoning_content survive the round-trip. run_task prefers it and only falls back to prose-pair seeding when no transcript exists or it fails to load (never both, avoiding duplicated context). Adds 3 Rust regression tests.
Cap per-item transcript prose (parent + subagent) at 16 KiB so a runaway reasoning stream can't bloat the per-flush snapshot; the subagent interleaved thinking/text/tool transcript is now round-tripped with full fidelity. Stamp a monotonic seq on each ToolTimelineEntry (dedicated per-turn counter) and an additive per-request seq envelope on every web-channel event emitted by progress_bridge (WebChannelEvent.seq), so the frontend can dedup live vs replayed events by (request_id, seq) and order rehydrated snapshots identically to the live stream. Backward compatible: seq is optional and ignored by older clients. Mirror the new fields in app/src/types/turnState.ts.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 8 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (23)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 242a133433
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| a settled, marked-interrupted bubble surfaced on restore. Only | ||
| when NOT streaming live (the buffer is cleared by any live turn | ||
| in the slice; this guard is belt-and-braces). */} | ||
| {!isSending && selectedInterruptedAssistant ? ( |
There was a problem hiding this comment.
Clear interrupted partials when starting a new turn
When a restored interrupted thread is used again, this only hides the old partial while isSending is true; the live socket path clears processing/streaming on inference_start/chat_done but never clears the new interruptedAssistantByThread entry. After the replacement turn completes, isSending becomes false and the stale interrupted bubble can reappear below the new answer until some later snapshot hydration happens to clear it, so the buffer should be cleared when a fresh live turn starts (or when that turn settles).
Useful? React with 👍 / 👎.
Summary
web_chat/run_task.rs+agent/harness/session/runtime.rsseed the resumed model context from the full-fidelitysession_raw/*.jsonltranscript by thread id — restoring tool calls, tool-role results, andreasoning_content— falling back to the old prose-pair seeding only when no transcript exists. Previously the prose seeding always won and the rich JSONL was never read.PastTurnInsights; an interrupted turn's persisted partial answer surfaces as an "Interrupted"-marked bubble via the newInterruptedAnswer(not written to the durable log); restored subagent trails include reasoning/text (tool-only fallback for legacy snapshots); hydration orders byseqwhen present. DeadreasoningTimelineItemunion member removed.seqenvelope (timeline-refactor Phase 4 amendment).progress_bridgestamps a per-request monotonicseqon every socket event;seqis persisted on tool-timeline rows; an optionalseqis declared on the frontend socket event types.Problem
Session-restore was silently losing information: restored transcripts "forgot" tool calls and reasoning even though full-fidelity
session_raw/*.jsonlwas present on disk. Debugging located the loss in the seams between the three persistence layers, not in persistence itself — the read/restore paths discarded the rich transcript and rebuilt context from lossy prose pairs. This left resumed conversations (both the model's own context and the rendered UI history) missing tool calls, tool results, reasoning, interrupted partials, and subagent trails.Solution
Fix the read/restore paths, informed by how Codex (rollout replay through live handlers, persistence policy, turn markers) and hermes-agent (insertion-order replay, two-projection resume) stay lossless:
reasoning_contentinto the resumed context, with the prose-pair path kept strictly as a fallback for threads that have no transcript on disk.seqwhere available and degrading gracefully (tool-only) for legacy snapshots.seqenvelope gives every socket event a per-request monotonic ordinal so restore ordering is deterministic rather than arrival-dependent; persisted on tool-timeline rows and declared optional on the wire types.Plan reference:
docs/plans/conversations-timeline-refactor.md.Deliberately out of scope (follow-ups):
ConversationTimelinein production.Submission Checklist
diff-cover) meet the gate enforced by.github/workflows/ci-lite.yml. Runpnpm test:coverageandpnpm test:rustlocally; PRs below 80% on changed lines will not merge.docs/TEST-COVERAGE-MATRIX.md.## Related.docs/RELEASE-MANUAL-SMOKE.md.## Related.Impact
session_raw/*.jsonl; threads without a transcript fall back to the prior behaviour.seq/reasoning degrade gracefully (arrival order / tool-only trails). The interrupted partial is display-only and never written to the durable log.Related
ConversationTimelinein production.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/transcript-restore-fidelity242a13343326045450542689ae8da755bbbf8b7fValidation Run
pnpm --filter openhuman-app format:checkpnpm typecheckturn_stateround-trip + cap + seq-monotonicity (33 turn_state, 13 progress_bridge, 7 resume passing); 45 frontend tests across 4 touched files.cargo clippy -p openhuman -- -D warningsclean.app/src-taurichanges in this branch.Validation Blocked
command:pnpm --filter openhuman-app rust:clippy(Tauri-shell clippy, invoked by the local pre-push hook)error:failed to load source for dependency 'tauri'— the vendoredapp/src-tauri/vendor/tauri-cef/crates/directory is absent in this worktree.impact:None on this PR. The failure is pre-existing local-environment breakage in the vendored CEF tauri crates, unrelated to this branch (which touches zero files underapp/src-tauri/). All pre-push checks against the changed code (core clippy, format, lint, tsc) passed. The branch was pushed with--no-verifyper the repo's sanctioned bypass for pre-existing unrelated hook failures; CI runs these checks in a correctly-provisioned environment.Behavior Changes
Parity Contract
seq/reasoning degrade gracefully (arrival order, tool-only subagent trails).Duplicate / Superseded PR Handling