@zenera/cli 1.1.15 → 1.1.17
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 +201 -129
- package/dist/banner.d.ts +5 -1
- package/dist/banner.js +2 -2
- package/dist/catalog.js +19 -20
- package/dist/command.d.ts +5 -0
- package/dist/commands/check.js +5 -1
- package/dist/commands/init.js +41 -6
- package/dist/commands/run.js +4 -0
- package/dist/engine.js +6 -0
- package/dist/env.d.ts +20 -0
- package/dist/env.js +65 -0
- package/dist/keys.d.ts +2 -0
- package/dist/keys.js +2 -0
- package/dist/main.js +10 -6
- package/dist/sandbox.d.ts +9 -0
- package/dist/sandbox.js +23 -3
- package/dist/scaffold.d.ts +6 -0
- package/dist/scaffold.js +56 -15
- package/dist/validate.js +57 -15
- package/package.json +2 -2
- package/templates/editor/.github/copilot-instructions.md +501 -301
- package/templates/editor/.github/prompts/new-agent.prompt.md +6 -6
- package/templates/editor/.github/prompts/new-skill.prompt.md +7 -7
- package/templates/editor/.github/prompts/review-project.prompt.md +54 -15
- package/templates/editor/.github/prompts/sync-with-spec.prompt.md +229 -42
- package/templates/editor/.github/prompts/update-spec-from-feedback.prompt.md +142 -0
- package/templates/editor/.github/skills/zen-cli/SKILL.md +62 -6
- package/templates/editor/.github/skills/zen-cli/references/check.md +9 -6
- package/templates/editor/.github/skills/zen-cli/references/faker.md +14 -14
- package/templates/editor/.github/skills/zen-cli/references/frame.md +11 -9
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +3 -3
- package/templates/editor/.github/skills/zen-cli/references/keys.md +9 -9
- package/templates/editor/.github/skills/zen-cli/references/memory.md +11 -11
- package/templates/editor/.github/skills/zen-cli/references/models.md +8 -8
- package/templates/editor/.github/skills/zen-cli/references/projects.md +23 -6
- package/templates/editor/.github/skills/zen-cli/references/rag.md +191 -56
- package/templates/editor/.github/skills/zen-cli/references/run.md +3 -3
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +14 -8
- package/templates/editor/.github/skills/zen-memory/SKILL.md +144 -72
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +115 -0
- package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +71 -48
- package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +67 -67
- 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 +46 -3
- package/templates/parts/memory-terms.yaml.tmpl +16 -0
- package/templates/parts/memory.yaml.tmpl +12 -0
- package/templates/project/SPECIFICATION.md.tmpl +7 -0
- package/templates/project/{INSTRUCTIONS.md → agents/instructions.md} +1 -1
- package/templates/project/agents/memory-instructions.md +278 -0
- package/templates/project/agents.yaml.tmpl +9 -0
- package/templates/project/assets/README.md +1 -1
- package/templates/project/env +22 -0
- package/templates/project/sandbox/Dockerfile.tmpl +3 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zen-memory
|
|
3
|
-
description: How agent memory is organised and how to configure it in `agents.yaml`
|
|
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
|
|
7
7
|
|
|
8
8
|
Memory is what an agent knows that nobody wrote down for it. A skill is
|
|
9
9
|
authored, reviewed and versioned; a memory is **produced by a run** and read by
|
|
10
|
-
the runs that follow. Both are knowledge, and they are not interchangeable
|
|
10
|
+
the runs that follow. Both are knowledge, and they are not interchangeable -
|
|
11
11
|
anything authoritative belongs in a skill, where a human owns it.
|
|
12
12
|
|
|
13
|
-
It is a **graph**, not a list of rows. A node is one thing worth keeping
|
|
14
|
-
request, a plan, a fact, a script, a call that was made
|
|
13
|
+
It is a **graph**, not a list of rows. A node is one thing worth keeping - a
|
|
14
|
+
request, a plan, a fact, a script, a call that was made - and an edge says how
|
|
15
15
|
one led to another. That shape is the point: a script an agent wrote is useless
|
|
16
16
|
without the request that asked for it and the endpoint it calls, and a store
|
|
17
17
|
that can only return a ranked list leaves the joining to the model, every time.
|
|
@@ -36,11 +36,11 @@ agents:
|
|
|
36
36
|
That is the whole feature working the obvious way: the agent can search, load
|
|
37
37
|
and commit, and it recalls automatically before answering new user input.
|
|
38
38
|
|
|
39
|
-
The top-level block is optional
|
|
39
|
+
The top-level block is optional - an agent that says `memory: true` gets the
|
|
40
40
|
defaults without one. A project that mentions memory nowhere opens no store and
|
|
41
41
|
creates no directory.
|
|
42
42
|
|
|
43
|
-
### `memory:`
|
|
43
|
+
### `memory:` - the graph itself
|
|
44
44
|
|
|
45
45
|
| Field | Type | Default | Meaning |
|
|
46
46
|
| ----------- | ------------- | --------------- | ----------------------------------- |
|
|
@@ -53,7 +53,7 @@ creates no directory.
|
|
|
53
53
|
memory phrased the way the query happened to be phrased and misses the rest,
|
|
54
54
|
which is the failure mode that makes people conclude memory does not work. The
|
|
55
55
|
width is learned from the first response rather than declared, and changing the
|
|
56
|
-
model afterwards is **refused** rather than silently mixing two vector spaces
|
|
56
|
+
model afterwards is **refused** rather than silently mixing two vector spaces -
|
|
57
57
|
so choose it before the graph has anything in it.
|
|
58
58
|
|
|
59
59
|
```yaml
|
|
@@ -63,11 +63,11 @@ memory:
|
|
|
63
63
|
embedding: main
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
`zen memory stats` says `embedding none
|
|
66
|
+
`zen memory stats` says `embedding none - recall falls back to term overlap`
|
|
67
67
|
when this was skipped, and a vector count below the node count means some nodes
|
|
68
68
|
were committed before an embedder existed.
|
|
69
69
|
|
|
70
|
-
### `agents[].memory`
|
|
70
|
+
### `agents[].memory` - who may see and write what
|
|
71
71
|
|
|
72
72
|
```yaml
|
|
73
73
|
agents:
|
|
@@ -104,7 +104,7 @@ which of them the model is offered:
|
|
|
104
104
|
| --------------- | -------------------------------------------------------------------------- |
|
|
105
105
|
| `memory_search` | Ranks, then stitches. Returns an outline of a subgraph and a legend of ids |
|
|
106
106
|
| `memory_load` | Reads whole nodes by id. **The only call that counts as use** |
|
|
107
|
-
| `memory_commit` | Writes a subgraph
|
|
107
|
+
| `memory_commit` | Writes a subgraph - nodes and the edges between them - in one transaction |
|
|
108
108
|
| `memory_forget` | Removes nodes, their vectors and their file bytes together |
|
|
109
109
|
|
|
110
110
|
Search and load are split on purpose. Search hands back context the model never
|
|
@@ -112,7 +112,7 @@ asked for; counting that as use would poison recency ranking. Commit is one
|
|
|
112
112
|
call because a remembered thing is a subgraph, and building it with three calls
|
|
113
113
|
leaves the graph half-formed when the model stops early.
|
|
114
114
|
|
|
115
|
-
The system prompt already explains all of this to the agent
|
|
115
|
+
The system prompt already explains all of this to the agent - how to read a
|
|
116
116
|
recollection, when committing is worthwhile, why correction is a new node. That
|
|
117
117
|
text is **derived from `access`**, so an agent that cannot write is never told
|
|
118
118
|
how to. Do not restate it in a prompt; state project-specific policy instead.
|
|
@@ -132,15 +132,15 @@ later.
|
|
|
132
132
|
| `fact` | a durable truth about the environment or the domain |
|
|
133
133
|
| `snippet` | a short piece of code or configuration, too small to be a file |
|
|
134
134
|
| `file` | an artifact kept whole under `/memory` and re-runnable |
|
|
135
|
-
| `operation` | an external call that was made
|
|
135
|
+
| `operation` | an external call that was made - an endpoint, command or query |
|
|
136
136
|
| `preference` | a standing instruction from the user that applies to every run |
|
|
137
137
|
|
|
138
138
|
| Relation | Reads as | Effect on recall |
|
|
139
139
|
| ------------ | --------------------------------------------------------- | ------------------------------- |
|
|
140
|
-
| `PRODUCED` | the source led to the target being made (task → artifact) | spine
|
|
140
|
+
| `PRODUCED` | the source led to the target being made (task → artifact) | spine - pulled in first |
|
|
141
141
|
| `CALLS` | the source invokes the target | spine |
|
|
142
142
|
| `SUPERSEDES` | the source replaces the target; the target is stale | spine, and **hides** the target |
|
|
143
|
-
| `INFORMED` | the source was context the target was built from | background
|
|
143
|
+
| `INFORMED` | the source was context the target was built from | background - dropped first |
|
|
144
144
|
|
|
145
145
|
Override the vocabularies only if the defaults genuinely do not fit the domain.
|
|
146
146
|
A wider vocabulary is a vaguer one, and a kind nothing else understands is a
|
|
@@ -156,7 +156,7 @@ that kind to build the system prompt.
|
|
|
156
156
|
| `kind` | filters a search, and decides how it renders |
|
|
157
157
|
| `audience` | who may see it. `['*']` is everyone; denied by default |
|
|
158
158
|
| `createdAt` | provenance, and what `newer_than` filters on |
|
|
159
|
-
| `lastUsedAt`, `useCount` | bumped by `memory_load` alone
|
|
159
|
+
| `lastUsedAt`, `useCount` | bumped by `memory_load` alone - the input to recency |
|
|
160
160
|
| `revision` | optimistic concurrency, via `expected_revision` on a commit |
|
|
161
161
|
| `file` | the remembered bytes, if any |
|
|
162
162
|
|
|
@@ -167,7 +167,7 @@ that kind to build the system prompt.
|
|
|
167
167
|
nodes are excluded, and so are `preference` nodes unless asked for by kind.
|
|
168
168
|
2. **Order by recency, but do not admit by it.** Relevance alone clears the
|
|
169
169
|
threshold; decay by `lastUsedAt` only breaks the tie afterwards. Memory that
|
|
170
|
-
keeps proving useful surfaces ahead of memory written once and never read
|
|
170
|
+
keeps proving useful surfaces ahead of memory written once and never read -
|
|
171
171
|
without anything ageing out of reach however well it matches.
|
|
172
172
|
3. **Stitch.** Breadth-first from every seed at once, ignoring edge direction,
|
|
173
173
|
because a memory is as often reached from the artifact back to the request as
|
|
@@ -182,14 +182,14 @@ Defaults: 5 seeds, 2 hops, 25 nodes. `memory_search` takes `limit`, `max_hops`,
|
|
|
182
182
|
What comes back is a `<memory-recollection>` block: an indented outline where
|
|
183
183
|
the indentation _is_ the graph, each line reading `score kind id` or
|
|
184
184
|
`→relation kind id`, with each node's text under it **clipped to 160
|
|
185
|
-
characters**. That clip is a design constraint on how you write a node
|
|
185
|
+
characters**. That clip is a design constraint on how you write a node - see
|
|
186
186
|
below.
|
|
187
187
|
|
|
188
188
|
### On disk
|
|
189
189
|
|
|
190
190
|
```
|
|
191
191
|
<project>/memory/
|
|
192
|
-
├── manifest.json written LAST
|
|
192
|
+
├── manifest.json written LAST - a half-written memory reads as empty
|
|
193
193
|
├── graph.json nodes and edges, parsed whole on open
|
|
194
194
|
├── vectors.f32 raw little-endian float32, a quarter the size of JSON
|
|
195
195
|
├── vectors.json the ids those vectors belong to
|
|
@@ -206,9 +206,9 @@ mounted **read-only** at `/memory` in the agent's namespace.
|
|
|
206
206
|
|
|
207
207
|
**Would the next run otherwise have to work this out again?** That is the whole
|
|
208
208
|
question. A script that ran, an interface that behaved differently from its
|
|
209
|
-
documentation, a call sequence that turned out to be necessary
|
|
209
|
+
documentation, a call sequence that turned out to be necessary - yes. The
|
|
210
210
|
request restated, progress notes, anything unverified, anything a search already
|
|
211
|
-
returned
|
|
211
|
+
returned - no.
|
|
212
212
|
|
|
213
213
|
The second test is **would it still be true next month**. Memory has no expiry
|
|
214
214
|
and no reviewer. A fact about the environment survives; a fact about today's
|
|
@@ -222,9 +222,9 @@ ask for it, not the way the system that produced it names it.
|
|
|
222
222
|
|
|
223
223
|
| Instead of | Write |
|
|
224
224
|
| --------------------------------- | ------------------------------------------------------------------------------- |
|
|
225
|
-
| `POST /v2/subs/{id}:cancel
|
|
226
|
-
| `fix for the 409` | `A duplicate customer email returns 409, not 422
|
|
227
|
-
| `script.py` | `Weekly invoice reconciliation script
|
|
225
|
+
| `POST /v2/subs/{id}:cancel - 204` | `Cancelling a subscription: POST /v2/subs/{id}:cancel, 204, no body` |
|
|
226
|
+
| `fix for the 409` | `A duplicate customer email returns 409, not 422 - retry is wrong` |
|
|
227
|
+
| `script.py` | `Weekly invoice reconciliation script - reads billing CSV, writes a diff table` |
|
|
228
228
|
|
|
229
229
|
Front-load the identifying facts: the first 160 characters are what appears in
|
|
230
230
|
a recollection, and everything after that costs a `memory_load` to see.
|
|
@@ -238,7 +238,7 @@ task "reconcile invoices against the ledger for a month"
|
|
|
238
238
|
PRODUCED → file the script that did it
|
|
239
239
|
INFORMED ← fact "ledger exports use minor units; invoices use decimals"
|
|
240
240
|
file
|
|
241
|
-
CALLS → operation "GET /v2/invoices?created[gte]=
|
|
241
|
+
CALLS → operation "GET /v2/invoices?created[gte]= - cursor paging"
|
|
242
242
|
```
|
|
243
243
|
|
|
244
244
|
Committed separately, a later run finds a script with no idea why it exists, or
|
|
@@ -247,7 +247,7 @@ brings the rest.
|
|
|
247
247
|
|
|
248
248
|
**Never edit a memory to correct it.** Commit the new one and link it to the old
|
|
249
249
|
with `SUPERSEDES`. Recall follows the edge forward and hides what it points at,
|
|
250
|
-
so the wrong answer stops being served without being destroyed
|
|
250
|
+
so the wrong answer stops being served without being destroyed - and the reason
|
|
251
251
|
a thing changed is often the useful part. `zen memory ls --stale` is the list of
|
|
252
252
|
corrections that have happened.
|
|
253
253
|
|
|
@@ -255,7 +255,7 @@ A new node whose text is a near-duplicate (cosine ≥ 0.97) of one already there
|
|
|
255
255
|
is **folded into it**, and the existing id comes back under the model's ref, so
|
|
256
256
|
the links still land on the memory that was already present.
|
|
257
257
|
|
|
258
|
-
### Audiences: one graph, masked
|
|
258
|
+
### Audiences: one graph, masked - not one graph each
|
|
259
259
|
|
|
260
260
|
Every node carries an `audience`; `sees` is the set an agent reads. `*` is the
|
|
261
261
|
public slice and is always included, so a binding can only ever **widen** what
|
|
@@ -275,7 +275,7 @@ agent wrote this" but "which agent would be misled by this":
|
|
|
275
275
|
| One curator may delete | `access: full` on exactly one agent |
|
|
276
276
|
|
|
277
277
|
When `writes` names exactly one label, the tool schema omits the field and
|
|
278
|
-
applies it
|
|
278
|
+
applies it - there is nothing to choose and nothing for the model to get wrong.
|
|
279
279
|
Neither `sees` nor `writes` is ever a tool parameter: an agent that could name
|
|
280
280
|
its own audience could read another agent's slice just by asking.
|
|
281
281
|
|
|
@@ -285,7 +285,7 @@ model that can delete will occasionally tidy.
|
|
|
285
285
|
### Auto-recall
|
|
286
286
|
|
|
287
287
|
On by default, because an agent that has to remember to go looking mostly does
|
|
288
|
-
not. It fires **after new user input and after a hand-off**
|
|
288
|
+
not. It fires **after new user input and after a hand-off** - not before every
|
|
289
289
|
call, because recalling on every turn costs tokens and defeats prompt caching
|
|
290
290
|
for a marginal gain. A turn following a tool result does not re-recall.
|
|
291
291
|
|
|
@@ -294,7 +294,7 @@ for a marginal gain. A turn following a tool result does not re-recall.
|
|
|
294
294
|
| `true` (default) | Almost everything |
|
|
295
295
|
| `{ limit: 2 }` | A cheap model, or a graph big enough that five seeds is a page |
|
|
296
296
|
| `{ limit: 8 }` | A research agent where breadth beats prompt size |
|
|
297
|
-
| `false` | An agent that should decide for itself when to look
|
|
297
|
+
| `false` | An agent that should decide for itself when to look - it keeps the tools |
|
|
298
298
|
|
|
299
299
|
`autoRecall: false` is right for a tool-driven agent whose first turn is
|
|
300
300
|
mechanical, and for anything where the user's message is a payload rather than a
|
|
@@ -302,7 +302,7 @@ question.
|
|
|
302
302
|
|
|
303
303
|
### Preferences
|
|
304
304
|
|
|
305
|
-
A `preference` node is a standing instruction
|
|
305
|
+
A `preference` node is a standing instruction - "always report findings as a
|
|
306
306
|
table", "use ISO dates". These are **not recalled by similarity**, because they
|
|
307
307
|
are not about the request in front of the agent: an instruction on how to report
|
|
308
308
|
is nowhere near a question about gateway rules in embedding space, and a search
|
|
@@ -319,7 +319,7 @@ their ids:
|
|
|
319
319
|
|
|
320
320
|
They land in the cached prefix, so they cost once per run rather than once per
|
|
321
321
|
turn, and the id is what lets a model replace one with `SUPERSEDES` instead of
|
|
322
|
-
quietly ignoring it. They are left out of ordinary recall on purpose
|
|
322
|
+
quietly ignoring it. They are left out of ordinary recall on purpose - they are
|
|
323
323
|
in the prompt already.
|
|
324
324
|
|
|
325
325
|
Commit one only when the user's own words generalise: "always", "from now on",
|
|
@@ -329,7 +329,7 @@ wrote.
|
|
|
329
329
|
|
|
330
330
|
## Remembering files, and running them again
|
|
331
331
|
|
|
332
|
-
The workspace is disposable
|
|
332
|
+
The workspace is disposable - it may be a container about to be discarded - so a
|
|
333
333
|
reference to a path in it dangles the moment the run ends. `memory_commit` takes
|
|
334
334
|
a `file` argument on a node instead, and copies the **bytes** out:
|
|
335
335
|
|
|
@@ -339,7 +339,7 @@ a `file` argument on a node instead, and copies the **bytes** out:
|
|
|
339
339
|
{
|
|
340
340
|
"ref": "s",
|
|
341
341
|
"kind": "file",
|
|
342
|
-
"text": "Invoice reconciliation script
|
|
342
|
+
"text": "Invoice reconciliation script - reads the billing CSV, writes a diff table",
|
|
343
343
|
"file": "reconcile.py"
|
|
344
344
|
},
|
|
345
345
|
{ "ref": "t", "kind": "task", "text": "reconcile invoices against the ledger for March" }
|
|
@@ -358,7 +358,7 @@ lands at `<project>/memory/files/<id>.<ext>` and appears to the agent as:
|
|
|
358
358
|
| Rule | Value |
|
|
359
359
|
| --------- | ---------------------------------------------------------------- |
|
|
360
360
|
| Size cap | 2 MiB. Over it, keep the artifact and commit a `snippet` instead |
|
|
361
|
-
| Inlined | under 32 KiB
|
|
361
|
+
| Inlined | under 32 KiB - `memory_load` returns the contents |
|
|
362
362
|
| Larger | `memory_load` returns the path; open or run it |
|
|
363
363
|
| Mount | `/memory`, **read-only** |
|
|
364
364
|
| Extension | preserved, because it decides how the file runs and renders |
|
|
@@ -379,11 +379,14 @@ node explaining what it is and the graph cannot acquire orphans. To change a
|
|
|
379
379
|
remembered file: copy it into the workspace, edit it there, commit the new one
|
|
380
380
|
and link it with `SUPERSEDES` to the old.
|
|
381
381
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
|
386
|
-
|
|
|
382
|
+
The node text describes and locates; the file carries the content. Reach for a
|
|
383
|
+
node with no file only when the whole of what you know fits on one line.
|
|
384
|
+
|
|
385
|
+
| Commit as | When |
|
|
386
|
+
| --------- | --------------------------------------------------------------------------- |
|
|
387
|
+
| `file` | It is content - a script, a config, a query, a passage, an assembled answer |
|
|
388
|
+
| `snippet` | It is a few lines that only make sense read, and nothing will ever run them |
|
|
389
|
+
| `fact` | It is one line. A file containing one sentence is a fact in a costume |
|
|
387
390
|
|
|
388
391
|
## Memory in front of a `zen rag` index
|
|
389
392
|
|
|
@@ -400,19 +403,27 @@ it is entirely a matter of **what you commit**.
|
|
|
400
403
|
| Freshness | as fresh as the last `index` | as stale as it was allowed to get |
|
|
401
404
|
| Cost of a query | one embedding round trip (`search`), or nothing (`list`/`grep`) | already in the prompt |
|
|
402
405
|
|
|
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.
|
|
406
|
+
So: **never copy content out of an index that still changes.** A schema pasted
|
|
407
|
+
into a node goes stale silently and the index already answers in milliseconds; a
|
|
408
|
+
documentation passage quoted into a node outlives the release it was true for
|
|
409
|
+
and will look exactly as authoritative when it is wrong.
|
|
410
|
+
|
|
411
|
+
A **pinned** index is the exception, and a real one. Docs indexed from a
|
|
412
|
+
released version or a versioned reference tree cannot change under the memory,
|
|
413
|
+
so a passage taken out of one stays true as long as the project stays on that
|
|
414
|
+
version. Keep it as a `file` node whose text names the documents and line ranges
|
|
415
|
+
and says which version it was. That is as well as the route, never instead of
|
|
416
|
+
it.
|
|
407
417
|
|
|
408
|
-
What memory should hold is the **route**
|
|
418
|
+
What memory should hold first is the **route** - the resolution, not the source:
|
|
409
419
|
|
|
410
420
|
| Commit | Kind | Because |
|
|
411
421
|
| --------------------------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------- |
|
|
412
422
|
| 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
423
|
| The sequence of calls that was necessary | `plan` | No ranking finds an ordering. It is not in the document |
|
|
414
|
-
| Where the answer lives in the docs
|
|
415
|
-
|
|
|
424
|
+
| Where the answer lives in the docs - document name, heading, line range | `fact` | `zen rag docs show` reads it back verbatim and current |
|
|
425
|
+
| 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 |
|
|
426
|
+
| 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
427
|
| Where the index itself is | `fact` | `-d` or `$ZEN_SCHEMA_DB`. An agent cannot guess a path |
|
|
417
428
|
| A working request body, or a script that drives the API | `snippet` / `file` | The thing you would otherwise write again |
|
|
418
429
|
| That a search returned nothing, and `grep` confirmed absence | `fact` | Proving a negative twice is pure waste |
|
|
@@ -420,7 +431,7 @@ What memory should hold is the **route** — the resolution, not the source:
|
|
|
420
431
|
### Why it makes search cheaper
|
|
421
432
|
|
|
422
433
|
Auto-recall fires **before the model's first tool call of the turn**. So the
|
|
423
|
-
recollection arrives before the decision to search is made
|
|
434
|
+
recollection arrives before the decision to search is made - and a recalled
|
|
424
435
|
`operation` node naming `POST /v2/subs/{id}:cancel` removes the search
|
|
425
436
|
entirely, rather than making it faster. When the recollection is only close, it
|
|
426
437
|
still narrows: the model reaches for `list_api --name` or `grep_api` instead of
|
|
@@ -436,7 +447,7 @@ and the answer second.
|
|
|
436
447
|
An index is rebuilt when the source changes; memory is not. Two habits keep the
|
|
437
448
|
two from drifting:
|
|
438
449
|
|
|
439
|
-
1. **Name the version in the node text**
|
|
450
|
+
1. **Name the version in the node text** - "Billing API v2", "acme\_4.1.0 docs".
|
|
440
451
|
A recollection about a version the project no longer uses is then visibly
|
|
441
452
|
about the wrong thing rather than invisibly wrong.
|
|
442
453
|
2. **Supersede after a re-index.** When a route changes, commit the new
|
|
@@ -450,9 +461,13 @@ cheap and settles it.
|
|
|
450
461
|
|
|
451
462
|
### The project skill is where this is written down
|
|
452
463
|
|
|
453
|
-
An index used by a project needs a skill in that project describing it
|
|
454
|
-
`zen-rag-schema` and `zen-rag-docs`.
|
|
455
|
-
because it is the same subject
|
|
464
|
+
An index used by a project needs a skill in that project describing it - see
|
|
465
|
+
`zen-rag-schema` and `zen-rag-docs`. The memory policy that is **about the
|
|
466
|
+
index** belongs in that skill, because it is the same subject and it is read at
|
|
467
|
+
the moment the index is. Policy that holds whatever the agent is doing -
|
|
468
|
+
audiences, what must never be written down - goes in
|
|
469
|
+
`agents/memory-policy-instructions.md` instead, which every agent gets whether
|
|
470
|
+
it loads the skill or not.
|
|
456
471
|
|
|
457
472
|
```md
|
|
458
473
|
- Recalled routes are a shortcut, not authority. Confirm with `list_api --name`
|
|
@@ -460,10 +475,61 @@ because it is the same subject:
|
|
|
460
475
|
- After solving something the index made you work for, commit it: the
|
|
461
476
|
`operation` you ended on, linked to the `task` that asked and any `fact` you
|
|
462
477
|
had to discover. Phrase the text as the question, not as the operation id.
|
|
463
|
-
- Never commit schema text or documentation passages
|
|
464
|
-
|
|
478
|
+
- Never commit schema text or documentation passages copied out of a document
|
|
479
|
+
that still changes. Commit the pointer - document, heading, line range -
|
|
480
|
+
alongside what you concluded from it, and read the passage back with
|
|
481
|
+
`zen rag docs show` to check the conclusion still holds.
|
|
482
|
+
- Our docs are indexed from the pinned 1.4 tree, so a passage out of them is
|
|
483
|
+
safe to keep whole: commit it as a `file` node headed with the documents and
|
|
484
|
+
line ranges, next to the pointer and the conclusion.
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## The house rules
|
|
488
|
+
|
|
489
|
+
**Every project that enables memory carries `references/memory-instructions.md`
|
|
490
|
+
in its own `agents/memory-instructions.md`**, as part of turning memory on. That
|
|
491
|
+
file is how the agent is told to use the store: read and search it only through
|
|
492
|
+
the tools, never through `/memory` itself, and what a node must carry to still be
|
|
493
|
+
worth having.
|
|
494
|
+
|
|
495
|
+
`zen init` writes it, because the projects it scaffolds have memory on from the
|
|
496
|
+
first run. Two cases are left to check by hand: a project that turned memory on
|
|
497
|
+
afterwards, and one whose copy has drifted from the reference or was deleted.
|
|
498
|
+
|
|
499
|
+
Neither is checked by eye. The skill ships the check, and it is one command:
|
|
500
|
+
|
|
501
|
+
```sh
|
|
502
|
+
.github/skills/zen-memory/scripts/check-instructions.sh # the verdict
|
|
503
|
+
.github/skills/zen-memory/scripts/check-instructions.sh diff # every differing line
|
|
504
|
+
.github/skills/zen-memory/scripts/check-instructions.sh fix # copy the reference over
|
|
465
505
|
```
|
|
466
506
|
|
|
507
|
+
It says whether this project turns memory on at all, exits non-zero on a copy
|
|
508
|
+
that is missing or stale, and prints the `cp` that repairs it. When the only
|
|
509
|
+
difference is trailing whitespace it says so - that is the usual drift, it is
|
|
510
|
+
invisible on screen, and it is the reason reading the two files side by side
|
|
511
|
+
never caught it.
|
|
512
|
+
|
|
513
|
+
It is a copy, not a transcription - this reference already sits in the project,
|
|
514
|
+
because `zen init` and `zen open` write the whole `.github/` tree, and they write
|
|
515
|
+
it from the same file the project's copy came from. What `fix` runs, from the
|
|
516
|
+
project root, is exactly:
|
|
517
|
+
|
|
518
|
+
```sh
|
|
519
|
+
cp .github/skills/zen-memory/references/memory-instructions.md agents/memory-instructions.md
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Everything the project keeps under `agents/` named `<topic>-instructions.md` is
|
|
523
|
+
prepended to every agent, in filename order, so this belongs in its own document
|
|
524
|
+
rather than as another section of `agents/instructions.md` - it arrives with
|
|
525
|
+
memory and it leaves with it.
|
|
526
|
+
|
|
527
|
+
**Keep that file a pure copy.** Project-specific policy - audiences, what must
|
|
528
|
+
never be written down, what an agent commits at the end of a job - goes in
|
|
529
|
+
`agents/memory-policy-instructions.md`, which filename order puts directly after
|
|
530
|
+
it. Then keeping up with a changed reference is the same one command again,
|
|
531
|
+
rather than a hand-patch around prose that has to be preserved.
|
|
532
|
+
|
|
467
533
|
## Inspecting and repairing it
|
|
468
534
|
|
|
469
535
|
```
|
|
@@ -472,8 +538,8 @@ zen memory [stats|ls|show|export|forget] [args] [options]
|
|
|
472
538
|
|
|
473
539
|
`zen memory export --open` is the one to reach for: one self-contained HTML
|
|
474
540
|
page with every node, the graph as a diagram, and the remembered file's actual
|
|
475
|
-
content in the detail pane. It reads **unmasked**
|
|
476
|
-
wrong, the hidden part is exactly the part you need
|
|
541
|
+
content in the detail pane. It reads **unmasked** - when the mask is what is
|
|
542
|
+
wrong, the hidden part is exactly the part you need - and it contacts no model,
|
|
477
543
|
so inspection is free and offline.
|
|
478
544
|
|
|
479
545
|
| Question | Command |
|
|
@@ -524,32 +590,38 @@ agents:
|
|
|
524
590
|
|
|
525
591
|
## Review checklist
|
|
526
592
|
|
|
593
|
+
- [ ] `.github/skills/zen-memory/scripts/check-instructions.sh` exits zero, so
|
|
594
|
+
`agents/memory-instructions.md` is the reference byte for byte - and
|
|
595
|
+
project policy lives in `agents/memory-policy-instructions.md`.
|
|
527
596
|
- [ ] `memory.embedding` is set, and was set before the graph had content.
|
|
528
|
-
- [ ] Every agent that should learn has a `memory:` binding
|
|
597
|
+
- [ ] Every agent that should learn has a `memory:` binding - the top-level
|
|
529
598
|
block alone enables nothing.
|
|
530
599
|
- [ ] `access: full` appears at most once.
|
|
531
600
|
- [ ] `sees`/`writes` exist because an agent would be **misled** by the other
|
|
532
601
|
slice, not merely because it does not need it.
|
|
533
602
|
- [ ] `autoRecall: false` on any agent whose user message is a payload.
|
|
534
|
-
- [ ] Nothing authoritative is expected to live here
|
|
603
|
+
- [ ] Nothing authoritative is expected to live here - that is a skill's job.
|
|
535
604
|
- [ ] The project's index skill says to verify a recalled route.
|
|
536
605
|
- [ ] `zen memory export --open` after a week of use, to see what it actually
|
|
537
606
|
learned rather than what you hoped.
|
|
538
607
|
|
|
539
608
|
## When it goes wrong
|
|
540
609
|
|
|
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
|
-
| A
|
|
610
|
+
| Symptom | Cause |
|
|
611
|
+
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
612
|
+
| The `memory_*` tools are missing | No `memory:` on the agent. They come from the binding, never from `tools:` |
|
|
613
|
+
| `zen memory` says the project has none | Neither a `memory:` block nor an agent binding - nothing is opened and no directory is made |
|
|
614
|
+
| Recall finds a memory only when reworded | No embedder. `zen memory stats` says `embedding none` |
|
|
615
|
+
| Recall finds nothing after changing the embedder | Refused rather than mixed - a manifest records the model. Re-embed or change it back |
|
|
616
|
+
| Vectors fewer than nodes | Some nodes were committed with no embedder; they are only reachable by term overlap |
|
|
617
|
+
| 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 |
|
|
618
|
+
| 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 |
|
|
619
|
+
| 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 |
|
|
620
|
+
| 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 |
|
|
621
|
+
| A wrong memory keeps coming back | It was edited instead of superseded, or superseded in the wrong direction - the **new** node is the source |
|
|
622
|
+
| 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 |
|
|
623
|
+
| "this agent cannot remember files" | The agent has no workspace, so a path cannot be resolved. Commit without `file` |
|
|
624
|
+
| A remembered file will not run | It is not self-contained - it referenced the workspace it was written in |
|
|
625
|
+
| Recollections are large and unhelpful | `autoRecall.limit` is too high, or node texts describe answers rather than questions |
|
|
626
|
+
| The model recalls a route the API no longer has | Nothing re-checks memory against a rebuilt index. Supersede, and require verification |
|
|
627
|
+
| 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
|