Skip to content

Agent Memory

One-line summary

Soul is who YOU are. Agent Memory is how the agent OPERATES in your environment.

Every Synap tenant has two parallel cognition layers:

  • arx_soul — identity, preferences, corrections, dialect, context (about the user).
  • arx_agent_memory — operating patterns, routing rules, voice rules, discipline rules, conventions, do-nots (about the agent).

This page covers the agent_memory layer: schema, federation model, categories, the seeded defaults, the REST API, MCP tools, CLI subcommands, and the iOS UI.

Why it exists

Most "AI memory" systems mix three different things into one bag: who the user is, what the user has worked on, and how the assistant should behave. Synap separates them so an agent on a fresh device gets behavior-shaping rules from the graph without re-litigating identity every session.

The six categories

CategoryWhat it captures
operating_patternA recurring, validated approach. "Audit before patching", "Isolate one variable per test."
routing_ruleWhich model/personality to use for which kind of work. "Opus for reasoning, Haiku for routine edits."
voice_ruleVoice/tone discipline. "Emails in the user's voice, not the assistant's."
discipline_ruleProcess discipline. "Let counsel propose first."
conventionPersistent shape rules. "Capture format: [TAG] Title (YYYY-MM-DD): Details."
do_notHard never-do's. "Don't fabricate sources or library APIs."

Federation model

Each entry has a visibility scope:

  • Tenant-shared (agent IS NONE) — every registered agent in the tenant sees it.
  • Agent-private (agent = caller) — only the calling agent sees it.
  • Commissure-published (commissure ∈ caller.memberships) — agents who joined the bridge see it.

A query for "what should I obey?" returns the union: own ∪ tenant-shared ∪ via-commissure.

Commissures here are in-tenant lightweight bridges, distinct from the Federated-Commissure inter-tenant surface.

Cross-tenant federation (publish into a Federated-Commissure)

Tenant-shared agent_memory entries can be published into an inter-tenant federation commissure so other tenants in that bridge receive a mirror copy. The mirrors land tenant-shared on the receiver side, surfacing automatically to every receiver-tenant agent.

bash
# publish
synap agent-memory federate <memory-id> <federation-commissure-id>

# withdraw (deactivates all mirrors across receiver tenants;
# the source entry stays intact)
synap agent-memory unfederate <memory-id>

REST:

POST /api/v3/agent-memory/entries/{id}/federate   { commissure_id }
POST /api/v3/agent-memory/entries/{id}/unfederate

MCP: arx_agent_memory_federate + arx_agent_memory_unfederate.

The caller's tenant must be a member of the federation commissure (the /api/v3/federation/commissure surface). Federate is idempotent on (federation_commissure_id, federation_source) — re-publishing the same entry into the same commissure is a no-op. Receivers see mirrors tagged source = "federation:<commissure_id>" and federation_source = "<source_tenant>:<source_entry_id>" so the origin is auditable.

Trust-tier-aware federation

A receiver doesn't have to accept everything a sender publishes. Each entry can carry an author-declared trust tier, and each receiver can set a per-commissure floor. On publish, an entry is mirrored into a receiver tenant only if its tier satisfies that receiver's floor — otherwise it is silently skipped (no error; the publisher is told who refused and why).

Trust tiers, strictest to laxest:

device_only  <  trusted_cloud  <  general_cloud

device_only is the strictest (never leaves the device class); general_cloud is the laxest (any substrate). A floor of trusted_cloud means "I will accept entries marked as strict as, or stricter than, trusted_cloud" — i.e. device_only and trusted_cloud, but not general_cloud.

Author side — stamp a tier on an entry. Optional; unstamped entries are "unmarked".

bash
# CLI
synap agent-memory add convention "deploy via gitlab-first" --min-trust-tier trusted_cloud
synap agent-memory update <id> --min-trust-tier device_only   # `none` clears
# REST — min_trust_tier on create/update
POST  /api/v3/agent-memory/entries        { ..., "min_trust_tier": "trusted_cloud" }
PATCH /api/v3/agent-memory/entries/{id}   { "min_trust_tier": "device_only" }   # null clears
# curl one-liner
curl -X POST https://api.synap.ing/api/v3/agent-memory/entries \
  -H "X-ARX-Key: $KEY" -H 'content-type: application/json' \
  -d '{"content":"...","category":"convention","min_trust_tier":"trusted_cloud"}'

On iOS, the Add Agent Memory Entry sheet has a trust-tier picker (None / Device Only / Trusted Cloud / General Cloud).

Receiver side — set a per-commissure floor. You set the floor on your own membership in a federation commissure; it only affects what gets mirrored into your tenant.

bash
# CLI — `none` clears the floor (back to permissive)
synap commissures trust-tier <federation-commissure-id> trusted_cloud
# REST
PUT /api/v3/federation/commissure/{id}/trust-tier-floor   { "min_trust_tier": "trusted_cloud" }

On iOS, open the commissure detail sheet and tap your membership row's trust-tier floor to pick a value. In the web portal, the commissure detail page's members table has a trust-floor selector on your own row.

What happens on publish. federate returns entry_min_trust_tier, mirrored_to, and skipped_for_trust_tier — the receivers whose floor blocked this entry, with their floor and the entry's tier, so the gate is visible rather than silent:

json
{
  "entry_min_trust_tier": "general_cloud",
  "mirrored_to": [],
  "skipped_for_trust_tier": [
    { "tenant": "acme", "receiver_floor": "trusted_cloud", "entry_tier": "general_cloud" }
  ]
}

Gating truth table (entry tier vs. receiver floor):

Entry tierReceiver floorMirrored?Why
anyunset✅ yesNo floor → permissive, accept anything
device_onlytrusted_cloud✅ yesEntry is stricter than the floor (device_only ≤ trusted_cloud)
trusted_cloudtrusted_cloud✅ yesEntry equals the floor
general_cloudtrusted_cloud⛔ noEntry is laxer than the floor
unmarkedtrusted_cloud⛔ noFloor set + entry unmarked → blocked (receiver demands an explicit tier)
anymalformed floor✅ yesFail open — don't block on bad floor data

This is CSO BLOCKING #1: the same min_trust_tier vocabulary gates Chorus personality dispatch (a sensitive oracle pinned to trusted_cloud never runs on the general cloud) and federation mirroring. See ARX milestone oav5qo46.

Default seed (every new tenant)

Every new tenant gets 15 baseline rules tagged source = "synap:default-v1", plus a starter registry of 9 models and 6 personalities. They're tenant-shared so every agent in the tenant inherits them.

CategoryCountExamples
operating_pattern6Search-before-asking, capture-proactively, link-related-nodes, session-breadcrumbs
do_not3No fabrication, no unsolicited closers, don't re-ask what's in graph
discipline_rule2Good-faith dialect interpretation, match user register
convention2Capture format [TAG] Title (YYYY-MM-DD): Details, edge type vocabulary
voice_rule1User's voice not assistant's for outward artifacts
routing_rule1Sensitive content → local inference / privacy gateway

Don't like one? synap agent-memory deactivate <id> removes it. Bumping the version tag in a future release triggers re-seed for tenants on the old version.

Seeded model registry: Claude Opus 4.7 / Sonnet 4.6 / Haiku 4.5, GPT-5, Gemini 2 Pro, Gemma 3 on-device, Llama 3 on-device, MLX local, Synap Cortex.

Seeded personalities: default, patent_counsel, advisor_email, debug_terse, technical_review, synap_writer.

REST API

All endpoints are under /api/v3/agent-memory/ and require ARX JWT auth (Authorization: Bearer <token>) or an API key (X-ARX-Key: <key>).

Assembled view

MethodPathReturns
GET/api/v3/agent-memory?agent_id=<id>Entries visible to the agent, ordered category → priority → updated_at

Agents

MethodPathPurpose
POST/api/v3/agent-memory/agentsRegister an agent (idempotent on harness × device × name)
GET/api/v3/agent-memory/agentsList agents (filter harness=, active=true)
POST/api/v3/agent-memory/agents/{id}/heartbeatTouch last_seen
POST/api/v3/agent-memory/agents/{id}/deactivateSoft-remove
PATCH/api/v3/agent-memory/agents/{id}/routingSet default model + personality + available models + routing parent
GET/api/v3/agent-memory/agents/{id}/childrenList routing children
GET/api/v3/agent-memory/agents/{id}/commissuresList memberships

Models

MethodPathPurpose
POST/api/v3/agent-memory/modelsRegister a model (idempotent on provider × model_id)
GET/api/v3/agent-memory/modelsList
PATCH/api/v3/agent-memory/models/{id}Update fields

Personalities

MethodPathPurpose
POST/api/v3/agent-memory/personalitiesRegister (idempotent on name)
GET/api/v3/agent-memory/personalitiesList
PATCH/api/v3/agent-memory/personalities/{id}Update

Commissures

MethodPathPurpose
POST/api/v3/agent-memory/commissuresCreate a bridge (scope = shared / broadcast / directed)
GET/api/v3/agent-memory/commissuresList
POST/api/v3/agent-memory/commissures/{id}/membersAdd a member (permission = read / write / admin)
GET/api/v3/agent-memory/commissures/{id}/membersList members
DELETE/api/v3/agent-memory/commissures/{id}/members/{agent_id}Remove

Entries

MethodPathPurpose
POST/api/v3/agent-memory/entriesCreate entry
GET/api/v3/agent-memory/entriesList (filters: agent_id, category, commissure_id, active)
PATCH/api/v3/agent-memory/entries/{id}Update
POST/api/v3/agent-memory/entries/{id}/deactivateSoft-remove
GET/api/v3/agent-memory/search?q=...&agent_id=...&limit=25Keyword search across visible entries
POST/api/v3/agent-memory/entries/{id}/referencesLink entry to a thought
GET/api/v3/agent-memory/entries/{id}/referencesList referenced thoughts

MCP tools

The synap-mcp server exposes 16 tools in the arx_agent_* namespace, available to any MCP client (Claude Code, Cursor, Zed, opencode, Continue, Aider, custom).

ToolPurpose
arx_agent_registerRegister THIS agent (idempotent). Call on first turn. Cached in-process.
arx_agent_listList agents in the tenant
arx_agent_heartbeatTouch last_seen
arx_agent_set_routingBind agent → default model + personality + available models + routing parent
arx_model_registerRegister a known inference model
arx_model_listList models
arx_personality_registerRegister a personality (voice/behavior profile)
arx_personality_listList personalities
arx_agent_memoryAssemble visible memory for this agent (call on first turn alongside arx_soul)
arx_agent_memory_setCreate an entry. Default scope = private to agent. tenant_shared=true for shared. commissure_id=... to publish to a bridge.
arx_agent_memory_listList entries with filters
arx_agent_memory_searchKeyword search across visible entries
arx_agent_commissure_createCreate an in-tenant cognitive bridge
arx_agent_commissure_joinAdd an agent to a commissure
arx_agent_commissure_listList commissures or memberships

First-turn protocol

Every harness with MCP access should run this sequence on the first user turn:

text
1. arx_agent_register   (idempotent — caches agent_id in the MCP process)
2. arx_soul              (load identity profile)
3. arx_agent_memory      (load visible operating rules for THIS agent)

The cognition layer ships this protocol in ~/.synap/bootstrap.md, auto-loaded into every Synap-wired AI tool.

CLI subcommands

bash
# Browse and manage
synap agent-memory show [--agent <id>] [--category <c>]
synap agent-memory list [--agent <id>] [--category <c>] [--commissure <id>]
synap agent-memory add <category> "<content>" [--agent <id> | --tenant-shared] [--commissure <id>] [--priority N]
synap agent-memory search "<query>" [--limit N]
synap agent-memory deactivate <id>
synap agent-memory link <memory-id> <thought-id>

# Agent + routing
synap agents [list | register <name> | heartbeat <id> | deactivate <id> | routing <id> | children <id>]
synap models [list | register --name <n> --provider <p> --model-id <id>]
synap personalities [list | register <name>]
synap commissures [list | create <name> | members <id> | join <c-id> <a-id> | remove <c-id> <a-id>]

TUI

Open the full-screen TUI with synap tui. Two parallel screens:

  • Press 4 or run :go soul for the Soul screen.
  • Run :go agent-memory (or :go am) for the Agent Memory screen.

Both screens render category breakdown bars on the right and full entry detail for the cursor row.

iOS app

Under More → Features:

  • Soul (Identity) — browse + add + deactivate soul entries
  • Agent Memory — three-tab sheet (Memory / Agents / Routing). Tap any memory row for detail + deactivate. The + button creates a new entry with category picker, priority stepper, and a Visible to every agent toggle (private vs tenant-shared).

The iOS app auto-registers as a participating agent (harness = "ios-app") on first open, so its entries are visible to desktop agents via the tenant-shared lane or any commissure both parties join.

Privacy

  • The gating decision (what to dispatch, what to share) stays on the device or in the tenant — never crosses to a third-party gateway by default.
  • The synap:default-v1 seed contains no user-specific data; it's audience-agnostic baseline rules.
  • Per-entry source tagging lets you audit which rules came from which capture pathway.

Source of truth

Architecture decision: ARX milestone x118ycqz176bpg5h50a9 (sibling-to-soul partition). Default-seed milestone: ac9gzl8kp15t2tm9cdz6.

Patent-pending. Architecture details available under NDA.