Skip to content

Memory

Most AI chat tools work in sessions. You open a new chat, ask a question, get an answer, and the context is gone. Start a new chat tomorrow and the agent has no idea what you talked about. If the conversation gets too long, it gets truncated or “compacted” into a summary. You manage the lifecycle manually: which chat am I in, what context is still alive, what did I lose when it got too long.

q15 takes a different position. There is one conversation. It never resets. There are no sessions to start, no context windows to manage, no “new chat” button. You send a message on Monday, another on Thursday, another next month — and the agent picks up where you left off because its memory persists across the gap.

The question is: how do you keep one infinite conversation inside a model with a finite context window?

Every LLM has a fixed context window. When you send a message to the model, the harness assembles a prompt: system instructions, identity, recent conversation history, and your new message. The model processes all of it at once.

If your conversation is 500 turns long, you cannot send all 500 turns every time. The context window fills up. Something has to be dropped.

The standard approach is compaction. When the transcript exceeds a threshold, the harness asks the model to summarize the older portion into a compressed paragraph, then keeps only recent turns verbatim. The summary replaces the original messages.

flowchart TD
    subgraph compaction["Compaction cycle"]
        T1["Turns 1–200<br/>(full transcript)"] --> S["Model summarizes<br/>turns 1–180"]
        S --> Summary["Compressed summary<br/>(~500 tokens of prose)"]
        Summary --> M1["Next prompt:<br/>summary + turns 181–200"]
        T2["Turns 201–400<br/>(grows again)"] --> S2["Summarize again:<br/>summary + turns 181–380"]
        S2 --> Summary2["New compressed summary"]
        Summary2 --> M2["Next prompt:<br/>new summary + turns 381–400"]
    end
    style S fill:#fab387,stroke:#d97706,color:#1e1e2e
    style S2 fill:#fab387,stroke:#d97706,color:#1e1e2e
    style Summary fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style Summary2 fill:#313244,stroke:#89b4fa,color:#cdd6f4

This works, but it has problems:

  • Lossy and opaque. The summary is a paragraph of prose. You cannot query what was lost. If the model omitted a detail during summarization, it is gone — not from disk, but from the prompt. The agent no longer knows it.
  • Compounding drift. Each compaction cycle summarizes the previous summary, not the original turns. After three cycles, the summary is a summary of a summary of a summary. Detail erodes. Errors compound.
  • Unstructured. The summary is free-form text. There is no schema, no separation between active state and durable facts, no way to distinguish “what we’re working on right now” from “the user’s name is Adriaan.”
  • Blocking. Compaction happens during the user’s turn. The model is asked to summarize before it can answer the question. This adds latency and can fail mid-conversation.

q15 does not compress history. Instead, it maintains structured memory artifacts through background model calls called cognition jobs. These jobs run independently of the user’s conversation — on schedules and state-change triggers — and keep the memory layers current.

The full transcript is always persisted as JSON files under /memory/history/. Nothing is deleted. But only the recent unconsolidated turns are replayed into the prompt. Everything before the consolidation checkpoint is represented by the working memory artifact, which was built from those turns by a background job.

flowchart TD
    subgraph session["One continuous conversation (no sessions)"]
        U1["User message<br/>Monday"] --> R1["Agent reply"]
        R1 --> P1["Persist turn to<br/>/memory/history/"]
        P1 --> D1["Dirty state<br/>+1 turn"]
        D1 -->|"6+ dirty turns"| C1["Working memory<br/>consolidation job"]
        C1 --> CP1["Advance consolidation<br/>checkpoint to turn N"]
        CP1 --> WR["Working memory artifact<br/>updated with active state"]
        WR -->|"Next reply"| N1["Replay only turns<br/>after checkpoint"]
        U2["User message<br/>Thursday"] --> R2["Agent reply"]
        R2 --> N1
        N1 --> D2["Dirty state<br/>+1 turn"]
        D2 -->|"12+ dirty turns"| C2["Semantic memory<br/>extraction job"]
        C2 --> SM["facts.md, preferences.md,<br/>projects.md updated"]
        SM -->|"Available via tools"| N2["Agent can fetch<br/>on demand"]
    end
    style C1 fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style C2 fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style CP1 fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style WR fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style SM fill:#313244,stroke:#cba6f7,color:#cdd6f4

The key difference: compaction replaces history with a summary. Cognition jobs extract structured state and advance a checkpoint. The history is still on disk. The prompt carries the structured artifact plus only the recent tail that has not been consolidated yet.

The cognition system has three components: a controller that decides when to run jobs, a runner that executes them on the shared model/tool engine, and typed job definitions that describe what each job reads, writes, and how it validates its own output.

flowchart TD
    subgraph interactive["Interactive loop (user-facing)"]
        UM["User message"] --> Agent["Agent assembles prompt<br/>core + working memory + recent turns"]
        Agent --> Model["Model call + tools"]
        Model --> Reply["Reply to user"]
        Model --> Persist["Persist turn to<br/>/memory/history/"]
        Persist --> Notify["Notify controller"]
    end
    subgraph cognition["Cognition system (background)"]
        Controller["Controller<br/>trigger evaluation"] --> Runner["Runner<br/>shared engine"]
        Runner --> Job["Job definition<br/>build → run → apply"]
        Job --> Artifacts["Memory artifacts<br/>+ checkpoints"]
    end
    Notify --> Controller
    Artifacts -.->|"next prompt"| Agent
    style Controller fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Runner fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style Job fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style Artifacts fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style Notify fill:#f38ba8,stroke:#dc2626,color:#1e1e2e

The controller and the interactive loop share the same model client, tool registry, and model-selection planner. Cognition jobs can use a different model than the interactive loop — each job type can be configured with its own model override, or it inherits the current interactive model. This lets you assign a stronger reasoning model to verification, a large-context model to semantic extraction, and a lighter model to working memory consolidation.

Every cognition job follows the same five-phase lifecycle, enforced by the runner:

flowchart TD
    Trigger["1. Trigger fires<br/>startup, schedule, or state"] --> Build["2. Build phase<br/>job loads context via ContextLoader"]
    Build --> Run["3. Run phase<br/>model call on shared engine<br/>with scoped tools"]
    Run --> Apply["4. Apply phase<br/>job validates output<br/>and persists artifacts"]
    Apply --> Record["5. Record phase<br/>run record appended<br/>checkpoints advanced"]
    style Trigger fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Build fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style Run fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style Apply fill:#f9e2af,stroke:#df8e1d,color:#1e1e2e
    style Record fill:#313244,stroke:#89b4fa,color:#cdd6f4
  1. Trigger — The controller evaluates startup rules, cron schedules, and state-change rules to decide whether a job should run.
  2. Build — The job’s Build method loads its required context through the ContextLoader interface: memory files, transcript slices, checkpoints, and prior cognition artifacts. It assembles a Spec containing the objective, completion contract, prompt sections, allowed tools, and tool-call policy.
  3. Run — The runner sends the assembled prompt to the model on the shared engine. The model can use tools (file reads, web fetches) within the job’s scoped allowlist. The runner captures the final text, message history, model ref, and turn count.
  4. Apply — The job’s ApplyResult method validates the output. Each job enforces a completion contract: working memory must touch its target file with a file tool; semantic extraction must inspect all three canonical files; verification must produce non-empty artifact content. If validation fails, the run is recorded as a failure and the dirty state is preserved for retry.
  5. Record — The controller appends a RunRecord to /memory/cognition/runs/ with full provenance: job type, trigger cause, start/finish time, input/output sequence, model used, success/failure, summary, and any attempt failures. Checkpoints are advanced only on success.

Each cognition job has three trigger types:

Trigger When it fires Example
Startup Once, when the controller starts Consolidate any unconsolidated tail from before the last restart
Schedule On a UTC cron spec 0 4 * * * (daily 04:00) for working memory
State When the dirty-turn count exceeds a threshold 6+ unconsolidated turns for working memory

State triggers fire opportunistically. Every time a turn is persisted, the controller is notified. It checks whether any job’s dirty-turn threshold has been met. If yes, the job runs immediately — no need to wait for the next cron tick.

sequenceDiagram
    participant User
    participant Agent as q15-agent
    participant Store as Memory store
    participant Ctrl as Cognition controller
    participant Runner as Cognition runner

    User->>Agent: Message
    Agent->>Agent: Assemble prompt<br/>(core + working + recent turns)
    Agent->>Agent: Call model, run tools
    Agent->>Store: AppendTurn()
    Store->>Ctrl: NotifyStateChange()
    Ctrl->>Ctrl: Check dirty-turn thresholds
    Note over Ctrl: 6+ dirty turns? Run working memory job
    Ctrl->>Runner: Run consolidation job
    Runner->>Runner: Load recent transcript<br/>+ working memory + verification
    Runner->>Runner: Model call: consolidate state
    Runner->>Store: Update WORKING_MEMORY.md
    Ctrl->>Store: Advance consolidation checkpoint
    Note over Ctrl: Next reply replays only<br/>turns after checkpoint

The controller runs serially — one job at a time — and never blocks the interactive loop. If a job is running when the user sends a new message, the reply uses the current (possibly slightly stale) memory artifacts. The next consolidation cycle will pick up the new turns.

Job type: verification_review

Triggers:

Rule Spec Threshold
Schedule 0 3,15 * * * (twice daily)
State dirty-tail threshold 8+ turns

What it reads:

Input Source How loaded
Core memory /memory/core/AGENT.md, USER.md, SOUL.md LoadCoreMemory
Working memory /memory/working/WORKING_MEMORY.md LoadWorkingMemory
Transcript head state /memory/history/state/head.json LoadHead
Consolidation checkpoint /memory/history/state/consolidation_checkpoint.json LoadConsolidationCheckpoint
Prior verification review /memory/cognition/state/verification_review.md LoadCognitionArtifact
Recent transcript (24 turns) /memory/history/turns/ LoadRecentMessages

What it produces:

Output How persisted
Verification review artifact (markdown) StoreCognitionArtifact/memory/cognition/state/verification_review.md

Allowed tools: read_file, web_fetch, web_search — read-only. No mutating tools. The verification job never edits files directly. It can inspect files and fetch external evidence, but its output is the review artifact itself, produced as the model’s final response text.

Completion contract: The final response must be non-empty markdown. The framework persists it to the artifact path. The model is instructed to use the section order: Review Target, Assessment Summary, Issues Identified, Recommendations, Unresolved Items. If the response is empty, the run fails.

How its output is consumed: The verification review artifact is not read by the interactive agent. It is consumed exclusively by the other two cognition jobs:

  • The working memory consolidation job loads it as verification_review_input and uses it as correction input: if the review flags a working memory entry as stale or unsupported, the consolidation job removes or rewrites it.
  • The semantic memory extraction job loads it the same way: if the review flags a semantic entry as contradicted, the extraction job removes, rewrites, or downgrades it with explicit uncertainty.
flowchart TD
    subgraph reads["Verification review reads"]
        VR_WM["Working memory"] --> VR
        VR_CM["Core memory"] --> VR
        VR_HS["Head state"] --> VR
        VR_CC["Consolidation checkpoint"] --> VR
        VR_PV["Prior verification review"] --> VR
        VR_RT["Recent transcript<br/>(24 turns)"] --> VR
    end
    VR["Verification review job"] --> Artifact["verification_review.md"]
    Artifact -->|"correction input"| WM_C["Working memory consolidation"]
    Artifact -->|"correction input"| SM_C["Semantic memory extraction"]
    style VR fill:#f9e2af,stroke:#df8e1d,color:#1e1e2e
    style Artifact fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style WM_C fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style SM_C fill:#cba6f7,stroke:#8839ef,color:#1e1e2e

Job type: working_memory.consolidate

Triggers:

Rule Spec Threshold
Startup unconsolidated tail any
Schedule 0 4 * * * (daily 04:00)
State dirty-tail threshold 6+ turns

What it reads:

Input Source How loaded
Working memory /memory/working/WORKING_MEMORY.md LoadWorkingMemory
Verification review /memory/cognition/state/verification_review.md LoadCognitionArtifact
Recent transcript (16 turns) /memory/history/turns/ LoadRecentMessages

What it produces:

Output How persisted
Updated working memory artifact (markdown) File tools → /memory/working/WORKING_MEMORY.md
Advanced consolidation checkpoint StoreConsolidationCheckpoint/memory/history/state/consolidation_checkpoint.json

Allowed tools: read_file, write_file, edit_file, apply_patch — scoped to /memory/working/WORKING_MEMORY.md only. The job must touch its target file with a file tool before completing. A response without a target-file tool call is invalid and the run fails.

Completion contract: The model must call read_file, write_file, edit_file, or apply_patch on the target file. If the file needs changes, it updates it while preserving the canonical section structure: Current Priorities, Active Tasks, Open Threads, Recent Progress, Pending Checks, Temporary Context. If no change is needed, it calls read_file and leaves the file unchanged.

How its output is consumed: The working memory artifact is auto-injected into every prompt as a system prompt section. This is the primary mechanism for sessionless continuity. When the consolidation job succeeds and advances the checkpoint, the next reply replays only turns after the checkpoint boundary — the working memory artifact carries the state of everything before it.

flowchart TD
    subgraph reads["Working memory consolidation reads"]
        WM_WM["Current working memory"] --> WM
        WM_VR["Verification review"] --> WM
        WM_RT["Recent transcript<br/>(16 turns)"] --> WM
    end
    WM["Working memory consolidation job"] -->|"file tools"| WMF["WORKING_MEMORY.md<br/>updated"]
    WM -->|"on success"| CP["Consolidation checkpoint<br/>advanced"]
    WMF -->|"auto-injected"| Prompt["Next prompt<br/>system section"]
    CP -->|"replay boundary"| Replay["Recent turns<br/>after checkpoint only"]
    style WM fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style WMF fill:#313244,stroke:#a6e3a1,color:#cdd6f4
    style CP fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Prompt fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style Replay fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e

Job type: semantic_memory.extract

Triggers:

Rule Spec Threshold
Schedule 0 5 * * * (daily 05:00)
State dirty-tail threshold 12+ turns

What it reads:

Input Source How loaded
Semantic memory (3 files) /memory/semantic/facts.md, preferences.md, projects.md LoadSemanticMemory
Working memory /memory/working/WORKING_MEMORY.md LoadWorkingMemory
Verification review /memory/cognition/state/verification_review.md LoadCognitionArtifact
Semantic extraction checkpoint /memory/cognition/state/semantic_extraction_checkpoint.json LoadSemanticExtractionCheckpoint
Transcript since last checkpoint /memory/history/turns/ LoadMessagesSinceSeq (or LoadLatestMessages fallback)

What it produces:

Output How persisted
Updated facts.md File tools → /memory/semantic/facts.md
Updated preferences.md File tools → /memory/semantic/preferences.md
Updated projects.md File tools → /memory/semantic/projects.md
Advanced semantic extraction checkpoint StoreSemanticExtractionCheckpoint/memory/cognition/state/semantic_extraction_checkpoint.json

Allowed tools: read_file, write_file, edit_file, apply_patch — scoped to the three semantic memory paths only. The job must inspect all three files with file tools. Missing any target file invalidates the run.

Completion contract: The model must call a file tool on each of the three canonical files. If a file needs changes, it updates only that file while preserving the fixed section structure. If a file does not need changes, it calls read_file on it and leaves it unchanged. The job also runs a cross-file de-duplication pass: fact side clauses are removed from preferences.md and preference phrasing is removed from facts.md.

Canonical file structure:

File Required H2 headings Content scope
facts.md Confirmed Facts, Grounded Inferences Objective durable facts: biography, capabilities, tool/runtime facts
preferences.md User Preferences, Collaboration Preferences Durable subjective preferences: likes, dislikes, styles, choices
projects.md Active Projects, Durable Project Knowledge Stable project knowledge beyond the working-memory window

How its output is consumed: Semantic memory is not auto-injected. The interactive agent fetches it on demand using the read_file tool when it determines that durable context is relevant. This keeps the prompt bounded while still making long-term knowledge available. The system prompt tells the agent where these files live and what headings they use, so it knows when and how to look them up.

flowchart TD
    subgraph reads["Semantic memory extraction reads"]
        SM_SF["Semantic files<br/>(facts, preferences, projects)"] --> SM
        SM_WM["Working memory"] --> SM
        SM_VR["Verification review"] --> SM
        SM_CP["Semantic extraction checkpoint"] --> SM
        SM_RT["Transcript since<br/>last checkpoint"] --> SM
    end
    SM["Semantic memory extraction job"] -->|"file tools"| F1["facts.md"]
    SM -->|"file tools"| F2["preferences.md"]
    SM -->|"file tools"| F3["projects.md"]
    SM -->|"on success"| SCP["Semantic extraction<br/>checkpoint advanced"]
    F1 -.->|"tool-fetched on demand"| Agent1["Interactive agent<br/>read_file"]
    F2 -.->|"tool-fetched on demand"| Agent2["Interactive agent<br/>read_file"]
    F3 -.->|"tool-fetched on demand"| Agent3["Interactive agent<br/>read_file"]
    style SM fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style F1 fill:#313244,stroke:#cba6f7,color:#cdd6f4
    style F2 fill:#313244,stroke:#cba6f7,color:#cdd6f4
    style F3 fill:#313244,stroke:#cba6f7,color:#cdd6f4
    style SCP fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Agent1 fill:#f38ba8,stroke:#dc2626,color:#1e1e2e
    style Agent2 fill:#f38ba8,stroke:#dc2626,color:#1e1e2e
    style Agent3 fill:#f38ba8,stroke:#dc2626,color:#1e1e2e

The three jobs produce artifacts that reach the interactive agent through four distinct consumption paths. Understanding which path each artifact uses is key to understanding how the sessionless workflow stays bounded.

flowchart TD
    subgraph jobs["Cognition jobs produce artifacts"]
        VR["Verification review"] --> VRA["verification_review.md"]
        WM["Working memory consolidation"] --> WMA["WORKING_MEMORY.md"]
        SM["Semantic memory extraction"] --> SMA["facts.md<br/>preferences.md<br/>projects.md"]
    end
    subgraph consumption["Consumption paths"]
        VRA -->|"cognition-internal<br/>never reaches prompt"| VR_C["Read by next<br/>consolidation + extraction"]
        WMA -->|"auto-injected<br/>every turn"| Prompt["System prompt section"]
        SMA -->|"tool-fetched<br/>on demand"| Tools["read_file call<br/>by interactive agent"]
        History["Full transcript<br/>on disk"] -->|"checkpoint replay<br/>bounded window"| Prompt
    end
    subgraph vector["Vector search path"]
        SMA -->|"optional sync"| Qdrant["Qdrant collection<br/>'semantic'"]
        Core["Core memory files"] -->|"optional sync"| Qdrant2["Qdrant collection<br/>'core'"]
        Library["Library content"] -->|"optional sync"| Qdrant3["Qdrant collection<br/>'library'"]
        Zettel["Zettelkasten notes"] -->|"optional sync"| Qdrant4["Qdrant collection<br/>'zettelkasten'"]
        Qdrant -->|"embed_search<br/>hybrid query"| Agent["Interactive agent"]
        Qdrant2 --> Agent
        Qdrant3 --> Agent
        Qdrant4 --> Agent
    end
    style VR fill:#f9e2af,stroke:#df8e1d,color:#1e1e2e
    style WM fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style SM fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style Prompt fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style Qdrant fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Agent fill:#f38ba8,stroke:#dc2626,color:#1e1e2e

Core memory files and the working memory artifact are injected into the system prompt on every turn. They are always present. The agent does not need to call any tool to access them.

Artifact Path Injected as
Core memory files /memory/core/*.md System prompt section
Working memory artifact /memory/working/WORKING_MEMORY.md System prompt section

Semantic memory files and zettelkasten notes are not injected. The system prompt tells the agent where they live and what structure they use, but the agent must call read_file to access them. This keeps the prompt bounded — only the artifacts that are likely relevant to the current turn are loaded.

Artifact Path Accessed via
Semantic memory files /memory/semantic/*.md read_file tool call
Zettelkasten notes /memory/notes/zettel/*.md read_file tool call
Zettelkasten inbox /memory/notes/inbox/*.md read_file tool call
Structure maps /memory/notes/maps/*.md read_file tool call

Path 3: Checkpoint-aware replay (bounded window)

Section titled “Path 3: Checkpoint-aware replay (bounded window)”

The full transcript is persisted on disk under /memory/history/turns/. It is never deleted. But only the recent unconsolidated tail is replayed into the prompt. The consolidation checkpoint marks the boundary: everything before it is represented by the working memory artifact; everything after it is replayed verbatim.

flowchart TD
    subgraph disk["Full transcript on disk (never deleted)"]
        Old["Turns 1 to 47<br/>(before checkpoint)"]
        New["Turns 48 to 50<br/>(after checkpoint)"]
    end
    CP["Consolidation checkpoint<br/>at turn 47"]
    CP -.->|"marks the boundary"| New
    Old -.->|"represented by"| WM["WORKING_MEMORY.md"]
    New -->|"replayed into"| Prompt["Goes into prompt"]
    WM -->|"injected into"| Prompt
    style CP fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Old fill:#313244,stroke:#585b70,color:#cdd6f4
    style New fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style WM fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style Prompt fill:#313244,stroke:#89b4fa,color:#cdd6f4

The transcript sequence metadata lives at /memory/history/state/head.json. The consolidation checkpoint lives at /memory/history/state/consolidation_checkpoint.json. On startup, existing history is eagerly upgraded in place to the current schema. Unreadable turn files are moved to quarantine, not dropped.

When embeddings are configured, memory content can be indexed into Qdrant and searched semantically. This is a fourth consumption path that supplements the others — the agent can call embed_search to find relevant content across collections without knowing which file to read.

Collection Source content Search tool
semantic Semantic memory files (facts.md, preferences.md, projects.md) embed_search
core Core memory files (AGENT.md, USER.md, SOUL.md) embed_search
library Books, articles, and documents from /workspace/library/ embed_search
zettelkasten Atomic knowledge notes from /memory/notes/zettel/ embed_search

Search runs in hybrid mode by default: dense Gemini vectors for semantic recall combined with Qdrant BM25 sparse vectors for lexical precision. The agent calls embed_search with a natural-language query and receives ranked results with collection labels, file paths, and relevance scores.

flowchart TD
    subgraph sources["Memory content sources"]
        SM_F["Semantic memory<br/>files"] --> Sync["embed_sync"]
        Core_F["Core memory<br/>files"] --> Sync
        Lib["Library<br/>/workspace/library/"] --> Sync
        Zet["Zettelkasten<br/>/memory/notes/zettel/"] --> Sync
    end
    Sync --> Qdrant["Qdrant<br/>vector database"]
    Qdrant -->|"dense + sparse<br/>hybrid index"| Indexed["Indexed<br/>embeddings + BM25"]
    Indexed -->|"embed_search<br/>query"| Search["Agent calls<br/>embed_search"]
    Search --> Results["Ranked results<br/>with file paths<br/>+ relevance scores"]
    Results --> Agent["Agent reads<br/>relevant files"]
    style Qdrant fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style Sync fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style Search fill:#f38ba8,stroke:#dc2626,color:#1e1e2e
    style Results fill:#313244,stroke:#89b4fa,color:#cdd6f4

Embeddings are optional. When not configured, the agent relies on the other three consumption paths. When configured, the semantic and core collections let the agent search its own memory semantically — finding relevant facts, preferences, or identity context without manually reading every file. The library and zettelkasten collections extend search to the broader knowledge base.

See the embeddings documentation for configuration details.

Each cognition job type can be assigned a specific model, independent of the interactive model. Jobs without an override inherit the current interactive model. This lets you optimize for the specific demands of each job:

Job type Typical demand Suggested model profile
verification_review Strong reasoning, critical analysis Highest-quality reasoning model
semantic_memory.extract Large context (3 files + transcript + verification), complex categorization Large context window, strong instruction following
working_memory.consolidate Bounded context (1 file + 16 turns), simpler single-file update Lighter, faster model — runs most frequently

The runner shares the same engine infrastructure as the interactive loop (model client, tool registry, model-selection planner). Cognition model refs are configured at startup. If no cognition model is specified, the interactive model is used for all jobs.

Every cognition run is recorded as an append-only JSON file under /memory/cognition/runs/YYYY/MM/DD/. Each record contains:

Field Description
type Job type (verification_review, semantic_memory.extract, working_memory.consolidate)
cause Trigger cause (kind, rule ID, fired-at, scheduled-for, reason)
started_at Run start time (UTC)
finished_at Run finish time (UTC)
input_seq Transcript head sequence at run start
output_seq Transcript head sequence at run finish
succeeded Whether the run completed successfully
summary Short model-produced summary of what changed
metadata Job-specific metadata (file paths, changed flags)
model_ref Which model was used
attempt_failures Failed model attempts within the run (model ref + error)
error Error message if the run failed

Job trigger state (last run, dirty tracking, consecutive failures, scheduled-for times) is persisted per job type under /memory/cognition/triggers/jobs/<job_type>.json. This state survives restarts and lets the controller resume where it left off.

The three jobs form a self-correcting loop. The verification job audits state; the consolidation and extraction jobs apply corrections. This cycle runs continuously in the background, keeping memory artifacts accurate without any user intervention.

flowchart TD
    VR["Verification review<br/>audits state"] -->|"flags stale<br/>unsupported<br/>contradicted entries"| WM["Working memory<br/>consolidation"]
    VR -->|"flags stale<br/>unsupported<br/>contradicted entries"| SM["Semantic memory<br/>extraction"]
    WM -->|"removes or rewrites<br/>flagged entries"| WMF["WORKING_MEMORY.md<br/>corrected"]
    SM -->|"removes, rewrites,<br/>or downgrades"| SMF["facts.md<br/>preferences.md<br/>projects.md<br/>corrected"]
    WM -->|"updates active state"| SM
    WMF -->|"next turn"| Prompt["Prompt"]
    SMF -.->|"on demand"| Prompt
    style VR fill:#f9e2af,stroke:#df8e1d,color:#1e1e2e
    style WM fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style SM fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style WMF fill:#313244,stroke:#a6e3a1,color:#cdd6f4
    style SMF fill:#313244,stroke:#cba6f7,color:#cdd6f4
    style Prompt fill:#313244,stroke:#89b4fa,color:#cdd6f4

The correction cycle has a specific ordering:

  1. Verification review runs first (lowest dirty-turn threshold: 8, or twice daily at 03:00/15:00 UTC). It produces a fresh review artifact.
  2. Working memory consolidation runs next (6+ dirty turns, or daily at 04:00 UTC). It reads the verification review as correction input and applies it to the working memory artifact.
  3. Semantic memory extraction runs last (12+ dirty turns, or daily at 05:00 UTC). It reads both the verification review and the freshly consolidated working memory, then applies corrections to the semantic files.

This ordering is not hardcoded — it emerges from the trigger thresholds and cron schedules. The state triggers fire opportunistically when dirty-turn counts cross thresholds, so a burst of conversation can trigger all three jobs in sequence within a few minutes.

/memory/cognition/ is system-owned state. The agent does not read or write here during interactive turns. It contains:

/memory/cognition/
├── state/
│ ├── verification_review.md # Latest verification review artifact
│ └── semantic_extraction_checkpoint.json # Semantic extraction boundary
├── triggers/
│ └── jobs/
│ ├── verification_review.json # Trigger state per job type
│ ├── semantic_memory.extract.json
│ └── working_memory.consolidate.json
└── runs/
└── YYYY/MM/DD/
└── HHMMSS.<job_type>.json # Append-only run records

The consolidation checkpoint lives under /memory/history/state/consolidation_checkpoint.json alongside the transcript head state, because it is a replay boundary — it belongs to the history layer, not the cognition layer.

Here is what actually goes into the model’s context on every turn, ordered from most stable to least stable:

flowchart TD
    subgraph prompt["System messages (cached prefix)"]
        SP["System prompt<br/>code-owned execution policy"]
        CM["Core memory<br/>AGENT.md · USER.md · SOUL.md"]
        SC["Skill catalog<br/>available skills + descriptions"]
        WM["Working memory<br/>WORKING_MEMORY.md"]
    end
    subgraph ctx["Conversation context"]
        RT["Recent transcript<br/>turns after consolidation checkpoint<br/>(bounded replay window)"]
        UM["Current user message<br/>+ temporal metadata"]
    end
    SP --> CM --> SC --> WM --> RT --> UM
    style SP fill:#89b4fa,stroke:#2563eb,color:#1e1e2e
    style CM fill:#cba6f7,stroke:#8839ef,color:#1e1e2e
    style SC fill:#fab387,stroke:#d97706,color:#1e1e2e
    style WM fill:#a6e3a1,stroke:#16a34a,color:#1e1e2e
    style RT fill:#313244,stroke:#89b4fa,color:#cdd6f4
    style UM fill:#f38ba8,stroke:#dc2626,color:#1e1e2e
  1. System prompt — Code-owned execution policy: autonomy rules, tool persistence, verification loop, output contract. This is compiled into the binary, not stored in memory. It is the most stable part of the prefix and changes only on upgrades. It also tells the agent where every memory layer lives, which headings semantic files use, and which paths are auto-injected versus tool-fetched.
  2. Core memoryAGENT.md, USER.md, SOUL.md. Agent identity, user profile, voice and principles. Auto-injected every turn. These are durable files that change rarely.
  3. Skill catalog — Descriptions of installed skills. Auto-injected so the model knows what capabilities are available. Changes when skills are added or removed.
  4. Working memoryWORKING_MEMORY.md. Bounded active state: current priorities, active tasks, open threads, recent progress, pending checks. Auto-injected every turn. This is the artifact maintained by the consolidation cognition job.
  5. Recent transcript — The last N turns after the consolidation checkpoint. This is the unconsolidated tail — turns that have not yet been processed by a cognition job. Bounded by the configured replay window (default: 6 turns).
  6. User message — The current message, with temporal metadata (local time, day of week, gap since last message).

The ordering is deliberate: most stable first, least stable last. This lets providers cache the prefix across working memory changes — the system prompt and core memory rarely change, so the cached prefix can be reused even when working memory updates.

Layer Path Purpose Auto-injected? Maintained by
Core /memory/core/ Agent identity, personality, self-model Yes, every turn Operator (you)
Working /memory/working/WORKING_MEMORY.md Bounded active state Yes, every turn Consolidation job
Semantic /memory/semantic/ Durable extracted knowledge No (tool-fetched) Semantic extraction job
History /memory/history/ Full episodic transcript as JSON No (replayed) Append on every turn
Cognition /memory/cognition/ Job state, run records, review artifacts No (system-owned) Cognition controller
Notes /memory/notes/ Zettelkasten notebook No (tool-fetched) Agent on demand

Files under /memory/core/ define who the agent is. These are always in the prompt:

  • AGENT.md — role and behavioral protocol
  • USER.md — user identity, preferences, communication norms
  • SOUL.md — voice, teaching style, working principles

These are durable identity files. They change when you change them — not automatically. The cognition jobs never write to core memory. This is deliberate: your agent’s identity is not something a background model call should rewrite.

/memory/working/WORKING_MEMORY.md holds bounded active state: what we are working on right now, what is unresolved, what was recently done, what needs checking. It is auto-injected into every turn and is the primary mechanism for sessionless continuity.

The consolidation job keeps it compact. When a task is resolved, the job removes it. When a thread is abandoned, the job drops it. When the user adds a new constraint, the job captures it. The working memory artifact is the agent’s scratch pad for the current moment — not a history log.

Other files under /memory/working/ are not prompt-visible. Only WORKING_MEMORY.md is auto-injected.

Three canonical files hold durable extracted knowledge:

  • facts.md — confirmed facts and grounded inferences
  • preferences.md — user preferences and collaboration preferences
  • projects.md — active projects and durable project knowledge

Semantic memory is not auto-injected. The agent fetches it on demand using read_file when it determines that durable context is relevant. This keeps the prompt bounded while still making long-term knowledge available.

The semantic extraction job maintains these files. It promotes only explicit, durable statements or claims corroborated by repeated evidence. It does not promote single-turn temporary tasks or speculative guesses. If the verification review flags a semantic entry as stale or contradicted, the extraction job removes or rewrites it.

Completed turns are stored as JSON files under /memory/history/turns/YYYY/MM/DD/. The full transcript persists forever. Nothing is deleted.

The replay window is checkpoint-aware. When the consolidation job succeeds, it advances a checkpoint recording the last consolidated turn sequence number. The next LoadRecentMessages call starts from that checkpoint — it replays only turns after it. If no checkpoint exists (first boot), it falls back to the last N turns.

The semantic extraction job has its own independent checkpoint at /memory/cognition/state/semantic_extraction_checkpoint.json. It loads turns since its last checkpoint, not since the consolidation checkpoint. This means semantic extraction can process a different (typically larger) slice of transcript than working memory consolidation.

/memory/cognition/ is system-owned state. The agent does not read or write here during interactive turns. It contains:

  • state/ — verification review artifact, semantic extraction checkpoint
  • triggers/jobs/ — per-job trigger state (last run, dirty tracking, consecutive failures)
  • runs/ — append-only run records with full provenance (model used, input/output sequence, success/failure, summary, attempt failures)

/memory/notes/ contains the auxiliary zettelkasten notebook:

  • notes/inbox/ — incoming notes
  • notes/zettel/ — atomic knowledge notes
  • notes/maps/ — structure maps and indexes

These are not prompt-visible. They are durable knowledge infrastructure that the agent can search and reference on demand, often backed by embedding collections.

Compaction Cognition jobs
What happens to old turns Summarized into prose, then dropped from prompt Persisted on disk forever; represented by structured artifacts
What the prompt carries Summary paragraph + recent turns Structured working memory + recent unconsolidated turns
Lossiness Lossy — detail erodes with each compaction cycle Non-lossy — full transcript on disk; structured extraction is additive
Structure Free-form prose, no schema Canonical headings, typed files, enforced section structure
Verifiability Cannot query what was lost Full run records with model, input/output seq, success/failure
Correction No mechanism — errors in summary compound Verification job flags stale/unsupported claims; next cycle corrects them
When it runs Blocking, during the user’s turn Background, between turns
Failure mode Conversation stalls if summarization fails Job fails, dirty state preserved, retries on next trigger
Model choice Same model as conversation Per-job model override or interactive model inheritance

When you talk to q15, you are not managing sessions. You are having one conversation with an agent that remembers. The working memory artifact carries your current thread forward. The semantic memory files carry durable facts about you and your projects. The full transcript is on disk if anything ever needs to be revisited. And all of this is maintained by background jobs that run while you are not looking — not by compressing your words into a paragraph, but by extracting structured state that the next turn can use.

The verification job audits that state for accuracy. The consolidation job keeps the active-state artifact current. The extraction job promotes durable knowledge. The correction cycle between them means memory does not just persist — it gets better over time, as stale entries are flagged and corrected without any user intervention.

Embeddings & Search

How Qdrant-backed embedding collections let the agent search its own knowledge base.

Architecture

The three-service model and how memory fits into the turn flow.

Workspace

The durable project tree that pairs with memory for long-running work.