In-chat memory: persist tool calls and results so the model stops re-reading every document
nwhitehouse fixed the root cause of "the model forgets what it read last turn." Tool calls and results are now persisted to `chat_messages` and replayed to the LLM on every subsequent turn, using the OpenAI canonical `tool_calls`/`tool_call_id` pairing that Olava already speaks.
The original system prompt explicitly admitted the limitation: "You do NOT retain document content between conversation turns. You MUST call read_document at the start of every response." Every turn re-read every document regardless of whether anything had changed. The fix is a schema extension and a replay path, not a prompt workaround.
Migration 003_memory_persistence.sql adds four nullable columns to chat_messages: tool_call_id and tool_name (for role='tool' rows), assistant_text (raw model output including <tool_call> markup, stored separately from the events[] array used by the frontend), and assistant_tool_calls jsonb (the structured tool_calls array aggregated per turn). The migration also allows role='tool' rows alongside the existing user and assistant rows. All changes are additive; existing rows stay valid.
During tool execution, runToolCalls now inserts a role='tool' row for each result. The route handlers aggregate tool_calls from the turn and write assistant_text to the assistant row. On the next turn, buildMessages reads the full history - user rows, assistant rows with their tool_calls, and tool-result rows - and feeds the canonical sequence to the LLM. The system prompt warning is replaced with a positive instruction: use tool results already in the history rather than re-reading documents you've already inspected.
A follow-up commit fixed a display bug introduced in the first pass: mikeApi.getChat() was coercing every non-user row to role='assistant', so persisted tool-result rows were appearing as empty assistant bubbles on chat reload. The fix preserves role='tool' through the API response; ChatView's existing filter handles hiding them.
The prior workaround, enrichWithPriorEvents, which summarized prior tool names into the last assistant message, is deleted. Full replay makes it obsolete.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?