Skip to content

Latest commit

 

History

History
320 lines (237 loc) · 11.1 KB

File metadata and controls

320 lines (237 loc) · 11.1 KB

Peers

Issue #679 introduces a peer registry: a generalization of Remnic's singular identity-anchor model into a multi-peer schema. Every party that interacts with a Remnic store — the operator themself, other humans, other agents, and non-conversational integrations — can be represented as a Peer with an evolving cognitive profile.

All five PRs are now merged. This page covers the complete feature.

Concepts

A peer is a stable, opaque identity. Each peer has three on-disk kernel files under peers/{peer-id}/:

File Purpose Update cadence Owner
identity.md Slow-changing facts: id, kind, displayName, timestamps, free-form notes. Manual / rare. Operator.
profile.md Evolving cognitive profile derived from session signals. JSON payload inside a fenced block, with per-field provenance. Background reasoner (async). Reasoner.
interactions.log.md Append-only signal log the reasoner reads. Per turn / per signal. Append-only.

Peer kinds

Peer.kind is one of:

  • self — the current Remnic operator. Supersedes the legacy identity-anchor model; remnic peer migrate seeds peers/self/identity.md from existing legacy data.
  • human — another human collaborator distinct from self.
  • agent — another AI agent (e.g. Claude Code, Codex, Hermes).
  • integration — a non-conversational integration that produces signals but does not act on its own (cron, webhook, importer).

Peer ids

Peer.id must match ^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$, 1–64 characters, with no leading/trailing/consecutive separators. The storage layer validates this on every read and write — .., paths with /, and other traversal-ish strings are rejected before any filesystem operation.

Registry storage layout

All peer files live under {memoryDir}/peers/:

{memoryDir}/
└── peers/
    ├── self/
    │   ├── identity.md          # id, kind, displayName, notes
    │   ├── profile.md           # evolving cognitive profile (reasoner-owned)
    │   └── interactions.log.md  # append-only signal log
    ├── alice/
    │   ├── identity.md
    │   └── interactions.log.md
    └── codex-agent/
        ├── identity.md
        ├── profile.md
        └── interactions.log.md

The identity.md format uses a minimal YAML frontmatter:

---
id: "self"
kind: self
displayName: "Self"
createdAt: "2026-04-27T00:00:00.000Z"
updatedAt: "2026-04-27T00:00:00.000Z"
---

## Migrated from identity-anchor.md

...operator identity notes...

All string values are double-quoted with \\ and \" escapes. Timestamps are bare ISO-8601 strings. The body after the closing --- is free-form markdown.

Profile reasoner

The async profile reasoner (shipped in PR 2/5) runs inside the Dreams REM phase. It reads recent interactions.log.md entries and derives structured profile.md fields with provenance. Each field records:

  • observedAt — ISO-8601 timestamp of the observation.
  • signal — the log entry text that drove the inference.
  • sourceSessionId — (optional) originating session.
  • note — (optional) free-form reasoner annotation.

The reasoner is disabled by default and must be opted in via peer.profileReasoner.enabled: true in the plugin config.

Recall integration

PR 3/5 wires the peer registry into the recall pipeline. When a peer is registered for the active session (orchestrator.setPeerIdForSession), Remnic injects a brief excerpt from that peer's profile.md into the prompt context as a ## Peer Profile section.

Recall X-ray annotation

When xrayCapture: true is passed to recall, the resulting RecallXraySnapshot includes a peerProfileInjection field:

{
  peerProfileInjection: {
    peerId: "codex-agent",
    fieldsInjected: 2        // number of fields after maxFields cap
  } | null                   // null = no injection (disabled, no peer, no profile)
}
  • Non-null — peer profile was injected; fieldsInjected indicates how many fields were included after the peerProfileRecallMaxFields cap.
  • Explicit null — feature was enabled and a peer was registered, but no profile was found or it had no fields.
  • Absent (undefined) — peerProfileRecallEnabled was false or no peer was registered for the session; the field is omitted entirely.

This lets operators correlate recall-quality differences with peer-profile injection in the X-ray trace.

CLI commands

All peer commands live under remnic peer:

remnic peer list [--json]
remnic peer show <id> [--json]
remnic peer set <id> [--kind <kind>] [--display-name <name>] [--notes <text>] [--json]
remnic peer delete <id> [--json]
remnic peer forget <id> --confirm yes [--json]
remnic peer profile <id> [--json]
remnic peer migrate [--dry-run] [--display-name <name>] [--json]

remnic peer list

Lists all registered peers. --json emits { "peers": [...] }.

remnic peer show <id>

Shows the identity record for a single peer. Exits non-zero when the peer is not found.

remnic peer set <id>

Creates or updates a peer. On first write, --kind sets the peer kind (default "human" when omitted at service layer). On subsequent writes, kind is immutable — pass --display-name or --notes to update those fields instead.

remnic peer delete <id>

Removes peers/{id}/identity.md. The peer directory and companion files (profile.md, interactions.log.md) are left in place. Idempotent: returns a no-op result when the peer does not exist.

remnic peer forget <id> --confirm yes

DESTRUCTIVE. Purges the entire peer directory — identity.md, profile.md, interactions.log.md, and any other companion files under peers/{id}/. All data is permanently removed.

remnic peer forget <id> --confirm yes [--json]

Requires --confirm yes exactly. Any other value (or omitting the flag) aborts the command with a non-zero exit code and prints a usage error.

Properties:

  • Idempotent — if the peer directory does not exist the command returns a no-op result ({ ok: true, purged: false }) rather than erroring.
  • Safe to run twice — second call after a successful purge is a no-op.
  • All-or-nothing — uses fs.rm({ recursive: true }) so the OS handles partial-directory state atomically; no file-by-file manual cleanup.

Contrast with peer delete: peer delete only removes identity.md and leaves the peer directory and companion files intact (useful when you want to de-register a peer identity without losing its interaction history). peer forget removes everything.

Example output:

Purged all data for peer "codex-agent".
Peer "codex-agent" directory not found (no-op).

The --json flag emits { "ok": true, "purged": true|false }.

HTTP surface

DELETE /engram/v1/peers/:id?forget=true
Content-Type: application/json

{ "confirm": "yes" }

Returns 200 { "ok": true, "purged": true|false } on success. Returns 400 { "error": "confirm_required" } when the body omits { "confirm": "yes" }.

MCP tool

engram.peer_forget (also remnic.peer_forget):

{ "id": "codex-agent", "confirm": "yes" }

Returns { "ok": true, "purged": true|false }. Throws when confirm is absent or not "yes".

remnic peer profile <id>

Prints the evolving cognitive profile for a peer. The profile is written by the async reasoner; exits non-zero when no profile exists yet.

remnic peer migrate

Migrates legacy identity-anchor data into peers/self/identity.md.

remnic peer migrate [--dry-run] [--display-name <name>] [--json]

What it reads:

  • {memoryDir}/identity/identity-anchor.md — structured sections (## Identity Traits, ## Communication Preferences, etc.) written by engram_identity_anchor_update. Embedded verbatim in peer.notes under a ## Migrated from identity-anchor.md label.
  • {memoryDir}/IDENTITY.md — free-form reflection entries appended by the extraction engine. Embedded as a ## Migrated from IDENTITY.md section.

Both sources are optional. If neither exists, a self peer is created with no notes. Symlinked source files are silently skipped.

Properties:

  • Idempotent — if peers/self/identity.md already exists the command returns immediately without overwriting.
  • Non-destructive — legacy files are never deleted. Verify the result with remnic peer show self before archiving legacy data.
  • --dry-run — computes and prints the proposed peer record without writing anything to disk.

Example output:

Migrated identity-anchor data to peers/self/identity.md.
  Read anchor:  /path/to/memory/identity/identity-anchor.md

Legacy identity-anchor files are untouched. Verify the migration result
with `remnic peer show self` before archiving legacy files.

HTTP and MCP surfaces

PR 4/5 shipped the following HTTP endpoints and MCP tools:

Surface HTTP MCP tool
List peers GET /engram/v1/peers engram.peer_list
Get peer GET /engram/v1/peers/:id engram.peer_get
Set peer PUT /engram/v1/peers/:id engram.peer_set
Delete peer (identity only) DELETE /engram/v1/peers/:id engram.peer_delete
Forget peer (full purge) DELETE /engram/v1/peers/:id?forget=true (body: {"confirm":"yes"}) engram.peer_forget
Get profile GET /engram/v1/peers/:id/profile engram.peer_profile_get

Relationship to the legacy identity-anchor

The legacy engram_identity_anchor_get and engram_identity_anchor_update MCP tools continue to work unchanged. identityAnchorUpdate is now deprecated — the method is preserved for backward compatibility but will be removed in a future major version. Use peerSet({ id: "self", ... }) or remnic peer set self to update the self peer going forward.

After running remnic peer migrate you can verify with:

remnic peer show self

And then optionally archive the legacy files:

mv ~/.remnic/identity/identity-anchor.md ~/.remnic/identity/identity-anchor.md.bak

Privacy posture

  • Local-only by default. Peer kernel files are stored under the same memoryDir tree as the rest of Remnic. They are not synced or exported anywhere automatically.
  • No personality inference. The reasoner only derives profile fields from observable signals — explicit preferences, communication-style cues, recurring concerns, decision patterns. Sentiment-based or affective inference is a non-goal.
  • Provenance everywhere. Every profile field carries a list of PeerProfileFieldProvenance entries pointing back to the originating session/signal. You can audit exactly why a profile claim exists.
  • Forget is destructive. Use remnic peer forget <id> --confirm yes to permanently purge the full peer directory (identity, profile, and interaction log). Use remnic peer delete <id> to remove only the identity kernel while preserving companion files.
  • Capsule export gating. Capsule export (issue #676) does not include peer profiles unless explicitly opted in.