Commit Graph

5 Commits

Author SHA1 Message Date
Teknium 3b672a6897 fix: delete-path drops every scope of a removed id; legacy purge latches on success only
Review follow-ups on the salvage (#94914): the delete path derives its
scope from the removed row while saves derive from ownerRoute — a shape
drift orphaned the entry until LRU eviction. Deletes now sweep every
scope of the stored id (safe: deleted ids are never reused), while the
failed-resume path keeps the exact-scope drop (a same-id twin in another
profile must keep its tail). purgeLegacyV1 latches only after a
completed sweep so a mid-sweep throw retries next touch.
2026-08-26 18:23:44 -07:00
Bruno Bza 03f5302a22 fix(desktop): scope durable transcript-tail cache by owning profile/connection
Stored session ids are only unique within one profile's state.db, and
localStorage survives profile switches in the same window. The durable
transcript-tail cache keyed entries by bare stored id, so a tail cached
while working in profile A was painted against profile B's backend after
a switch; that backend never held the session and retried it on every
wake ('session not found'), matching the desktop.log pattern of repeated
session.reclaimed (ws_orphan_reap) events for ids that live in another
profile's state.db (#94828).

Scope every entry by its owner — the same {connectionId, profile} shape
the REST layer already threads through getLatestSessionMessages and the
in-memory twin stores in TranscriptTailState:

- key entries under v2:[connectionId, profile, storedId]; loads only
  return a tail saved under the SAME scope
- thread the session's owning scope through all call sites
  (resumeSession's warm-path save + cold-paint load/rollback drop,
  final save, removeSession's delete drop)
- sweep pre-scoping v1 entries once per window: a bare-id key cannot be
  attributed to an owner, so it must never paint again
- dropTranscriptTail with a scope leaves other backends' same-id tails
  intact
2026-08-26 18:23:44 -07:00
hermes-seaeye[bot] 4b860d8193 fmt(js): npm run fix on merge (#92399)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-22 17:34:58 +00:00
Teknium 40f3e58f6d fix(desktop): stop manufacturing duplicate toolCallIds at the fold, repair poisoned cached tails
Two follow-up layers on top of the salvaged runtime-boundary guard (#87871):

- coalesceToolOnlyAssistants now folds via concatToolPartsUnique, dropping an
  incoming tool-call part whose toolCallId the predecessor already carries.
  Two individually-clean rows sharing an id (structural carry-over re-attaching
  a cached row's calls) no longer become one crashing message — and no longer
  render the same call twice. Root-cause analysis by @marketing2981 (#87857).
- loadTranscriptTail repairs a poisoned persisted tail on read; installs
  already carrying a duplicate in hermes.transcript-tail.v1:* stop
  crash-looping after upgrade instead of re-deriving the same collision
  every launch.
- Regression tests for all three layers, incl. the end-to-end repository link
  test (from #92093 by @RasputinKaiser) and the cross-message ids-stay-
  untouched contract (per-response tool numbering, e.g. Kimi — #90545 by
  @M7MMAD-OMAR). Each test sabotage-verified against its reverted layer.
2026-08-22 10:30:31 -07:00
Teknium f8767d1e71 perf(desktop): durable transcript-tail cache — bot wakes paint at ~0ms
Second layer of the "make hydration feel instant" work (on top of the
paint-first wait): persist a bounded tail (40 msgs / 256KB / 50-session
LRU) of every reconciled transcript in localStorage, keyed by durable
stored-session id.

- Cold resume paints the cached tail immediately — the wake is visually
  complete before any network I/O; the REST prefetch / runtime resume
  reconcile the authoritative transcript over it when they land.
- The cached paint is DISPLAY-ONLY: reconciliation treats the view as
  empty (viewMessagesForReconcile), so authoritative content replaces the
  provisional paint wholesale — no grafting onto stale rows. Failure
  latches also treat it as empty, so a cached paint can never mask a
  genuinely stranded resume; a resume that proves the session empty rolls
  the paint back and drops the poisoned entry.
- Saves happen only post-reconcile (cold path + warm activate path);
  deletes drop the entry; a gateway/mode re-home wipes the cache (another
  backend can recycle stored ids).

Sabotage-proven: disabling the cache load fails 5/7 cache tests; the
display-only contract is covered by the existing resume reconciliation
suite (663 tests green).
2026-08-18 15:52:39 -07:00