# Agent Memory Extension NLSPEC

## Purpose

The Agent Memory extension gives a pi project a durable, human-editable memory store. Memories are project-local facts, decisions, preferences, and conventions that future agents can retrieve with normal file tools.

V1 intentionally avoids global memory, vector search, autonomous background writes, and public memory mutation tools.

## Non-goals

- No Global Store in v1.
- No vector database or embeddings.
- No custom memory search/read tool.
- No always-on background memory extraction.
- No public agent-callable memory write tools.
- No hard guarantee against shell-based mutation beyond simple obvious command blocking.

## Repository layout

```text
.pi/
  extensions/
    agent-memory/
      index.ts
      prompts/
        memory-recall.md
  memory/
    INDEX.md
    topics/
      <topic>.md
```

`.pi/memory/` is created lazily only when approved memories are first written.

## Memory store

The Project Store lives at `.pi/memory/`.

It contains:

- `INDEX.md`: generated Retrieval Map.
- `topics/*.md`: human-editable Topic Files.

### Topic filename normalization

Candidate topic names are normalized as:

- lowercase
- trimmed
- spaces/underscores converted to hyphens
- unsafe characters removed
- path separators rejected/removed
- fallback: `general`

## Memory Block format

Each memory is a Markdown section inside a Topic File.

```md
## mem_YYYYMMDD_HHMMSS_xxxx

Concepts: testing, auth, integration tests
Aliases: unit tests, test strategy
Applies to: src/auth/**, tests/auth/**
Source: user-confirmed
Created: 2026-05-27T10:30:12Z
Updated: 2026-05-27T10:30:12Z
Hash: sha256:<canonical-content-hash>

Prefer integration tests for auth flows because they catch middleware and session behavior that unit tests miss.
```

Required fields:

- heading matching `## mem_YYYYMMDD_HHMMSS_xxxx`
- non-empty `Concepts:`
- `Source:`
- `Created:`
- non-empty body
- `Hash:` for extension-created new blocks; legacy blocks without `Hash:` remain readable but should produce a reindex warning

Optional fields:

- `Aliases:`
- `Applies to:`
- `Updated:`; extension-created memories should include it equal to `Created:`

`Hash:` is a SHA-256 Canonical Content Hash for future OCC-protected update/forget operations. It is computed from canonicalized semantic fields, excluding `Updated:` and excluding `Hash:` itself. The canonical hash input is line-based:

```text
id:<memory-id>
concepts:<comma-joined normalized concepts in stored order>
aliases:<comma-joined normalized aliases in stored order>
applies_to:<comma-joined normalized applies_to in stored order>
source:<source>
created:<created>
body:<body trimmed with normalized LF line endings>
```

Append operations do not use OCC; appending agents are responsible for consulting relevant memories and avoiding duplicates before proposing a new memory.

Concepts and aliases are normalized by lowercase + trim only.

## Topic File format

A topic file starts with a human-editable warning:

```md
# testing

Human-editable project memories. Keep each memory as a `## mem_...` block with metadata lines, then run `/memory reindex` after manual edits.
```

New Memory Blocks are appended to the end of the selected Topic File.

## Retrieval Map format

`INDEX.md` is generated and overwritten by the extension.

```md
# Memory Retrieval Map

Generated by the agent-memory extension. Do not edit manually; edit Topic Files and run `/memory reindex`.

This file helps agents find relevant project memories. Start here, then read topic files under `topics/`.

## Topics

### testing
Path: topics/testing.md
Concepts: testing, auth, integration tests
Aliases: unit tests, test strategy
Applies to: src/auth/**

Summary:
- Prefer integration tests for auth flows.
- Mock external OAuth providers at the boundary.
```

The Retrieval Map is a lightweight retrieval aid, not a duplicate memory database.

## Prompt template

The extension bundles `prompts/memory-recall.md` and registers it via `resources_discover`.

The prompt should:

- retrieve memories relevant to a user query
- prefer using a scout/subagent if available
- otherwise use root-agent `read`/`grep`/`find`
- start at `.pi/memory/INDEX.md`
- read relevant Topic Files as needed
- return Memory IDs, topic paths, and short excerpts
- if `.pi/memory/INDEX.md` is missing, report: `No project memories exist yet.`

## Memory awareness

Writing memories is worthless if a fresh agent never knows they exist. To close the recall loop, the extension surfaces the Project Store at the start of each agent turn.

On `before_agent_start`, if `.pi/memory/INDEX.md` exists and the store has at least one topic, the extension appends a short awareness note to the chained system prompt. The note:

- states that durable project memories exist and points at `.pi/memory/INDEX.md`
- lists the current topic names
- tells the agent to consult the Retrieval Map and read relevant Topic Files before acting on related tasks
- reminds the agent not to edit `.pi/memory/` directly, and to use `/memory extract` and `/memory reindex`

Constraints:

- The note surfaces topic names and a pointer only; it never inlines memory bodies, so context cost stays bounded regardless of store size.
- The note is appended (chained), never replaces an existing system prompt.
- The note is skipped while `/memory extract` is running so it cannot pollute the extraction prompt.
- If the store does not exist or has no topics, no note is added.

Manual retrieval via the `memory-recall` prompt template remains available for explicit, deeper retrieval. The awareness note is the lightweight always-on nudge; the prompt template is the on-demand deep retrieval.

## Tool surface

### Normal tools

`memory_stores` is always available.

It reports:

```json
{
  "stores": [
    {
      "name": "project",
      "path": ".pi/memory",
      "exists": true,
      "indexPath": ".pi/memory/INDEX.md",
      "topics": ["testing", "architecture"]
    }
  ]
}
```

### Extraction-only tool

`memory_candidate` is available only during `/memory extract`.

It is non-mutating. It records proposed memories for later user review.

Required input:

```json
{
  "topic": "testing",
  "body": "Prefer integration tests for auth flows because...",
  "concepts": ["testing", "auth"]
}
```

Optional input:

```json
{
  "aliases": ["integration tests"],
  "applies_to": ["src/auth/**"]
}
```

Limits:

- maximum 5 candidates per extraction run
- body max 1000 characters
- concepts max 8
- aliases max 8
- applies_to max 8

If more than 5 candidates are proposed, keep the first 5 and warn.

No public `memory_add`, `memory_update`, or `memory_search` tools exist in v1. Forgetting is command-only through `/memory forget`.

## Commands

### `/memory status`

Shows Project Store status:

- path
- whether store exists
- index path
- topic names
- stale Retrieval Map warning if any topic file mtime is newer than `INDEX.md`

If `.pi/memory/` does not exist, show `Project Store: not created yet.` Do not create files.

### `/memory extract`

Explicit user-invoked Memory Extraction workflow.

Input to extraction:

- current session branch only
- user and assistant text only
- no tool results
- no extension custom entries/checkpoints
- existing `INDEX.md` content if present

Behavior:

1. Temporarily expose `memory_candidate`.
2. Ask the current root model to propose up to 5 add-only Memory Candidates.
3. Instruct the model to consult the Retrieval Map and relevant Topic Files before proposing appends, and to avoid duplicates already represented in memory.
4. Disable `memory_candidate` after extraction.
5. Show candidates one-by-one.
6. User may approve, reject, stop, or edit routing/index fields only.
7. Routing edit opens a small JSON editor for `topic`, `concepts`, `aliases`, and `applies_to`.
8. Invalid routing JSON reopens the editor with an error and cancel option.
9. Batch-write all approved candidates at the end.
10. Append Memory Blocks to Topic Files.
11. Rebuild `INDEX.md` using the same logic as `/memory reindex`.
12. If any memories were written, append a non-context `memory-extraction` checkpoint containing created memory IDs and timestamp.

If no candidates are found, notify the user and offer retry with guidance.

If `.pi/memory/` is missing, create it lazily only when approved candidates are written.

Approved extraction memories use:

```md
Source: user-confirmed
```

### `/memory forget <memory-id> [expected-hash]`

Explicitly removes one Memory Block from its Topic File, then rebuilds `INDEX.md` using the same logic as `/memory reindex`.

Behavior:

1. Validate the memory id shape.
2. Search Topic Files for the matching Memory Block.
3. If `expected-hash` is provided, fail when the current block hash differs.
4. Remove only the matching Memory Block section.
5. Rebuild `INDEX.md`.

Legacy Memory Blocks without `Hash:` may be forgotten without an expected hash. Future OCC-aware callers should provide `expected-hash` when available.

### `/memory reindex [store]`

V1 only supports the Project Store.

Accepted forms:

- `/memory reindex`
- `/memory reindex project`

If `.pi/memory/` does not exist, report `Project Store does not exist; nothing to reindex.`

Reindex behavior:

- parse `topics/*.md`
- best-effort index valid Memory Blocks
- warn on malformed blocks
- warn on legacy valid blocks without `Hash:`
- warn on blocks whose `Hash:` does not match current canonical content
- overwrite `INDEX.md`
- no confirmation required

## Safety rules

### Built-in file writes

Built-in `edit` and `write` tool calls targeting `.pi/memory/` are always blocked.

Internal extension writes are allowed.

### Bash commands

If a `bash` command contains `.pi/memory` and appears write/destructive, block it.

Block patterns include:

- `>`
- `>>`
- `rm`
- `mv`
- `cp`
- `sed -i`
- `perl -i`
- `tee`
- `touch`
- `mkdir`
- `rmdir`

Likely read/search commands are allowed, such as:

- `grep`
- `rg`
- `cat`
- `find`
- `ls`

Blocked message:

> Direct bash mutation of `.pi/memory/` is blocked. Use `/memory extract` to add memories or `/memory reindex` to rebuild the Retrieval Map.

### Memory Consultation

When built-in `read` or `grep` touches `.pi/memory/`, show a UI indication that memory was consulted.

In v1, consultations are UI-only and are not persisted as custom session entries.

## Human edits

Humans may edit Topic Files directly outside pi.

After manual edits, users should run `/memory reindex`.

`/memory status` warns if topic files appear newer than the Retrieval Map.

## Concurrency

Extraction writes, forget, and reindex use a store-level mutation queue/lock, e.g. `.pi/memory/.lock`.

No rollback is required for failed multi-file writes in v1. Fail with a clear error. Git can be used for recovery.

## Session persistence

Successful `/memory extract` writes append a non-context custom entry:

```json
{
  "type": "memory-extraction",
  "store": "project",
  "memoryIds": ["mem_20260527_103012_a7f3"],
  "timestamp": "2026-05-27T10:30:12Z"
}
```

This checkpoint is not included in future extraction prompts in v1.
