cf60ebbdfd264c3fc067b9e3230df8752c50d2ec
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
02e270a47e |
fix: editing a message in an old session fails (profile DB + window-relative ordinal) (#91302)
* fix(gateway): persist prompt.submit truncation to the session's own profile DB `_get_db()` returns the LAUNCH profile's SessionDB handle. App-global remote mode gives a session its own profile (`session["profile_home"]`) whose transcript lives in that profile's `state.db`, so a write keyed on `session_key` that goes through `_get_db()` addresses the wrong database. In the `prompt.submit` truncate branch that has two consequences. The edit/resend never sticks — `session.resume` reopens the profile db and resurrects the undone turns — and when the launch profile happens to hold a row under the same session id, the truncated transcript is inserted into a profile the session does not belong to. It also silently voids the branch's own fail-closed contract. The handler persists before it rewrites `session["history"]` precisely so that a failed write refuses the turn and leaves memory and DB aligned; that only holds if the handle it checks is the one that owns the row. `_session_db(session)` is the profile-aware resolver that already exists for this: the profile's `state.db` when `profile_home` is set, otherwise the shared launch handle. Non-profile sessions are unaffected — `_session_db` borrows the same shared handle and leaves it open. `active_only=True` and `archive_dropped=True` are carried through unchanged; only the handle the call is made against changes. * fix(gateway): resolve the /undo command against the session's own profile DB `command.dispatch`'s `/undo` branch opened the launch profile's handle via `_get_db()`, but every read and write under it is scoped by session id: `list_recent_user_messages`, `rewind_to_message` and the `get_messages_as_conversation` reload all key on `session_key`. For a session with its own profile (`session["profile_home"]`) the rows live in that profile's `state.db`, so against the launch handle `list_recent_user_messages` returns nothing and the command fails closed with `4018 "no user messages to undo"` — for the entire session, on every invocation, even though the transcript is right there in the profile db. Route the whole branch through `_session_db(session)`, which yields the db that owns the session's row and closes a profile handle on exit. Sessions without a profile keep borrowing the shared launch handle exactly as before, so this is behaviourally identical for them. * fix(gateway): read /history and /context from the session's own profile DB `_format_live_history_output` and `_format_live_context_output` rebuild the transcript from the database rather than from `session["history"]`, because the in-memory list is empty for a session this process did not run itself. Both reads are scoped by session id but were issued against `_get_db()`, the launch profile's handle. A session with its own profile (`session["profile_home"]`) keeps its rows in that profile's `state.db`, so both reads come back empty and the commands under-report: `/history` renders "No conversation history yet." and `/context` falls back to the empty in-memory list and reports a conversation of zero messages. Both swallow their exceptions, so there is no error either — just a wrong answer about the user's own transcript. Resolve both through `_session_db(session)`, the profile-aware resolver used by the rest of the session-scoped paths. * test(gateway): cover session-scoped transcript ops against a profile DB Regression coverage for the three session-scoped sites that resolved against the launch profile's handle instead of the db owning the session's row. Each test drives the real JSON-RPC entry point with a session carrying `profile_home`, seeds the transcript into the profile's own `state.db`, and asserts against both databases. Per site, with the production change reverted to its pre-fix form: - `prompt.submit` truncation — `test_truncation_persists_to_the_profile_db` and `test_truncation_does_not_copy_rows_into_the_launch_profile` fail. The second seeds a row under the same session id in the launch db so the foreign write succeeds instead of failing a key check, which is the case that copies a transcript into a profile it does not belong to. - `/undo` — `test_undo_rewinds_the_profile_transcript` fails with `4018 "no user messages to undo"`. - `/history` and `/context` — `test_history_reads_the_profile_transcript` and `test_context_reads_the_profile_transcript` fail, reporting an empty conversation. `test_undo_still_uses_the_shared_handle_without_a_profile` and `test_truncation_without_a_profile_uses_the_shared_handle` pin the unchanged path: with no `profile_home` the resolver must borrow the shared launch handle and leave it open. Both stay green in every direction, so a future change cannot satisfy the profile cases by abandoning the shared one. * fix(desktop): aim truncations by durable id alone on tail-only transcripts The cold-open transcript is a newest-first prefetch page (LATEST_SESSION_MESSAGES_LIMIT = 120) with the resume RPC sent omit_messages — older rows only arrive via "Show earlier" backfill. planEdit/planReload/planRestore still counted truncate ordinals over that windowed list, so every edit/reload/restore in a session longer than the prefetch page sent a window-relative ordinal alongside the durable row/message id. The gateway's #82959 cross-check resolved the durable id to its full-history ordinal, read the offset as drift, and refused with 4030 — making the Edit affordance permanently dead in long sessions. When the transcript may be tail-only (the transcript-tail bookkeeping's possiblyTruncated), drop the client ordinal and address the truncation by durable id alone — the same rule runRewindSubmit already applies to content-resolved row ids (#87059). The ordinal tripwire stays on whenever the transcript is complete. Closes #88082 * fix(desktop): drop client rewind ordinal whenever a durable id is present #88092 gated the drop on tail-only prefetch. After in-place compact the live scrollback is treated as complete, so Restore still sent a display-lineage ordinal next to a resolved row id and the gateway refused with 4030 (#89244). prefix_user_count is structurally 0 on in-place because get_ancestor_display_prefix is cross-session. Same choke point: if a durable truncate_before_row_id or a real truncate_before_message_id is present, omit the client ordinal. confirm_empty_truncate is still carried from a caller ordinal of 0. Unknown ids still fail closed at 4018. Closes #89244 --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com> Co-authored-by: zengzheqing <yuntianqing@yahoo.com> |