[feat-017] Persist tool calls + results so chat remembers across turns
Tier 1 of the memory hierarchy: in-chat tool result replay. Eliminates the
"finch loses context" failure where every new turn re-read the same
documents because the system prompt at chatTools.ts:730 told the model it
could not retain doc content between turns.
Schema (migration 003_memory_persistence.sql):
- chat_messages.tool_call_id, tool_name (for role='tool' rows)
- chat_messages.assistant_text (raw model output incl. <tool_call> markup)
- chat_messages.assistant_tool_calls jsonb (structured tool_calls per turn)
- All columns nullable; existing rows remain valid.
Persistence + replay flow:
- runLLMStream now takes chatId. Inside the runTools callback it inserts
one role='tool' row per tool result and aggregates the structured
tool_calls onto a turn-level array returned to the route handler.
- Route handlers (chat.ts, projectChat.ts) persist assistant_text=fullText
and assistant_tool_calls on the assistant row at end-of-turn (alongside
the existing events[] which the frontend still uses for display).
- Both routes now load conversation history from chat_messages directly
rather than trusting the frontend payload, so prior tool results +
assistant tool_calls flow into buildMessages on every turn.
- buildMessages emits role='tool' rows with tool_call_id, and
role='assistant' rows with their tool_calls + assistant_text - the
canonical OpenAI {assistant.tool_calls} → {tool.tool_call_id} pairing.
- LlmMessage type extended to support role='tool', tool_call_id, and
tool_calls. Olava adapter passes them through; claude.ts and gemini.ts
drop them (those paths are dead per coerceToOlava).
Removed enrichWithPriorEvents - it was a partial workaround that summarised
prior tool *names* into the last assistant message. Full tool result replay
makes it obsolete.
System prompt updated at chatTools.ts:752 - the "you do NOT retain document
content" warning is replaced with positive guidance pointing the model at
the tool history below.
Frontend (ChatView.tsx) filters role='tool' rows from the rendered chat;
visible UI is unchanged. MikeMessage type updated to admit the new role.
Tier 2 (per-project persistent facts across chats) is deferred to feat-018
per scope decision.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| Repository | nwhitehouse/mike |
|---|---|
| Author | Nick Whitehouse <nick.whitehouse@mccarthyfinch.com> |
| Authored | |
| Parents | 24e7b703 |
| Stats | 10 files changed , +429 , -158 |
| Part of | In-chat memory: persist tool calls + results across turns (feat-017) |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-563ffde8.md
from inside the repo you want the change in.