Three live tests now cover the model-as-judge memory loop end-to-end
against the real Anthropic API:
1. memory_update via [id] (already existed) — model extracts ID from
the auto-tail block, calls memory_update with the exact seeded ID.
2. memory_delete via [id] (new) — same handle, destructive verb.
Proves the [id] prefix is verb-agnostic. Prompt explicitly tells
the model to skip memory_search / memory_list so the test targets
the direct-from-tail path.
3. memory_save → dedup-hint mechanism (new) — when the model fires
memory_save on near-duplicate content, the proxy's _execute_save
must return a "Similar memory exists" note carrying the existing
memory's exact ID. Without this hint, parallel duplicates would
silently accumulate, polluting the cache prefix.
The dedup test asserts the MECHANISM (hint contains seeded ID),
not the model's downstream behaviour. The hint text intentionally
ends with "or ignore if these are distinct facts", so the model is
free to decline consolidation. Whether it consolidates depends on a
judgement call about whether two phrasings name the same fact —
intentionally outside this test's contract.
Refactor: extract _seed_memory and _install_tool_call_recorder
module-level helpers so all three live tests read as their intent.
Recorder also captures the tool result now (needed to inspect the
JSON-encoded dedup hint).
Pre-this-PR the auto-injected memory block rendered rows as `1. <content>`
with no addressable handle. To UPDATE or DELETE a row the model first had
to call memory_search to discover its ID — two round trips, against the
model-as-judge architecture.
This PR adds three tightly-coupled affordances so the model can act on
memory directly:
1. Auto-tail rows now carry the memory ID:
`1. [mem_alpha_001] User prefers Python`
The bracketed token is the canonical ID — same identifier accepted by
memory_update and memory_delete.
2. New `memory_list` tool — chronological browse (vs `memory_search`'s
semantic lookup). Returns recent memories with their IDs. Backend
dispatches to `Backend.list_memories` if available, else falls back
to an empty-query `search_memories`. Caps at 100 entries.
3. ID-usage guidance text appended to the auto-tail block. Tells the
model that bracketed IDs can go straight to memory_update /
memory_delete with no intervening search. The guidance lives in the
user-message tail (never system) — preserves cache-prefix byte
stability (invariant I2).
`memory_update` and `memory_delete` tool descriptions also point at the
[id] block as a valid ID source — keeps tool docs consistent with the
new affordance.
Verification:
- 10/10 tests pass in tests/test_memory_auto_tail.py (incl. 2 new
guidance tests + 2 new ID-format tests)
- 31/31 tests pass in tests/test_memory_handler_native_ops.py (incl. 4
new memory_list dispatch tests + existing assertions updated for the
[id] format change)
- Golden fixtures regenerated for the tool-description copy changes
(tests/fixtures/memory_tool_definitions/{anthropic,openai}.json)
- Live end-to-end test against real Anthropic API
(tests/test_proxy_memory_integration.py::TestMemoryIdAutoTailAndUpdate):
seeded memory → auto-tail → Claude → memory_update with exact ID.
PASSED.
- Fix Anthropic cache token cost formula: input_tokens, cache_read, and
cache_write are all separate (not overlapping), so don't subtract
cache_write from input_tokens
- Default memory user ID to "default" when x-headroom-user-id header is
not provided, removing the need for client configuration
- Update CLI help text and tests to reflect new default behavior
Implement comprehensive memory system supporting:
- Local backend (SQLite + FTS5 + HNSW) for zero-dependency operation
- Mem0 backends (Neo4j + Qdrant) for production graph memory
- DirectMem0Adapter for optimized pre-extracted data (bypasses LLM)
- Memory extraction with facts, entities, and relationships
- Proxy integration with --memory flag for automatic memory injection
Key components:
- headroom/memory/backends/: LocalBackend, Mem0Backend, DirectMem0Adapter
- headroom/memory/system.py: MemorySystem with tool-based interface
- headroom/memory/extraction.py: Entity and relationship extraction
- headroom/proxy/memory_handler.py: Proxy integration layer
- headroom/prediction/feature_extractor.py: Content analysis features
Testing:
- 217 new memory system tests covering all backends
- LoCoMo evaluation framework for memory quality assessment
- Integration tests for proxy memory functionality
Also removes deprecated example files in favor of focused test coverage.