@zenera/cli 1.1.16 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -19
- package/dist/banner.d.ts +19 -0
- package/dist/banner.js +12 -9
- package/dist/catalog.js +19 -20
- package/dist/command.d.ts +3 -0
- package/dist/commands/check.js +5 -1
- package/dist/commands/index.js +4 -1
- package/dist/commands/init.js +42 -7
- package/dist/commands/inspect.js +77 -13
- package/dist/commands/key.js +49 -1
- package/dist/commands/memory.js +16 -3
- package/dist/commands/meta.d.ts +3 -0
- package/dist/commands/meta.js +596 -0
- package/dist/commands/run.js +64 -35
- package/dist/engine.d.ts +15 -0
- package/dist/engine.js +26 -1
- package/dist/env.d.ts +20 -0
- package/dist/env.js +65 -0
- package/dist/home.d.ts +2 -0
- package/dist/home.js +2 -0
- package/dist/keys.d.ts +2 -0
- package/dist/keys.js +2 -0
- package/dist/main.js +1 -1
- package/dist/meta.d.ts +221 -0
- package/dist/meta.js +695 -0
- package/dist/podman.js +64 -2
- package/dist/projects.d.ts +9 -1
- package/dist/projects.js +19 -2
- package/dist/resolve.js +7 -1
- package/dist/sandbox.d.ts +11 -0
- package/dist/sandbox.js +24 -4
- package/dist/scaffold.d.ts +6 -0
- package/dist/scaffold.js +56 -15
- package/dist/session.d.ts +10 -0
- package/dist/session.js +19 -1
- package/dist/term.d.ts +7 -0
- package/dist/term.js +8 -1
- package/dist/tui/app.js +33 -5
- package/dist/tui/wrap.d.ts +1 -1
- package/dist/tui/wrap.js +1 -1
- package/dist/validate.js +57 -15
- package/package.json +3 -2
- package/templates/editor/.github/copilot-instructions.md +313 -94
- package/templates/editor/.github/prompts/project-review.prompt.md +74 -0
- package/templates/editor/.github/prompts/spec-apply-feedback.prompt.md +142 -0
- package/templates/editor/.github/prompts/spec-sync-project.prompt.md +392 -0
- package/templates/editor/.github/skills/zen-cli/references/check.md +6 -3
- package/templates/editor/.github/skills/zen-cli/references/frame.md +5 -3
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +4 -3
- package/templates/editor/.github/skills/zen-cli/references/memory.md +14 -0
- package/templates/editor/.github/skills/zen-cli/references/projects.md +19 -2
- package/templates/editor/.github/skills/zen-cli/references/run.md +75 -16
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +6 -0
- package/templates/editor/.github/skills/zen-memory/SKILL.md +153 -33
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +115 -0
- package/templates/editor/.github/skills/zen-review/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +119 -0
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +104 -0
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +197 -0
- package/templates/editor/.github/skills/zen-spec-sync/scripts/snapshot.sh +200 -0
- package/templates/editor/.vscode/settings.json +44 -4
- package/templates/parts/memory-terms.yaml.tmpl +16 -0
- package/templates/parts/memory.yaml.tmpl +12 -0
- package/templates/project/SPECIFICATION.md.tmpl +32 -2
- package/templates/project/agents/memory-instructions.md +278 -0
- package/templates/project/agents.yaml.tmpl +12 -3
- package/templates/project/env +28 -0
- package/templates/project/gitignore +4 -4
- package/templates/editor/.github/prompts/review-project.prompt.md +0 -36
- package/templates/editor/.github/prompts/sync-with-spec.prompt.md +0 -205
- /package/templates/project/{INSTRUCTIONS.md → agents/instructions.md} +0 -0
|
@@ -29,6 +29,7 @@ skill.
|
|
|
29
29
|
| Flag | Meaning |
|
|
30
30
|
| ----------------------- | -------------------------------------------- |
|
|
31
31
|
| `--project <name\|dir>` | Which project. Inferred from the directory |
|
|
32
|
+
| `--dir <dir>` | Read this memory directory instead |
|
|
32
33
|
| `--kind <name>` | Only this kind of node |
|
|
33
34
|
| `--audience <name>` | Only nodes committed under this label |
|
|
34
35
|
| `--files` | Only nodes that remember a file |
|
|
@@ -38,6 +39,19 @@ skill.
|
|
|
38
39
|
| `--open` | Open the exported page |
|
|
39
40
|
| `--yes` | Do not ask before removing |
|
|
40
41
|
|
|
42
|
+
## A graph that is not the project's
|
|
43
|
+
|
|
44
|
+
`--dir` opens a memory directory as it stands, and skips project resolution
|
|
45
|
+
entirely: there need be no `agents.yaml` anywhere above it. That is what to use
|
|
46
|
+
for a graph a run was pointed at with `zen run --memory <dir>`, and for a copy
|
|
47
|
+
taken out of a running session - copy the directory, delete its `.lock`, and
|
|
48
|
+
read the copy while the run continues.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
zen run --memory .tmp/mem "…" remember into .tmp/mem for this run
|
|
52
|
+
zen memory --dir .tmp/mem stats then read that graph, from anywhere
|
|
53
|
+
```
|
|
54
|
+
|
|
41
55
|
## It reads unmasked, on purpose
|
|
42
56
|
|
|
43
57
|
Memory is masked per agent: a node's `audience` decides who can see it. Recall
|
|
@@ -19,18 +19,30 @@ What it writes:
|
|
|
19
19
|
|
|
20
20
|
```
|
|
21
21
|
zenera.json { version, name }
|
|
22
|
-
INSTRUCTIONS.md house rules
|
|
23
22
|
agents.yaml the configuration
|
|
23
|
+
agents/instructions.md house rules, prepended to every agent
|
|
24
|
+
agents/memory-instructions.md more of them, about the memory store
|
|
24
25
|
agents/prompts/default.md the default agent's prompt
|
|
25
26
|
agents/skills/ empty, for skills
|
|
26
27
|
assets/README.md
|
|
27
28
|
sandbox/Dockerfile the image the sandbox builds
|
|
28
29
|
sessions/ empty
|
|
30
|
+
.env this project's environment, git-ignored
|
|
29
31
|
.gitignore
|
|
30
32
|
.vscode/settings.json editor files
|
|
31
33
|
.github/ copilot instructions, prompts, this skill
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
More house rules can be added by hand: any `agents/<topic>-instructions.md` is
|
|
37
|
+
read too, in filename order, and prepended to every agent. A capability that
|
|
38
|
+
needs standing rules - memory, say - gets its own file rather than another
|
|
39
|
+
section of `agents/instructions.md`. Memory's are not written from scratch:
|
|
40
|
+
`agents/memory-instructions.md` is the same file as
|
|
41
|
+
`.github/skills/zen-memory/references/memory-instructions.md`, which init puts
|
|
42
|
+
in the project as well, so a copy that was lost or went stale is restored rather
|
|
43
|
+
than rewritten - `.github/skills/zen-memory/scripts/check-instructions.sh` says
|
|
44
|
+
which of the two it is, and `… fix` does the copy.
|
|
45
|
+
|
|
34
46
|
The project's own files are never overwritten - `--force` is what allows
|
|
35
47
|
writing into an occupied directory, and the files already there stay. The
|
|
36
48
|
editor files are the exception: `.vscode/settings.json` and the `.github/` tree
|
|
@@ -38,7 +50,12 @@ are ours and are replaced on every `init` and every `zen open`, so edits to them
|
|
|
38
50
|
do not survive.
|
|
39
51
|
|
|
40
52
|
The default agent gets the file tools and a sandboxed shell, plus `exa:*` when
|
|
41
|
-
the keyring holds an Exa key.
|
|
53
|
+
the keyring holds an Exa key. It also gets memory: the store is scaffolded in
|
|
54
|
+
`memory/`, the agent is bound to it with `memory: true`, and it is vectorised by
|
|
55
|
+
the chosen provider's embedding model. A provider that publishes none -
|
|
56
|
+
Anthropic - gets the store without an `embeddings:` entry, and recall ranks by
|
|
57
|
+
term overlap until one is added; an entry naming a provider with no embeddings
|
|
58
|
+
API is a project that fails to load, not one that degrades.
|
|
42
59
|
|
|
43
60
|
**Choosing the model.** Without `--model`, the keyring is asked - not counted.
|
|
44
61
|
Stored credentials are probed, because holding a key is not the same as holding
|
|
@@ -9,24 +9,78 @@ answers once on stdout and exits.
|
|
|
9
9
|
|
|
10
10
|
## Options
|
|
11
11
|
|
|
12
|
-
| Flag | What it does
|
|
13
|
-
| ----------------------- |
|
|
14
|
-
| `--project <name\|dir>` | Which project. Inferred from the directory otherwise
|
|
15
|
-
| `--session <id>` | Continue a particular session
|
|
16
|
-
| `--new` | Start a
|
|
17
|
-
| `--workspace <dir>` | What the agent may read and write
|
|
18
|
-
| `--
|
|
19
|
-
| `--
|
|
20
|
-
| `--
|
|
21
|
-
| `--
|
|
22
|
-
| `--plain` |
|
|
23
|
-
| `--theme <dark\|light>` |
|
|
24
|
-
| `--out <file>` |
|
|
25
|
-
| `--yes` |
|
|
12
|
+
| Flag | What it does |
|
|
13
|
+
| ----------------------- | -------------------------------------------------------- |
|
|
14
|
+
| `--project <name\|dir>` | Which project. Inferred from the directory otherwise |
|
|
15
|
+
| `--session <id>` | Continue a particular session |
|
|
16
|
+
| `--new` | Start a new session rather than continuing one |
|
|
17
|
+
| `--workspace <dir>` | What the agent may read and write |
|
|
18
|
+
| `--memory <dir>` | Where the agents remember into, instead of the project's |
|
|
19
|
+
| `--model <ref>` | Override the default model for this run |
|
|
20
|
+
| `--image <ref>` | Override the container image commands run in |
|
|
21
|
+
| `--read-only` | Withhold every tool that can write |
|
|
22
|
+
| `--plain` | Never open the TUI; a prompt is then required |
|
|
23
|
+
| `--theme <dark\|light>` | Palette for the TUI; `auto` detects. `$ZENERA_THEME` |
|
|
24
|
+
| `--out <file>` | Put the answer in this file instead of on stdout |
|
|
25
|
+
| `--yes` | Say yes to the workspace and install questions |
|
|
26
26
|
|
|
27
27
|
Flags always beat the file: the repository states intent, the invocation
|
|
28
28
|
overrides it.
|
|
29
29
|
|
|
30
|
+
## Where the answer goes
|
|
31
|
+
|
|
32
|
+
The answer goes to stdout and the progress to stderr, so `zen run "…" >
|
|
33
|
+
answer.md` leaves a file with the answer in it and nothing else, watched or not.
|
|
34
|
+
`--out` is a destination rather than a copy: it takes the answer off stdout, so
|
|
35
|
+
the file is the only place it lands.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
zen run "what changed?" > out.md answer in the file, progress on screen
|
|
39
|
+
zen run "what changed?" > out.md 2>/dev/null ... and nothing on screen at all
|
|
40
|
+
zen run --out out.md "what changed?" the same file, stdout left empty
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
There is no `--quiet`. Narration is on stderr already, so a redirect is enough;
|
|
44
|
+
`--json` silences it for machine use.
|
|
45
|
+
|
|
46
|
+
`--json` answers with the paths as well as the answer: the session and run
|
|
47
|
+
directories, every file the run left behind, and every tree the agent could see,
|
|
48
|
+
with the path it had on this machine beside the path it had inside the
|
|
49
|
+
container.
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"session": { "id": "...", "dir": "<project>/sessions/<id>" },
|
|
54
|
+
"run": {
|
|
55
|
+
"id": "...",
|
|
56
|
+
"dir": "<project>/sessions/<id>/runs/<run-id>",
|
|
57
|
+
"input": ".../input.md",
|
|
58
|
+
"output": ".../output.md",
|
|
59
|
+
"state": ".../state.json",
|
|
60
|
+
"meta": ".../meta.json",
|
|
61
|
+
"report": ".../report.html"
|
|
62
|
+
},
|
|
63
|
+
"mounts": [
|
|
64
|
+
{ "host": "...", "at": "/workspace", "readOnly": false },
|
|
65
|
+
{ "host": "...", "at": "/assets", "readOnly": true },
|
|
66
|
+
{ "host": "...", "at": "/skills/<name>", "readOnly": true }
|
|
67
|
+
],
|
|
68
|
+
"agent": "...",
|
|
69
|
+
"stopReason": "final",
|
|
70
|
+
"durationMs": 0,
|
|
71
|
+
"usage": {},
|
|
72
|
+
"output": "the answer"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`mounts` also lists the container's `$HOME` and, when keys are forwarded, the
|
|
77
|
+
credential files mounted under `/run/zenera/keys`.
|
|
78
|
+
|
|
79
|
+
`--plain` never opens the TUI. The TUI is the only thing that can ask for a
|
|
80
|
+
prompt, so with `--plain` the prompt has to be there already - in the argument
|
|
81
|
+
or on stdin - and `zen run --plain` with neither is an error instead of a
|
|
82
|
+
window. Say it in a script that must not meet one.
|
|
83
|
+
|
|
30
84
|
## Which word is the project
|
|
31
85
|
|
|
32
86
|
The first positional is read as a project when it names one, and as the first
|
|
@@ -42,7 +96,7 @@ zen run --project why "why?" when the project is called "why"
|
|
|
42
96
|
The prompt comes from the argument, or from stdin, or from the TUI:
|
|
43
97
|
|
|
44
98
|
```
|
|
45
|
-
git diff | zen run
|
|
99
|
+
git diff | zen run "summarise this diff"
|
|
46
100
|
```
|
|
47
101
|
|
|
48
102
|
## What a prompt on the command line implies
|
|
@@ -56,10 +110,15 @@ there is someone to ask, still asks.
|
|
|
56
110
|
There is no `resume`. A session continues itself, because its state is what it
|
|
57
111
|
is; `--session <id>` picks which one.
|
|
58
112
|
|
|
113
|
+
`--new` only matters when there is no prompt. A prompt on the command line
|
|
114
|
+
already starts a new session, so `zen run --new "what changed?"` is the same as
|
|
115
|
+
`zen run "what changed?"`. Without a prompt the TUI asks which session to
|
|
116
|
+
continue, and `--new` is how you skip that question. It contradicts `--session`.
|
|
117
|
+
|
|
59
118
|
## The TUI
|
|
60
119
|
|
|
61
120
|
Drawn only when there is a terminal on both stdin and stdout, no prompt,
|
|
62
|
-
no `--plain
|
|
121
|
+
no `--plain` and no `--json`. It streams the answer, shows
|
|
63
122
|
reasoning as it arrives, and reports per-turn and per-session token usage in the
|
|
64
123
|
footer.
|
|
65
124
|
|
|
@@ -47,6 +47,12 @@ Top level, and again per agent, where an agent's block is merged over the base.
|
|
|
47
47
|
`CREDENTIAL`: the keyring has already materialised real credentials into the
|
|
48
48
|
process environment, and a sandbox is the last place they should be forwarded to.
|
|
49
49
|
|
|
50
|
+
The door for those is the project's `.env`, which `zen init` writes and
|
|
51
|
+
`.gitignore` covers. `zen run` reads it before loading the project and forwards
|
|
52
|
+
every name in it into the container **by name**, so the value never reaches an
|
|
53
|
+
argv. `keys: false` and `zen run --no-keys` withhold it along with the API keys.
|
|
54
|
+
The shell's environment wins over the file; the keyring fills in the rest.
|
|
55
|
+
|
|
50
56
|
## `persist: true` is usually what you want
|
|
51
57
|
|
|
52
58
|
Only `/workspace` and `/home/agent` survive an ephemeral container. A
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zen-memory
|
|
3
|
-
description: How agent memory is organised and how to configure it in `agents.yaml` - the `memory:` block, per-agent `access`/`sees`/`writes`/`autoRecall`, the four `memory_*` tools, kinds and relations, how recall ranks and stitches a subgraph, and how to design a memory strategy for a project. Includes what to commit and what never to, how to keep a working file under `/memory` and re-run it later, and how to put memory in front of a `zen rag schema` or `zen rag docs` index so a search that already succeeded once is not paid for again.
|
|
3
|
+
description: How agent memory is organised and how to configure it in `agents.yaml` - the `memory:` block, per-agent `access`/`sees`/`writes`/`autoRecall`, the four `memory_*` tools, kinds and relations, how recall ranks and stitches a subgraph, and how to design a memory strategy for a project. Includes the usage rules every memory-enabled project must carry in `agents/memory-instructions.md` (references/memory-instructions.md) and the script that says whether that copy is still current (scripts/check-instructions.sh), what to commit and what never to, how to keep a working file under `/memory` and re-run it later, and how to put memory in front of a `zen rag schema` or `zen rag docs` index so a search that already succeeded once is not paid for again.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Memory
|
|
@@ -197,8 +197,9 @@ below.
|
|
|
197
197
|
└── .lock held while a run is writing; a dead pid's lock is stale
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
It is
|
|
201
|
-
mounted **read-only** at `/memory` in the agent's
|
|
200
|
+
It is committed with the project - everything but `.lock`, which is runtime
|
|
201
|
+
state. The directory is mounted **read-only** at `/memory` in the agent's
|
|
202
|
+
namespace.
|
|
202
203
|
|
|
203
204
|
## Designing a memory strategy
|
|
204
205
|
|
|
@@ -363,6 +364,40 @@ lands at `<project>/memory/files/<id>.<ext>` and appears to the agent as:
|
|
|
363
364
|
| Mount | `/memory`, **read-only** |
|
|
364
365
|
| Extension | preserved, because it decides how the file runs and renders |
|
|
365
366
|
|
|
367
|
+
### The agent has to be able to write the file first
|
|
368
|
+
|
|
369
|
+
`memory_commit` **copies** a file that already exists. It is not an upload
|
|
370
|
+
channel and it cannot create one. So everything too big for a node's `text` - a
|
|
371
|
+
script, a config, an assembled passage - depends on the agent having a tool that
|
|
372
|
+
puts bytes in the **workspace**, and an agent with none can only ever commit
|
|
373
|
+
text no matter how much the memory design leans on `file` nodes.
|
|
374
|
+
|
|
375
|
+
| The agent's `tools:` | Can it commit a `file`? |
|
|
376
|
+
| ---------------------------- | ----------------------------------------------------------------------- |
|
|
377
|
+
| `write_file` / `apply_patch` | Yes. Write it, then name the same path |
|
|
378
|
+
| `run_command` only | Yes, but only under `/workspace` - a heredoc into `/tmp` is unreachable |
|
|
379
|
+
| Neither | No. Everything worth keeping has to fit on one line of `text` |
|
|
380
|
+
|
|
381
|
+
The workspace is the only tree that round-trips, because it is the only
|
|
382
|
+
writable one. `/assets`, `/skills` and `/memory` resolve as well - they are
|
|
383
|
+
mounts the file tools know by name - but they are **read-only**, so nothing can
|
|
384
|
+
be put there to be remembered. Every other absolute path is real to
|
|
385
|
+
`run_command` and outside the workspace root, so a file written to `/tmp` comes
|
|
386
|
+
back as:
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
error: outside the workspace: /tmp/report.py
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
A bare error carrying none of the `hint` the other memory refusals do, because
|
|
393
|
+
containment is a workspace answer rather than a memory one.
|
|
394
|
+
|
|
395
|
+
This is the trap in a `tools:` list trimmed to `run_command` alone: the shell
|
|
396
|
+
can create the file, so `file` nodes look supported, but nothing in the agent's
|
|
397
|
+
context ever uses workspace vocabulary and the model has no reason to prefer
|
|
398
|
+
`/workspace` over `/tmp`. Either give it the file tools, or say in its prompt
|
|
399
|
+
that work it means to keep goes in `/workspace`.
|
|
400
|
+
|
|
366
401
|
Reusing one is the point of keeping it:
|
|
367
402
|
|
|
368
403
|
```
|
|
@@ -379,11 +414,14 @@ node explaining what it is and the graph cannot acquire orphans. To change a
|
|
|
379
414
|
remembered file: copy it into the workspace, edit it there, commit the new one
|
|
380
415
|
and link it with `SUPERSEDES` to the old.
|
|
381
416
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
|
386
|
-
|
|
|
417
|
+
The node text describes and locates; the file carries the content. Reach for a
|
|
418
|
+
node with no file only when the whole of what you know fits on one line.
|
|
419
|
+
|
|
420
|
+
| Commit as | When |
|
|
421
|
+
| --------- | --------------------------------------------------------------------------- |
|
|
422
|
+
| `file` | It is content - a script, a config, a query, a passage, an assembled answer |
|
|
423
|
+
| `snippet` | It is a few lines that only make sense read, and nothing will ever run them |
|
|
424
|
+
| `fact` | It is one line. A file containing one sentence is a fact in a costume |
|
|
387
425
|
|
|
388
426
|
## Memory in front of a `zen rag` index
|
|
389
427
|
|
|
@@ -400,18 +438,26 @@ it is entirely a matter of **what you commit**.
|
|
|
400
438
|
| Freshness | as fresh as the last `index` | as stale as it was allowed to get |
|
|
401
439
|
| Cost of a query | one embedding round trip (`search`), or nothing (`list`/`grep`) | already in the prompt |
|
|
402
440
|
|
|
403
|
-
So: **never copy index
|
|
404
|
-
stale silently and the index already answers in milliseconds; a
|
|
405
|
-
passage quoted into a node outlives the release it was true for
|
|
406
|
-
exactly as authoritative when it is wrong.
|
|
441
|
+
So: **never copy content out of an index that still changes.** A schema pasted
|
|
442
|
+
into a node goes stale silently and the index already answers in milliseconds; a
|
|
443
|
+
documentation passage quoted into a node outlives the release it was true for
|
|
444
|
+
and will look exactly as authoritative when it is wrong.
|
|
445
|
+
|
|
446
|
+
A **pinned** index is the exception, and a real one. Docs indexed from a
|
|
447
|
+
released version or a versioned reference tree cannot change under the memory,
|
|
448
|
+
so a passage taken out of one stays true as long as the project stays on that
|
|
449
|
+
version. Keep it as a `file` node whose text names the documents and line ranges
|
|
450
|
+
and says which version it was. That is as well as the route, never instead of
|
|
451
|
+
it.
|
|
407
452
|
|
|
408
|
-
What memory should hold is the **route** - the resolution, not the source:
|
|
453
|
+
What memory should hold first is the **route** - the resolution, not the source:
|
|
409
454
|
|
|
410
455
|
| Commit | Kind | Because |
|
|
411
456
|
| --------------------------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------- |
|
|
412
457
|
| The endpoint that turned out to be the answer, and its shape | `operation` | A `search_api` costs a round trip and returns five ranked guesses; this names one |
|
|
413
458
|
| The sequence of calls that was necessary | `plan` | No ranking finds an ordering. It is not in the document |
|
|
414
459
|
| Where the answer lives in the docs - document name, heading, line range | `fact` | `zen rag docs show` reads it back verbatim and current |
|
|
460
|
+
| The passage itself, when the index is pinned to a released version | `file` | It cannot change under you, and the pointer alone makes the next run re-read it |
|
|
415
461
|
| A convention the index cannot state - auth, casing, base path, the error envelope | `fact` | It is not in the graph, and it is wrong to re-derive it every run |
|
|
416
462
|
| Where the index itself is | `fact` | `-d` or `$ZEN_SCHEMA_DB`. An agent cannot guess a path |
|
|
417
463
|
| A working request body, or a script that drives the API | `snippet` / `file` | The thing you would otherwise write again |
|
|
@@ -451,8 +497,12 @@ cheap and settles it.
|
|
|
451
497
|
### The project skill is where this is written down
|
|
452
498
|
|
|
453
499
|
An index used by a project needs a skill in that project describing it - see
|
|
454
|
-
`zen-rag-schema` and `zen-rag-docs`.
|
|
455
|
-
because it is the same subject
|
|
500
|
+
`zen-rag-schema` and `zen-rag-docs`. The memory policy that is **about the
|
|
501
|
+
index** belongs in that skill, because it is the same subject and it is read at
|
|
502
|
+
the moment the index is. Policy that holds whatever the agent is doing -
|
|
503
|
+
audiences, what must never be written down - goes in
|
|
504
|
+
`agents/memory-policy-instructions.md` instead, which every agent gets whether
|
|
505
|
+
it loads the skill or not.
|
|
456
506
|
|
|
457
507
|
```md
|
|
458
508
|
- Recalled routes are a shortcut, not authority. Confirm with `list_api --name`
|
|
@@ -460,10 +510,61 @@ because it is the same subject:
|
|
|
460
510
|
- After solving something the index made you work for, commit it: the
|
|
461
511
|
`operation` you ended on, linked to the `task` that asked and any `fact` you
|
|
462
512
|
had to discover. Phrase the text as the question, not as the operation id.
|
|
463
|
-
- Never commit schema text or documentation passages
|
|
464
|
-
document, heading, line range -
|
|
513
|
+
- Never commit schema text or documentation passages copied out of a document
|
|
514
|
+
that still changes. Commit the pointer - document, heading, line range -
|
|
515
|
+
alongside what you concluded from it, and read the passage back with
|
|
516
|
+
`zen rag docs show` to check the conclusion still holds.
|
|
517
|
+
- Our docs are indexed from the pinned 1.4 tree, so a passage out of them is
|
|
518
|
+
safe to keep whole: commit it as a `file` node headed with the documents and
|
|
519
|
+
line ranges, next to the pointer and the conclusion.
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
## The house rules
|
|
523
|
+
|
|
524
|
+
**Every project that enables memory carries `references/memory-instructions.md`
|
|
525
|
+
in its own `agents/memory-instructions.md`**, as part of turning memory on. That
|
|
526
|
+
file is how the agent is told to use the store: read and search it only through
|
|
527
|
+
the tools, never through `/memory` itself, and what a node must carry to still be
|
|
528
|
+
worth having.
|
|
529
|
+
|
|
530
|
+
`zen init` writes it, because the projects it scaffolds have memory on from the
|
|
531
|
+
first run. Two cases are left to check by hand: a project that turned memory on
|
|
532
|
+
afterwards, and one whose copy has drifted from the reference or was deleted.
|
|
533
|
+
|
|
534
|
+
Neither is checked by eye. The skill ships the check, and it is one command:
|
|
535
|
+
|
|
536
|
+
```sh
|
|
537
|
+
.github/skills/zen-memory/scripts/check-instructions.sh # the verdict
|
|
538
|
+
.github/skills/zen-memory/scripts/check-instructions.sh diff # every differing line
|
|
539
|
+
.github/skills/zen-memory/scripts/check-instructions.sh fix # copy the reference over
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
It says whether this project turns memory on at all, exits non-zero on a copy
|
|
543
|
+
that is missing or stale, and prints the `cp` that repairs it. When the only
|
|
544
|
+
difference is trailing whitespace it says so - that is the usual drift, it is
|
|
545
|
+
invisible on screen, and it is the reason reading the two files side by side
|
|
546
|
+
never caught it.
|
|
547
|
+
|
|
548
|
+
It is a copy, not a transcription - this reference already sits in the project,
|
|
549
|
+
because `zen init` and `zen open` write the whole `.github/` tree, and they write
|
|
550
|
+
it from the same file the project's copy came from. What `fix` runs, from the
|
|
551
|
+
project root, is exactly:
|
|
552
|
+
|
|
553
|
+
```sh
|
|
554
|
+
cp .github/skills/zen-memory/references/memory-instructions.md agents/memory-instructions.md
|
|
465
555
|
```
|
|
466
556
|
|
|
557
|
+
Everything the project keeps under `agents/` named `<topic>-instructions.md` is
|
|
558
|
+
prepended to every agent, in filename order, so this belongs in its own document
|
|
559
|
+
rather than as another section of `agents/instructions.md` - it arrives with
|
|
560
|
+
memory and it leaves with it.
|
|
561
|
+
|
|
562
|
+
**Keep that file a pure copy.** Project-specific policy - audiences, what must
|
|
563
|
+
never be written down, what an agent commits at the end of a job - goes in
|
|
564
|
+
`agents/memory-policy-instructions.md`, which filename order puts directly after
|
|
565
|
+
it. Then keeping up with a changed reference is the same one command again,
|
|
566
|
+
rather than a hand-patch around prose that has to be preserved.
|
|
567
|
+
|
|
467
568
|
## Inspecting and repairing it
|
|
468
569
|
|
|
469
570
|
```
|
|
@@ -486,9 +587,17 @@ so inspection is free and offline.
|
|
|
486
587
|
| Why was that recalled? | `zen memory show <id>` |
|
|
487
588
|
| That should never have been written down | `zen memory forget <id>` |
|
|
488
589
|
|
|
590
|
+
The directory is not fixed. `zen run --memory <dir>` sends one run's memory
|
|
591
|
+
somewhere else - a scratch graph for a trial, one per branch, or a shared one
|
|
592
|
+
outside the repository - and `zen memory --dir <dir>` reads any such directory
|
|
593
|
+
with no project around it. Together they are also how you read a graph while a
|
|
594
|
+
run holds its `.lock`: copy the directory, delete the copy's `.lock`, and point
|
|
595
|
+
`--dir` at the copy.
|
|
596
|
+
|
|
489
597
|
`zen inspect` answers the other half: a run report shows the recollection block
|
|
490
598
|
exactly as the model received it, which is how you tell "memory had nothing"
|
|
491
|
-
apart from "memory had it and the model ignored it".
|
|
599
|
+
apart from "memory had it and the model ignored it". A run given `--memory`
|
|
600
|
+
wants `zen inspect --memory <dir>` to rebuild its report against the same graph.
|
|
492
601
|
|
|
493
602
|
## A worked configuration
|
|
494
603
|
|
|
@@ -524,10 +633,16 @@ agents:
|
|
|
524
633
|
|
|
525
634
|
## Review checklist
|
|
526
635
|
|
|
636
|
+
- [ ] `.github/skills/zen-memory/scripts/check-instructions.sh` exits zero, so
|
|
637
|
+
`agents/memory-instructions.md` is the reference byte for byte - and
|
|
638
|
+
project policy lives in `agents/memory-policy-instructions.md`.
|
|
527
639
|
- [ ] `memory.embedding` is set, and was set before the graph had content.
|
|
528
640
|
- [ ] Every agent that should learn has a `memory:` binding - the top-level
|
|
529
641
|
block alone enables nothing.
|
|
530
642
|
- [ ] `access: full` appears at most once.
|
|
643
|
+
- [ ] Any agent the design expects to commit `file` nodes can write to the
|
|
644
|
+
workspace - `write_file`/`apply_patch`, or `run_command` with `/workspace`
|
|
645
|
+
named in its prompt. Nothing else round-trips.
|
|
531
646
|
- [ ] `sees`/`writes` exist because an agent would be **misled** by the other
|
|
532
647
|
slice, not merely because it does not need it.
|
|
533
648
|
- [ ] `autoRecall: false` on any agent whose user message is a payload.
|
|
@@ -538,18 +653,23 @@ agents:
|
|
|
538
653
|
|
|
539
654
|
## When it goes wrong
|
|
540
655
|
|
|
541
|
-
| Symptom
|
|
542
|
-
|
|
|
543
|
-
| The `memory_*` tools are missing
|
|
544
|
-
| `zen memory` says the project has none
|
|
545
|
-
| Recall finds a memory only when reworded
|
|
546
|
-
| Recall finds nothing after changing the embedder
|
|
547
|
-
| Vectors fewer than nodes
|
|
548
|
-
| The graph fills with restated requests
|
|
549
|
-
|
|
|
550
|
-
|
|
|
551
|
-
|
|
|
552
|
-
| A
|
|
553
|
-
|
|
|
554
|
-
|
|
|
555
|
-
|
|
|
656
|
+
| Symptom | Cause |
|
|
657
|
+
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
658
|
+
| The `memory_*` tools are missing | No `memory:` on the agent. They come from the binding, never from `tools:` |
|
|
659
|
+
| `zen memory` says the project has none | Neither a `memory:` block nor an agent binding - nothing is opened and no directory is made |
|
|
660
|
+
| Recall finds a memory only when reworded | No embedder. `zen memory stats` says `embedding none` |
|
|
661
|
+
| Recall finds nothing after changing the embedder | Refused rather than mixed - a manifest records the model. Change it back, or start a new memory |
|
|
662
|
+
| Vectors fewer than nodes | Some nodes were committed with no embedder; they are only reachable by term overlap |
|
|
663
|
+
| The graph fills with restated requests | The commit rule is not in a prompt or skill. State the "would a later run redo this" test |
|
|
664
|
+
| An agent greps `/memory` or reads `graph.json` | The house-rules block is missing from `agents/memory-instructions.md` - the system prompt never forbids it |
|
|
665
|
+
| A recalled fact cannot be checked or continued | Nodes were committed with no provenance. The block's "say where it came from" rule is what prevents it |
|
|
666
|
+
| Recall returns pointers and the run re-reads it all | Content was committed as `text` alone. A node's text describes and locates; the content belongs in a file |
|
|
667
|
+
| A wrong memory keeps coming back | It was edited instead of superseded, or superseded in the wrong direction - the **new** node is the source |
|
|
668
|
+
| An agent cannot see a node you can | Its `audience` is not in that agent's `sees`. Invisible and missing are the same thing, on purpose |
|
|
669
|
+
| "this agent cannot remember files" | The agent has no workspace, so a path cannot be resolved. Commit without `file` |
|
|
670
|
+
| `error: outside the workspace: /tmp/…` | The file is real to the shell and outside the only tree memory reads. Write it under `/workspace` |
|
|
671
|
+
| Long content arrives as `text`, never as a `file` | The agent has no tool that writes to the workspace, so it has no path to name |
|
|
672
|
+
| A remembered file will not run | It is not self-contained - it referenced the workspace it was written in |
|
|
673
|
+
| Recollections are large and unhelpful | `autoRecall.limit` is too high, or node texts describe answers rather than questions |
|
|
674
|
+
| The model recalls a route the API no longer has | Nothing re-checks memory against a rebuilt index. Supersede, and require verification |
|
|
675
|
+
| A second run of the same project refuses to start | The directory lock. A lock whose process is gone is stale and is taken over |
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Whether this project's memory house rules are still the rules.
|
|
4
|
+
#
|
|
5
|
+
# check-instructions.sh diff the copy against the reference
|
|
6
|
+
# check-instructions.sh diff the same, showing every differing line
|
|
7
|
+
# check-instructions.sh fix overwrite the copy from the reference
|
|
8
|
+
#
|
|
9
|
+
# `agents/memory-instructions.md` is a *copy* of the `zen-memory` skill's
|
|
10
|
+
# `references/memory-instructions.md`, and the reference is rewritten by every
|
|
11
|
+
# `zen init` and `zen open`. So the copy goes stale silently, and it drifts by
|
|
12
|
+
# trailing whitespace inside tables, which no one can see by reading. That is
|
|
13
|
+
# the whole reason this is a script and not a checklist line.
|
|
14
|
+
#
|
|
15
|
+
# Run it from anywhere; it finds the project root from its own location.
|
|
16
|
+
|
|
17
|
+
set -eu
|
|
18
|
+
cd "$(dirname "$0")/../../../.."
|
|
19
|
+
|
|
20
|
+
COPY=agents/memory-instructions.md
|
|
21
|
+
REFERENCE=.github/skills/zen-memory/references/memory-instructions.md
|
|
22
|
+
REMEDY="cp $REFERENCE $COPY"
|
|
23
|
+
|
|
24
|
+
if [ ! -f "$REFERENCE" ]; then
|
|
25
|
+
echo "check-instructions.sh: no $REFERENCE in $PWD" >&2
|
|
26
|
+
echo "check-instructions.sh: run \`zen open\` to rewrite the .github tree" >&2
|
|
27
|
+
exit 2
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# The project turns memory on when something declares it — the top-level block
|
|
31
|
+
# or an agent's binding — so any `memory:` key in the configuration counts.
|
|
32
|
+
# The config is found by name, in the order a run finds it.
|
|
33
|
+
config() {
|
|
34
|
+
for file in agents.yaml agents.yml agents/agents.yaml agents/agents.yml; do
|
|
35
|
+
if [ -f "$file" ]; then
|
|
36
|
+
echo "$file"
|
|
37
|
+
return 0
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
uses_memory() {
|
|
43
|
+
# `|| true` because no configuration at all is an answer, not a failure.
|
|
44
|
+
conf="$(config || true)"
|
|
45
|
+
if [ -z "$conf" ]; then
|
|
46
|
+
return 1
|
|
47
|
+
fi
|
|
48
|
+
grep -Eq '^[[:space:]]*(-[[:space:]]+)?memory:' "$conf"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# The file with every line's trailing whitespace removed. Two of these being
|
|
52
|
+
# equal is the drift nobody can see by reading, named as such.
|
|
53
|
+
trimmed() {
|
|
54
|
+
sed 's/[[:space:]]*$//' "$1"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
report() {
|
|
58
|
+
if uses_memory; then
|
|
59
|
+
echo "memory: on"
|
|
60
|
+
else
|
|
61
|
+
echo "memory: off"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [ ! -f "$COPY" ]; then
|
|
65
|
+
if uses_memory; then
|
|
66
|
+
echo "missing: $COPY"
|
|
67
|
+
echo "remedy: $REMEDY"
|
|
68
|
+
return 1
|
|
69
|
+
fi
|
|
70
|
+
echo "ok: no copy, and nothing needs one"
|
|
71
|
+
return 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if cmp -s "$REFERENCE" "$COPY"; then
|
|
75
|
+
if ! uses_memory; then
|
|
76
|
+
# Harmless, but it is rules for a capability nothing turned on.
|
|
77
|
+
echo "note: the copy is current, but no agent has a memory: binding"
|
|
78
|
+
fi
|
|
79
|
+
echo "ok: $COPY is the reference, byte for byte"
|
|
80
|
+
return 0
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo "drift: $COPY differs from the reference"
|
|
84
|
+
# Said out loud because it is invisible on screen, and is the usual cause:
|
|
85
|
+
# a trailing space inside a table, or a lost final newline.
|
|
86
|
+
if [ "$(trimmed "$REFERENCE")" = "$(trimmed "$COPY")" ]; then
|
|
87
|
+
echo "drift: trailing whitespace only — nothing you could have seen"
|
|
88
|
+
fi
|
|
89
|
+
echo "remedy: $REMEDY"
|
|
90
|
+
return 1
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
show_diff() {
|
|
94
|
+
report || true
|
|
95
|
+
# `-L` names the two sides, since both files are called the same thing.
|
|
96
|
+
diff -u -L reference -L "$COPY" "$REFERENCE" "$COPY" || true
|
|
97
|
+
# The report's verdict, not the diff's.
|
|
98
|
+
report >/dev/null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fix() {
|
|
102
|
+
cp "$REFERENCE" "$COPY"
|
|
103
|
+
echo "wrote: $COPY"
|
|
104
|
+
echo "note: project policy belongs in agents/memory-policy-instructions.md"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
case "${1:-status}" in
|
|
108
|
+
status) report ;;
|
|
109
|
+
diff) show_diff ;;
|
|
110
|
+
fix) fix ;;
|
|
111
|
+
*)
|
|
112
|
+
echo "usage: check-instructions.sh [status|diff|fix]" >&2
|
|
113
|
+
exit 2
|
|
114
|
+
;;
|
|
115
|
+
esac
|