Skip to content

fix(chat): persist post-turn log state at monologue end (memorize items only saved on next turn) - #1806

Open
Zenetusken wants to merge 2 commits into
agent0ai:mainfrom
Zenetusken:fix/post-turn-chat-persistence
Open

fix(chat): persist post-turn log state at monologue end (memorize items only saved on next turn)#1806
Zenetusken wants to merge 2 commits into
agent0ai:mainfrom
Zenetusken:fix/post-turn-chat-persistence

Conversation

@Zenetusken

Copy link
Copy Markdown

Problem

Post-turn log entries are not persisted when they happen. The regular chat save hook lives at message_loop_end/_90_save_chat.py, which runs before monologue_end extensions. Anything logged from monologue_end — the "Waiting for input" progress reset (_90_waiting_for_input_msg) and the memory plugin's memorize utility items (_50/_51, created at loop end and filled in seconds later by a background DeferredTask) — therefore misses the turn's final save.

Measured live on a finished chat: the memorize items were absent from chat.json minutes after the turn completed; the previous turn's memorize items were only present because the user's next message triggered a save. Consequences:

  • restart the server in the window between turn end and the next turn → the memorize log entries are gone permanently;
  • the persisted progress line stays stale (e.g. "Responding") instead of "Waiting for input".

The memorized content itself is unaffected (the memory DB writes independently); this is about the chat log's durability.

Fix

  • New extensions/python/monologue_end/_95_save_chat.py — persists the chat when the monologue ends (mirrors the message_loop_end hook, including the BACKGROUND-context skip). Captures the memorize items' creation and the "Waiting for input" progress even when memorization is disabled.
  • _50_memorize_fragments / _51_memorize_solutions — a finally block persists the chat when the background task exits on any path (success, empty/invalid result, provider error), capturing the terminal headings and the finished marker added in fix(webui+memory): close post-turn utility process groups (memory memorization renders as in-flight "Processing...") #1805.

Ordering is safe: _95 runs after _90_waiting_for_input_msg and after _50/_51 item creation; the tasks' own save lands last with the final state. _serialize_log already guards concurrent log mutation with the log lock, and writes are atomic renames, so saving from the background thread is safe.

Stacked on #1805 (shares the _50/_51 except-block region; this branch contains that PR's commit and the diff shrinks automatically once it merges).

Tests

tests/test_post_turn_chat_persistence.py (6 tests): both memorize tasks save the chat on the empty-result and provider-error paths; the new hook saves regular contexts and skips BACKGROUND ones. All 6 fail on pre-fix sources, all pass with the fix. Arc-8's test doubles were updated to no-op the new save call (persistence is covered by its own tests).

Full-suite regression evidence is posted as a comment (same harness as #1805).

The memory plugin's monologue_end extensions (_50_memorize_fragments,
_51_memorize_solutions) log a utility item when the agent loop ends and
fill it in from a background task. By then the turn's process group was
already completed by the final response, so the Web UI opened a new
process group for these items. That group contains no agent steps, so
its title stayed on the "Processing..." placeholder, and no later event
ever marked it complete, so its steps kept the in-flight shiny animation
and the phase never received an END badge: a finished turn looked like
it was still running ("Processing..." + flashing utility items with a
"waiting for input" status bar).

Backend: the extensions now mark the utility item with a finished kvp on
every terminal update path (early returns, post-loop success marker, and
the exception path), and keep these post-turn bookkeeping items off the
status bar with update_progress="none" (creation, terminal updates, and
the error-path warning, which could previously pin the progress line to
the warning heading with progress_active=true until the next turn).

Frontend: drawMessageUtil closes the last process group when a utility
step arrives with kvps.finished (mirroring drawMessageInfo), and
updateProcessGroupHeader plus the full-log classifier fall back to the
last step's title for groups without agent steps instead of leaving the
"Processing..." placeholder.

Regression coverage: tests/test_post_turn_utility_group.py runs the real
memorize() coroutines against stubbed agents (empty result, successful
insert, and provider-error paths all emit exactly one terminal finished
marker; warnings stay off the status bar) and extracts the real group
header functions from messages.js into a Node fake-DOM harness (utility
-only group titles from its last step, completes with END badge, shiny
removed; mixed groups still prefer the last agent step). All 9 tests
fail on the pre-fix sources and pass with the fix.
The regular chat save hook lives at message_loop_end (_90_save_chat),
which runs before monologue_end extensions. Anything logged from
monologue_end — the "Waiting for input" progress reset and the memory
plugin's memorize utility items (created at loop end, filled in by a
background task) — therefore missed the turn's final save. Those entries
reached chat.json only when the next turn happened to trigger a save,
and were lost entirely if the server restarted in between (measured
live: a finished chat's memorize items were absent from chat.json
minutes after completion; the previous turn's only appeared after the
user's next message).

- new extensions/python/monologue_end/_95_save_chat.py persists the chat
  when the monologue ends, capturing memorize item creation and the
  "Waiting for input" progress (skips ephemeral BACKGROUND contexts,
  mirroring the message_loop_end hook);
- the memorize background tasks persist their terminal state (final
  headings + finished marker) in a finally block, covering every exit
  path including provider errors.

Stacked on agent0ai#1805 (shares the _50/_51 except-block region).

Regression coverage: tests/test_post_turn_chat_persistence.py asserts
both memorize tasks save the chat on the empty-result and error paths,
and that the new monologue_end hook saves regular contexts while
skipping BACKGROUND ones. All 6 fail on pre-fix sources; pass with the
fix. Arc-8 test doubles updated to no-op the new save call (persistence
is covered by its own tests).
@Zenetusken

Copy link
Copy Markdown
Author

Full-suite regression evidence (v2.8 image harness, pytest tests/, same 8 pre-broken collection files excluded as on #1805):

Zenetusken added a commit to Zenetusken/agent-zero that referenced this pull request Aug 3, 2026
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