@zenera/cli 1.1.10 → 1.1.12

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.
Files changed (65) hide show
  1. package/README.md +69 -15
  2. package/dist/banner.js +69 -47
  3. package/dist/cache.d.ts +98 -0
  4. package/dist/cache.js +301 -0
  5. package/dist/catalog.d.ts +3 -0
  6. package/dist/catalog.js +35 -11
  7. package/dist/commands/cache.d.ts +7 -0
  8. package/dist/commands/cache.js +245 -0
  9. package/dist/commands/check.js +6 -3
  10. package/dist/commands/index.js +5 -0
  11. package/dist/commands/init.js +8 -4
  12. package/dist/commands/key.js +68 -14
  13. package/dist/commands/memory.d.ts +3 -0
  14. package/dist/commands/memory.js +375 -0
  15. package/dist/commands/models.js +17 -1
  16. package/dist/commands/run.js +11 -3
  17. package/dist/commands/sandbox.js +70 -23
  18. package/dist/engine.js +14 -9
  19. package/dist/home.d.ts +2 -2
  20. package/dist/home.js +2 -2
  21. package/dist/keys.d.ts +22 -0
  22. package/dist/keys.js +105 -2
  23. package/dist/lib.d.ts +1 -0
  24. package/dist/lib.js +1 -0
  25. package/dist/liveness.js +11 -0
  26. package/dist/main.js +9 -0
  27. package/dist/podman.js +43 -9
  28. package/dist/projects.js +10 -2
  29. package/dist/resolve.d.ts +4 -0
  30. package/dist/resolve.js +43 -17
  31. package/dist/scaffold.js +22 -1
  32. package/dist/term.d.ts +23 -2
  33. package/dist/term.js +215 -8
  34. package/dist/tui/app.d.ts +10 -0
  35. package/dist/tui/app.js +478 -54
  36. package/dist/tui/theme.d.ts +13 -2
  37. package/dist/tui/theme.js +26 -13
  38. package/dist/tui/wrap.d.ts +92 -0
  39. package/dist/tui/wrap.js +147 -2
  40. package/dist/validate.d.ts +2 -0
  41. package/dist/validate.js +87 -2
  42. package/package.json +2 -2
  43. package/templates/editor/.github/copilot-instructions.md +200 -55
  44. package/templates/editor/.github/prompts/new-agent.prompt.md +5 -2
  45. package/templates/editor/.github/prompts/review-project.prompt.md +5 -0
  46. package/templates/editor/.github/prompts/sync-with-spec.prompt.md +11 -4
  47. package/templates/editor/.github/skills/zen-cli/SKILL.md +3 -0
  48. package/templates/editor/.github/skills/zen-cli/references/faker.md +18 -8
  49. package/templates/editor/.github/skills/zen-cli/references/keys.md +7 -7
  50. package/templates/editor/.github/skills/zen-cli/references/memory.md +113 -0
  51. package/templates/editor/.github/skills/zen-cli/references/rag.md +2 -2
  52. package/templates/editor/.github/skills/zen-cli/references/run.md +3 -1
  53. package/templates/editor/.github/skills/zen-memory/SKILL.md +555 -0
  54. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +575 -0
  55. package/templates/editor/.github/skills/{api-schema-index → zen-rag-schema}/SKILL.md +4 -4
  56. package/templates/editor/.vscode/settings.json +4 -2
  57. package/templates/parts/exa.prompt.md.tmpl +4 -0
  58. package/templates/parts/exa.spec.md.tmpl +2 -0
  59. package/templates/project/INSTRUCTIONS.md +8 -0
  60. package/templates/project/SPECIFICATION.md.tmpl +47 -0
  61. package/templates/project/agents/prompts/{default.md → default.md.tmpl} +4 -0
  62. package/templates/project/agents.yaml.tmpl +4 -0
  63. package/templates/project/gitignore +7 -1
  64. package/templates/project/scripts/_setup.sh +101 -0
  65. package/templates/project/SPECIFICATION.md +0 -6
@@ -141,8 +141,9 @@ my-project/
141
141
  │ ├── SKILL.md
142
142
  │ └── scripts/calculate.py run at /skills/refund_policy/scripts — §3.4.1
143
143
  ├── assets/ reference material, read-only at /assets
144
+ ├── memory/ the graph agents remember into — §5.3
144
145
  ├── sandbox/Dockerfile the image commands run in — §3.7
145
- └── sessions/ run state, memory, whatever the agent wrote
146
+ └── sessions/ run state, whatever the agent wrote
146
147
  ```
147
148
 
148
149
  Only `agents.yaml` is required, and only `agents:` is required inside it.
@@ -224,6 +225,81 @@ agents do, for the same reason — it is a prefix somebody pays for:
224
225
  hand, so **edits inside `.github/` do not survive**. Project-specific conventions
225
226
  belong in `INSTRUCTIONS.md` and the agent prompts, which are never overwritten.
226
227
 
228
+ ### 2.5 What a running agent can actually see
229
+
230
+ The tree in §2.1 is the **author's** view. A running agent sees almost none of
231
+ it. Its entire world is the tools it was granted (§3.5) and four paths:
232
+
233
+ | Path | Is | Access |
234
+ | ------------ | ----------------------------------------- | ------------------------------------------------------ |
235
+ | `/workspace` | the session's workspace — the work itself | read/write; `zen run --read-only` withholds the writes |
236
+ | `/assets` | `assets/`, reference material — §3.11 | read-only |
237
+ | `/skills` | the whole skill catalog — §3.4.1 | read-only |
238
+ | `/memory` | files carried by remembered nodes — §5.3 | read-only, and only when `memory:` is configured |
239
+
240
+ Commands run inside the container `sandbox:` describes (§3.7): the same
241
+ `/workspace`, and whatever the image ships.
242
+
243
+ Everything else in the project directory is **invisible at run time** —
244
+ `agents.yaml`, `INSTRUCTIONS.md`, `agents/prompts/`, `agents/skills/` under that
245
+ name, `sandbox/Dockerfile`, `.env`, `.github/`, `sessions/`, and the project
246
+ root itself. `zen` reads them to build the run; they are never a path an agent
247
+ can open.
248
+
249
+ Two consequences:
250
+
251
+ - **The workspace is not the project.** It defaults to the session's own empty
252
+ folder, and is usually some other repository entirely — `--workspace`, or the
253
+ directory the user was standing in. A prompt saying "read `agents.yaml`" or
254
+ "the rules are in `agents/skills/`" names a file that is not there.
255
+ - **Write the mount path, never the repository path.**
256
+ `/skills/refund_policy/rates.csv`, not
257
+ `agents/skills/refund_policy/rates.csv`; `/assets/handbook.md`, not
258
+ `assets/handbook.md`. A relative path resolves against `/workspace`, which is
259
+ the one place the file is not.
260
+
261
+ An agent that must know something about its own configuration is **told it in
262
+ prose**, in the prompt — never sent to read the file that configured it.
263
+
264
+ #### Checklist — no path an agent cannot open
265
+
266
+ Run this over `INSTRUCTIONS.md`, every `agents/prompts/*.md` and every
267
+ `agents/skills/*/SKILL.md` after editing any of them. Every path-shaped token in
268
+ those files is read by a model as an instruction to open something:
269
+
270
+ - [ ] Every absolute path begins `/workspace`, `/assets`, `/skills` or
271
+ `/memory`. Nothing else exists.
272
+ - [ ] No project file is named: `agents.yaml`, `INSTRUCTIONS.md`,
273
+ `agents/prompts/…`, `agents/skills/…`, `sandbox/Dockerfile`, `.env`,
274
+ `.github/…`, `sessions/…`, `SPECIFICATION.md`, `README.md`, `docs/…`.
275
+ - [ ] Nothing escapes a mount: no `../`, no `~`, no host path (`/Users/…`,
276
+ `/home/<someone>`), no `/etc`, `/var` or `/tmp` expected to hold anything
277
+ the project put there.
278
+ - [ ] Each `/assets/…` and `/skills/<name>/…` path names a file that is actually
279
+ committed there — `zen check` lists what every skill folder ships (§3.4.1).
280
+ - [ ] A skill referring to its own sibling file writes the full
281
+ `/skills/<name>/` prefix, not `./rates.csv`: the body is rendered into a
282
+ prompt, not executed from its folder.
283
+ - [ ] `/memory` is named only in prompts for agents that have `memory:` (§5.3),
284
+ and `/assets` only where an `assets/` directory exists.
285
+ - [ ] The agent holding the prompt has the tool the path implies — `read_file`
286
+ to open one, `sandbox:*` to run one (§3.4.1).
287
+ - [ ] Every instruction that opens a file states what to do when it is not there
288
+ (§4.2, rule 6). Workspaces differ between runs; absence is normal.
289
+ - [ ] Relative paths appear only where `/workspace`-relative is what was meant.
290
+
291
+ Finding the candidates, from the project root — every path-shaped token in the
292
+ three trees, minus the ones under a mount:
293
+
294
+ ```sh
295
+ grep -Eohr '[~.]?/?[A-Za-z0-9_.-]*/[A-Za-z0-9_.*-]+' \
296
+ INSTRUCTIONS.md agents/prompts agents/skills \
297
+ | sort -u | grep -Ev '^/(workspace|assets|skills|memory)(/|$)'
298
+ ```
299
+
300
+ Everything it prints is either a false positive (a url, a `path/to` in a
301
+ sentence, `and/or`) or a path the agent cannot open. There is no third case.
302
+
227
303
  ---
228
304
 
229
305
  ## 3. File formats
@@ -307,6 +383,9 @@ wrong provider (§7.3), and any combination of knobs the vendor rejects at reque
307
383
  time, such as OpenAI reasoning on chat completions (§7.6). Both surface on the
308
384
  first call, so read §7 before writing a `models:` entry.
309
385
 
386
+ **Caught by `zen check`, not by the loader** — a handoff with no path back
387
+ (`handoff.one-way`, §6.3). The project runs; it just cannot return.
388
+
310
389
  **Comments in `agents.yaml`** — this file is the architecture diagram of the
311
390
  project, and its comments are read by whoever has to change it next. Write them
312
391
  at that level:
@@ -1028,22 +1107,38 @@ agents:
1028
1107
 
1029
1108
  ### 5.3 Memory
1030
1109
 
1031
- Memory is the read-write twin of skills: written by the run, not authored. A
1032
- **scope** is a namespace string; agents bound to the same scope share what is
1033
- in it, and the default scope is the agent's own — so memory is private until
1034
- something says otherwise. Recall is automatic: matches for the current input
1035
- are injected before each call, which is what makes memory work without the
1036
- model remembering to look, and an agent with write access also gets tools to
1037
- search, add, update and delete entries.
1110
+ Memory is the read-write twin of skills: written by the run, not authored. It is
1111
+ a **graph** — a node is one thing worth keeping, an edge says how one led to
1112
+ another — so recall returns the connected piece rather than a ranked list, and a
1113
+ remembered script arrives with the request that asked for it.
1038
1114
 
1039
- It is a session-level facility rather than an `agents.yaml` key: `zen run`
1040
- binds each session's store under `sessions/`, so memory travels with the
1041
- session and is not part of this repository.
1115
+ It is configured in `agents.yaml`: a top-level `memory:` block says where the
1116
+ graph lives and what vectorises it, and `agents[].memory` says what each agent
1117
+ may do with it. It lives with the **project**, not the session — a memory that
1118
+ died with the session would be a cache. Recall is automatic on turns that follow
1119
+ new user input, which is what makes memory work without the model remembering to
1120
+ look.
1121
+
1122
+ ```yaml
1123
+ memory:
1124
+ embedding: main # without one, recall falls back to term overlap
1125
+ agents:
1126
+ - name: auditor
1127
+ memory: true # read, write, and recall before each new question
1128
+ ```
1129
+
1130
+ Every node carries an **audience**, and an agent's `sees` is the set it reads:
1131
+ one graph, masked per agent, never one graph each. The four `memory_*` tools are
1132
+ derived from `access` and are never listed in `tools:`.
1042
1133
 
1043
1134
  Do not treat memory as a database. It is for things learned that should
1044
1135
  persist. Anything authoritative — a rate, a policy clause, a procedure — belongs
1045
1136
  in a skill, where it is versioned and reviewable.
1046
1137
 
1138
+ The full treatment — kinds and relations, how ranking and stitching spend their
1139
+ budget, remembered files under `/memory`, audience design, and how to put memory
1140
+ in front of a `zen rag` index — is in the `zen-memory` skill.
1141
+
1047
1142
  ### 5.4 Cache discipline
1048
1143
 
1049
1144
  Rules that follow directly from "stable prefix = cache hit":
@@ -1086,18 +1181,20 @@ Do **not** split because:
1086
1181
 
1087
1182
  **Router + specialists.** A cheap, fast agent whose only job is classification
1088
1183
  and handoff. Its prompt is short, it holds few tools, and it must be forbidden
1089
- from answering. Specialists never hand back to it.
1184
+ from answering. Every specialist hands back to it — a handoff does not return on
1185
+ its own, so without that edge the first routed question is the last one routed.
1090
1186
 
1091
1187
  ```yaml
1092
1188
  agents:
1093
1189
  - { name: intake, model: router, handoffs: [billing, technical, escalation] }
1094
- - { name: billing, model: balanced, skills: { allow: [refund_policy, invoicing] } }
1095
- - { name: technical, model: balanced, tools: [search_logs, restart_service] }
1096
- - { name: escalation, model: careful }
1190
+ - { name: billing, model: balanced, handoffs: [intake], skills: { allow: [refund_policy] } }
1191
+ - { name: technical, model: balanced, handoffs: [intake], tools: [search_logs] }
1192
+ - { name: escalation, model: careful, handoffs: [intake] }
1097
1193
  ```
1098
1194
 
1099
1195
  **Pipeline.** Fixed stages, each handing to the next; only the last answers the
1100
- user. Encode the order in `handoffs:` so a stage cannot skip ahead.
1196
+ user. Encode the order in `handoffs:` so a stage cannot skip ahead — and close
1197
+ the loop, by handing back from the last stage to the first.
1101
1198
 
1102
1199
  **Fan-out / join.** For independent parallel work — ten regions, four review
1103
1200
  lenses, six candidate suppliers — declare `fork:` on the agent that owns the
@@ -1113,12 +1210,39 @@ multi-agent instinct suggests.
1113
1210
  Write it as a routing condition: _"Applies the written peril policies to a
1114
1211
  claim and explains the outcome."_ — not _"The adjuster agent."_
1115
1212
  - Handoffs are bare name strings; there is no per-edge configuration.
1116
- - Self-handoff is a load error. Cycles are legal but usually a bug — a router in
1117
- the `handoffs` of its own specialists produces ping-pong.
1213
+ - Self-handoff is a load error.
1214
+ - **Every handoff must be returnable.** For every `A → B` there must be a path
1215
+ from `B` back to `A`. Indirect counts — `B → C → A` is a way home — so the
1216
+ rule is that every handoff edge lies on a cycle. `zen check` warns
1217
+ (`handoff.one-way`) on any edge that does not.
1118
1218
  - Handoff collapses history by policy: the receiving agent sees a selection, not
1119
1219
  the full transcript. Do not assume it saw a detail three turns back; if it
1120
1220
  matters, put it in the handoff.
1121
1221
 
1222
+ **Why the return edge is not optional.** A handoff is a one-way door: control
1223
+ moves and stays moved. Nothing hands it back, so an agent reached by a dead-end
1224
+ edge owns the conversation for the rest of the session — the next question,
1225
+ whatever it is about, is answered by the specialist the router sent the user to.
1226
+ The drawn architecture holds for one turn and then quietly stops being the
1227
+ architecture.
1228
+
1229
+ **Forks are the exception, and the alternative.** A branch runs, answers, and
1230
+ control returns to the agent that forked it; the return is the mechanism, not an
1231
+ edge anyone has to declare. So there are two shapes and they are not
1232
+ interchangeable:
1233
+
1234
+ - The other agent should **own the conversation from here** → `handoffs:`, and
1235
+ something on the far side has to lead back.
1236
+ - This agent needs **an answer and then carries on** → `fork:`. Never a handoff:
1237
+ a handoff spends the conversation to get the answer.
1238
+
1239
+ But the return is **condensed**: `A → fork → B` rejoins as one tool result
1240
+ holding B's answer, so A never sees the steps B took — no tool calls, no files
1241
+ read, no intermediate reasoning. That is what makes fanning out cheap, and it is
1242
+ what to design around: whatever A will need must be _in_ the answer, so say so in
1243
+ B's prompt. Work whose value is the trace rather than the conclusion does not
1244
+ survive a fork.
1245
+
1122
1246
  ### 6.4 Forking (fan-out / join)
1123
1247
 
1124
1248
  Forking is **opt-in per agent**. Without the key the agent is never offered the
@@ -1158,7 +1282,9 @@ Rules worth knowing before you write the key:
1158
1282
  - Nesting is capped by the run's `maxForkDepth` (2 by default), so a branch may
1159
1283
  fork again but not without bound.
1160
1284
  - Fork vs handoff: a handoff is _one_ conversation changing owner; a fork is the
1161
- _same_ question asked N times at once and merged.
1285
+ _same_ question asked N times at once and merged. **A fork returns; a handoff
1286
+ does not** — which is why a handoff needs a return path (§6.3) and a fork
1287
+ needs nothing.
1162
1288
 
1163
1289
  Choose `context:` deliberately — the model sets it per call, so say in the
1164
1290
  agent's prompt which one this work wants:
@@ -1237,8 +1363,8 @@ model: balanced # fallback for agents that do not pin their own
1237
1363
 
1238
1364
  agents:
1239
1365
  - { name: intake, model: router, handoffs: [adjuster] }
1240
- - { name: adjuster, model: balanced, handoffs: [escalation] }
1241
- - { name: escalation, model: careful }
1366
+ - { name: adjuster, model: balanced, handoffs: [escalation, intake] }
1367
+ - { name: escalation, model: careful, handoffs: [intake] }
1242
1368
  ```
1243
1369
 
1244
1370
  ### 7.3 Shorthand
@@ -1567,9 +1693,9 @@ zen models pick --embedding # or --chat; the ref goes to stdout, alone
1567
1693
  Then re-run `zen check`.
1568
1694
 
1569
1695
  CLI (`zen --help` for the authoritative list): `zen init`, `zen run`, `zen check`,
1570
- `zen inspect`, `zen key`, `zen models`, `zen list`. **stdout is the answer, stderr
1571
- is the narration**; every command takes `--json`. Exit codes: `0` ok, `1` failed,
1572
- `2` usage, `3` invalid project, `4` no usable credential.
1696
+ `zen inspect`, `zen memory`, `zen key`, `zen models`, `zen list`. **stdout is the
1697
+ answer, stderr is the narration**; every command takes `--json`. Exit codes: `0`
1698
+ ok, `1` failed, `2` usage, `3` invalid project, `4` no usable credential.
1573
1699
 
1574
1700
  ---
1575
1701
 
@@ -1582,7 +1708,7 @@ Before finishing any change here:
1582
1708
  - [ ] `agents.yaml` still loads; no unknown keys, no dangling names
1583
1709
  - [ ] Top-level `default:` names the entry agent explicitly
1584
1710
  - [ ] Every agent has a `description:` written as a routing condition
1585
- - [ ] No self-handoff; no accidental cycle back to the router
1711
+ - [ ] No self-handoff; every handoff has a path back (`zen check` warns if not)
1586
1712
  - [ ] Names match `^[a-z0-9]+(?:[-_][a-z0-9]+)*$`
1587
1713
  - [ ] An agent expected to fan out has `fork:`, and its prompt says when to use it
1588
1714
  - [ ] Comments explain the design, not the runtime or the key they sit above
@@ -1596,6 +1722,16 @@ Before finishing any change here:
1596
1722
  - [ ] No facts, rates or figures embedded in a prompt
1597
1723
  - [ ] No hedging, no meta-talk about the runtime
1598
1724
 
1725
+ **Paths (§2.5 — run its checklist over the three trees)**
1726
+
1727
+ - [ ] Every absolute path in `INSTRUCTIONS.md`, `agents/prompts/*.md` and
1728
+ `agents/skills/*/SKILL.md` is under `/workspace`, `/assets`, `/skills` or
1729
+ `/memory`
1730
+ - [ ] No prompt or skill names a project file — `agents.yaml`, a prompt path, a
1731
+ skill's repository path, `sandbox/Dockerfile`, `.env`, `sessions/`
1732
+ - [ ] No `../`, `~`, or host path; nothing assumes the workspace is the project
1733
+ - [ ] Every `/assets/…` and `/skills/<name>/…` path names a committed file
1734
+
1599
1735
  **Skills**
1600
1736
 
1601
1737
  - [ ] Every skill is `<name>/SKILL.md` — no bare `<name>.md` in the catalog
@@ -1640,34 +1776,37 @@ Before finishing any change here:
1640
1776
 
1641
1777
  ## 10. Where to change what
1642
1778
 
1643
- | Symptom | Change this |
1644
- | ------------------------------------------ | ------------------------------------------------------------------- |
1645
- | Wrong tone, wrong format, wrong length | `INSTRUCTIONS.md` (all agents) or the agent prompt |
1646
- | Says something forbidden | `INSTRUCTIONS.md` prohibition, stated specifically |
1647
- | Ignores a rule that only applies sometimes | Move the rule into a skill with a sharp description |
1648
- | Never loads the skill it should | The skill's `description`; or `preload` it |
1649
- | Loads too much, answers slowly | `allow:`, `maxIndexEntries:`, or `discovery: search` |
1650
- | Invents a number | A skill holding the figure, or a script that computes it — §3.4.1 |
1651
- | A skill in the catalog is never offered | It is a bare `<name>.md`; move it to `<name>/SKILL.md` — §3.4 |
1652
- | Cannot find a file its own skill names | Absolute `/skills/<name>/…` path, and `sandbox:*` — §3.4.1 |
1653
- | Rewrites a whole file to change one line | A prompt line preferring `apply_patch` — §3.6 |
1654
- | Edits files it should only be reading | Subtract the mutating tools, or `zen run --read-only` |
1655
- | Cannot run the build or the tests | Grant `sandbox:*`; add the toolchain to `sandbox/Dockerfile` |
1656
- | Installs the same packages on every run | Put them in `sandbox/Dockerfile`, or `sandbox.persist: true` — §3.7 |
1657
- | Answers from stale knowledge of the world | Grant `web_search` + `web_read`, and say when — §3.8 |
1658
- | Cites a page it only saw the excerpt of | A prompt line: `web_read` before quoting — §3.8 |
1659
- | Every web call refuses | No Exa key: `zen key add exa` — `zen check` warns — §3.8 |
1660
- | A model or embedder refuses every call | `zen models test <ref>` — if `blocked`, `zen models pick` — §8 |
1661
- | Answers instead of routing | Router prompt prohibition; check `handoffs:` |
1662
- | Routes to the wrong specialist | The target agents' `description:` fields |
1663
- | Loses a detail after a handoff | Say it in the handoff; check the collapse policy |
1664
- | Works through N independent items serially | `fork:` on that agent, and a prompt line — §6.4 |
1665
- | Forks when the steps actually depend | Prompt line: branches cannot see each other |
1666
- | Slow and expensive on trivial cases | Demote that agent's model tier / reasoning effort |
1667
- | Fails only on genuinely hard cases | Promote that agent's tier, or split the hard path out |
1668
- | Shows no reasoning while it works | Turn summaries on for that model — §7.6 |
1669
- | Forgets across conversations | Continue the session rather than starting a new one |
1670
- | Breaks at load with a named path | Read the message — it names the exact key |
1779
+ | Symptom | Change this |
1780
+ | --------------------------------------------- | ------------------------------------------------------------------- |
1781
+ | Wrong tone, wrong format, wrong length | `INSTRUCTIONS.md` (all agents) or the agent prompt |
1782
+ | Says something forbidden | `INSTRUCTIONS.md` prohibition, stated specifically |
1783
+ | Ignores a rule that only applies sometimes | Move the rule into a skill with a sharp description |
1784
+ | Never loads the skill it should | The skill's `description`; or `preload` it |
1785
+ | Loads too much, answers slowly | `allow:`, `maxIndexEntries:`, or `discovery: search` |
1786
+ | Invents a number | A skill holding the figure, or a script that computes it — §3.4.1 |
1787
+ | A skill in the catalog is never offered | It is a bare `<name>.md`; move it to `<name>/SKILL.md` — §3.4 |
1788
+ | Cannot find a file its own skill names | Absolute `/skills/<name>/…` path, and `sandbox:*` — §3.4.1 |
1789
+ | Tries to open `agents.yaml` or its own prompt | The prompt names a file no agent can see — §2.5 |
1790
+ | Says a file is missing that is in the project | The workspace is not the project directory — §2.5 |
1791
+ | Rewrites a whole file to change one line | A prompt line preferring `apply_patch` — §3.6 |
1792
+ | Edits files it should only be reading | Subtract the mutating tools, or `zen run --read-only` |
1793
+ | Cannot run the build or the tests | Grant `sandbox:*`; add the toolchain to `sandbox/Dockerfile` |
1794
+ | Installs the same packages on every run | Put them in `sandbox/Dockerfile`, or `sandbox.persist: true` — §3.7 |
1795
+ | Answers from stale knowledge of the world | Grant `web_search` + `web_read`, and say when — §3.8 |
1796
+ | Cites a page it only saw the excerpt of | A prompt line: `web_read` before quoting — §3.8 |
1797
+ | Every web call refuses | No Exa key: `zen key add exa` — `zen check` warns — §3.8 |
1798
+ | A model or embedder refuses every call | `zen models test <ref>` — if `blocked`, `zen models pick` — §8 |
1799
+ | Answers instead of routing | Router prompt prohibition; check `handoffs:` |
1800
+ | Routes to the wrong specialist | The target agents' `description:` fields |
1801
+ | Gets stuck in the agent it routed to | The target needs a handoff back — §6.3 |
1802
+ | Loses a detail after a handoff | Say it in the handoff; check the collapse policy |
1803
+ | Works through N independent items serially | `fork:` on that agent, and a prompt line — §6.4 |
1804
+ | Forks when the steps actually depend | Prompt line: branches cannot see each other |
1805
+ | Slow and expensive on trivial cases | Demote that agent's model tier / reasoning effort |
1806
+ | Fails only on genuinely hard cases | Promote that agent's tier, or split the hard path out |
1807
+ | Shows no reasoning while it works | Turn summaries on for that model — §7.6 |
1808
+ | Forgets across conversations | Continue the session rather than starting a new one |
1809
+ | Breaks at load with a named path | Read the message — it names the exact key |
1671
1810
 
1672
1811
  ---
1673
1812
 
@@ -1680,6 +1819,10 @@ Before finishing any change here:
1680
1819
  versioned, cannot be shared, and is paid for on every call.
1681
1820
  - **The bare skill file.** `agents/skills/<name>.md` instead of a folder. It can
1682
1821
  never grow the table or script the next revision of the rule will want — §3.4.
1822
+ - **Pointing an agent at the project.** A prompt or skill naming `agents.yaml`,
1823
+ `INSTRUCTIONS.md`, `agents/skills/<name>/…` or `sandbox/Dockerfile`. None of
1824
+ them are on any path the agent can open, so the instruction fails on the turn
1825
+ that follows it — §2.5.
1683
1826
  - **Arithmetic in prose.** A skill that walks the model through a calculation it
1684
1827
  will get wrong, in a folder that could have held the script — §3.4.1.
1685
1828
  - **Politeness padding.** "Please try your best to be helpful." Costs tokens,
@@ -1689,8 +1832,10 @@ Before finishing any change here:
1689
1832
  - **Fixing prompts with models.** See §4.4.
1690
1833
  - **The chatty router.** A router that answers before handing off, because its
1691
1834
  prompt never forbade it.
1692
- - **Ping-pong handoffs.** Specialists that hand back to the router, which hands
1693
- back to a specialist.
1835
+ - **The one-way handoff.** A specialist with no way back, so the router routes
1836
+ once and then never sees the conversation again — §6.3.
1837
+ - **Handing off for an answer.** Wanting one lookup and spending the whole
1838
+ conversation on it. That is a `fork:`, which returns by itself — §6.4.
1694
1839
  - **Forking a chain.** Branches never see each other, so a fork whose second
1695
1840
  branch needs the first branch's answer is a sequence wearing a fork's clothes.
1696
1841
  - **Installing the toolchain every run.** A prompt that begins with `apt-get
@@ -29,8 +29,11 @@ If it is warranted:
29
29
  3. Add the `agents.yaml` entry. `description:` is written **for the model** — it
30
30
  is the whole of what a sibling sees when deciding to hand off. Grant the
31
31
  narrowest `tools:` the job needs.
32
- 4. Wire the handoffs in both directions only if both directions are real.
33
- Specialists handing back to a router is ping-pong; prefer terminating.
32
+ 4. Wire the return edge. A handoff does not come back on its own, so every
33
+ `A → B` needs a path from B back to A — directly, or through another agent.
34
+ `zen check` warns on an edge that has none. If what this agent really wants
35
+ is an _answer_ rather than to give up the conversation, declare `fork:`
36
+ instead: a branch returns by itself.
34
37
  5. Add a one-line comment above the entry saying why this agent exists — its
35
38
  job or its tier, not what the keys mean.
36
39
 
@@ -17,6 +17,11 @@ Work the checklist in §9 of the copilot instructions. In particular:
17
17
  `INSTRUCTIONS.md` that is true of only one agent.
18
18
  - **Facts.** Any number, threshold, fee or date living in a prompt is in the
19
19
  wrong file. Name it and say which skill it belongs in.
20
+ - **Paths.** Work the checklist in §2.5 over `INSTRUCTIONS.md`,
21
+ `agents/prompts/*.md` and `agents/skills/*/SKILL.md`. Every absolute path must
22
+ be under `/workspace`, `/assets`, `/skills` or `/memory`; nothing may name a
23
+ project file the agent cannot open, and nothing may assume the workspace is
24
+ this directory.
20
25
  - **Skill descriptions.** Each one a condition, not a title; no two overlapping.
21
26
  - **Tools.** Every grant justified by the agent's job; nothing mutating held by
22
27
  an agent that only reads; every granted tool mentioned by the prompt.
@@ -98,16 +98,19 @@ not a paragraph of instructions for a human.
98
98
  - `scripts/_setup.sh` is the **only** entry point — the leading underscore is
99
99
  what separates the runner from the steps it runs. It runs the steps in the
100
100
  order they depend on each other and is the single command that initialises the
101
- project. Consolidate any setup script that lives elsewhere into this shape,
102
- including one left at the project root.
101
+ project. `zen init` writes it; adding a step is adding `scripts/<name>.sh` and
102
+ its name to the `STEPS` list at the top of it. Consolidate any setup script
103
+ that lives elsewhere into this shape, including one left at the project root.
103
104
  - Every script starts `set -eu`, works from any working directory
104
105
  (`cd "$(dirname "$0")/.."` first), needs no arguments, and exits non-zero on
105
106
  failure so `scripts/_setup.sh` stops rather than continuing on a broken step.
106
107
  - **Re-entrancy is required.** Running `scripts/_setup.sh` a second time must be
107
108
  safe. A step whose output is present and newer than its inputs prints that it
108
- is up to date and returns 0; otherwise it redoes the work idempotently, writing
109
+ is up to date and exits **3**, which is how the runner tells `skipped` from
110
+ `ok`; otherwise it redoes the work idempotently, writing
109
111
  to a temporary path and moving it into place so an interrupted run never leaves
110
- a half-built artefact behind. `scripts/_setup.sh --force` redoes everything.
112
+ a half-built artefact behind. `scripts/_setup.sh --force` redoes everything,
113
+ and reaches each step as `$FORCE=1`.
111
114
  - **It must be watchable.** `scripts/_setup.sh` tees each step's output to
112
115
  `.tmp/logs/setup-<step>.log`, prints a heartbeat line while a long step runs,
113
116
  and finishes with one line per step: `ok`, `skipped`, or `failed`.
@@ -170,6 +173,10 @@ It is slow — an embedding index is minutes, not seconds — so start it in the
170
173
  background and follow the logs instead of waiting blind:
171
174
 
172
175
  - Tail `.tmp/logs/setup-*.log` and check the output artefact is still growing.
176
+ - **Wait in a loop, never in one long sleep.** Sleep at most 30 seconds at a
177
+ time, then check the log and say what it shows, and repeat until the step
178
+ finishes. A single long block makes the run unwatchable and hides a failure
179
+ that happened in the first ten seconds.
173
180
  - Report progress as you go: which step is running, how long it has been going,
174
181
  what the last log line said.
175
182
  - Do not kill a quiet step. Confirm the process is dead or the log and the
@@ -24,6 +24,7 @@ zen key <ls|add|use|check|rm|show|env> [ref] [options]
24
24
  zen models <providers|ls|search|show|test|pick> [ref] [options]
25
25
  zen check [name|dir] [--project <name|dir>] [--no-sandbox] [--no-models] [--strict] [--quiet]
26
26
  zen inspect [run] [--session <id>] [--open] [--rebuild] [--serve [port]]
27
+ zen memory [stats|ls|show|export|forget] [args] [options]
27
28
  zen sandbox [status|up|pull|clean|disk] [options]
28
29
  zen version
29
30
 
@@ -48,6 +49,7 @@ guess a flag, and do not read them all.
48
49
  | Which models exist, whether one works, recovering from a refusal | [models.md](./references/models.md) |
49
50
  | The container shell commands run in, images, `persist` | [sandbox.md](./references/sandbox.md) |
50
51
  | Run reports, trajectories, what a session directory holds | [inspect.md](./references/inspect.md) |
52
+ | What the agents remember, why something was recalled, forgetting | [memory.md](./references/memory.md) |
51
53
  | `zen faker` — a mock API from an OpenAPI/Swagger document | [faker.md](./references/faker.md) |
52
54
  | `zen rag` — searching an API description, or a pile of markdown | [rag.md](./references/rag.md) |
53
55
 
@@ -59,6 +61,7 @@ zen check validate everything before spending a turn
59
61
  zen run the TUI
60
62
  zen run "what changed?" one answer, this directory as the workspace
61
63
  zen inspect --open what the model was actually given
64
+ zen memory export --open the whole memory graph, as a page
62
65
  zen key ls --check which credentials still work
63
66
  zen models test <ref> whether one model actually answers
64
67
  zen models pick --embedding the first embedder that does, on stdout
@@ -91,17 +91,27 @@ Probes are synthetic on purpose — real request bodies never reach a prompt.
91
91
 
92
92
  ## The cache
93
93
 
94
- Under `~/.zenera/neo/faker/generators/<key>/`, one directory per operation. A
95
- generator that a model gave up on is remembered, so a hopeless operation is not
96
- re-asked on every request; a _transient_ failure — a 429, a dropped socket — is
97
- not, because it is about this minute rather than this operation.
98
-
99
- `zen faker cache clear` removes the generators and the container together. They
100
- have to go together: the container's name is a hash of its configuration, so
101
- deleting the directory alone would leave a stopped container bind-mounted onto a
94
+ Generators live in the machine's shared cache, `~/.zenera/neo/cache/faker-generator/`,
95
+ keyed by the operation's shape — so the same spec served from two directories is
96
+ written once, and a spec edit produces a new key rather than a stale answer. A
97
+ generator that a model gave up on is remembered for the life of the process, so
98
+ a hopeless operation is not re-asked on every request; a _transient_ failure — a
99
+ 429, a dropped socket — is not, because it is about this minute rather than this
100
+ operation.
101
+
102
+ `~/.zenera/neo/faker/` is the container's workspace, and scratch: a cache hit is
103
+ copied into it, because the container can only run what is under its mount.
104
+
105
+ `zen faker cache ls` lists what has been generated. `zen faker cache clear`
106
+ removes the generators, the workspace and the container together. They have to
107
+ go together: the container's name is a hash of its configuration, so deleting
108
+ the workspace alone would leave a stopped container bind-mounted onto a
102
109
  directory that no longer exists, and every generator would fail with
103
110
  `python3: can't open file '/workspace/generators/…/gen.py'`.
104
111
 
112
+ `zen cache ls --kind faker-generator` reaches the same entries from the other
113
+ side, along with everything else this machine has cached.
114
+
105
115
  ## Documents it accepts
106
116
 
107
117
  Swagger 2 and OpenAPI 3.0/3.1, JSON or YAML, `$ref`s resolved. Several documents
@@ -25,8 +25,8 @@ keys; one of them is active.
25
25
  | `zen key show <ref> [--reveal]` | Masked by default |
26
26
  | `zen key env [provider …]` | Shell exports, for other tools |
27
27
 
28
- `zen key add` also takes `--project` and `--location`, for a Vertex service
29
- account.
28
+ `zen key add` also takes `--gcp-project` and `--gcp-location`, for a Vertex
29
+ service account.
30
30
 
31
31
  ```
32
32
  zen key add openai # prompts, echo off
@@ -56,18 +56,18 @@ echo-off prompt and from nowhere else.
56
56
 
57
57
  A **service-account file** is absorbed into `~/.zenera/neo/keys/` and
58
58
  `GOOGLE_APPLICATION_CREDENTIALS` points at it. It wants a project too, from
59
- `--project`, or `GOOGLE_CLOUD_PROJECT`, or the `project_id` inside the file, and
60
- a region from `--location` (`global` otherwise). Application Default
59
+ `--gcp-project`, or `GOOGLE_CLOUD_PROJECT`, or the `project_id` inside the file,
60
+ and a region from `--gcp-location` (`global` otherwise). Application Default
61
61
  Credentials from `gcloud auth application-default login` work with no entry at
62
62
  all.
63
63
 
64
64
  An **express-mode key** is an ordinary secret under `VERTEX_API_KEY`, and
65
65
  addresses no project: a key and a project are alternatives, and sending both
66
- gets a `403` that mentions neither. `--project` and `--location` are therefore
67
- refused alongside a key.
66
+ gets a `403` that mentions neither. `--gcp-project` and `--gcp-location` are
67
+ therefore refused alongside a key.
68
68
 
69
69
  ```
70
- zen key add vertex --project acme-prod --location europe-west4
70
+ zen key add vertex --gcp-project acme-prod --gcp-location europe-west4
71
71
  # paste a path → the file shape
72
72
  # paste a key → the express shape
73
73
  ```