Skip to content

fix(run_agent): clamp _last_flushed_db_idx after scaffolding drop (#31507) - #31517

Closed
0xsir0000 wants to merge 1 commit into
NousResearch:mainfrom
0xsir0000:fix/flush-idx-overshoot-31507
Closed

fix(run_agent): clamp _last_flushed_db_idx after scaffolding drop (#31507)#31517
0xsir0000 wants to merge 1 commit into
NousResearch:mainfrom
0xsir0000:fix/flush-idx-overshoot-31507

Conversation

@0xsir0000

Copy link
Copy Markdown
Contributor

Summary

_persist_session runs _drop_trailing_empty_response_scaffolding before _flush_messages_to_session_db. If an earlier intermediate flush advanced _last_flushed_db_idx to a length that included the scaffolding tail, the drop leaves the index strictly greater than len(messages). The next flush computes flush_from = max(start_idx, _last_flushed_db_idx) >= len(messages), slices messages[flush_from:] to empty, and silently writes nothing — including any final assistant response appended after the drop. CLI sessions that reuse the same AIAgent across turns are the common trigger, which is why state.db loses assistant rows while the WebUI mirror keeps the full transcript.

Fix

Resetting flush_from to start_idx (as the report suggested) would re-flush messages already persisted, so instead clamp _last_flushed_db_idx down to len(messages) right after the scaffolding drop. New messages appended on the next turn are then flushed exactly once — preserving the #860 dedup invariant.

  • run_agent.py: clamp _last_flushed_db_idx after _drop_trailing_empty_response_scaffolding
  • tests/run_agent/test_empty_response_recovery_persistence.py: two new regression tests (one direct, one end-to-end against a real SessionDB); existing stub fixture updated to seed the new attribute it now reads

Test plan

  • pytest tests/run_agent/test_empty_response_recovery_persistence.py — 5 passed (2 existing + 3 new)
  • pytest tests/run_agent/test_860_dedup.py tests/run_agent/test_compression_persistence.py tests/run_agent/test_message_sequence_repair.py tests/run_agent/test_413_compression.py — 40 passed (dedup / compression / repair invariants unchanged)
  • pytest tests/cli/test_branch_command.py tests/cli/test_cli_new_session.py — 23 passed (other _last_flushed_db_idx consumers unaffected)

Fixes #31507

…usResearch#31507)

`_persist_session` runs `_drop_trailing_empty_response_scaffolding`
before `_flush_messages_to_session_db`. If an earlier intermediate flush
advanced `_last_flushed_db_idx` to a length that included the
scaffolding tail, the drop leaves the index strictly greater than
`len(messages)`. The next flush computes
`flush_from = max(start_idx, _last_flushed_db_idx) >= len(messages)`,
slices `messages[flush_from:]` to an empty list, and silently writes
nothing — including any final assistant response appended after the
drop. CLI sessions that reuse the same `AIAgent` across turns are the
common trigger, which is why state.db is missing assistant rows while
the WebUI mirror keeps the full transcript.

Resetting `flush_from` to `start_idx` (as the report suggested) would
re-flush messages already persisted, so instead clamp
`_last_flushed_db_idx` to `len(messages)` right after the drop. New
messages appended in the next turn are then flushed exactly once.

Tests cover both the direct invariant (clamp happens, flush is invoked)
and an end-to-end SessionDB scenario where the post-drop assistant
response must land in the DB.

Fixes NousResearch#31507
@teknium1

Copy link
Copy Markdown
Contributor

Resolved by #46071. Same stale flush-cursor/shortened-message-list failure mode from #31507; the merged fix writes unflushed message dicts by identity instead of relying on the overshooting cursor.

@teknium1 teknium1 closed this Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

State.db loses assistant responses when _drop_trailing_empty_response_scaffolding causes _last_flushed_db_idx to overshoot len(messages)

3 participants