@llblab/pi-actors 0.15.0 → 0.16.0
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 +44 -8
- package/CHANGELOG.md +19 -0
- package/README.md +35 -47
- package/banner.jpg +0 -0
- package/docs/template-recipes.md +39 -22
- package/docs/tool-registry.md +48 -42
- package/index.ts +34 -0
- package/lib/async-runs.ts +10 -0
- package/lib/config.ts +1 -0
- package/lib/paths.ts +6 -1
- package/lib/prompts.ts +2 -2
- package/lib/recipe-discovery.ts +226 -0
- package/lib/recipe-migration.ts +123 -0
- package/lib/recipe-references.ts +45 -13
- package/lib/recipe-usage.ts +44 -0
- package/lib/registry.ts +48 -15
- package/lib/runtime.ts +59 -15
- package/lib/tools.ts +41 -1
- package/package.json +5 -3
- package/skills/actors/SKILL.md +25 -7
- package/skills/swarm/SKILL.md +1 -1
package/BACKLOG.md
CHANGED
|
@@ -2,18 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
## Open Work
|
|
4
4
|
|
|
5
|
-
No
|
|
5
|
+
No release-blocking work remains for `0.16.0`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Future Work
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
16
|
-
|
|
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,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0: File-Discovered Recipe Registry Migration
|
|
4
|
+
|
|
5
|
+
- `[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.
|
|
6
|
+
- `[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.
|
|
7
|
+
- `[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.
|
|
8
|
+
- `[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.
|
|
9
|
+
- `[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.
|
|
10
|
+
- `[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.
|
|
11
|
+
- `[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.
|
|
12
|
+
- `[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.
|
|
13
|
+
- `[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.
|
|
14
|
+
- `[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.
|
|
15
|
+
- `[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.
|
|
16
|
+
- `[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.
|
|
17
|
+
- `[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.
|
|
18
|
+
- `[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.
|
|
19
|
+
- `[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.
|
|
20
|
+
- `[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.
|
|
21
|
+
|
|
3
22
|
## 0.15.0: Packaged Actors Skill And Actor Vocabulary Cleanup
|
|
4
23
|
|
|
5
24
|
- `[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**:
|
|
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
|
|
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
|
-
##
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
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="
|
|
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="
|
|
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
|
-
|
|
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
|
-
"
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
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
|
|
250
|
+
## Resulting Recipe Files
|
|
261
251
|
|
|
262
|
-
The commands above persist
|
|
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
|
-
"
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
package/docs/template-recipes.md
CHANGED
|
@@ -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
|
|
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
|
-
"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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
|
|
package/docs/tool-registry.md
CHANGED
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
# Tool Registry
|
|
2
2
|
|
|
3
|
-
`pi-actors` stores
|
|
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
|
-
##
|
|
7
|
+
## Registry Model
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The 0.16 registry source is file-discovered recipes, not a live tool-only JSON file:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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=
|
|
29
|
-
description="Transcribe audio
|
|
30
|
-
template="
|
|
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
|
|
65
|
+
template="docs-review" \
|
|
56
66
|
args="scope:path,model:string"
|
|
57
67
|
```
|
|
58
68
|
|
|
59
|
-
This
|
|
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,
|
|
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
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
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
|
|
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
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
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
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
|
+
}
|