@llblab/pi-actors 0.15.0 → 0.16.1

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/BACKLOG.md CHANGED
@@ -2,18 +2,54 @@
2
2
 
3
3
  ## Open Work
4
4
 
5
- No open release-blocking work remains for `0.15.0`.
5
+ No release-blocking work remains for `0.16.0`.
6
6
 
7
- The former active tracks are now release-ready:
7
+ ## Future Work
8
8
 
9
- - Universal actor communication: public guidance centers `spawn`, `message`, `inspect`, actor messages, mailbox contracts, and artifacts; low-level lifecycle/storage wording is confined to implementation/diagnostic docs.
10
- - Component parameterization and composition: packaged recipes are policy-light, require caller-provided model/model-pool policy, expose reusable knobs, validate imports/mailboxes, and are covered by the actors Recipe Navigator.
11
- - Structured utility transforms: current utility surface is sufficient for shipped pipelines; add future utilities only when a repeated packaged-pipeline need appears.
9
+ ### Recipe Registry Curation UX
12
10
 
13
- ## Future Work
11
+ - Priority: Medium.
12
+ - Status: `inspect target=recipes view=status|summary` exposes active, shadowed, invalid, disabled, and diagnostic recipe state. User-owned recipes track extension-maintained `usage.calls` and `usage.last_called`.
13
+ - Goal: Help operators curate the sticky `~/.pi/agent/recipes` tool surface without automatic deletion or demotion.
14
+ - Direction:
15
+ - Include usage fields in recipe registry summaries.
16
+ - Add cleanup recommendations for stale, duplicate, low-use, too-specific, invalid, disabled, and shadowing recipes.
17
+ - Recommend explicit actions only: keep as tool, set `tool: false`, merge, delete, or archive.
18
+ - Keep cleanup operator-gated; never silently delete or demote during unrelated work.
19
+ - Exit:
20
+ - Operators can ask why a recipe/tool exists and what cleanup action is reasonable without reading files manually.
14
21
 
15
- - Add new utilities or pipelines only when a concrete repeated task pattern justifies them.
16
- - Continue opportunistic actor-vocabulary cleanup when touching implementation docs or diagnostics.
22
+ ### Host-Level Tool Unregistration
23
+
24
+ - Priority: Low.
25
+ - Status: Deleted recipe files are removed from the active tool set on reactive reload; host-level registered tool definitions cannot currently be unregistered by this extension.
26
+ - Goal: Remove stale dynamically registered tool definitions completely when the host API supports it.
27
+ - Direction:
28
+ - Track pi extension API support for custom tool unregistration.
29
+ - Replace active-tool deactivation fallback with real unregister when available.
30
+ - Preserve current safe behavior: deleted tools should not remain active after reload.
31
+ - Exit:
32
+ - Deleting a recipe file removes the corresponding runtime tool definition and active-tool entry without session restart.
33
+
34
+ ### Recipe Discovery Expansion
35
+
36
+ - Priority: Low.
37
+ - Direction:
38
+ - Add nested recipe directories only after flat `recipes/*.json` discovery semantics are stable.
39
+ - Keep same-id priority and invalid-blocking behavior explicit if nested ids are introduced.
40
+
41
+ ### Recipe Usage Telemetry Evolution
42
+
43
+ - Priority: Low.
44
+ - Direction:
45
+ - Consider sidecar stats sync/backup policy after inline user-owned `usage.calls` / `usage.last_called` proves useful.
46
+ - Do not add failure counters as primary usefulness evidence unless there is a strong operator-facing need.
47
+
48
+ ### Opportunistic Recipe Library Growth
49
+
50
+ - Priority: Low.
51
+ - Direction:
52
+ - Add new utilities or pipelines only when a concrete repeated task pattern justifies them.
17
53
 
18
54
  ## Blocked Work
19
55
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.1: Recipe Registry Hotfix
4
+
5
+ - `[Runtime]` Prevented invalid user recipe files from aborting extension startup when tool-schema generation fails, surfacing a warning and skipping the offending tool instead. Impact: one bad recipe in `~/.pi/agent/recipes` no longer takes down the pi-actors extension.
6
+ - `[Recipe Discovery]` Excluded the legacy migration report file from recipe discovery. Impact: `actors-tools-migration-report.json` no longer appears as a broken recipe/tool candidate after migration.
7
+ - `[Package]` Bumped package and packaged skill metadata to `0.16.1` for the hotfix release.
8
+
9
+ ## 0.16.0: File-Discovered Recipe Registry Migration
10
+
11
+ - `[Version]` Began the `0.16.0` breaking-change cycle and captured the file-discovered recipe registry migration plan in `BACKLOG.md`. Impact: the next release target is now explicit: replace `actors-tools.json` as live registry with validated recipe files, filename identity, `tool` exposure, override/disable semantics, migration reporting, registry inspection, and usage-informed cleanup.
12
+ - `[Recipe References]` Started filename-identity support for recipes by deriving the recipe id from the JSON filename when `name` is omitted, while preserving optional `tool`, `disabled`, and `description` metadata through recipe resolution. Impact: new recipe files can move toward filename-as-identity without losing human-readable descriptions or tool exposure flags.
13
+ - `[Recipe Discovery]` Added an initial file-discovered recipe registry domain with flat root scanning, filename ids, priority shadowing, invalid high-priority recipe blocking, disabled overrides, and `tool: true` exposure detection. Impact: the 0.16 registry migration now has a tested discovery core before wiring it into runtime loading.
14
+ - `[Recipe Migration]` Added a legacy registry migration domain that converts `actors-tools.json` entries into user recipe files with `tool: true`, preserves descriptions/args/defaults/templates, refuses to overwrite existing recipe files, writes a migration report, and archives the source only when migration has no conflicts or invalid entries. Impact: the breaking registry transition now has a tested compatibility path from the old live registry.
15
+ - `[Recipe Discovery]` Captured the priority model that treats packaged pi-actors recipes as a standard library below ad hoc user-selected recipe files and below `~/.pi/agent/recipes/*.json`, with priority applying only to matching filename ids. Impact: override behavior now has a documented lens and a regression for standard-library versus user recipe precedence.
16
+ - `[Recipe Discovery]` Added source-level default tool exposure so the high-priority user recipe root can behave as the operator-managed tool set by default, while packaged/ad hoc recipes stay component-like unless they opt in with `tool: true` and any recipe can opt out with `tool: false`. Impact: 0.16 keeps the discoverability advantage of the old tool-only registry without forcing a separate live tool config.
17
+ - `[Runtime]` Wired session-start tool loading to migrate the legacy registry, discover recipe-file tools from `~/.pi/agent/recipes` and packaged recipes, and register only active exposed recipes as runtime tools. Impact: the new recipe-discovered registry path is now active in runtime loading instead of only existing as standalone discovery/migration helpers.
18
+ - `[Registry]` Changed `register_tool` persistence to write/update/delete user recipe files under the recipe root instead of mutating the legacy JSON registry, while still activating the tool in the current session. Impact: newly registered tools now enter the 0.16 recipe-discovered registry directly.
19
+ - `[Docs]` Reworked tool-registry documentation, README examples, recipe docs, actor skill guidance, and prompt copy around recipe-file persistence, the user recipe directory as the default tool set, packaged recipes as standard-library components, and `actors-tools.json` as legacy migration input. Impact: public guidance now matches the 0.16 runtime path instead of the old live JSON registry.
20
+ - `[Skill]` Added actors-skill guidance for same-name recipe priority, recipe-root-as-muscle-memory, usage counters, and an explicit cleanup rule for stale/default tools without automatic deletion or demotion. Impact: agents get the override model, sticky-tool tradeoff, and cleanup heuristic directly in the compact operational reference.
21
+ - `[Recipe Usage]` Added extension-maintained recipe launch metadata updates for user-owned runtime tools and direct async recipe starts, tracking `usage.calls` and `usage.last_called` without failure counters while leaving packaged standard-library recipes immutable, then documented the cleanup interpretation in recipe and tool-registry docs. Impact: the operator-managed recipe/tool set now accrues cleanup evidence for muscle-memory review from actual extension launches rather than manual agent bookkeeping.
22
+ - `[Runtime]` Added a best-effort user recipe-root watcher that debounces file changes and reloads recipe-backed tools during the active session, plus runtime-tool fingerprinting so repeated reloads do not re-register unchanged tools while changed definitions refresh, and stale recipe tools are removed from the active tool set on reload when their recipe disappears. Impact: newly created or edited valid recipe files can be connected without a full restart, without duplicate registration churn for unchanged recipes, while deleted recipe tools are deactivated even though host-level unregistration is still not available.
23
+ - `[Recipe Discovery]` Added a discovery summary helper and wired/documented `inspect target=recipes view=status|summary` to report active, shadowed, invalid, disabled, and diagnostic recipe entries. Impact: the registry inspection surface can explain why tools are present, hidden, broken, or disabled from the normal actor inspection tool.
24
+ - `[Backlog]` Reconciled 0.16 planning state after implementation of the core runtime path, usage counters, and reactive recipe reload behavior. Impact: remaining open work is now framed as hardening, inspection UX, and release validation instead of rediscovering already implemented pieces.
25
+ - `[Docs]` Normalized registry examples toward filename/tool ids that match the snake_case tool naming convention, avoiding mixed hyphen/underscore examples around `docs_review`, and aligned package image metadata with the local pi-actors banner. Impact: recipe identity, generated tool naming, and package presentation are less ambiguous in 0.16 guidance.
26
+ - `[Backlog]` Reconciled the recipe registry inspection surface after wiring the initial `inspect target=recipes` summary, then closed the active `0.16.0` release backlog by moving remaining non-blocking ideas to future curation, host-unregistration, discovery expansion, telemetry evolution, and opportunistic recipe-library sections. Impact: the backlog now distinguishes completed release scope from future follow-up work.
27
+
3
28
  ## 0.15.0: Packaged Actors Skill And Actor Vocabulary Cleanup
4
29
 
5
30
  - `[Skill]` Reworked the packaged `actors` skill as a dense self-contained extension reference rather than a scenario catalog or changelog narrative, bundled the `swarm` methodology skill alongside it, synchronized packaged skill metadata versions with the package version, included `skills/` in the npm package contents, registered both skills in package metadata, and linked them from the README start points. Impact: fresh agents get compact practical coverage of pi-actors operation plus complementary multi-agent methodology without duplicating the two roles.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # pi-actors
2
2
 
3
- Actor runtime and orchestrator for agent-managed local processes.
3
+ > Actor runtime and orchestrator for agent-managed local processes.
4
+
5
+ [<img alt="Actors*" src="banner.jpg" />](https://github.com/llblab/pi-actors "pi-actors")
4
6
 
5
7
  `pi-actors` turns local programs, scripts, services, recipes, and long-running processes into addressable actors that agents can start, message, inspect, and compose. A music player, a sub-agent fanout, a repo-health pipeline, or any trusted local process can become an actor when it has a template-backed launch path, a mailbox contract, and observable runtime state.
6
8
 
@@ -44,7 +46,7 @@ The key move is not just “register a command.” It is to wrap a process in an
44
46
  - **Control**: `message` sends typed envelopes to runs, branches, tools, or the coordinator.
45
47
  - **Observation**: `inspect` reads status, logs, messages, mailbox metadata, files, and artifacts intentionally.
46
48
  - **Persistence**: `artifacts` and state files make outcomes durable.
47
- - **Memory**: `actors-tools.json` stores reusable actor-control wrappers across sessions.
49
+ - **Memory**: `~/.pi/agent/recipes/*.json` stores reusable actor-control wrappers across sessions.
48
50
 
49
51
  ## Key Features
50
52
 
@@ -52,7 +54,7 @@ The key move is not just “register a command.” It is to wrap a process in an
52
54
  - **Agent-Managed Processes**: Wraps sub-agents, media players, pipelines, diagnostics, and other local programs as controllable entities instead of one-off commands.
53
55
  - **Message-Oriented Control**: Uses `spawn`, `message`, and `inspect` as the public coordination vocabulary for start, control, and observation.
54
56
  - **Mailbox Contracts**: Lets recipes declare what messages they accept and emit, so agents can discover how to interact with an actor.
55
- - **Actor Tool Registry**: Stores persistent actor-control tool definitions in `~/.pi/agent/actors-tools.json` and registers them automatically on session start.
57
+ - **Actor Tool Registry**: Stores persistent actor-control tools as recipe files in `~/.pi/agent/recipes/*.json` and registers them automatically on session start.
56
58
  - **Command Template Substrate**: Keeps process launch portable with named placeholders, typed args, defaults, sequences, guarded nodes, retries, failure policy, and `parallel: true` fanout.
57
59
  - **Composable Actor Recipes**: Stores reusable recipe JSON under `~/.pi/agent/recipes/*.json`; recipes can import other recipes, reuse defaults, declare artifacts, and opt into detached actor lifecycle with `async: true`.
58
60
  - **Coordinator-Scoped Observability**: Shows ambient triangles for active actor runs and sends compact completion or request-for-attention follow-ups only to the launching coordinator.
@@ -73,27 +75,17 @@ From git:
73
75
  pi install git:github.com/llblab/pi-actors
74
76
  ```
75
77
 
76
- ## Rename Migration
78
+ ## Registry Migration
77
79
 
78
- `pi-actors` reads persistent actor-control tools from:
80
+ `pi-actors` now reads persistent actor-control tools from:
79
81
 
80
82
  ```text
81
- ~/.pi/agent/actors-tools.json
82
- ```
83
-
84
- If you previously used `pi-auto-tools`, copy the old registry intentionally:
85
-
86
- ```bash
87
- cp ~/.pi/agent/auto-tools.json ~/.pi/agent/actors-tools.json
83
+ ~/.pi/agent/recipes/*.json
88
84
  ```
89
85
 
90
- If you installed the brief `0.12.0`/`0.12.1` line and created `tools.json`, copy that file instead:
86
+ That directory is the operator-managed tool set: user recipe files there become tools by default unless they set `tool: false`. Packaged recipes are the lower-priority standard library of declarative actor components and opt into tools with `tool: true`.
91
87
 
92
- ```bash
93
- cp ~/.pi/agent/tools.json ~/.pi/agent/actors-tools.json
94
- ```
95
-
96
- The extension does not silently rewrite old registry files; keep or delete the old file after confirming the new registry loads as expected.
88
+ If `~/.pi/agent/actors-tools.json` exists from an older release, pi-actors treats it as legacy compatibility input, migrates entries into recipe files when possible, writes a migration report, and archives the legacy file only when migration is clean.
97
89
 
98
90
  ## Mental Model
99
91
 
@@ -129,7 +121,7 @@ Move to actor recipes when work is long-running, parallel, service-like, or agen
129
121
 
130
122
  ```json
131
123
  {
132
- "name": "docs-review",
124
+ "name": "docs_review",
133
125
  "async": true,
134
126
  "args": ["scope:path", "model:string"],
135
127
  "defaults": {},
@@ -144,7 +136,7 @@ Move to actor recipes when work is long-running, parallel, service-like, or agen
144
136
  Expose a reusable actor recipe as a normal capability:
145
137
 
146
138
  ```text
147
- register_tool name=docs_review description="Start an async docs review actor" template="docs-review.json" args="scope:path,model:string"
139
+ register_tool name=docs_review description="Start an async docs review actor" template="docs_review" args="scope:path,model:string"
148
140
  ```
149
141
 
150
142
  `Task` is the user's work item. `Template` is the execution graph. `Actor recipe` is saved JSON. `Run` is one actor instance with status, logs, messages, cancellation, artifacts, and ambient triangles.
@@ -213,27 +205,25 @@ For reusable actor workflows, keep the large template and mailbox contract in a
213
205
  ```text
214
206
  register_tool name=docs_review \
215
207
  description="Start an async docs review actor" \
216
- template="docs-review.json" \
208
+ template="docs_review" \
217
209
  args="scope:path,model:string"
218
210
  ```
219
211
 
220
212
  If the recipe file contains `async: true`, calling `docs_review` starts a detached run and returns metadata immediately. If `async` is omitted or false, the same recipe runs foreground and returns normal tool output.
221
213
 
222
- A recipe can also be co-located in `actors-tools.json` when keeping metadata and the recipe body together is clearer:
214
+ When keeping metadata and the recipe body together is clearer, `register_tool` writes a complete user recipe file:
223
215
 
224
216
  ```json
225
217
  {
226
- "review_docs": {
227
- "description": "Start an async docs review",
228
- "name": "review-docs",
229
- "async": true,
230
- "args": ["scope:path", "model:string"],
231
- "template": "pi -p --model {model} --tools read,bash \"Review {scope}\""
232
- }
218
+ "description": "Start an async docs review",
219
+ "tool": true,
220
+ "async": true,
221
+ "args": ["scope:path", "model:string"],
222
+ "template": "pi -p --model {model} --tools read,bash \"Review {scope}\""
233
223
  }
234
224
  ```
235
225
 
236
- A co-located recipe entry still cannot define `tool`; it must own `template` directly.
226
+ The filename is the recipe id/tool name, `async: true` selects detached run mode, and `template` remains the executable body.
237
227
 
238
228
  ### Sub-agent
239
229
 
@@ -257,30 +247,28 @@ Delete a tool:
257
247
  register_tool name=call_subagent template=null
258
248
  ```
259
249
 
260
- ## Resulting Config
250
+ ## Resulting Recipe Files
261
251
 
262
- The commands above persist entries like this in `~/.pi/agent/actors-tools.json`; tool names come from the top-level keys. Stored entries keep `template` last so flags and metadata are read before executable content:
252
+ The commands above persist files under `~/.pi/agent/recipes/`. Tool names come from recipe filenames. Stored recipes keep `template` last so flags and metadata are read before executable content:
263
253
 
264
254
  ```json
265
255
  {
266
- "transcribe": {
267
- "description": "Transcribe a local audio file",
268
- "template": "/path/to/stt --file {file} --lang {lang=ru}"
269
- },
270
- "call_subagent": {
271
- "description": "Run pi as a non-interactive sub-agent",
272
- "args": ["prompt:string", "model:string"],
273
- "template": "pi -p --model {model} --no-tools {prompt}"
274
- },
275
- "docs_review": {
276
- "description": "Start an async docs review actor",
277
- "args": ["scope:path", "model:string"],
278
- "template": "docs-review.json"
279
- }
256
+ "description": "Transcribe a local audio file",
257
+ "tool": true,
258
+ "template": "/path/to/stt --file {file} --lang {lang=ru}"
259
+ }
260
+ ```
261
+
262
+ ```json
263
+ {
264
+ "description": "Run pi as a non-interactive sub-agent",
265
+ "tool": true,
266
+ "args": ["prompt:string", "model:string"],
267
+ "template": "pi -p --model {model} --no-tools {prompt}"
280
268
  }
281
269
  ```
282
270
 
283
- This file is the durable actor-tool registry. `register_tool` is the interactive API; `actors-tools.json` is the persisted state that is loaded on future sessions.
271
+ The recipe directory is the durable actor-tool registry. `register_tool` is the interactive API; recipe files are the persisted state loaded on future sessions.
284
272
 
285
273
  ## Manage Actors
286
274
 
package/banner.jpg ADDED
Binary file
@@ -21,6 +21,8 @@ A recipe wraps one command-template tree. The wrapped `template` keeps the norma
21
21
 
22
22
  Layer boundary: `imports`, `{ "name": "alias" }` imported-recipe nodes, `{alias.defaults.key}` references, fallback expressions, and recipe-local ternaries are recipe-loading features. They resolve before the command-template graph runs and do not extend the portable Command Template Standard. Typed imports are recipe definitions: they expose the imported recipe's command-template-shaped metadata (`template`, `args`, `defaults`, flags, and `values`), while async-run launch fields such as `async` and `state_dir` remain lifecycle configuration for starting a run, not part of the imported execution graph.
23
23
 
24
+ Packaged recipes are the pi-actors recipe standard library: declarative actor config components that can be imported, launched, inspected, overridden, or composed by user recipes. Treat them as stable building blocks rather than user-local policy.
25
+
24
26
  ## Layer Ownership
25
27
 
26
28
  Template-recipe standard owns:
@@ -67,6 +69,36 @@ Async recipe:
67
69
 
68
70
  `name` names the saved definition when an explicit name is needed. File-backed recipes usually omit it because the filename is the canonical recipe id. `template` is the command-template tree. `async: true` selects detached run mode when the recipe is invoked through a registered tool.
69
71
 
72
+ ## Discovery Priority
73
+
74
+ Recipe priority only matters when two discovered recipes have the same filename id. The conceptual ladder from lowest to highest priority is:
75
+
76
+ 1. No recipe for that id.
77
+ 2. Packaged pi-actors recipe components, acting as the standard library.
78
+ 3. Explicitly referenced ad hoc user recipe files located outside `~/.pi/agent/recipes`.
79
+ 4. User recipe files under `~/.pi/agent/recipes/*.json`.
80
+
81
+ The high-priority user recipe directory is also the default tool set: recipes placed there are agent tools unless they explicitly set `tool: false`. This preserves the old advantage of a tool-only registry because listing `~/.pi/agent/recipes` shows the operator-managed tool surface. Packaged and ad hoc recipes are recipe components by default; they opt into agent-tool exposure with `tool: true`.
82
+
83
+ Higher-priority files shadow lower-priority files with the same basename. A highest-priority invalid recipe is still visible and blocks fallback so operators do not accidentally run packaged behavior when a user override is broken. A highest-priority recipe with `disabled: true` also blocks fallback and intentionally disables that id.
84
+
85
+ ## Usage Metadata
86
+
87
+ User-owned recipes may accumulate extension-maintained usage metadata:
88
+
89
+ ```json
90
+ {
91
+ "usage": {
92
+ "calls": 12,
93
+ "last_called": "2026-05-22T10:30:00.000Z"
94
+ }
95
+ }
96
+ ```
97
+
98
+ The extension increments `usage.calls` and updates `usage.last_called` when it starts that concrete recipe, either through a recipe-backed tool call or a direct async recipe-file run. Agents should treat these fields as cleanup evidence, not as authored recipe contract. Packaged standard-library recipes are not mutated for usage metadata.
99
+
100
+ There is intentionally no failure counter in the recipe contract. A failed launch can reflect caller misuse, missing runtime values, or an environmental problem rather than recipe uselessness. Cleanup decisions should be explicit operator work: keep as a tool, set `tool: false`, merge, delete, or archive.
101
+
70
102
  For object form, keep `template` last. Recipe metadata comes first; executable content stays last.
71
103
 
72
104
  ## Named Artifacts
@@ -171,34 +203,19 @@ Call-time params override file params. `values` are merged with file values; cal
171
203
 
172
204
  ## Registered Recipe Tools
173
205
 
174
- A registered tool can point at an actor recipe by storing the recipe path or name in `template`:
206
+ A registered tool is a recipe file exposed as an agent tool. User recipes under `~/.pi/agent/recipes/*.json` are tools by default; packaged/ad hoc recipes opt in with `tool: true`:
175
207
 
176
208
  ```json
177
209
  {
178
- "docs_review": {
179
- "description": "Start an async docs review actor",
180
- "args": ["scope:path", "model:string"],
181
- "template": "docs-review.json"
182
- }
183
- }
184
- ```
185
-
186
- If `docs-review.json` contains `async: true`, calling `docs_review` starts a detached actor run and returns metadata. If `async` is omitted or false, calling `docs_review` executes the recipe foreground and returns normal tool output.
187
-
188
- A registered tool may also co-locate an actor recipe directly in `actors-tools.json`:
189
-
190
- ```json
191
- {
192
- "review_docs": {
193
- "description": "Start an async docs review",
194
- "name": "review-docs",
195
- "async": true,
196
- "template": "review {scope}"
197
- }
210
+ "description": "Start an async docs review actor",
211
+ "tool": true,
212
+ "async": true,
213
+ "args": ["scope:path", "model:string"],
214
+ "template": "review {scope} --model {model}"
198
215
  }
199
216
  ```
200
217
 
201
- The co-located entry must still own `template` directly and must not define `tool`.
218
+ If a tool recipe contains `async: true`, calling the tool starts a detached actor run and returns metadata. If `async` is omitted or false, calling the tool executes the recipe foreground and returns normal tool output.
202
219
 
203
220
  ## Values And Public Args
204
221
 
@@ -1,33 +1,43 @@
1
1
  # Tool Registry
2
2
 
3
- `pi-actors` stores registered actor-control command-template tools and template-recipe launchers in `~/.pi/agent/actors-tools.json` and registers them automatically on session start.
3
+ `pi-actors` stores persistent agent tools as recipe files under `~/.pi/agent/recipes/*.json` and registers the active tool set automatically on session start.
4
4
 
5
- This document is the local adaptation of the portable [Command Template Standard](./command-templates.md).
5
+ This document is the local adaptation of the portable [Command Template Standard](./command-templates.md) and the recipe-file runtime described in [Template Recipe Standard](./template-recipes.md).
6
6
 
7
- ## Rename Migration
7
+ ## Registry Model
8
8
 
9
- `pi-actors` reads only `~/.pi/agent/actors-tools.json` as its registry source. When moving from `pi-auto-tools`, copy the previous registry explicitly:
9
+ The 0.16 registry source is file-discovered recipes, not a live tool-only JSON file:
10
10
 
11
- ```bash
12
- cp ~/.pi/agent/auto-tools.json ~/.pi/agent/actors-tools.json
13
- ```
11
+ - `~/.pi/agent/recipes/*.json` is the highest-priority user recipe root and the operator-managed tool set.
12
+ - Recipes in that root are tools by default.
13
+ - `tool: false` keeps a user recipe file recipe-only.
14
+ - Packaged pi-actors recipes are the lower-priority standard library of declarative actor config components.
15
+ - Packaged or ad hoc recipes opt into tool exposure with `tool: true`.
16
+ - Recipe identity is the filename basename; `~/.pi/agent/recipes/docs_review.json` has id/tool name `docs_review`.
17
+
18
+ `~/.pi/agent/actors-tools.json` is legacy compatibility input. On startup, pi-actors migrates it into recipe files when possible, writes a migration report, and archives the source only when migration has no conflicts or invalid generated recipes.
19
+
20
+ Because the user recipe directory is sticky agent muscle memory, runtime launches update `usage.calls` and `usage.last_called` on user-owned recipe files. Use that evidence during focused cleanup passes: keep valuable tools, set `tool: false` for useful components that should leave the active tool surface, merge duplicates, or delete/archive low-value files. The extension does not maintain a failure counter and agents should not silently clean tools during unrelated work.
14
21
 
15
- If a short-lived `~/.pi/agent/tools.json` file exists from the early `pi-actors` rename window, copy that file instead:
22
+ `register_tool` is the preferred agent-facing mutation API. It creates, updates, and deletes recipe files in `~/.pi/agent/recipes`; agents do not need to edit the files directly for normal registration. Direct file edits are still valid for operators and advanced agents. Runtime behavior is reactive: file creation, deletion, or edits in the user recipe root trigger validation and tool-set refresh, with invalid recipes surfaced as diagnostics rather than silently ignored.
16
23
 
17
- ```bash
18
- cp ~/.pi/agent/tools.json ~/.pi/agent/actors-tools.json
24
+ Inspect the discovered registry with:
25
+
26
+ ```text
27
+ inspect target=recipes view=status
28
+ inspect target=recipes view=summary verbose=true
19
29
  ```
20
30
 
21
- No automatic rewrite is performed so operators can decide when to retire old config files.
31
+ The summary reports active, shadowed, invalid, disabled, and diagnostic entries so operators can answer why a tool is present, hidden, broken, or disabled.
22
32
 
23
33
  ## Registering Tools
24
34
 
25
35
  `register_tool` is the interactive API for listing, creating, updating, or deleting persistent tools. Call it without arguments to list registered tools.
26
36
 
27
37
  ```text
28
- register_tool name=transcribe_groq \
29
- description="Transcribe audio files using Groq Whisper API" \
30
- template="~/.pi/agent/skills/groq-stt/scripts/transcribe.mjs {file} {lang=ru} {model=whisper-large-v3-turbo}"
38
+ register_tool name=transcribe_audio \
39
+ description="Transcribe an audio file" \
40
+ template="~/bin/transcribe {file:path} {lang=ru} {model:string}"
31
41
  ```
32
42
 
33
43
  ```text
@@ -47,32 +57,30 @@ Use `update=true` to overwrite an existing tool. Omit `template` and co-located
47
57
  ]
48
58
  ```
49
59
 
50
- For reusable actor workflows, register a small tool whose `template` points to an actor recipe instead of embedding the launch graph in the tool itself:
60
+ For reusable actor workflows, register a small tool whose `template` points to an existing actor recipe instead of embedding the launch graph in the tool itself:
51
61
 
52
62
  ```text
53
63
  register_tool name=docs_review \
54
64
  description="Start an async docs review actor" \
55
- template="docs-review.json" \
65
+ template="docs-review" \
56
66
  args="scope:path,model:string"
57
67
  ```
58
68
 
59
- This stores the recipe path in the registry as `template`. If `~/.pi/agent/recipes/docs-review.json` contains `async: true`, calling the tool starts a detached actor run and returns metadata immediately. If `async` is omitted or false, the same recipe runs foreground and returns normal tool output.
69
+ This writes or updates `~/.pi/agent/recipes/docs_review.json` with `tool: true` and a recipe-reference template. If the referenced recipe contains `async: true`, calling the tool starts a detached actor run and returns metadata immediately. If `async` is omitted or false, the same recipe runs foreground and returns normal tool output.
60
70
 
61
- When co-location is clearer than a separate file, the registry entry may include recipe fields directly beside tool metadata:
71
+ When co-location is clearer than a separate file, `register_tool` writes the recipe fields directly into the user recipe file:
62
72
 
63
73
  ```json
64
74
  {
65
- "review_docs": {
66
- "description": "Start an async docs review",
67
- "name": "review-docs",
68
- "async": true,
69
- "args": ["scope:path", "model:string"],
70
- "template": "pi -p --model {model} --tools read,bash \"Review {scope}\""
71
- }
75
+ "description": "Start an async docs review",
76
+ "tool": true,
77
+ "async": true,
78
+ "args": ["scope:path", "model:string"],
79
+ "template": "pi -p --model {model} --tools read,bash \"Review {scope}\""
72
80
  }
73
81
  ```
74
82
 
75
- This is still not a cycle: `name` names the saved definition when it differs from the tool key, `async: true` selects detached run mode, and `template` remains the executable body. Co-located recipe entries must not define `tool`.
83
+ This is still not a cycle: the filename is the saved definition id, `async: true` selects detached run mode, and `template` remains the executable body.
76
84
 
77
85
  Delete a tool with `template=null`:
78
86
 
@@ -82,24 +90,22 @@ register_tool name=call_subagent template=null
82
90
 
83
91
  ## Stored Shape
84
92
 
85
- Tool names come from the top-level registry keys. Tool entries define `template`; it may be an inline command template, a template recipe JSON path/name, or the body of a co-located template recipe when `async` or entry-local `name` is present. Template entries keep `template` last, matching the command-template readability rule. The commands above persist entries like this:
93
+ Tool names come from recipe filenames in `~/.pi/agent/recipes`. Recipe files define `template`; it may be an inline command template, a command-template sequence, or an async recipe body. Template entries keep `template` last, matching the command-template readability rule. The commands above persist recipe files like this:
86
94
 
87
95
  ```json
88
96
  {
89
- "transcribe_groq": {
90
- "description": "Transcribe audio files using Groq Whisper API",
91
- "template": "~/.pi/agent/skills/groq-stt/scripts/transcribe.mjs {file} {lang=ru} {model=whisper-large-v3-turbo}"
92
- },
93
- "call_subagent": {
94
- "description": "Run pi as a non-interactive sub-agent",
95
- "args": ["prompt:string", "model:string"],
96
- "template": "pi -p --model {model} --no-tools {prompt}"
97
- },
98
- "docs_review": {
99
- "description": "Start an async docs review actor",
100
- "args": ["scope:path", "model:string"],
101
- "template": "docs-review.json"
102
- }
97
+ "description": "Transcribe an audio file",
98
+ "tool": true,
99
+ "template": "~/bin/transcribe {file:path} {lang=ru} {model:string}"
100
+ }
101
+ ```
102
+
103
+ ```json
104
+ {
105
+ "description": "Run pi as a non-interactive sub-agent",
106
+ "tool": true,
107
+ "args": ["prompt:string", "model:string"],
108
+ "template": "pi -p --model {model} --no-tools {prompt}"
103
109
  }
104
110
  ```
105
111
 
@@ -108,7 +114,7 @@ Tool names come from the top-level registry keys. Tool entries define `template`
108
114
  When `args` is omitted, `pi-actors` derives tool parameters from placeholders in `template`:
109
115
 
110
116
  ```text
111
- template="~/bin/transcribe {file} {lang=ru} {model=whisper-large-v3-turbo}"
117
+ template="~/bin/transcribe {file:path} {lang=ru} {model:string}"
112
118
  ```
113
119
 
114
120
  The optional `args` field is an explicit placeholder declaration, matching the command-template standard. Untyped declarations remain valid:
package/index.ts CHANGED
@@ -40,6 +40,8 @@ const RESERVED_TOOL_NAMES = new Set([
40
40
  export default function toolRegistryExtension(pi: ExtensionAPI) {
41
41
  let runsAnimationInterval: NodeJS.Timeout | undefined;
42
42
  let runsNotifyTimeout: NodeJS.Timeout | undefined;
43
+ let recipeReloadTimeout: NodeJS.Timeout | undefined;
44
+ let recipeRootWatcher: FSWatcher | undefined;
43
45
  let stateRootWatcher: FSWatcher | undefined;
44
46
  const runDirWatchers = new Map<string, FSWatcher>();
45
47
  const observedRuns = new Map<string, Observability.RunObservedStatus>();
@@ -147,23 +149,54 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
147
149
  watchRunDir(ctx, `${RUN_STATE_ROOT}/${entry.name}`);
148
150
  }
149
151
  }
152
+ const closeRecipeWatcher = (): void => {
153
+ recipeRootWatcher?.close();
154
+ recipeRootWatcher = undefined;
155
+ if (recipeReloadTimeout) clearTimeout(recipeReloadTimeout);
156
+ recipeReloadTimeout = undefined;
157
+ };
158
+ const scheduleRecipeReload = (ctx: ExtensionContext): void => {
159
+ if (recipeReloadTimeout) clearTimeout(recipeReloadTimeout);
160
+ recipeReloadTimeout = setTimeout(() => {
161
+ runtime.loadTools(ctx);
162
+ ctx.ui.notify("Recipe tools refreshed from ~/.pi/agent/recipes", "info");
163
+ }, 150);
164
+ recipeReloadTimeout.unref?.();
165
+ };
166
+ const watchRecipeRoot = (ctx: ExtensionContext): void => {
167
+ const recipeRoot = Paths.getRecipeRoot();
168
+ if (recipeRootWatcher || !existsSync(recipeRoot)) return;
169
+ try {
170
+ recipeRootWatcher = watch(recipeRoot, () => scheduleRecipeReload(ctx));
171
+ recipeRootWatcher.on("error", () => {
172
+ recipeRootWatcher?.close();
173
+ recipeRootWatcher = undefined;
174
+ });
175
+ } catch {
176
+ // Watching is best-effort; restarting the session reloads recipe tools.
177
+ }
178
+ };
150
179
  const actorToolDefinitions = new Map<string, any>();
151
180
  const runtime = Runtime.createAutoToolsRuntime({
152
181
  configPath: CONFIG_PATH,
153
182
  exec: CommandTemplates.execCommandTemplate,
183
+ getActiveTools: () => pi.getActiveTools(),
154
184
  getAllTools: () => pi.getAllTools(),
155
185
  registerTool: (definition) => {
156
186
  actorToolDefinitions.set(definition.name, definition);
157
187
  pi.registerTool(definition);
158
188
  },
159
189
  reservedToolNames: RESERVED_TOOL_NAMES,
190
+ setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
160
191
  });
161
192
  pi.on("session_start", async (_event, ctx) => {
162
193
  await Temp.prepareExtensionTempDir(TEMP_DIR);
163
194
  runtime.loadTools(ctx);
164
195
  updateRunUi(ctx);
165
196
  closeRunWatchers();
197
+ closeRecipeWatcher();
166
198
  refreshRunWatchers(ctx);
199
+ watchRecipeRoot(ctx);
167
200
  if (runsAnimationInterval) clearInterval(runsAnimationInterval);
168
201
  runsAnimationInterval = setInterval(() => updateRunUi(ctx, false), 1000);
169
202
  runsAnimationInterval.unref?.();
@@ -172,6 +205,7 @@ export default function toolRegistryExtension(pi: ExtensionAPI) {
172
205
  if (runsAnimationInterval) clearInterval(runsAnimationInterval);
173
206
  runsAnimationInterval = undefined;
174
207
  closeRunWatchers();
208
+ closeRecipeWatcher();
175
209
  });
176
210
  pi.on("before_agent_start", async (event) => ({
177
211
  systemPrompt: `${event.systemPrompt}\n\n${Prompts.ONBOARDING_SYSTEM_PROMPT}`,
package/lib/async-runs.ts CHANGED
@@ -30,6 +30,7 @@ import { substituteCommandTemplateToken } from "./command-templates.ts";
30
30
  import { writeJsonAtomic } from "./file-state.ts";
31
31
  import * as Paths from "./paths.ts";
32
32
  import * as RecipeReferences from "./recipe-references.ts";
33
+ import * as RecipeUsage from "./recipe-usage.ts";
33
34
 
34
35
  export interface AsyncRunStartParams {
35
36
  async?: boolean;
@@ -172,6 +173,12 @@ function resolveRecipeFile(file: string): string {
172
173
  return RecipeReferences.resolveRecipePath(file, DEFAULT_RECIPE_ROOT);
173
174
  }
174
175
 
176
+ function isMutableUsageRecipeFile(file: string): boolean {
177
+ const userRoot = resolve(DEFAULT_RECIPE_ROOT);
178
+ const resolved = resolve(file);
179
+ return resolved.startsWith(`${userRoot}/`);
180
+ }
181
+
175
182
  function readRecipeFile(file: string): AsyncRunStartParams {
176
183
  const path = resolveRecipeFile(file);
177
184
  const raw = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
@@ -316,6 +323,9 @@ export function startRun(
316
323
  ? resolveRecipeFile(startParams.file)
317
324
  : undefined;
318
325
  const recipe = startParams.name || getRunIdFromFile(recipeFile);
326
+ if (recipeFile && isMutableUsageRecipeFile(recipeFile)) {
327
+ RecipeUsage.recordRecipeLaunch(recipeFile);
328
+ }
319
329
  const outFd = openSync(stdout, "a");
320
330
  const errFd = openSync(stderr, "a");
321
331
  const argv = ["--experimental-strip-types", RUNNER_PATH, stateDir];
package/lib/config.ts CHANGED
@@ -23,6 +23,7 @@ export interface RegisteredTool {
23
23
  template?: CommandTemplateValue;
24
24
  storedArgs?: string[];
25
25
  storedDefaults?: Record<string, string>;
26
+ sourcePath?: string;
26
27
  }
27
28
 
28
29
  export interface LoadConfigResult {
package/lib/paths.ts CHANGED
@@ -5,7 +5,8 @@
5
5
  */
6
6
 
7
7
  import { homedir } from "node:os";
8
- import { join, resolve } from "node:path";
8
+ import { dirname, join, resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
9
10
 
10
11
  export function getAgentDir(
11
12
  env: Record<string, string | undefined> = process.env,
@@ -33,3 +34,7 @@ export function getRunStateRoot(agentDir = getAgentDir()): string {
33
34
  export function getRecipeRoot(agentDir = getAgentDir()): string {
34
35
  return join(agentDir, "recipes");
35
36
  }
37
+
38
+ export function getPackagedRecipeRoot(): string {
39
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "recipes");
40
+ }