TurnMemory In Keen¶
Table of Contents¶
- The Idea
- Memory Layers
- Lifecycle Of A Turn
- What The Next Turn Gets
- Historical Tool Activity
- Why Keen Does This
- Tradeoffs
- Assistant Turn Reliability
- Bottom Line
The Idea¶
TurnMemory addresses a simple problem: a coding agent needs detailed tool state while it is actively working, but keeping every tool call and result forever makes later turns noisy, expensive, and harder to reason about.
Keen therefore treats the end of a turn as a compression boundary. The raw execution trace is discarded after successful completion and replaced by a small, provider-neutral summary.
TurnMemory is not a transcript, hidden chain of thought, or planner database. It retains only:
- durable outcomes that are likely to matter later, such as changed files and failed bash commands
- compact historical tool activity that preserves where real tool execution occurred between assistant prose segments
It never retains file contents, search results, command output, arbitrary tool input, MCP arguments, or MCP result content.
Memory Layers¶
Keen uses four related forms of state:
| Layer | Lifetime | Contents | Purpose |
|---|---|---|---|
| Current-turn execution | One active assistant turn | Provider-native tool calls and results | High-fidelity tool-loop reasoning |
| Durable outcome memory | Later turns and persisted sessions | Changed files and failed bash commands | Preserve materially useful outcomes |
| Historical tool activity | Later turns and persisted sessions | Tool, bounded target, success/error status, prose offset | Preserve the protocol shape of real execution |
| Pending provider state | Until an interrupted turn resumes or completes | Provider-native in-progress messages | Recover incomplete tool loops without lossy conversion |
The historical activity layer does not retain what a tool returned. A later turn that needs a file, command result, search result, MCP response, or external state must query it again.
Lifecycle Of A Turn¶
- A new user turn starts with retained conversation messages and
TurnMemoryfrom earlier assistant turns. - During the active turn, the provider may emit assistant prose, tool calls, and tool results over several loop iterations.
- The REPL keeps an ordered stream of assistant, tool, bash, permission, and diff segments.
- On completion, Keen walks those segments and records each completed tool at the number of assistant-text bytes emitted before it.
- Keen stores the flattened assistant prose separately from the compact
TurnMemory. - When formatting that assistant message for a later provider request, Keen injects historical activity annotations at the saved offsets and appends durable outcome memory.
- The visible response and session transcript continue to show the original assistant prose and normal tool rendering, not the injected annotations.
- If the turn fails mid-loop, provider-native pending state remains the recovery mechanism.
What The Next Turn Actually Gets¶
On the next user turn, Keen sends:
- prior user messages
- prior assistant prose
- historical activity annotations inserted between the relevant prior assistant prose segments
- durable
Tool memoryoutcomes - pending provider-native state from a prior failed turn, when present
This preserves a compact causal pattern:
assistant intent
→ historical record of an actual tool invocation
→ assistant conclusion
It does not replay full structured tool calls or their outputs. The model can see that an earlier invocation happened, but it cannot rely on the discarded result as current evidence.
Historical Tool Activity¶
A provider-facing assistant message may be formatted like this:
Let me inspect the stream handler.
<historical_tool_activity>
{"tool":"read_file","status":"success","target":"internal/llm/anthropic.go"}
</historical_tool_activity>
The terminal event is handled after content blocks finish.
The annotation is generated by Keen and is not part of Message.Content. The system prompt tells the model never to emit or imitate it; current-turn work still requires a real tool call.
Placement¶
Each activity stores a byte offset into the flattened assistant prose. The offset equals the cumulative byte length of assistant segments preceding the completed tool segment. Formatting uses that offset to restore the activity between prose segments without storing a duplicate copy of the prose in TurnMemory.
Multiple tools may share an offset. Their original execution order is retained. Invalid persisted offsets are ignored rather than causing formatting to fail.
Fields¶
| Field | Meaning |
|---|---|
text_offset |
Byte position in assistant prose where the annotation is injected |
tool |
Keen tool name, or logical MCP tool name |
status |
success when the invocation completed without a tool error; otherwise error |
target |
Optional allowlisted, bounded target such as a path, pattern, command, URL, or subagent name |
server |
MCP server name when the invocation used call_mcp_tool |
Targets are allowlisted by tool type and length-limited. File paths are made relative to the workspace when possible. Web URLs omit credentials, query parameters, and fragments. Raw outputs, complete errors, replacement text, written content, MCP arguments, and arbitrary input maps are not retained.
MCP calls¶
MCP wrapper calls are represented by their logical server and tool rather than only by call_mcp_tool:
<historical_tool_activity>
{"server":"context7","tool":"query-docs","status":"success"}
</historical_tool_activity>
This records that the invocation occurred. It does not retain the MCP arguments, response, preview, or artifact path, and does not establish that the external information is still current or factually correct.
What status means¶
success means only that Keen completed the tool invocation without a reported tool error. It does not guarantee that:
- the tool output was factually correct
- a search found useful results
- an external mutation had the desired broader effect
- the underlying workspace or service remains unchanged
error records only failure, not the full error text.
Why Keen Does This¶
The design balances three pressures:
- the active agent needs high-fidelity tool state while solving the current task
- later turns need continuity and a truthful record that actions actually occurred
- conversation context should not accumulate large, stale, or untrusted tool outputs
Retaining prose while deleting every sign of tool activity can produce a misleading history in which the assistant appears to announce an action and then claim completion without executing anything. Historical annotations repair that protocol shape without turning TurnMemory into a raw execution archive.
The durable outcome layer remains intentionally narrow. Changed files and failed commands carry useful continuity, while read/search/MCP results are expected to be refreshed when needed.
Tradeoffs¶
Benefits¶
- Smaller context than full tool-trace retention
- Better distinction between narrated intent and actual prior execution
- No persistence of large or untrusted tool results
- Provider-neutral formatting
- Fresh reads of mutable workspace and external state
- Legible, bounded cross-turn memory
Costs¶
- Rich investigative details from prior outputs are still lost
- Later turns may repeat reads, searches, commands, and MCP calls
- Synthetic textual annotations are less exact than provider-native tool blocks
- Byte offsets require validation when loading persisted state
- Compact history can reduce prompt/KV-cache continuity compared with retaining a full trace
This design works best when the workspace is the source of truth, read-only tools are cheap to repeat, and lean context is preferred over exhaustive replay. A fuller trace or richer planner state may suit long investigations with expensive, irreproducible external observations.
Assistant Turn Reliability¶
Pending Turn State¶
A single assistant turn can involve many provider tool-loop iterations. If it ends abnormally after tool work has accumulated, converting that partial exchange into generic conversation messages would be lossy and could invite side-effect re-execution.
Each LLM client therefore stores pending state in its provider-native message format. On the next StreamChat call, that state is injected before the new user message so the model can resume from the prior work.
| Event | Meaning | Pending state action |
|---|---|---|
Done |
Normal completion | Cleared or never saved |
Incomplete |
Turn ended abnormally after work occurred | Saved for the next call |
Error |
Turn failed before recoverable provider work accumulated | Not saved |
Pending state is in-memory only, does not survive process crashes, avoids re-executing completed tools, and is cleared after successful recovery. Persisted transcript and TurnMemory may still describe the visible partial turn, but provider-native pending state—not historical annotations—is authoritative for resuming the incomplete tool loop.
Retries within the same active turn rewind trailing unsealed assistant/reasoning segments. Historical activity is collected only from the final surviving segment list, so abandoned retry prose is not retained.
Bottom Line¶
TurnMemory is a compact execution summary rather than a transcript.
Inside a turn, Keen keeps rich provider-native tool state. Across completed turns, it retains durable outcomes plus bounded records of where real tools ran. The records establish prior invocation, not retained evidence or current state. Failed turns use temporary provider-native pending state for recovery.