Persistent memory in Sorcar: the wiring, a refresh tool, and a day of real tasks

2026-09-13 · builder model: claude-fable-5 · independent reviewer: gpt-5.6-sol (read-only)

This report covers three things: (1) how the memoryfield prototype (a directory of Markdown pages with a SQLite vector index) is now wired into every Sorcar agent run behind a config flag, (2) the new memory_refresh maintenance tool, and (3) what the agent actually wrote to memory when twelve real tasks from the task history were replayed back-to-back as independent sessions sharing one memory.

1. The wiring

~/.kiss/config.json use_memory: true memory_dir: "" (→ ~/.kiss/memories) env KISS_USE_MEMORY (override) SorcarAgent.run() _memory_root_for_run gates: × append_basic_tools=False × docker_image set × cc/* and codex/* models system prompt SYSTEM.md (or LITE) + MEMORY_PROTOCOL tool list (_get_tools) Bash, Read, Edit, Write, browser, … + memory_search / pull / read / write + memory_list / delete / refresh shared store: <memory_dir>/*.md pages + text-embedding-3-small.sqlite3 index
One decision point: _memory_root_for_run() in SorcarAgent.run(). Sub-agents spawned by run_parallel go through the same path, so a whole task tree shares one store consistently. Tools and protocol are always enabled together — never one without the other.

2. New: memory_refresh

Requested mid-task: a way to keep the store fresh. memory_refresh(stale_days=30, duplicate_threshold=0.9) does three things in one call:

  1. Re-index: forces an incremental sync, so pages edited outside the agent (a human in an editor, git pull) are re-embedded and deleted pages drop out of the index.
  2. Near-duplicate report: VectorIndex.near_duplicates() scans all stored embedding pairs and lists pages at or above the similarity threshold, so the agent can merge them (memory_write the merged page, memory_delete the loser).
  3. Stale report: pages whose updated frontmatter is older than stale_days are listed for re-verification or deletion. Pages with missing or unparseable timestamps are deliberately skipped — staleness cannot be established for them.

Run against the store the agent built during the day's replay:

Index refreshed: 1 added, 0 updated, 0 removed, 5 unchanged.
Near-duplicate pages (merge with memory_write, then memory_delete the loser):
  nonstick-brand-comparison-2026 ~ sensarte-cookware-brand-facts  (similarity 0.747)
  sorcar-agent-failed-abruptly-sentinel ~ sorcar-task-classifier  (similarity 0.630)

(That output used a demonstration threshold of 0.55; at the default 0.9 neither pair is flagged, which is correct — these pages are related, not redundant.)

3. A day of real tasks

Twelve real past tasks from ~/.kiss/sorcar.db were replayed sequentially, each as a fresh SorcarAgent session on claude-fable-5 (no shared conversation), all sharing tmp/memory-day/memories. Every tool call was logged. Total replay cost: $14.74.

#Task (real, from history)TimeCostMemory activity
0hi12s$0.11searched, wrote nothing (right call)
1what tools are available to you?17s$0.14none (listed memory tools in its answer)
2is the KISSAgent agentic?39s$0.51searched; wrote kissagent-agentic-mode
3…the KISSAgent that checks if a task is simple/development62s$0.61recalled task 2's page; wrote sorcar-task-classifier
4is SENSARTE an American brand? …safe?247s$3.75searched; wrote sensarte-cookware-brand-facts
5Compare SENSARTE with Carote, GreenPan, Caraway…290s$5.74recalled task 4's page (skipped re-research); wrote comparison page that cross-references it
6BMW 330i drive cycle40s$0.22searched, wrote nothing
7how does automerge work in non-worktree mode?94s$0.70searched; wrote sorcar-worktree-automerge-vs-nonwt-guard
8change the "Another tab is running…" message61s$0.48none (small edit, nothing durable)
9add a classify-tasks parameter to server run()113s$0.96recalled task 3's classifier page, then verified the parameter already exists
10uv run check --full and fix132s$0.21none (checks passed, nothing to record)
11why is the last task shown as failed?152s$1.32searched; wrote sorcar-agent-failed-abruptly-sentinel

What the day shows

4. Independent review (gpt-5.6-sol, read-only, ~$7.2)

SeverityFindingStatus
HighCLI run-to-completion models (cc/*, codex/*) would get MEMORY_PROTOCOL but can never call KISS-registered toolsFixed — gated in _memory_root_for_run
HighDocker runs would expose host-path memory tools inside the container boundaryFixed — Docker runs get no memory
HighPre-existing: a caller-supplied model_config["system_instruction"] silently replaces the whole composed system prompt (not just the memory block) via setdefaultReported; pre-existing, affects all prompts, out of this change's scope
MediumSemantic search always uses text-embedding-3-small, so recall silently needs an OpenAI key; the offline hashing embedder is never auto-selectedKnown prototype limit (search degrades to a tool error; writes still work)
MediumEmbedding API cost is not folded into the task's budget accountingKnown limit (~$0.01 per 300 pages)
MediumConcurrent sub-agents share the page directory without cross-process file locking (lost-update window on simultaneous writes to the same page)Known limit; SQLite index itself uses per-op connections with 30s busy timeout
MediumNo settings-panel UI for the two new keys (config file / env only)Deliberate for a flag that defaults off
Lowmemory_* names not in the MCP tool-name reservation set; unvalidated memory_dir paths surface as repeated tool errorsReported

The reviewer confirmed the paths that matter are correct: ChatSorcarAgent, WorktreeSorcarAgent, server/daemon runs, run_agent, and run_parallel children all converge on the same flag logic; the SYSTEM_LITE (simple-task) path still gets the protocol; entry and finally cleanup prevent reuse leaks; and the new DEFAULTS keys round-trip correctly through sanitize/load/save and old clients.

5. Verification

6. How to enable

# persistent (all future runs)
python -c "from kiss.core.vscode_config import load_config, save_config; \
c = load_config(); c['use_memory'] = True; save_config(c)"

# or one process only
KISS_USE_MEMORY=1 <run sorcar>

Pages land in ~/.kiss/memories/*.md — plain Markdown you can read, edit, or delete in any editor; the index rebuilds itself on the next search.

Recommendation: the wiring is safe to enable for single-user, non-Docker use on an installation with an OpenAI key (for embeddings). Before flipping the default on: auto-select the offline embedder when no OpenAI key exists, fold embedding cost into task accounting, and add cross-process page locking for heavy run_parallel use.