skip to content
$worker

memory

v0.2.12

Durable cross-session agent memory — named banks of always-injected markdown rules and auto-extracted memories, hybrid BM25 + entity recall, pinning, and supersede-never-delete history. Plain files on disk; every operation is a traced function.

iiiverified
155 installs0 in 7d0 today
install
$iii trigger compose::add worker=memory@next
binarylicense: Apache-2.0agentbm25contextmemoryragrecall
  • macOS: arm64 · x64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64

exact versions are immutable; binary and bundle artifacts are digest-pinned.

agent-ready brief for v0.2.12
install + config + dependencies + readme + api reference, all in one place. fetch as agent-context.md for an llm to consume.
the same content rendered as discrete blocks below is exposed as a single markdown document at /workers/memory.md?version=next. paste it into an llm prompt or pipe it through curl from a worker.

install

install
$iii trigger compose::add worker=memory@0.2.12

dependencies

readme

README.md

memory

Durable cross-session agent memory. Named banks hold two kinds of content: rules (markdown documents injected whole into every turn's system prompt) and memories (auto-extracted records recalled on demand). Everything is a plain file you can open, a function you can call, and an event you can watch: memory that acts visibly, not magically.

Install

iii trigger compose::add worker=memory

The default bank main materializes on first use. No configuration required; without llm-router extraction degrades to explicit memory::save calls, and without the harness the worker still serves its full RPC surface.

Quickstart

Save something, then see exactly what a turn on that topic would be given:

iii trigger memory::save text="User publishes blog posts on Tuesday mornings" pinned=true tags=blog
iii trigger memory::recall query="when do I publish articles"

Give the bank a standing instruction — injected into the system prompt of every session using it:

iii trigger memory::rule::set name=style content="# Style\nFormal register. No em-dashes."

With the harness running there is nothing else to wire: chat normally and memory captures in the background. Say "never start replies with the word Certainly" in a conversation and it lands in the bank's auto-managed learned rule; state something durable and it appears as a memory after the turn. If memory seems absent, iii trigger memory::doctor runs a real save→recall roundtrip and names what is degraded.

Configuration

All fields hot-reload through the configuration worker and are editable in the worker's explicit form in the Console's global Settings modal; data_dir reopens the store on the fly. Fields: data_dir, default_bank, inject_rules, inject_memories, recall_limit, recall_budget_tokens, extraction_enabled, extraction_model, extraction_window, extraction_timeout_ms, max_memories_per_turn, rule_learning_enabled, max_rule_chars, decay_half_life_days, embeddings_enabled, embedding_model.

Why this shape

  • Files are the source of truth. rules/*.md and memories.jsonl under the data dir. Edit them in any editor; memory::reload (or a restart) picks the edits up. The search index is a RAM-only cache rebuilt from the files at boot, so store and index can never diverge across restarts.
  • Crash-safe by construction. Every mutation appends one fsynced JSONL line before touching RAM. There is no shutdown flush to get wrong. An unwritable data dir is boot-fatal: this worker never silently runs in RAM.
  • Supersede, never delete. Updates append revisions; deletes append tombstones; trashed banks move to .trash/. Any state is recoverable.
  • Pinning. A pinned memory ranks higher in recall and is untouchable by every automatic path.
  • Tags. Topical labels WITHIN a bank ("iii", "billing") for filtering — organization, not ranking. Set them on save/update, extraction suggests them automatically, and memory::list/memory::recall take a tag filter.
  • One LLM call per turn, zero at query time. Extraction runs in the background after a turn completes (ADD-only, content-fingerprinted so redelivery reinforces instead of duplicating). Recall is BM25 + entity match + corroboration + recency, plus a semantic signal when router::embed is available: sub-millisecond at this scale.
  • Rules learn from corrections. Extraction classifies standing instructions (style directives, workflow corrections) separately from memories and appends them to the bank's auto-managed learned rule — correcting the agent in chat updates the system prompt for every later turn. Hand-authored rules are never touched; dedup by content fingerprint; rule_learning_enabled turns it off.
  • Honest health. memory::doctor runs a real save→recall→trash roundtrip and reports sibling reachability. memory::recall names the retrieval mode it ran. Degradation is explicit, never silent.

How it hooks into the harness

Seam What happens
harness::hook::pre-generate (fail-open, priority 100) Injects the session bank's rules into the system prompt (stable per session: keeps the provider prompt cache warm) and up to recall_limit recalled memories as one appended message
harness::turn-completed Spawns one background router::complete extraction pass over the last extraction_window user/assistant messages

Bank selection order: turn metadata memory_bank → session metadata memory_bank (session::set-meta) → configured default_bank. A session-lookup failure injects nothing rather than falling back across banks.

All seam bindings are one-shot at startup and rely on the engine's recoverable triggers (iii #1962, engine ≥ 0.21.8): bound before the owning sibling (harness, session-manager, queue) is up, they park as pending intents and activate when the trigger type registers. On older engines the binds are silently dropped.

Functions

Function Purpose
memory::bank::create / list / delete Banks as first-class named scopes ("blog" vs "coding" vs "personal"); delete moves to .trash/
memory::save Explicit save ("remember this"): fingerprinted, reinforces on repeat
memory::get / list / update / delete / pin Memory CRUD; delete tombstones; update bumps a revision in the log
memory::recall The exact scorer the hook uses: preview what a turn would be given; tag narrows to one label
memory::tags Distinct tags across a bank's live memories with counts (the filter source)
memory::preview Full injection dry-run for a hypothetical chat message: rules section (budgets applied), memories post ambient floor and token budget, appended message verbatim — the hook's exact code path
memory::rule::list / set The always-injected markdown rules; empty content removes
memory::supersede Retire one memory in favor of another (tombstone + pointer) — the consolidation seam
memory::doctor End-to-end self-test (roundtrip + sibling reachability)
memory::reload Reload every bank from disk after hand-editing files

Custom trigger types

Bind live views here instead of polling; both are filterable by bank in the binding config, delivery is fire-and-forget, at-least-once, unordered.

  • memory::item-changed — a memory was created / updated / superseded / deleted. Payload: {event_type, bank, memory}.
  • memory::bank-changed — a bank was created / trashed, or its rules changed. Payload: {event_type, bank}.

Storage layout

Compose stores this tree under III_COMPOSE_DIR. A standalone worker uses its process current directory for the same relative default.

<III_COMPOSE_DIR>/data/memory/
  main/
    bank.yaml          # description
    rules/style.md     # always-injected markdown
    memories.jsonl     # append-only memory log (full records, last-wins by id)
    vectors.jsonl      # derived embedding sidecar (rebuildable)
  .trash/              # trashed banks, timestamped

Permissions

Suggested defaults ship in iii-permissions.yaml: agents get memory::recall, memory::save, memory::get, memory::list, memory::update, memory::delete, memory::pin; the hook/pipeline internals are denied, and bank/rule writes plus memory::reload are human-owned surfaces (console, REST, CLI) by default.

The memory family

Like llm-router and its provider workers, memory is a family of narrow siblings rather than one grown monolith — this worker owns the store, per-turn injection, capture, and recall, and stays deliberately small:

Worker Role Status
memory (this) Banks, rules, memories: storage, harness injection, background capture, hybrid recall, live trigger types shipped
memory-consolidate Background hygiene on a schedule: deterministic dedup of near-duplicate memories, catch-up-on-boot scheduling. Supersede-only through memory::supersede, never touches pinned records; every change lands as a memory::item-changed event shipped

The split is the point: consolidation can be installed, stopped, or removed without touching stored memory, and this worker never grows a scheduler.

Boundaries

Context-manager compresses one history for one turn; this worker is the durable cross-session sibling its spec reserves (see tech-specs/2026-06-agentic/memory.md). Recall fuses BM25 with a semantic signal when router::embed has an embed-capable provider; memory::recall.retrieval names the mode either way. Consolidation belongs to the memory-consolidate sibling, not here.

api reference (json)

agent-api-reference.json
{
  "functions": [
    {
      "description": "Create a named memory bank: an isolated scope of rules and memories, e.g. one per project or persona. Idempotent; sessions select it via metadata `memory_bank`.",
      "metadata": {},
      "name": "memory::bank::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "description": {
            "default": null,
            "description": "Human description shown in listings and the console.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Bank name, `[a-z0-9][a-z0-9_-]{0,63}` (it becomes a folder name).",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "BankCreateRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "BankInfo": {
            "description": "Summary row for `memory::bank::list` and the console page.",
            "properties": {
              "description": {
                "default": "",
                "type": "string"
              },
              "memories": {
                "description": "Live memory (memory) count.",
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "pinned": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "rules": {
                "description": "Rule (always-injected markdown) count.",
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "memories",
              "name",
              "pinned",
              "rules"
            ],
            "type": "object"
          }
        },
        "properties": {
          "bank": {
            "$ref": "#/definitions/BankInfo"
          },
          "created": {
            "description": "False when the bank already existed (create is idempotent).",
            "type": "boolean"
          }
        },
        "required": [
          "bank",
          "created"
        ],
        "title": "BankCreateResponse",
        "type": "object"
      }
    },
    {
      "description": "Delete a memory bank: the folder moves to the store's .trash, recoverable by hand.",
      "metadata": {},
      "name": "memory::bank::delete",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "BankDeleteRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "trashed_to": {
            "description": "Where the bank folder was moved (under the store's `.trash/`).",
            "type": "string"
          }
        },
        "required": [
          "ok",
          "trashed_to"
        ],
        "title": "BankDeleteResponse",
        "type": "object"
      }
    },
    {
      "description": "List every memory bank with memory/rule/pin counts.",
      "metadata": {},
      "name": "memory::bank::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "BankListRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "BankInfo": {
            "description": "Summary row for `memory::bank::list` and the console page.",
            "properties": {
              "description": {
                "default": "",
                "type": "string"
              },
              "memories": {
                "description": "Live memory (memory) count.",
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "pinned": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "rules": {
                "description": "Rule (always-injected markdown) count.",
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "memories",
              "name",
              "pinned",
              "rules"
            ],
            "type": "object"
          }
        },
        "properties": {
          "banks": {
            "items": {
              "$ref": "#/definitions/BankInfo"
            },
            "type": "array"
          }
        },
        "required": [
          "banks"
        ],
        "title": "BankListResponse",
        "type": "object"
      }
    },
    {
      "description": "Delete a memory (tombstone: sets invalid_at). It leaves recall immediately but stays on disk, readable with include_superseded.",
      "metadata": {},
      "name": "memory::delete",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "DeleteRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "description": "Shared response for update/delete/pin.",
        "properties": {
          "memory": {
            "$ref": "#/definitions/Memory"
          }
        },
        "required": [
          "memory"
        ],
        "title": "MemoryResponse",
        "type": "object"
      }
    },
    {
      "description": "Self-test memory end to end: data-dir writability, a scratch-bank write/read/trash roundtrip, and sibling reachability (router, session-manager). Reports degraded states explicitly, not a bare process-up health.",
      "metadata": {},
      "name": "memory::doctor",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "DoctorRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "DoctorCheck": {
            "properties": {
              "detail": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "ok": {
                "type": "boolean"
              }
            },
            "required": [
              "detail",
              "name",
              "ok"
            ],
            "type": "object"
          }
        },
        "properties": {
          "checks": {
            "items": {
              "$ref": "#/definitions/DoctorCheck"
            },
            "type": "array"
          },
          "ok": {
            "description": "True only when every REQUIRED check passed (sibling reachability is reported but advisory).",
            "type": "boolean"
          }
        },
        "required": [
          "checks",
          "ok"
        ],
        "title": "DoctorResponse",
        "type": "object"
      }
    },
    {
      "description": "Internal: queue-delivered extraction job — errors propagate so the queue retries and dead-letters instead of losing the pass.",
      "metadata": {
        "internal": true
      },
      "name": "memory::extract-job",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "One durable extraction job (the `data` of a queue message).",
        "properties": {
          "session_id": {
            "type": "string"
          }
        },
        "required": [
          "session_id"
        ],
        "title": "ExtractJobInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "AckResponse",
        "type": "object"
      }
    },
    {
      "description": "Fetch one memory by id, including superseded/tombstoned records.",
      "metadata": {},
      "name": "memory::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "GetRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "properties": {
          "memory": {
            "$ref": "#/definitions/Memory"
          }
        },
        "required": [
          "memory"
        ],
        "title": "GetResponse",
        "type": "object"
      }
    },
    {
      "description": "Internal: harness pre-generate hook — injects the session bank's rules into the system prompt and recalled memories as one appended message. Never denies.",
      "metadata": {
        "internal": true
      },
      "name": "memory::hook::pre-generate",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "GenerateInput": {
            "properties": {
              "messages": {
                "default": null,
                "description": "Transcript messages as assembled so far (schema-free: shapes belong to the router)."
              },
              "system_prompt": {
                "default": "",
                "type": "string"
              }
            },
            "type": "object"
          }
        },
        "description": "Envelope the harness posts to `pre-generate` hooks. Only the fields this worker reads are typed; everything else is ignored.",
        "properties": {
          "generate": {
            "anyOf": [
              {
                "$ref": "#/definitions/GenerateInput"
              },
              {
                "type": "null"
              }
            ]
          },
          "metadata": {
            "default": null,
            "description": "Turn metadata (`harness::send` options.metadata); `memory_bank` here overrides the session-level selection for this turn."
          },
          "session_id": {
            "default": "",
            "type": "string"
          }
        },
        "title": "PreGenerateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Mutations": {
            "properties": {
              "append_messages": {
                "items": true,
                "type": "array"
              },
              "system_prompt": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "append_messages"
            ],
            "type": "object"
          }
        },
        "description": "Hook reply: `continue` with optional mutations (never deny — memory is an enrichment, not a gate).",
        "properties": {
          "annotations": {
            "additionalProperties": true,
            "type": [
              "object",
              "null"
            ]
          },
          "decision": {
            "type": "string"
          },
          "mutations": {
            "anyOf": [
              {
                "$ref": "#/definitions/Mutations"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "decision"
        ],
        "title": "HookResponse",
        "type": "object"
      }
    },
    {
      "description": "List a bank's memories page by page, newest first. `include_superseded` shows history too.",
      "metadata": {},
      "name": "memory::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "include_superseded": {
            "default": null,
            "description": "Include superseded/tombstoned records (the history view).",
            "type": [
              "boolean",
              "null"
            ]
          },
          "limit": {
            "default": null,
            "description": "Page size, default 50, max 500.",
            "format": "uint",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "offset": {
            "default": null,
            "format": "uint",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "tag": {
            "default": null,
            "description": "Only memories carrying this tag.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "ListRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "properties": {
          "memories": {
            "items": {
              "$ref": "#/definitions/Memory"
            },
            "type": "array"
          },
          "total": {
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "memories",
          "total"
        ],
        "title": "ListResponse",
        "type": "object"
      }
    },
    {
      "description": "Internal: hot-reload memory from the authoritative configuration when it changes — reopens the store on a data_dir change and swaps the per-call tuning snapshot otherwise.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "memory::on-config-change",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Internal `memory::on-config-change` trigger payload. The handler re-fetches the authoritative configuration (trusting `new_value` from a discoverable bus function would let any caller inject config).",
        "properties": {
          "id": {
            "default": null,
            "description": "Configuration id that changed (advisory; the handler re-fetches).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "OnConfigChangeEvent",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "OnConfigChangeResponse",
        "type": "object"
      }
    },
    {
      "description": "Internal: session::deleted handler — drops the session's extraction cursor.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "memory::on-session-deleted",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "`session::deleted` payload (the field this worker reads).",
        "properties": {
          "session_id": {
            "default": "",
            "type": "string"
          }
        },
        "title": "SessionDeletedInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "AckResponse",
        "type": "object"
      }
    },
    {
      "description": "Internal: harness turn-completed handler — enqueues one durable extraction job (a single router::complete call) for the finished turn; inline fallback without a queue.",
      "metadata": {
        "internal": true
      },
      "name": "memory::on-turn-completed",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "`harness::turn-completed` payload (the fields this worker reads).",
        "properties": {
          "session_id": {
            "default": "",
            "type": "string"
          },
          "status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "turn_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "TurnCompletedInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "AckResponse",
        "type": "object"
      }
    },
    {
      "description": "Pin or unpin a memory. Pinned memories rank higher in recall and are untouchable by every automatic path.",
      "metadata": {},
      "name": "memory::pin",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "pinned": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "pinned"
        ],
        "title": "PinRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "description": "Shared response for update/delete/pin.",
        "properties": {
          "memory": {
            "$ref": "#/definitions/Memory"
          }
        },
        "required": [
          "memory"
        ],
        "title": "MemoryResponse",
        "type": "object"
      }
    },
    {
      "description": "Preview the memory injection for a hypothetical chat message: the exact system-prompt memory section (rules, budgets, truncation markers), the memories the turn would be handed (post ambient floor and token budget), and the appended message verbatim. Runs the pre-generate hook's own code.",
      "metadata": {},
      "name": "memory::preview",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "description": "Bank to preview; the configured default when omitted.",
            "type": [
              "string",
              "null"
            ]
          },
          "query": {
            "description": "The hypothetical user message (what someone would type in chat).",
            "type": "string"
          }
        },
        "required": [
          "query"
        ],
        "title": "PreviewRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "PreviewMemory": {
            "properties": {
              "memory": {
                "$ref": "#/definitions/Memory"
              },
              "score": {
                "description": "Recall score; 0.0 for memories added by the ambient floor.",
                "format": "float",
                "type": "number"
              }
            },
            "required": [
              "memory",
              "score"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "properties": {
          "bank": {
            "type": "string"
          },
          "memories": {
            "description": "Memories the turn would be handed, post ambient floor and token budget, in injection order.",
            "items": {
              "$ref": "#/definitions/PreviewMemory"
            },
            "type": "array"
          },
          "message": {
            "description": "The appended `<memory>` message body, verbatim; absent when no memory fits the budget.",
            "type": [
              "string",
              "null"
            ]
          },
          "retrieval": {
            "description": "Retrieval mode that ran (`bm25-entity` or `bm25-entity-semantic`).",
            "type": "string"
          },
          "rules": {
            "description": "Rules included, and whether any were cut by `max_rule_chars`.",
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          },
          "rules_truncated": {
            "type": "boolean"
          },
          "system_prompt_section": {
            "description": "The memory section appended to the system prompt (ambient header + rules), exactly as injected — truncation markers included.",
            "type": "string"
          }
        },
        "required": [
          "bank",
          "memories",
          "retrieval",
          "rules",
          "rules_truncated",
          "system_prompt_section"
        ],
        "title": "PreviewResponse",
        "type": "object"
      }
    },
    {
      "description": "Search a bank's memories relevant to a query (BM25 + entities + corroboration + recency; no LLM). The same scorer the pre-generate hook uses — call it to preview an injection.",
      "metadata": {},
      "name": "memory::recall",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "description": "Bank to search; the configured default when omitted.",
            "type": [
              "string",
              "null"
            ]
          },
          "include_superseded": {
            "default": null,
            "description": "Also rank superseded/tombstoned records (the history view).",
            "type": [
              "boolean",
              "null"
            ]
          },
          "limit": {
            "default": null,
            "description": "Max memories returned (default: the configured recall_limit).",
            "format": "uint",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "query": {
            "type": "string"
          },
          "tag": {
            "default": null,
            "description": "Only rank memories carrying this tag.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "query"
        ],
        "title": "RecallRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "ScoredMemory": {
            "properties": {
              "memory": {
                "$ref": "#/definitions/Memory"
              },
              "score": {
                "format": "float",
                "type": "number"
              }
            },
            "required": [
              "memory",
              "score"
            ],
            "type": "object"
          }
        },
        "properties": {
          "bank": {
            "type": "string"
          },
          "memories": {
            "items": {
              "$ref": "#/definitions/ScoredMemory"
            },
            "type": "array"
          },
          "retrieval": {
            "description": "Retrieval mode actually used — degradation is explicit, never silent. Currently always `bm25-entity`; a semantic signal joins when the router grows an embeddings surface.",
            "type": "string"
          }
        },
        "required": [
          "bank",
          "memories",
          "retrieval"
        ],
        "title": "RecallResponse",
        "type": "object"
      }
    },
    {
      "description": "Drop all in-RAM state and reload every bank from disk — the recovery hatch after editing bank files by hand.",
      "metadata": {},
      "name": "memory::reload",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "ReloadRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "banks": {
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          },
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "banks",
          "ok"
        ],
        "title": "ReloadResponse",
        "type": "object"
      }
    },
    {
      "description": "List a bank's rules (markdown, injected whole into the system prompt on every turn). Stored as plain files under the bank's rules/ folder; editing them by hand is equivalent.",
      "metadata": {},
      "name": "memory::rule::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "description": "Bank whose rules to list; the configured default when omitted.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "RuleListRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Rule": {
            "description": "One markdown rule: always injected whole into the system prompt for sessions using its bank. Stored as `rules/<name>.md` — open it in any editor.",
            "properties": {
              "content": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "updated_at": {
                "description": "Milliseconds since epoch (file mtime).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "content",
              "name",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "bank": {
            "type": "string"
          },
          "rules": {
            "items": {
              "$ref": "#/definitions/Rule"
            },
            "type": "array"
          }
        },
        "required": [
          "bank",
          "rules"
        ],
        "title": "RuleListResponse",
        "type": "object"
      }
    },
    {
      "description": "Create or replace one rule (atomic write; empty content removes it). Rules are plain markdown files under the bank's rules/ folder.",
      "metadata": {},
      "name": "memory::rule::set",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "description": "Bank to write into; the configured default when omitted. Created on first use.",
            "type": [
              "string",
              "null"
            ]
          },
          "content": {
            "description": "Markdown content. Empty removes the rule.",
            "type": "string"
          },
          "name": {
            "description": "Rule name, `[a-z0-9][a-z0-9_-]{0,63}` (it becomes `<name>.md`).",
            "type": "string"
          }
        },
        "required": [
          "content",
          "name"
        ],
        "title": "RuleSetRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "exists": {
            "description": "False when the empty content removed the rule.",
            "type": "boolean"
          },
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "exists",
          "ok"
        ],
        "title": "RuleSetResponse",
        "type": "object"
      }
    },
    {
      "description": "Save one memory explicitly. Content-fingerprinted: re-saving the same text reinforces the existing memory instead of duplicating it. Use when the user says `remember this`.",
      "metadata": {},
      "name": "memory::save",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "description": "Target bank; the configured default when omitted.",
            "type": [
              "string",
              "null"
            ]
          },
          "entities": {
            "default": [],
            "description": "Entity handles (people/projects/tools) used as a retrieval signal.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "pinned": {
            "default": false,
            "type": "boolean"
          },
          "session_id": {
            "default": null,
            "description": "Provenance session, when saved on behalf of a conversation.",
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "default": [],
            "description": "Topical labels for filtering within the bank (organization, not ranking).",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "text": {
            "description": "The memory, one self-contained sentence.",
            "type": "string"
          }
        },
        "required": [
          "text"
        ],
        "title": "SaveRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "properties": {
          "created": {
            "description": "False when the text fingerprint matched an existing memory (it was reinforced instead).",
            "type": "boolean"
          },
          "memory": {
            "$ref": "#/definitions/Memory"
          }
        },
        "required": [
          "created",
          "memory"
        ],
        "title": "SaveResponse",
        "type": "object"
      }
    },
    {
      "description": "Retire one memory in favor of another: tombstone with a superseded_by pointer, never a plain delete. The consolidation seam; pinned memories cannot be superseded.",
      "metadata": {},
      "name": "memory::supersede",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Memory to retire.",
            "type": "string"
          },
          "superseded_by": {
            "description": "Live memory that replaces it.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "superseded_by"
        ],
        "title": "SupersedeRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "description": "Shared response for update/delete/pin.",
        "properties": {
          "memory": {
            "$ref": "#/definitions/Memory"
          }
        },
        "required": [
          "memory"
        ],
        "title": "MemoryResponse",
        "type": "object"
      }
    },
    {
      "description": "List the tags across a bank's live memories with usage counts, most used first — the filter source for tag-scoped list/recall.",
      "metadata": {},
      "name": "memory::tags",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "TagsRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TagCount": {
            "properties": {
              "count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "tag": {
                "type": "string"
              }
            },
            "required": [
              "count",
              "tag"
            ],
            "type": "object"
          }
        },
        "properties": {
          "bank": {
            "type": "string"
          },
          "tags": {
            "items": {
              "$ref": "#/definitions/TagCount"
            },
            "type": "array"
          }
        },
        "required": [
          "bank",
          "tags"
        ],
        "title": "TagsResponse",
        "type": "object"
      }
    },
    {
      "description": "Serve the memory worker's injected console UI assets (content function for its console:script / console:style triggers).",
      "metadata": {
        "internal": true
      },
      "name": "memory::ui-content",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Input of the content function: the console asks for one asset by path.",
        "properties": {
          "path": {
            "description": "The asset path from the trigger config (e.g. `state/page.js`).",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "title": "UiContentInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Output of the content function.",
        "properties": {
          "content": {
            "description": "The asset source, verbatim.",
            "type": "string"
          },
          "content_type": {
            "description": "MIME type the console should serve the asset with.",
            "type": "string"
          }
        },
        "required": [
          "content",
          "content_type"
        ],
        "title": "UiContentResult",
        "type": "object"
      }
    },
    {
      "description": "Revise a memory's text, entities, or pin flag in place (same id, bumped revision; the prior revision stays in the on-disk log).",
      "metadata": {},
      "name": "memory::update",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "bank": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "entities": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "pinned": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "tags": {
            "default": null,
            "description": "Replace the tag set (empty list clears).",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "text": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "title": "UpdateRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Confidence": {
            "description": "How a memory entered the store.",
            "oneOf": [
              {
                "description": "Stated directly in the source conversation.",
                "enum": [
                  "extracted"
                ],
                "type": "string"
              },
              {
                "description": "Derived by the extraction model rather than stated verbatim.",
                "enum": [
                  "inferred"
                ],
                "type": "string"
              },
              {
                "description": "Saved explicitly by an agent or a human (highest trust).",
                "enum": [
                  "stated"
                ],
                "type": "string"
              }
            ]
          },
          "Memory": {
            "description": "One memory item. Serialized whole as a `memories.jsonl` line; replay is last-wins by `id`, so an update appends a full record with a bumped `revision` and a delete appends a tombstoned record (`invalid_at` set) — history is never destroyed, only superseded.",
            "properties": {
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "corroboration": {
                "default": 0,
                "description": "Times this memory was independently re-observed. Boosts recall rank.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "description": "Milliseconds since epoch.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "entities": {
                "default": [],
                "description": "Entity handles used as an exact-match retrieval signal.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "id": {
                "description": "Content fingerprint (`fp` + FNV-1a of the normalized text), so re-ingesting the same source reinforces instead of duplicating.",
                "type": "string"
              },
              "invalid_at": {
                "description": "Set when the memory stopped being true (superseded or deleted). The record stays on disk and remains queryable with `include_superseded: true`.",
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "pinned": {
                "default": false,
                "description": "Pinned memories are never touched by any automatic path (consolidation, decay); only an explicit `memory::pin`/`memory::update` changes them.",
                "type": "boolean"
              },
              "revision": {
                "default": 0,
                "description": "Bumped on every appended revision of this id; replay keeps the highest.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Provenance"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "superseded_by": {
                "description": "Memory id that replaced this one, when superseded by content.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "tags": {
                "default": [],
                "description": "Topical labels for filtering WITHIN a bank (\"iii\", \"workers\", \"billing\") — organization, not ranking. Normalized lowercase, capped like entities. Old records without the field load as untagged.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "updated_at": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "confidence",
              "created_at",
              "id",
              "text",
              "updated_at"
            ],
            "type": "object"
          },
          "Provenance": {
            "description": "Where a memory came from. Every extracted memory points back into session-manager; nothing in memory duplicates the transcript store.",
            "properties": {
              "agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "entry_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "description": "Shared response for update/delete/pin.",
        "properties": {
          "memory": {
            "$ref": "#/definitions/Memory"
          }
        },
        "required": [
          "memory"
        ],
        "title": "MemoryResponse",
        "type": "object"
      }
    }
  ],
  "triggers": [
    {
      "description": "A memory bank was created or trashed. Payload: { event_type, bank }.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by both trigger types.",
        "properties": {
          "bank": {
            "description": "Only deliver events for this bank.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "memory::bank-changed",
      "return_schema": {}
    },
    {
      "description": "A memory was created, updated, superseded, or deleted in a memory bank. Payload: { event_type, bank, memory }. Bind console/live views here.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "description": "Config accepted by both trigger types.",
        "properties": {
          "bank": {
            "description": "Only deliver events for this bank.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BindingConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "memory::item-changed",
      "return_schema": {}
    }
  ]
}