## Description
`headroom wrap --memory` could never import memories: the startup sync
subprocess (`python -m
headroom.memory.sync`) and the in-process Codex memory import both built
their backend with
`LocalBackendConfig(db_path=...)`, which defaults `embedder_backend` to
`"local"` —
sentence-transformers + PyTorch (~2 GB). On the proxy extras that
dependency is absent, so sync
crashed with `ImportError: sentence-transformers is required for
LocalEmbedder` while the proxy
itself served memory fine via the torch-free ONNX backend. This routes
both paths through a
shared `_build_sync_backend` helper that uses `embedder_backend="onnx"`,
matching the proxy MCP
server (`headroom/memory/mcp_server.py`).
Closes#1092
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Changes Made
- `headroom/memory/sync.py`: added `_build_sync_backend(db_path)` that
constructs the backend
with `embedder_backend="onnx"`; the sync CLI subprocess now uses it.
- `headroom/cli/wrap.py`: the in-process Claude→DB memory import (Codex
wrap path) now uses the
same helper instead of the LOCAL-defaulting `LocalBackendConfig`.
- `tests/test_memory_sync.py`: added
`test_sync_backend_uses_onnx_embedder` regression test.
## Testing
- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] New tests added for new functionality
### Test Output
```text
$ python -m pytest tests/test_memory_sync.py -q
31 passed
$ python -m ruff check headroom/memory/sync.py headroom/cli/wrap.py tests/test_memory_sync.py
All checks passed!
```
## Real Behavior Proof
- Environment: Windows 11, Python 3.13, headroom on branch
fix/1092-memory-sync-onnx-embedder
- Exact command / steps: Ran the memory-sync suite + ruff, and an import
smoke that builds the
sync backend: `python -c "from headroom.memory.sync import
_build_sync_backend;
print(_build_sync_backend('x.db')._config.embedder_backend)"`.
- Observed result: 31 tests pass (incl. the new regression test), ruff
clean, and the smoke
prints `onnx` — the sync backend no longer defaults to the
sentence-transformers embedder.
- Not tested: Did not run a full live `headroom wrap claude --memory`
end to end (needs the
ONNX model download + Claude memory files); the same-model
(all-MiniLM-L6-v2, 384-dim) ONNX
backend the proxy already uses keeps vectors DB-compatible, so no
migration is involved.
## Review Readiness
- [x] I have performed a self-review
- [x] This PR is ready for human review
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- test_memory_sync.py: remove unused imports (asyncio, MagicMock,
AgentMemory, AgentMemoryAdapter, SyncResult), fix import sorting
- test_ws_memory_relay.py: remove unused pytest import and unused
output_index variable, fix import sorting
- test_wrap_copilot.py: provide dummy API keys in test env — the
BYOK validation added in 7a7b8b6 requires ANTHROPIC_API_KEY or
OPENAI_API_KEY to be set
- test_package_init_lazy.py: stop hardcoding version string that
breaks on every bump; assert it's a non-empty string instead
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Memory saved in one agent (Codex, Claude Code, Aider) is now accessible
from any other agent through a shared headroom DB. Three integration layers:
MCP Server (headroom.memory.mcp_server):
- stdio MCP server that Codex/Claude discover natively via config.toml
- memory_search with supersession filtering (only active memories returned)
- memory_save accepts atomic facts array — each fact stored/indexed individually
- Auto-supersession: new facts that match existing ones (≥0.70 similarity)
retire the old entry via the supersedes/superseded_by lineage chain
- ONNX embedder pre-loaded at startup (no cold-start on first query)
- HuggingFace offline mode eliminates network latency on startup
Sync Engine (headroom.memory.sync):
- Bidirectional sync: DB ↔ agent-native memory files
- Pluggable adapters: ClaudeCodeAdapter (frontmatter .md files + MEMORY.md index),
CodexAdapter (AGENTS.md sections)
- Fast no-op: fingerprint comparison skips sync when nothing changed (<5ms)
- Content-hash dedup prevents duplicate memories across agents
- Lineage metadata: source_agent, source_file, content_hash, synced_at
- Anti-echo: memories imported from an agent are not re-exported to that agent
- CLI entry point: python -m headroom.memory.sync --agent claude|codex
Wrap CLI integration:
- `wrap codex --memory`: registers MCP server + AGENTS.md guidance + syncs
Claude memories into DB for MCP search
- `wrap claude --memory`: bidirectional sync at startup (DB ↔ Claude files)
- MCP config re-injected after provider config to survive file rewrite
- Cross-platform: Windows path handling in TOML configs and path sanitization
Proxy improvements:
- Responses API: tool format conversion (Chat Completions → Responses API)
- Responses API: memory tool calls handled with proper continuation
- WebSocket: buffer-then-decide relay suppresses memory tool events from
Codex, executes them transparently, relays only the final answer
- memory_handler: supports Responses API function_call format (call_id,
top-level arguments, output[] extraction)
- HNSW vector index now persists to disk via auto_save + save_path
Tests: 37 new tests covering WS relay event suppression, sync import/export,
bidirectional sync, idempotency, fast no-op, lineage, cross-agent interop