fix(session-db): persist post-flush /steer mutation to SQLite - #52604
Open
youngting520 wants to merge 1 commit into
Open
youngting520 wants to merge 1 commit into
youngting520 wants to merge 1 commit into
Conversation
Author
|
Re-ran the regression module on Ubuntu (Linux 6.17, x86_64) via the canonical CI hasn't been able to run on this PR yet — as a cross-repo first-time |
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
2 times, most recently
from
July 2, 2026 15:35
4aa74f9 to
0699bf6
Compare
teknium1
reviewed
Jul 15, 2026
teknium1
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for tracing this to the incremental-flush marker path. The persistence gap remains on current main: agent/tool_executor.py:996-1005 flushes a tool row before /steer mutates it, while run_agent.py:1850-1851 skips that marker-stamped message on the later flush.
Problems
tests/run_agent/test_steer_sessiondb_persistence.py:56usestempfile.mkdtemp()for the patched Hermes home without cleanup. The test helper should usetmp_pathor aTemporaryDirectoryowner so test runs do not leave directories behind.
Suggested changes
- Preserve the current-main persistence behavior while salvaging the row-update logic; GitHub currently reports this branch as non-mergeable against
main. - Keep the SQLite/FTS regression coverage, including the direct post-flush mutation case.
This is an automated hermes-sweeper review.
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
2 times, most recently
from
July 16, 2026 14:14
c480f84 to
a89d4f2
Compare
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
from
July 19, 2026 06:12
a89d4f2 to
fef2ef0
Compare
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
9 times, most recently
from
July 30, 2026 11:33
497bb1f to
afb3ede
Compare
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
from
August 2, 2026 14:08
7e5ebc7 to
c034f31
Compare
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
from
August 4, 2026 18:04
c034f31 to
993bc47
Compare
Mid-turn /steer appends its marker to a tool result that the incremental flush may already have written to state.db, leaving the durable row stale (resume/session API/search would replay a transcript that differs from what produced the answer). The aggregate tool-output budget rewrite has the same post-flush mutation shape. Both mutation sites now stamp _db_content_update_pending, and the flush performs a scoped in-place UPDATE (via the opt-in _row_id contract, falling back to a unique tool_call_id) instead of skipping the row; the identity-prefix fast path stops before any pending mutation. Rebased onto main after 0fd0db1 ("preserve /steer through turn-budget enforcement"): upstream now keeps /steer pending until after aggregate budget enforcement, so this revision drops the per-tool drains and the _steer_budget_protected_suffix machinery the original patch added to defend them.
youngting520
force-pushed
the
fix/steer-sessiondb-persistence
branch
from
August 4, 2026 18:05
993bc47 to
749d315
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes a durable-persistence bug where a mid-turn
/steeris delivered to the live model but is not written back to SQLiteSessionDB(state.db). A later resume, session API read, or search could therefore replay a transcript that differs from the content that actually produced the answer.It also fixes the sibling post-flush mutation in aggregate tool-output budget enforcement:
enforce_turn_budgetcan rewrite an already-flushed tool result in place, leaving the durable row with the pre-budget content while the model sees the replacement.Root cause
AIAgent._flush_messages_to_session_db()marks message dicts with_db_persistedafter writing them. Later flushes skip those dicts, and the bounded identity-prefix scan can skip the entire already-seen prefix./steerand aggregate budget enforcement intentionally mutate tool-result content after that first write, so the durable row remained stale.Approach on current
mainThis revision is rebased directly onto
9712b8f0cand follows the current batched persistence design:_db_content_update_pendingon the intentionally mutated tool message._row_idand falling back only to a uniquetool_call_id.append_messages_batch()transaction. The batch API now returns row IDs only whenreturn_row_ids=True; the live flush uses that opt-in to stamp freshly inserted tool messages without changing the default inserted-count contract.The original revision also re-added per-tool steer drains. Current main deliberately delivers
/steerat the batch-end drain after aggregate budget enforcement, so this revision preserves that ordering and does not restore the old drain scheme.Changes made
agent/persistence_markers.pycentralizes_db_persistedand_db_content_update_pending.run_agent.pyupdates pending durable rows, integrates row-ID capture with batched inserts, stops the prefix fast path at pending mutations, and strips private persistence metadata from JSON snapshots.hermes_state.pyadds fail-closedupdate_tool_message_content(...)and opt-in row-ID returns for batched inserts.agent/agent_runtime_helpers.py,agent/conversation_loop.py, andtools/tool_result_storage.pyexplicitly mark the three intentional mutation sites.tui_gateway/methods_session.pypreserves tool pairing and child-row identity without reverting current main's chunked batch-copy optimization.agent/context_compressor.pyremoves both persistence markers when re-baselining compacted messages.Validation
The focused run includes:
tests/run_agent/test_steer_sessiondb_persistence.pytests/hermes_state/test_append_messages_batch.pytests/tools/test_tool_result_storage.pytests/agent/test_context_compressor.pytests/run_agent/test_run_agent.pytests/test_tui_gateway_server.pyScope / impact
Affected: incremental SQLite persistence for normal CLI, gateway, and TUI turns, plus TUI branch copies.
No model-tool schema, config key, system-prompt content, or user-facing command surface changes. Full-transcript rewrite paths remain unchanged, and current main's batch-end-only steer delivery remains intact.
Checklist
main_row_idcontract; no duplicate row-id metadata