Skip to content

perf(desktop): keep transcript state incremental - #2937

Merged
M4n5ter merged 26 commits into
mainfrom
perf/desktop-transcript-replica
Aug 13, 2026
Merged

perf(desktop): keep transcript state incremental#2937
M4n5ter merged 26 commits into
mainfrom
perf/desktop-transcript-replica

Conversation

@M4n5ter

@M4n5ter M4n5ter commented Aug 13, 2026

Copy link
Copy Markdown
Member
English

Summary

Desktop previously opened a session by materializing its complete durable transcript, so startup time and memory grew with the full conversation even when the user only needed the latest turns.

This PR moves Desktop onto the bounded transcript data plane from #2922:

  • Main owns a bounded, sequence-addressed sparse replica and sends fragmented, generation-fenced changes to the renderer.
  • Sessions open from a small tail. Scrolling upward automatically prefetches an earlier range while preserving the visible anchor.
  • A bounded sampled prompt rail keeps long-history navigation available without enumerating the complete transcript.
  • Durable advancement, active-overlay handoff, reconnect, settlement, and turn-dependent actions no longer require full-history polling.

The primary gain here is bounded session opening and history residency. The larger rendering-speed improvement comes from the follow-up that virtualizes the mounted conversation; Host-side search is also kept separate.

中文

概述

Desktop 以前会在打开会话时完整物化全部 durable transcript,因此启动耗时和内存会随完整对话增长,即使用户只需要查看最新几轮。

本 PR 让 Desktop 使用 #2922 提供的有界 transcript 数据平面:

  • Main 持有有界、按 sequence 寻址的 sparse replica,并向 Renderer 发送分片且带 generation fence 的增量变更。
  • 会话从较小的 tail 开始显示;用户向上滚动时自动预取更早范围,并保持当前可见锚点。
  • 有界采样的 prompt rail 保留长历史快速导航能力,不需要枚举完整 transcript。
  • Durable advancement、active-overlay handoff、重连、settlement 和依赖 Turn 的操作不再依赖完整历史轮询。

本 PR 的主要收益是让会话打开和历史常驻内存有界。更显著的渲染提速来自后续的会话 DOM 虚拟化;Host-side search 也保持为独立后续工作。

Refs #2913

Checklist

  • Tests cover the observable behavior
  • Lint, format, typecheck, and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described above
  • No

Base automatically changed from perf/transcript-data-plane-v2 to main August 13, 2026 06:30
@M4n5ter
M4n5ter force-pushed the perf/desktop-transcript-replica branch 5 times, most recently from 3c02213 to 4464f27 Compare August 13, 2026 10:28
M4n5ter added 23 commits August 13, 2026 21:21
Make Desktop Main own a bounded sparse transcript replica and publish bounded changes to the renderer. Replace chat-path full materialization and settlement polling with range advancement, and expose paged turn summaries for history-dependent actions.
Keep turn projection chunk-aware and page it by bounded contributions rather than raw messages. Preserve transcript consumer intent across Host replacement, advance durable read markers, and make retries reopen failed generations.\n\nRelease consumed Client overlays, bound projector identities to in-flight work, and evict cold Desktop replicas under the global resident-byte budget while refreshing them on demand.
Allow a renderer transcript open to remain pending while the Runtime Host source is detached, then complete it against the replacement source. Stale source completions are closed without deleting the current registration.
Keep transcript intent pending across Host replacement, but let renderer teardown cancel the pending consumer immediately. Retry failures now remain visible instead of escaping as unhandled promises.
Keep live subscriptions available while cold transcript replicas are rebuilt, and make pending renderer opens cancellable through the Main boundary. Preserve accumulated companion history, hydrate search targets outside the tail window, bound turn-summary scans, and stream binary IPC fragments to avoid base64 amplification.
Pause the retiring event pump while a cold transcript candidate commits so old frames cannot cross into the replacement projector. Restore and drain the healthy subscription if the candidate fails.
Broadcast each encoded transcript batch to every renderer consumer. Prepare replacement reconciliation asynchronously, then activate it synchronously after ownership and failure checks so a failed candidate or concurrent recovery cannot displace a healthy replica.
Leave sessions:unobserve with the persistent Desktop owner instead of registering it again for every Runtime Host candidate. This preserves cross-candidate observation cleanup and avoids duplicate Electron handlers after the scoped IPC integration.
Route transcript reads through the active Host scope while keeping teardown persistent across candidate replacement. Bound revision settlement and message decoding independently from replica cache size so recovery cannot hang or reject otherwise supported records.
Serialize Main-to-Renderer transcript batches behind explicit acknowledgements so large records cannot build an unbounded Electron queue. Keep the established 15 MiB record path within a 16 MiB hard limit, restore the 64 MiB aggregate cache budget, and fence settlement commits against cancellation and session switches.
Keep at most one pending incremental delivery and replace additional changes with a fresh bounded replica reset after the renderer catches up. Enforce the session budget across overlay and durable records so retained presentation state cannot bypass cache accounting.
Bind transcript events and follow-up requests to the Host generation that opened them. Give each renderer consumer an independent bounded ACK window, coalesce queued changes as one bounded delta, and account retained delivery data with replica cache capacity so a slow consumer cannot block peers or escape cleanup.
Move the transcript generation scope with each accepted reset so acknowledgements and range requests follow a restored consumer onto the replacement Runtime Host without accepting stale-target frames.
Charge coalesced eviction and overlay-completion identities by their encoded wire size so delivery admission reflects every retained payload class before accepting more transcript state.
Account fragment assembly against the shared Desktop transcript budget while it is being decoded, and avoid duplicate full-record copies. Keep renderer delivery flowing with a true sliding acknowledgement window so one completed batch immediately admits the next.
Keep preparing replicas inside the global memory ledger until activation, and bound turn diagnostics before publishing them over the Runtime Host protocol.\n\nReplace manual history paging with bounded multi-page prefetch on upward scroll. Preserve the reader's message anchor while older turns mount incrementally, and expose the complete prompt index so the existing rail can navigate outside the resident tail.
When a range contains only part of the transcript, resolve scroll-spy endpoints from mounted turns instead of the complete navigation index. This preserves the tick selected for an unloaded turn after its range arrives.
Bound the prompt rail and turn-summary wire projection while preserving navigable history. Keep transcript ownership, sparse sends, cross-session scroll restoration, and return-to-latest positioning correct across asynchronous replacement and progressive mounting.
Give the active overlay and sparse durable window separate bounded residency so a valid overlay cannot make a successfully loaded history page immediately unreachable. Global eviction continues to account for their combined bytes.
Apply external preparation accounting before mutating replica-local balances so a rejected global allocation cannot be released again during teardown.
When subscription recovery overlaps the initial reset acknowledgement, wait for the owner to stabilize and deliver the replacement generation before completing the renderer open. Any post-registration failure now tears down the consumer.
Load a larger history range in one renderer commit, keep progressive anchor lookup incremental, and account reset encoding work without double-charging the resident replica. Keep transcript acknowledgements available while a Runtime Host candidate reconnects.
Replace full turn enumeration with bounded storage landmarks so session opening remains tail-first. Treat detached transcript acknowledgements as stale, and skip message descendants during incremental scroll-anchor lookup.
Use the existing ordered root-turn authority to discover prompt landmarks without scanning tool-heavy transcript gaps. Keep the sampled index as the rail's ordering authority so sparse loaded ranges cannot reorder navigation.
@M4n5ter
M4n5ter force-pushed the perf/desktop-transcript-replica branch from 0f1de89 to 9998a4c Compare August 13, 2026 13:22
Retain the landmark watermark through Desktop so newly durable prompts refresh the bounded rail index without reopening the session. Keep mixed legacy and indexed prompts navigable while using endpoint lookups instead of scanning the transcript for bounds.
A durable steering row can arrive before its live event on the independent transcript stream. Hold that trailing row behind the current live content until the event projection catches up, preserving the answer DOM and smooth redaction rewrite across remounts.
@M4n5ter
M4n5ter marked this pull request as ready for review August 13, 2026 14:02
@M4n5ter

M4n5ter commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

I’ve completed multiple rounds of review, including re-reviewing the follow-up fixes and validating the Desktop behavior end to end. The current revision meets the merge bar from my side. ✅

@M4n5ter
M4n5ter merged commit 57ef948 into main Aug 13, 2026
10 checks passed
@M4n5ter
M4n5ter deleted the perf/desktop-transcript-replica branch August 13, 2026 14:13
sunheyi6 added a commit to sunheyi6/maka-agent that referenced this pull request Aug 14, 2026
Resolve conflicts from the transcript range-store refactor (apache#2937 and
related) that removed the readMessages bridge and refreshMessagesUntilTurn
polling:

- runtime-host-session-execution-ipc-main.ts: keep both
  isClosedSteeringAdmission (mid-turn steer) and
  normalizeTranscriptRangeRequest (transcript range) helpers.
- bridge-contract.d.ts: keep steer(messageId?) and drop the removed
  readMessages method.
- app-shell-chat-actions.ts: drop the removed refreshMessagesUntilTurn
  settle poll (readMessages is gone); the transcript range controller
  now owns refresh.
- app-shell-first-send-cleanup.test.ts: keep both imports, adapt send
  mocks to the disposition/turnId contract, and track transcript refresh
  through the range controller instead of readMessages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant