Skip to content

TurnMemory In Keen

Table of Contents

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

  1. A new user turn starts with retained conversation messages and TurnMemory from earlier assistant turns.
  2. During the active turn, the provider may emit assistant prose, tool calls, and tool results over several loop iterations.
  3. The REPL keeps an ordered stream of assistant, tool, bash, permission, and diff segments.
  4. On completion, Keen walks those segments and records each completed tool at the number of assistant-text bytes emitted before it.
  5. Keen stores the flattened assistant prose separately from the compact TurnMemory.
  6. When formatting that assistant message for a later provider request, Keen injects historical activity annotations at the saved offsets and appends durable outcome memory.
  7. The visible response and session transcript continue to show the original assistant prose and normal tool rendering, not the injected annotations.
  8. 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 memory outcomes
  • 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.