@llblab/pi-actors 0.14.3 → 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/AGENTS.md +5 -1
- package/BACKLOG.md +54 -32
- package/CHANGELOG.md +39 -0
- package/README.md +53 -61
- package/banner.jpg +0 -0
- package/docs/actor-messages.md +1 -1
- package/docs/async-runs.md +4 -4
- package/docs/command-templates.md +11 -11
- package/docs/recipe-library.md +7 -3
- package/docs/task-first-recipes.md +44 -43
- package/docs/template-recipes.md +45 -23
- package/docs/tool-registry.md +50 -42
- package/index.ts +34 -0
- package/lib/actor-messages.ts +20 -7
- package/lib/async-runs.ts +35 -12
- package/lib/command-templates.ts +6 -1
- package/lib/config.ts +3 -2
- package/lib/execution.ts +9 -5
- package/lib/observability.ts +20 -10
- package/lib/paths.ts +6 -1
- package/lib/prompts.ts +14 -21
- 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 +237 -65
- package/package.json +21 -11
- package/recipes/coordinator-locker.json +46 -0
- package/recipes/music-player.json +16 -2
- package/recipes/pipeline-architect-coordinator.json +11 -3
- package/recipes/pipeline-artifact-bundle.json +12 -3
- package/recipes/pipeline-artifact-report.json +9 -3
- package/recipes/pipeline-artifact-write.json +9 -3
- package/recipes/pipeline-async-run-ops.json +18 -9
- package/recipes/pipeline-checkpoint-continuation.json +14 -3
- package/recipes/pipeline-development-tasking.json +12 -3
- package/recipes/pipeline-docs-maintenance.json +12 -3
- package/recipes/pipeline-media-library.json +12 -3
- package/recipes/pipeline-quorum-review.json +12 -9
- package/recipes/pipeline-release-readiness.json +27 -9
- package/recipes/pipeline-release-summary.json +89 -0
- package/recipes/pipeline-repo-health.json +12 -3
- package/recipes/pipeline-research-synthesis.json +11 -3
- package/recipes/pipeline-review-readiness.json +12 -6
- package/recipes/subagent-artifact.json +9 -3
- package/recipes/subagent-checkpoint.json +10 -3
- package/recipes/subagent-conflict-report.json +11 -3
- package/recipes/subagent-contradiction-map.json +11 -3
- package/recipes/subagent-critic.json +11 -3
- package/recipes/subagent-evidence-map.json +11 -3
- package/recipes/subagent-followup.json +10 -3
- package/recipes/subagent-judge.json +11 -3
- package/recipes/subagent-merge.json +11 -3
- package/recipes/subagent-message.json +8 -3
- package/recipes/subagent-normalize.json +11 -3
- package/recipes/subagent-plan.json +11 -3
- package/recipes/subagent-prompt.json +10 -3
- package/recipes/subagent-quorum.json +10 -7
- package/recipes/subagent-review-coordinator.json +14 -6
- package/recipes/subagent-review.json +11 -3
- package/recipes/subagent-task-card.json +11 -3
- package/recipes/subagent-tools.json +10 -3
- package/recipes/subagent-verify.json +11 -3
- package/recipes/subagents-prompts.json +10 -3
- package/recipes/utility-coordinator-lock-snapshot.json +14 -0
- package/recipes/utility-run-ops-snapshot.json +3 -3
- package/recipes/utility-skill-summary.json +14 -0
- package/scripts/coordinator-locker.mjs +272 -0
- package/scripts/music-player.mjs +2 -1
- package/scripts/recipe-utils.mjs +239 -81
- package/scripts/validate-recipe.mjs +28 -10
- package/skills/actors/SKILL.md +301 -0
- package/skills/swarm/SKILL.md +451 -0
- package/skills/swarm/references/development-swarm.md +596 -0
|
@@ -30,13 +30,13 @@ scope snapshot → changelog/package check → release lens reviews → risk ver
|
|
|
30
30
|
|
|
31
31
|
Likely needed cells:
|
|
32
32
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
33
|
+
- Package metadata reader
|
|
34
|
+
- Changelog section extractor
|
|
35
|
+
- Package contents summarizer
|
|
36
|
+
- Validation command wrapper
|
|
37
|
+
- Release-risk reviewer
|
|
38
|
+
- Readiness merger/judge
|
|
39
|
+
- Release checklist artifact writer
|
|
40
40
|
|
|
41
41
|
Existing seeds:
|
|
42
42
|
|
|
@@ -49,7 +49,7 @@ Existing seeds:
|
|
|
49
49
|
|
|
50
50
|
Implemented seed:
|
|
51
51
|
|
|
52
|
-
- `pipeline-release-readiness`: changelog section → package summary → validation wrapper → release review coordinator → artifact report.
|
|
52
|
+
- `pipeline-release-readiness`: changelog section → package summary → packaged skill summary → validation wrapper → release review coordinator → artifact report.
|
|
53
53
|
|
|
54
54
|
### Repository Health Cell
|
|
55
55
|
|
|
@@ -63,12 +63,12 @@ git status/log → package/docs/backlog snapshot → validation summary → heal
|
|
|
63
63
|
|
|
64
64
|
Likely needed cells:
|
|
65
65
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
66
|
+
- Git status/log utility
|
|
67
|
+
- Package version reader
|
|
68
|
+
- Backlog open/blocked extractor
|
|
69
|
+
- Docs index checker
|
|
70
|
+
- Validation summary normalizer
|
|
71
|
+
- Next-action recommender
|
|
72
72
|
|
|
73
73
|
Existing seeds:
|
|
74
74
|
|
|
@@ -93,11 +93,11 @@ run-state summary → actor-message tail → stale/active classification → rec
|
|
|
93
93
|
|
|
94
94
|
Likely needed cells:
|
|
95
95
|
|
|
96
|
-
-
|
|
96
|
+
- Run summary helper
|
|
97
97
|
- JSONL actor-message tailer
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
98
|
+
- Stale-run classifier
|
|
99
|
+
- Control-message recommender
|
|
100
|
+
- Run report artifact
|
|
101
101
|
|
|
102
102
|
Existing seeds:
|
|
103
103
|
|
|
@@ -122,13 +122,13 @@ question framing → evidence map → contradiction map → claim verification
|
|
|
122
122
|
|
|
123
123
|
Likely needed cells:
|
|
124
124
|
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
125
|
+
- Question framer
|
|
126
|
+
- Source inventory utility
|
|
127
|
+
- Evidence mapper
|
|
128
|
+
- Contradiction mapper
|
|
129
|
+
- Verifier
|
|
130
|
+
- Synthesis merger
|
|
131
|
+
- Limitations normalizer
|
|
132
132
|
|
|
133
133
|
Existing seeds:
|
|
134
134
|
|
|
@@ -149,11 +149,11 @@ goal → mutation zones → task cards → validation gates → conflict risks
|
|
|
149
149
|
|
|
150
150
|
Likely needed cells:
|
|
151
151
|
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
152
|
+
- Mutation-zone planner
|
|
153
|
+
- Task-card generator
|
|
154
|
+
- Ownership/conflict checker
|
|
155
|
+
- Validation-gate normalizer
|
|
156
|
+
- Integrator handoff artifact
|
|
157
157
|
|
|
158
158
|
Existing seeds:
|
|
159
159
|
|
|
@@ -173,11 +173,11 @@ doc file inventory → index diff → stale link/routing review → rewrite sugg
|
|
|
173
173
|
|
|
174
174
|
Likely needed cells:
|
|
175
175
|
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
176
|
+
- Markdown index utility
|
|
177
|
+
- Link checker wrapper
|
|
178
|
+
- Docs consistency reviewer
|
|
179
|
+
- Docs update planner
|
|
180
|
+
- Docs artifact writer
|
|
181
181
|
|
|
182
182
|
Existing seeds:
|
|
183
183
|
|
|
@@ -202,10 +202,10 @@ media scan → playlist build → playback start → message summary → control
|
|
|
202
202
|
|
|
203
203
|
Likely needed cells:
|
|
204
204
|
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
205
|
+
- Playlist builder
|
|
206
|
+
- Music player
|
|
207
|
+
- Run/message summary
|
|
208
|
+
- Control recommender
|
|
209
209
|
|
|
210
210
|
Existing seeds:
|
|
211
211
|
|
|
@@ -226,8 +226,9 @@ Prefer adding a high-level recipe when at least three cells already exist and th
|
|
|
226
226
|
|
|
227
227
|
Good next candidates for the standard library after the first task-first wave:
|
|
228
228
|
|
|
229
|
-
1. Package/release metadata enrichment: implemented in `pipeline-release-readiness` by adding `utility-package-summary` between changelog extraction and validation, making release-readiness reports more evidence-rich without adding publish automation.
|
|
230
|
-
2.
|
|
231
|
-
3.
|
|
229
|
+
1. Package/release metadata enrichment: implemented in `pipeline-release-readiness` by adding `utility-package-summary` and `utility-skill-summary` between changelog extraction and validation, making release-readiness reports more evidence-rich without adding publish automation.
|
|
230
|
+
2. Evidence-only release summary: implemented as `pipeline-release-summary`, which composes changelog/package/skill/validation evidence into a release summary, risk checklist, and PR body draft artifact while leaving commit, PR, merge, tag, and publish actions to explicit release gates.
|
|
231
|
+
3. Artifact packaging and manifesting: implemented as `pipeline-artifact-bundle`, which composes optional validation, `pipeline-artifact-write`, `utility-artifact-manifest`, deterministic manifest writing, and an actor-message handoff when the caller explicitly requests filesystem writes.
|
|
232
|
+
4. Async run cleanup planning: extend async-run operations with stale-run classification and recommended `message`, `cancel`, or `kill` controls, keeping actual control execution operator-gated.
|
|
232
233
|
|
|
233
|
-
Each candidate should land with the minimum missing cells rather than a broad one-shot framework. Already implemented task-first seeds include `pipeline-release-readiness`, `pipeline-repo-health`, `pipeline-async-run-ops`, `pipeline-docs-maintenance`, `pipeline-media-library`, and `pipeline-artifact-bundle`.
|
|
234
|
+
Each candidate should land with the minimum missing cells rather than a broad one-shot framework. Already implemented task-first seeds include `pipeline-release-readiness`, `pipeline-release-summary`, `pipeline-repo-health`, `pipeline-async-run-ops`, `pipeline-docs-maintenance`, `pipeline-media-library`, and `pipeline-artifact-bundle`.
|
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
|
|
@@ -95,7 +127,12 @@ Use recipe-level `mailbox` to document the semantic messages a recipe actor acce
|
|
|
95
127
|
```json
|
|
96
128
|
{
|
|
97
129
|
"mailbox": {
|
|
98
|
-
"accepts": [
|
|
130
|
+
"accepts": [
|
|
131
|
+
"control.continue",
|
|
132
|
+
"control.revise",
|
|
133
|
+
"control.approve",
|
|
134
|
+
"control.stop"
|
|
135
|
+
],
|
|
99
136
|
"emits": ["checkpoint.needs_scope", "branch.done", "run.done"]
|
|
100
137
|
}
|
|
101
138
|
}
|
|
@@ -166,34 +203,19 @@ Call-time params override file params. `values` are merged with file values; cal
|
|
|
166
203
|
|
|
167
204
|
## Registered Recipe Tools
|
|
168
205
|
|
|
169
|
-
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`:
|
|
170
207
|
|
|
171
208
|
```json
|
|
172
209
|
{
|
|
173
|
-
"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
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.
|
|
182
|
-
|
|
183
|
-
A registered tool may also co-locate an actor recipe directly in `actors-tools.json`:
|
|
184
|
-
|
|
185
|
-
```json
|
|
186
|
-
{
|
|
187
|
-
"review_docs": {
|
|
188
|
-
"description": "Start an async docs review",
|
|
189
|
-
"name": "review-docs",
|
|
190
|
-
"async": true,
|
|
191
|
-
"template": "review {scope}"
|
|
192
|
-
}
|
|
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}"
|
|
193
215
|
}
|
|
194
216
|
```
|
|
195
217
|
|
|
196
|
-
|
|
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.
|
|
197
219
|
|
|
198
220
|
## Values And Public Args
|
|
199
221
|
|
package/docs/tool-registry.md
CHANGED
|
@@ -1,39 +1,49 @@
|
|
|
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
|
|
34
44
|
register_tool name=call_subagent \
|
|
35
45
|
description="Run pi as a non-interactive sub-agent" \
|
|
36
|
-
template="pi -p --model {model
|
|
46
|
+
template="pi -p --model {model} --no-tools {prompt}" args="prompt:string,model:string"
|
|
37
47
|
```
|
|
38
48
|
|
|
39
49
|
Use `update=true` to overwrite an existing tool. Omit `template` and co-located recipe fields during update to keep the previous execution binding.
|
|
@@ -47,31 +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
|
|
56
|
-
args="scope:path,model:string
|
|
65
|
+
template="docs-review" \
|
|
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
|
-
}
|
|
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}\""
|
|
71
80
|
}
|
|
72
81
|
```
|
|
73
82
|
|
|
74
|
-
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.
|
|
75
84
|
|
|
76
85
|
Delete a tool with `template=null`:
|
|
77
86
|
|
|
@@ -81,23 +90,22 @@ register_tool name=call_subagent template=null
|
|
|
81
90
|
|
|
82
91
|
## Stored Shape
|
|
83
92
|
|
|
84
|
-
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:
|
|
85
94
|
|
|
86
95
|
```json
|
|
87
96
|
{
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
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}"
|
|
101
109
|
}
|
|
102
110
|
```
|
|
103
111
|
|
|
@@ -106,7 +114,7 @@ Tool names come from the top-level registry keys. Tool entries define `template`
|
|
|
106
114
|
When `args` is omitted, `pi-actors` derives tool parameters from placeholders in `template`:
|
|
107
115
|
|
|
108
116
|
```text
|
|
109
|
-
template="~/bin/transcribe {file} {lang=ru} {model
|
|
117
|
+
template="~/bin/transcribe {file:path} {lang=ru} {model:string}"
|
|
110
118
|
```
|
|
111
119
|
|
|
112
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/actor-messages.ts
CHANGED
|
@@ -44,13 +44,15 @@ export function parseActorAddress(address: string): ActorAddress {
|
|
|
44
44
|
const value = address.trim();
|
|
45
45
|
if (value === "coordinator") return { kind: "coordinator" };
|
|
46
46
|
const separator = value.indexOf(":");
|
|
47
|
-
if (separator < 0)
|
|
47
|
+
if (separator < 0)
|
|
48
|
+
throw new Error(`Actor address must include kind: ${address}`);
|
|
48
49
|
const kind = value.slice(0, separator) as ActorAddressKind;
|
|
49
50
|
const rest = value.slice(separator + 1);
|
|
50
51
|
switch (kind) {
|
|
51
52
|
case "branch": {
|
|
52
53
|
const [run, branch, ...extra] = rest.split("/");
|
|
53
|
-
if (extra.length > 0)
|
|
54
|
+
if (extra.length > 0)
|
|
55
|
+
throw new Error(`Branch address has too many parts: ${address}`);
|
|
54
56
|
return {
|
|
55
57
|
kind,
|
|
56
58
|
value: assertToken(run || "", "branch run"),
|
|
@@ -74,14 +76,19 @@ export function formatActorAddress(address: ActorAddress): string {
|
|
|
74
76
|
return `${address.kind}:${assertToken(address.value || "", `${address.kind} address`)}`;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
function normalizeOptionalString(
|
|
79
|
+
function normalizeOptionalString(
|
|
80
|
+
value: unknown,
|
|
81
|
+
label: string,
|
|
82
|
+
): string | undefined {
|
|
78
83
|
if (value === undefined || value === null) return undefined;
|
|
79
84
|
if (typeof value !== "string") throw new Error(`${label} must be a string`);
|
|
80
85
|
const normalized = value.trim();
|
|
81
86
|
return normalized || undefined;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
|
-
function normalizeMetadata(
|
|
89
|
+
function normalizeMetadata(
|
|
90
|
+
value: unknown,
|
|
91
|
+
): Record<string, unknown> | undefined {
|
|
85
92
|
if (value === undefined || value === null) return undefined;
|
|
86
93
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
87
94
|
throw new Error("message metadata must be an object");
|
|
@@ -113,8 +120,14 @@ export function normalizeActorMessage(input: unknown): ActorMessage {
|
|
|
113
120
|
? { correlation_id: String(record.correlation_id) }
|
|
114
121
|
: {}),
|
|
115
122
|
...(from ? { from: formatActorAddress(parseActorAddress(from)) } : {}),
|
|
116
|
-
...(record.metadata !== undefined
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
...(record.metadata !== undefined
|
|
124
|
+
? { metadata: normalizeMetadata(record.metadata) }
|
|
125
|
+
: {}),
|
|
126
|
+
...(record.reply_to !== undefined
|
|
127
|
+
? { reply_to: String(record.reply_to) }
|
|
128
|
+
: {}),
|
|
129
|
+
...(record.summary !== undefined
|
|
130
|
+
? { summary: String(record.summary) }
|
|
131
|
+
: {}),
|
|
119
132
|
};
|
|
120
133
|
}
|