ai-memory v0.8.0

Persona-as-artifact (v0.7.0 QW-2)

A Persona is a curator-generated Markdown profile of an entity, synthesised from a cluster of MemoryKind::Reflection rows that reference that entity. Personas are the substrate-native expression of the Tencent L3 pattern (PersonaMem 48% → 76% on the long-horizon benchmark): the substrate distils the agent’s reflections about a subject into a stable, recallable artefact so the agent can re-load “what we know about Alice” with a single recall hit instead of paging through dozens of disjoint reflection rows.

What a Persona is

A first-class MemoryKind::Persona row in the memories table, with two extra columns populated:

The content column carries a 300–500 word Markdown body. Every claim in the body is footnoted with a [^N]: <reflection-id> citation so an operator inspecting the row can follow the link back to the originating reflection via ai-memory get <id>. The metadata column carries the persona envelope:

{
  "agent_id": "ai:curator",
  "persona": {
    "entity_id": "alice",
    "sources": ["<reflection-id>", "..."],
    "version": 1,
    "attest_level": "unsigned",
    "generated_at": "2026-05-15T00:00:00Z"
  }
}

The substrate writes one derived_from memory_link edge per source reflection so the KG walker (memory_find_paths, memory_kg_query) can follow the Persona → Reflection → Observation chain end-to-end. Every generation also appends a persona_generated row to signed_events with the sources hash as payload_hash; the H5 audit chain captures every regeneration as a distinct, signed event.

When to generate one

Three ways to trigger a generation:

  1. MCP write tool. memory_persona_generate({entity_id, namespace}) runs the curator synchronously and returns the new persona over the wire. Requires --tier smart or higher; the dispatcher refuses below.
  2. CLI. ai-memory persona <entity_id> --regenerate calls the same path from the operator’s shell.
  3. Namespace cadence. Set governance.auto_persona_trigger_every_n_memories = N on the namespace standard and the substrate’s post-reflect hook fires a deferred regeneration every N reflection writes that mention the entity. The hook is notify-class — failures are logged at tracing::warn!(target: "post_reflect.auto_persona", ...) and never propagated to the caller’s reflect response.

Binding a reflection to its entity (cadence trigger key)

A reflection “mentions” an entity through the descriptor resolved at write time into the indexed mentioned_entity_id column (and re-resolved at cadence time by resolve_entity_id). Resolution order:

  1. metadata.entity_id (primary). The structured tag on the reflection memory’s metadata.
  2. Top-level entity_id param (#1665 convenience). memory_reflect accepts a sibling entity_id string that desugars into metadata.entity_id when you have not set it directly. If both are supplied and differ, metadata.entity_id wins (a warn is logged); a blank/whitespace value is ignored.
  3. [entity:X] title marker (fallback). An [entity:<id>] token in the reflection title, for callers that have no structured id yet.

Whitespace is trimmed and empty values are skipped consistently on both the write-time denormaliser (crate::storage::extract_mentioned_entity_id) and the cadence resolver (resolve_entity_id), so the same descriptor counts on both sides (#1665). memory_store does not honor entity_id — the binding is reflection-kind only.

Provenance

The substrate is the source of truth. Every persona row carries verifiable provenance:

File-backed export

When the namespace policy carries governance.auto_export_personas_to_filesystem = true, the substrate writes ~/.ai-memory/personas/<namespace-sanitised>/<entity_id>.md after each generation. The file is a YAML-frontmatter Markdown document containing the same fields as the SQL row plus the rendered body. Operators may freely delete or regenerate the directory — the SQL row stays canonical.

The export is opt-in per namespace, symmetric with QW-1’s auto_export_reflections_to_filesystem. Operators who want governance enforcement without plaintext personas on disk leave the policy absent.

Read-back paths

Three equivalent ways to read the latest persona:

# CLI — Markdown to stdout
ai-memory persona alice --namespace team/alpha

# CLI — JSON envelope to stdout
ai-memory persona alice --namespace team/alpha --json

# MCP — read-only tool, available at Semantic+
memory_persona({entity_id: "alice", namespace: "team/alpha"})

The CLI honors the namespace flag (--namespace, defaults to global), the JSON toggle (--json), and the regeneration toggle (--regenerate). The regenerate path requires an LLM client; the CLI refuses with exit code 2 and a documented hint when none is wired (the MCP path is the standard way to regenerate because the daemon already owns the OllamaClient).

Trade-offs