@llblab/pi-actors 0.12.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 +72 -0
- package/BACKLOG.md +38 -0
- package/CHANGELOG.md +179 -0
- package/README.md +338 -0
- package/docs/README.md +21 -0
- package/docs/actor-messages.md +149 -0
- package/docs/async-runs.md +335 -0
- package/docs/command-templates.md +424 -0
- package/docs/component-recipes.md +148 -0
- package/docs/recipe-library.md +176 -0
- package/docs/task-first-recipes.md +233 -0
- package/docs/template-recipes.md +285 -0
- package/docs/tool-registry.md +142 -0
- package/index.ts +198 -0
- package/lib/actor-messages.ts +120 -0
- package/lib/async-runs.ts +688 -0
- package/lib/command-templates.ts +795 -0
- package/lib/config.ts +266 -0
- package/lib/execution.ts +720 -0
- package/lib/file-state.ts +24 -0
- package/lib/identity.ts +29 -0
- package/lib/observability.ts +525 -0
- package/lib/output.ts +123 -0
- package/lib/paths.ts +35 -0
- package/lib/prompts.ts +75 -0
- package/lib/recipe-references.ts +586 -0
- package/lib/registry.ts +302 -0
- package/lib/runtime.ts +101 -0
- package/lib/schema.ts +402 -0
- package/lib/temp.ts +44 -0
- package/lib/tools.ts +651 -0
- package/package.json +52 -0
- package/recipes/music-player.json +25 -0
- package/recipes/pipeline-architect-coordinator.json +88 -0
- package/recipes/pipeline-artifact-report.json +52 -0
- package/recipes/pipeline-artifact-write.json +66 -0
- package/recipes/pipeline-async-run-ops.json +67 -0
- package/recipes/pipeline-checkpoint-continuation.json +57 -0
- package/recipes/pipeline-development-tasking.json +73 -0
- package/recipes/pipeline-docs-maintenance.json +72 -0
- package/recipes/pipeline-media-library.json +51 -0
- package/recipes/pipeline-quorum-review.json +72 -0
- package/recipes/pipeline-release-readiness.json +83 -0
- package/recipes/pipeline-repo-health.json +81 -0
- package/recipes/pipeline-research-synthesis.json +87 -0
- package/recipes/pipeline-review-readiness.json +49 -0
- package/recipes/subagent-artifact.json +26 -0
- package/recipes/subagent-checkpoint.json +27 -0
- package/recipes/subagent-conflict-report.json +25 -0
- package/recipes/subagent-contradiction-map.json +26 -0
- package/recipes/subagent-critic.json +28 -0
- package/recipes/subagent-evidence-map.json +26 -0
- package/recipes/subagent-followup.json +27 -0
- package/recipes/subagent-judge.json +26 -0
- package/recipes/subagent-merge.json +26 -0
- package/recipes/subagent-message.json +29 -0
- package/recipes/subagent-normalize.json +24 -0
- package/recipes/subagent-plan.json +26 -0
- package/recipes/subagent-prompt.json +22 -0
- package/recipes/subagent-quorum.json +41 -0
- package/recipes/subagent-review-coordinator.json +107 -0
- package/recipes/subagent-review.json +30 -0
- package/recipes/subagent-task-card.json +28 -0
- package/recipes/subagent-tools.json +17 -0
- package/recipes/subagent-verify.json +27 -0
- package/recipes/subagents-prompts.json +32 -0
- package/recipes/utility-actor-message.json +24 -0
- package/recipes/utility-artifact-manifest.json +17 -0
- package/recipes/utility-artifact-write.json +17 -0
- package/recipes/utility-changelog-head.json +12 -0
- package/recipes/utility-changelog-section.json +14 -0
- package/recipes/utility-git-log.json +12 -0
- package/recipes/utility-git-status.json +10 -0
- package/recipes/utility-jsonl-tail.json +11 -0
- package/recipes/utility-markdown-index.json +15 -0
- package/recipes/utility-package-summary.json +12 -0
- package/recipes/utility-playlist-build.json +18 -0
- package/recipes/utility-playlist-scan.json +12 -0
- package/recipes/utility-run-state-files.json +14 -0
- package/recipes/utility-run-summary.json +12 -0
- package/recipes/utility-validate-recipe.json +14 -0
- package/recipes/utility-validation-wrapper.json +14 -0
- package/scripts/async-runner.mjs +170 -0
- package/scripts/music-player.mjs +637 -0
- package/scripts/recipe-utils.mjs +273 -0
- package/scripts/validate-recipe.mjs +89 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# Command Template Standard
|
|
2
|
+
|
|
3
|
+
Command templates are the portable integration format for deterministic local automation.
|
|
4
|
+
|
|
5
|
+
**Meta-contract:** transportable (bit-for-bit identical across projects), high-density (zero fluff), constant (evolve by crystallizing, not speculating), optimal minimum (add only when it hurts).
|
|
6
|
+
|
|
7
|
+
**Scope:** portable synchronous command execution format — shell-free exec, composition/pipes, optional timeout, delay-before-start, bounded retry, failure propagation, recover cleanup, output artifact selection, and handler-level fallback. Single JSON standard; no platform lock-in.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Extensions may choose their own config files, selectors, placeholder sources, and examples, but should preserve this core contract.
|
|
12
|
+
|
|
13
|
+
Layer boundary: command templates own only the synchronous execution graph. Recipe imports, import-reference expressions, recipe lookup, `async: true`, run ids, state dirs, mailbox controls, and actor-message routing are host/recipe/async-run configuration layers, not portable command-template syntax.
|
|
14
|
+
|
|
15
|
+
## Layer Ownership
|
|
16
|
+
|
|
17
|
+
Command-template standard owns:
|
|
18
|
+
|
|
19
|
+
- Command string splitting and direct argv execution.
|
|
20
|
+
- Placeholder resolution, typed public args, defaults, `??`, ternary string selection, and array-index placeholders.
|
|
21
|
+
- Synchronous graph shape: sequence, `parallel`, `when`, `repeat`, stdin flow, stdout joins, and output selection.
|
|
22
|
+
- Per-node execution controls: `timeout`, `delay`, `retry`, `failure`, and `recover`.
|
|
23
|
+
|
|
24
|
+
Command-template standard does not own:
|
|
25
|
+
|
|
26
|
+
- Where templates are stored or how they are named.
|
|
27
|
+
- Recipe imports, import references, or file lookup.
|
|
28
|
+
- Detached lifecycle, run ids, state dirs, logs, cancellation, mailbox controls, or actor-message routing.
|
|
29
|
+
- Registry metadata such as tool descriptions, package install paths, or operator policy.
|
|
30
|
+
|
|
31
|
+
## Shape
|
|
32
|
+
|
|
33
|
+
A command template is either a command-line string or an ordered array of command-template leaves:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"template": "/path/to/stt --file {file} --lang {lang=ru}"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
When the surrounding schema already implies a command template, the compact string form is equivalent:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
"/path/to/stt --file {file} --lang {lang=ru}"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
There is no portable `command` field. The command is derived from `template`: after splitting, the first word is the executable and the remaining words are argv args. Templates do not infer flags: `{file}` is one positional arg; `--file {file}` is a flag arg plus its value.
|
|
48
|
+
|
|
49
|
+
Common object fields:
|
|
50
|
+
|
|
51
|
+
- `label`: Optional human label for diagnostics and parallel branch reports.
|
|
52
|
+
- `parallel`: Optional boolean for array templates. Default is `false` for sequence; `true` runs children concurrently.
|
|
53
|
+
- `when`: Optional node guard. A false guard skips the node; strings may be `flag`, `!flag`, or `{flag?yes:no}` style expressions.
|
|
54
|
+
- `args`: Optional placeholder declarations. Untyped names remain valid; compact typed forms such as `file:path`, `request_timeout:int`, `speed:number`, `dry_run:bool`, `prompts:array`, and `mode:enum(check,fix)` are valid when the host supports typed tool schemas. Defaults belong in `defaults` or inline placeholder defaults; hosts may normalize interactive shorthand such as `request_timeout:int=60000` before persistence.
|
|
55
|
+
- `defaults`: Placeholder default values by name.
|
|
56
|
+
- `timeout`: Optional execution timeout in milliseconds. Omit it, or set `0`, to leave the command unbounded. Set an explicit positive timeout when a tool must fail closed instead of waiting indefinitely. Numeric control fields may be literal numbers or placeholders such as `"{timeout_ms}"`.
|
|
57
|
+
- `delay`: Optional wait in milliseconds before starting this node. Default is no delay. It may be a literal number or placeholder.
|
|
58
|
+
- `output`: Optional result selector. Default is `"stdout"`; runtime values such as `"ogg"` are valid.
|
|
59
|
+
- `retry`: Optional max attempts including the first. Default is `1`.
|
|
60
|
+
- `failure`: Optional failure propagation scope: `continue`, `branch`, or `root`. Default is `continue`.
|
|
61
|
+
- `recover`: Optional command template run between failed retry attempts. Recovery output is ignored; recovery failure stops retries.
|
|
62
|
+
- `template`: Required command string or ordered composition array.
|
|
63
|
+
|
|
64
|
+
For object form, write `template` last. Read the node flags first, then the executable content. Storage paths, labels, selectors, descriptions, and registry-specific metadata belong to each extension's local schema.
|
|
65
|
+
|
|
66
|
+
## Execution
|
|
67
|
+
|
|
68
|
+
A runtime must:
|
|
69
|
+
|
|
70
|
+
1. Split the template into shell-like words with simple single quotes, double quotes, and backslash escapes
|
|
71
|
+
2. Substitute placeholders inside each split word
|
|
72
|
+
3. Execute command + args directly, without shell evaluation
|
|
73
|
+
4. Treat exit code `0` as success and non-zero as failure
|
|
74
|
+
5. Use stdout as the default result channel and stderr only for diagnostics
|
|
75
|
+
|
|
76
|
+
Implementations may expand `~` in command position and may resolve relative command paths against the caller cwd.
|
|
77
|
+
|
|
78
|
+
## Placeholders
|
|
79
|
+
|
|
80
|
+
Supported forms:
|
|
81
|
+
|
|
82
|
+
| Form | Meaning |
|
|
83
|
+
| ------------------- | ------------------------------------------------ |
|
|
84
|
+
| `{name}` | Required value from runtime values or `defaults` |
|
|
85
|
+
| `{name=default}` | Inline default when no value is provided |
|
|
86
|
+
| `{name??fallback}` | Fallback when value is missing, null, or empty |
|
|
87
|
+
| `{name?yes:no}` | Ternary string selected by truthiness of `name` |
|
|
88
|
+
| `{items[index]}` | Array item selected by literal or repeat index |
|
|
89
|
+
|
|
90
|
+
Resolution order is runtime values → `defaults` → inline default → error. Nullish coalescing and ternary conditions treat missing, empty, `false`, `0`, and `no` as false. Use `??` for value fallback and ternaries for small string selection such as optional CLI flags; larger policy branches should stay in recipes, scripts, or separate template nodes. Default values that are themselves a single placeholder, such as `{prompt}` resolving to `{prompts[index]}`, are resolved recursively with a small depth guard. A repeat node may set `repeat` to `{items.length}` when an array arg should determine fanout width.
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"template": "/path/to/tts --text {text} --lang {lang=ru} --rate {rate=+30%}"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
With runtime values `{ "text": "hello" }`, argv is:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
["--text", "hello", "--lang", "ru", "--rate", "+30%"]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Use `defaults` for visible configuration data; use inline defaults for compact local literals. Prefer flag-style examples such as `/path/to/tool --file {file} --lang {lang=ru}` for readability, but positional forms such as `/path/to/tool {file} {lang=ru}` are valid when the invoked script defines that CLI contract.
|
|
105
|
+
|
|
106
|
+
Fallback values can be selected with nullish coalescing:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"template": "deploy --env {env??dev} --region {region??local}"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Optional flags can be mapped from boolean args with a ternary:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"args": ["target:path", "all:bool"],
|
|
119
|
+
"defaults": { "all": "true" },
|
|
120
|
+
"template": "validate-recipe {target} {all?--all:}"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Typed declarations annotate the public tool interface, not the shell command. They may live in `args` or inline placeholders such as `{request_timeout:int=60000}` and `{mode:enum(check,fix)=check}`. Use metadata-first authoring (`args` plus `defaults`) when long templates should stay visually short; use inline-first authoring when one self-contained `template` property is clearer. They do not sandbox or reinterpret the executable; they only let the host generate narrower input schemas and normalize runtime values before placeholder substitution. Untyped `args` and untyped placeholders continue to work unchanged.
|
|
125
|
+
|
|
126
|
+
Node control fields can also read public args. Use distinct arg names so execution controls stay visually separate from public inputs:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"args": ["timeout_ms:int"],
|
|
131
|
+
"timeout": "{timeout_ms}",
|
|
132
|
+
"template": "npm test"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Quoting
|
|
137
|
+
|
|
138
|
+
Placeholder values are not shell-escaped because no shell is used. A value containing spaces remains one argv item when it replaces one split word:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
template="echo {text}"
|
|
142
|
+
text="hello world"
|
|
143
|
+
args=["hello world"]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
A placeholder may also be embedded inside one word:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
template="/path/to/tool --file={file}"
|
|
150
|
+
file="/tmp/a b.ogg"
|
|
151
|
+
args=["--file=/tmp/a b.ogg"]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Use quotes only for literal template words that should contain spaces before placeholder substitution:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
template="echo 'literal words' {text}"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Composition
|
|
161
|
+
|
|
162
|
+
`template: [...]` means sequential composition by default; each leaf is a command template executed with one shared runtime value map:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"template": [
|
|
167
|
+
"/path/to/tts --text {text} --lang {lang=ru} --out {mp3}",
|
|
168
|
+
"ffmpeg -y -i {mp3} -c:a libopus {ogg}"
|
|
169
|
+
],
|
|
170
|
+
"output": "ogg"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Composition rules:
|
|
175
|
+
|
|
176
|
+
- Execute leaves in order when `parallel` is omitted or `false`
|
|
177
|
+
- Execute child templates concurrently when `parallel` is `true`
|
|
178
|
+
- Parallel composition uses soft-quorum semantics by default: failed children are reported as degraded branches unless failure propagation escalates
|
|
179
|
+
- Non-critical failures are recorded and execution continues, while `failure: "branch"` stops the current branch and `failure: "root"` aborts the root composition
|
|
180
|
+
- Treat the whole composition as one handler for selector matching and fallback
|
|
181
|
+
- Top-level `args` and `defaults` apply to every leaf unless the leaf defines private values
|
|
182
|
+
- Leaf `args` replace inherited `args`; leaf `defaults` merge over inherited defaults; `timeout` and `output` are not inherited into leaves
|
|
183
|
+
- Timeout is disabled by default; configure a positive `timeout` for bounded commands that should fail closed
|
|
184
|
+
- Each sequence leaf receives the previous leaf's stdout on stdin by default, while the final leaf stdout remains the default composition result
|
|
185
|
+
- Skipped nodes preserve current stdin/stdout flow and do not execute commands
|
|
186
|
+
- Each parallel child receives the same stdin, and child stdout values are joined in stable array order before flowing to the next sequence leaf
|
|
187
|
+
- Parallel branch joins include branch label and status, and tool details include branch metadata plus coverage summary
|
|
188
|
+
- Each leaf still applies its own inline defaults
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"template": [
|
|
193
|
+
"/path/to/tts --text {text} --lang {lang} --out {mp3}",
|
|
194
|
+
{
|
|
195
|
+
"defaults": { "codec": "libopus" },
|
|
196
|
+
"template": "ffmpeg -y -i {mp3} -c:a {codec} {ogg}"
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
"args": ["text", "lang", "mp3", "ogg"],
|
|
200
|
+
"defaults": { "lang": "en" },
|
|
201
|
+
"output": "ogg"
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`output` selects the primary result channel. Omitted `output` means `"stdout"`, and explicitly writing `"output": "stdout"` is valid standard syntax. Artifact-producing handlers may instead name a runtime value or placeholder path, e.g. `"ogg"` or `"{ogg}"`. Do not use `artifacts` in command-template nodes; named artifact manifests belong to the template-recipe layer.
|
|
206
|
+
|
|
207
|
+
### Repeat
|
|
208
|
+
|
|
209
|
+
`repeat` expands one command-template node N times before execution. It works with both sequence and parallel nodes and is useful when many branches differ only by a number.
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"parallel": true,
|
|
214
|
+
"repeat": 8,
|
|
215
|
+
"template": "render page{_(index+1)}.html --prev page{_(prev+1)}.html --next page{_(next+1)}.html --zero page{_index}.html"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Reserved repeat placeholders are injected into each repeated node:
|
|
220
|
+
|
|
221
|
+
- `{index}`: current zero-based index, `0..repeat-1`
|
|
222
|
+
- `{prev}` / `{next}`: wrapped zero-based neighbors
|
|
223
|
+
- `{repeat}`: total repeat count
|
|
224
|
+
|
|
225
|
+
Human 1-based numbering is intentionally expressed as limited arithmetic: `{index+1}`, `{prev+1}`, `{next+1}`.
|
|
226
|
+
|
|
227
|
+
Leading underscores on repeat placeholders request zero padding. One underscore means width 2, two underscores mean width 3, and so on:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
{_index} → 00, 01, ...
|
|
231
|
+
{_(index+1)} → 01, 02, ...
|
|
232
|
+
{__(index+1)} → 001, 002, ...
|
|
233
|
+
{_(prev+1)} → wrapped previous page number, padded to width 2
|
|
234
|
+
{_(next+1)} → wrapped next page number, padded to width 2
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Repeat expressions support only integers, `index`, `prev`, `next`, `repeat`, parentheses, and `+`, `-`, `*`, `/`, `%`. They are not JavaScript and cannot call functions or access properties.
|
|
238
|
+
|
|
239
|
+
Repeat placeholders are local generated values. Call-time args should not use these reserved names to override the repeat index.
|
|
240
|
+
|
|
241
|
+
Parallel nodes use the same object shape. Flags come first and `template` stays last:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"template": [
|
|
246
|
+
"prepare {out_dir}",
|
|
247
|
+
{
|
|
248
|
+
"parallel": true,
|
|
249
|
+
"template": [
|
|
250
|
+
{
|
|
251
|
+
"label": "gpt-5.5",
|
|
252
|
+
"timeout": 300000,
|
|
253
|
+
"template": "review-gpt {scope}"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"label": "deepseek-pro",
|
|
257
|
+
"timeout": 300000,
|
|
258
|
+
"template": "review-deepseek {scope}"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"label": "kimi",
|
|
262
|
+
"timeout": 300000,
|
|
263
|
+
"template": "review-kimi {scope}"
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
"merge {out_dir}"
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
A degraded parallel join is still usable when at least one branch succeeds:
|
|
273
|
+
|
|
274
|
+
```text
|
|
275
|
+
--- branch: gpt-5.5 status: done ---
|
|
276
|
+
review text
|
|
277
|
+
--- branch: deepseek-pro status: failed ---
|
|
278
|
+
exit: 1
|
|
279
|
+
stderr: provider balance exhausted
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Some local schemas may accept `pipe` as an alias, but the portable standard is `template: [...]`.
|
|
283
|
+
|
|
284
|
+
## Fail-Open Default Policy
|
|
285
|
+
|
|
286
|
+
By default, composition continues on failure: the failed step is logged and the next step executes. This is analogous to `make -k` — the user sees all failures at once and decides what to fix.
|
|
287
|
+
|
|
288
|
+
## Failure Propagation
|
|
289
|
+
|
|
290
|
+
By default, failed steps use `failure: "continue"`: record the failure, clear stdout for that step, and continue the current sequence. This preserves the fail-open profile.
|
|
291
|
+
|
|
292
|
+
Use `failure` when a node should stop more aggressively:
|
|
293
|
+
|
|
294
|
+
- `"continue"`: record the failure and continue the current sequence.
|
|
295
|
+
- `"branch"`: stop the current sequence/subtree and return a failed branch to the nearest parent. In a parallel node, sibling branches keep running and the join becomes degraded. At the root, branch failure is still a tool failure.
|
|
296
|
+
- `"root"`: abort the outermost composition.
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"parallel": true,
|
|
301
|
+
"template": [
|
|
302
|
+
{
|
|
303
|
+
"label": "agent-a",
|
|
304
|
+
"failure": "branch",
|
|
305
|
+
"template": [
|
|
306
|
+
"agent-a-work {scope}",
|
|
307
|
+
"agent-a-validate {scope}",
|
|
308
|
+
"agent-a-push {scope}"
|
|
309
|
+
]
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"label": "agent-b",
|
|
313
|
+
"failure": "branch",
|
|
314
|
+
"template": [
|
|
315
|
+
"agent-b-work {scope}",
|
|
316
|
+
"agent-b-validate {scope}",
|
|
317
|
+
"agent-b-push {scope}"
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
If `agent-a-validate` fails, `agent-a-push` is skipped, `agent-b` can still finish, and the parallel join reports degraded branch coverage.
|
|
325
|
+
|
|
326
|
+
## Retry
|
|
327
|
+
|
|
328
|
+
Set `retry: N` to attempt execution up to `N` times including the first. The first successful attempt stops the retry loop.
|
|
329
|
+
|
|
330
|
+
On leaf commands, retry repeats that command. On sequence or parallel nodes, retry repeats the whole node. A retried group only retries when the group returns a failure, so validator checkpoints normally pair group retry with `failure: "branch"` or `failure: "root"`.
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"failure": "branch",
|
|
335
|
+
"retry": 3,
|
|
336
|
+
"template": ["implement {scope}", "npm test", "git diff --check"]
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Here the whole group runs again when a validator fails. Without `failure: "branch"`, the failed validator would be logged and the group would continue by default.
|
|
341
|
+
|
|
342
|
+
## Recover
|
|
343
|
+
|
|
344
|
+
Set `recover` on a retried node to run cleanup after a failed attempt and before the next attempt. `recover` is another command template: it can be a string command, sequence, or parallel tree. Its output is ignored and the next retry receives the original stdin.
|
|
345
|
+
|
|
346
|
+
```json
|
|
347
|
+
{
|
|
348
|
+
"failure": "branch",
|
|
349
|
+
"retry": 3,
|
|
350
|
+
"recover": "git -C {work_dir} reset --hard HEAD",
|
|
351
|
+
"template": ["pi -p --tools read,edit,bash {scope_file}", "npm test"]
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`recover` is not a fallback success path. It is cleanup between attempts. Practical uses include resetting a worktree, removing temp files, clearing generated output, releasing a local lock, or stopping a helper process before trying the node again. If recovery fails, retries stop and the recovery failure is returned. Recovery uses fail-closed semantics by default; set an explicit `failure` inside a recover template only when a softer cleanup failure is intentional.
|
|
356
|
+
|
|
357
|
+
## Conditional Nodes
|
|
358
|
+
|
|
359
|
+
Set `when` to skip a node unless a boolean condition is true. This is node-level branching, not placeholder text selection. It is useful for optional validation, artifact, or reporting steps.
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"template": [
|
|
364
|
+
"prepare {target}",
|
|
365
|
+
{ "when": "run_tests", "template": "npm test" },
|
|
366
|
+
{ "when": "!run_tests", "template": "echo tests skipped" }
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Falsy values are missing, empty, `false`, `0`, and `no`. In a sequence, a skipped node preserves the previous stdout for the next step. In a parallel branch, a skipped node succeeds with empty branch output.
|
|
372
|
+
|
|
373
|
+
## Delay
|
|
374
|
+
|
|
375
|
+
Set `delay` to wait before starting a node. The value is milliseconds. Delay is not inherited into child nodes, just like `timeout`.
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"template": [
|
|
380
|
+
"prepare {scope}",
|
|
381
|
+
{ "delay": 1000, "template": "review {scope}" }
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
On a sequence node, `delay` waits before the sequence begins. On a parallel node, `delay` waits before launching its children. On a branch, `delay` waits before that branch starts, without blocking sibling branches.
|
|
387
|
+
|
|
388
|
+
Use `delay` only for explicit backoff, rate pacing, or staged launch. Do not use it as a scheduler.
|
|
389
|
+
|
|
390
|
+
## Progressive Disclosure
|
|
391
|
+
|
|
392
|
+
The standard uses a single `template` field that grows with the user's needs:
|
|
393
|
+
|
|
394
|
+
```text
|
|
395
|
+
string → leaf command
|
|
396
|
+
string[] → sequential composition
|
|
397
|
+
{ template } → leaf command object
|
|
398
|
+
{ parallel, template } → sequence or parallel subtree
|
|
399
|
+
{ parallel, when, args, defaults, delay, retry, failure, recover, output, template } → full node
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Start with a string. Add composition when needed. Add `parallel: true` when independent work can run concurrently. Add `when` when a node is conditional. Add delay when launch pacing matters. Add retry when flaky. Add `failure` when propagation scope matters. Add `recover` when a retried node needs cleanup before another attempt. Same contract, growing capability, no dead weight.
|
|
403
|
+
|
|
404
|
+
`parallel: true` is the synchronous fanout shape. Saved JSON belongs to the separate [Template Recipe Standard](./template-recipes.md); detached lifecycle, logs, cancellation, and durable state belong to the separate [Async Run Standard](./async-runs.md).
|
|
405
|
+
|
|
406
|
+
## Trust Boundary
|
|
407
|
+
|
|
408
|
+
Command templates avoid shell interpolation by splitting the template into argv first and substituting placeholders per arg. A placeholder value containing spaces remains one argv value, not a shell fragment.
|
|
409
|
+
|
|
410
|
+
This is not a sandbox. The executable still runs with the same user permissions as the host agent. Shells, interpreter eval modes, destructive filesystem commands, and local scripts remain trusted code. Examples that deserve extra operator attention:
|
|
411
|
+
|
|
412
|
+
- `bash`, `sh`, `zsh`, or `fish`, especially with `-c`.
|
|
413
|
+
- `node -e`, `python -c`, `ruby -e`, `perl -e`, or similar eval modes.
|
|
414
|
+
- `rm`, `mv`, `cp`, or `rsync` over broad paths or placeholder-derived paths.
|
|
415
|
+
|
|
416
|
+
Hosts may surface lightweight warnings for these obvious high-risk shapes. Warnings should inform review without blocking existing tools, because many trusted local wrappers intentionally use shells or filesystem mutation.
|
|
417
|
+
|
|
418
|
+
## Tool Boundary
|
|
419
|
+
|
|
420
|
+
Agent tools are a separate abstraction. A tool name is not a portable command template because the pi extension API exposes tool registration metadata, not a public extension-to-extension `executeTool(name, args)` contract. Until such an API exists, extensions should use command templates for deterministic local automation.
|
|
421
|
+
|
|
422
|
+
## Compatibility
|
|
423
|
+
|
|
424
|
+
Consumers should share this contract, not private registry fields or implementation details from any specific extension.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Component Recipes
|
|
2
|
+
|
|
3
|
+
Component recipes are small saved recipe definitions that expose one coordination capability each. They are the construction kit for higher-level subagent coordinators: a coordinator composes components instead of embedding one large orchestration DSL.
|
|
4
|
+
|
|
5
|
+
## Boundary
|
|
6
|
+
|
|
7
|
+
This is a weak abstract contract, not a hard dependency model.
|
|
8
|
+
|
|
9
|
+
- `pi-actors` provides root `recipes/` actor component definitions and runtime bindings.
|
|
10
|
+
- Portable coordination skills should target capabilities, not this extension by name.
|
|
11
|
+
- Local adapters bind abstract components to concrete recipes, command templates, async runs, model aliases, files, and tool registries.
|
|
12
|
+
- A component should be replaceable by another implementation with the same capability contract.
|
|
13
|
+
|
|
14
|
+
## Component Contract
|
|
15
|
+
|
|
16
|
+
Every reusable component recipe should make the following clear:
|
|
17
|
+
|
|
18
|
+
- **Capability**: the one operation it performs.
|
|
19
|
+
- **Args**: caller-controlled prompts, scopes, paths, models, and policy knobs.
|
|
20
|
+
- **Output**: the expected shape of stdout or artifact paths.
|
|
21
|
+
- **Messages**: optional actor-message envelopes for checkpoints, questions, progress, or findings.
|
|
22
|
+
- **Failure policy**: whether failures stop the root, only fail a branch, or are recoverable.
|
|
23
|
+
- **Non-goals**: coordination behavior the component intentionally does not own.
|
|
24
|
+
|
|
25
|
+
Reusable components should expose common policy knobs instead of baking in local choices: `model`, `thinking`, `tools`, `output_format`, `evidence_policy`, `risk_policy`, source policy, continuity/resume policy, handoff format, merge mode, model pools, and stage-specific models. Higher-level recipes may pass these knobs through so the same component can run as a safe no-tools reviewer, a file-reading reviewer, a release gate, a research synthesizer, a task-card author, or a high-thinking merger.
|
|
26
|
+
|
|
27
|
+
Keep components narrow. Higher-level recipes should own composition, not hidden behavior inside a leaf.
|
|
28
|
+
|
|
29
|
+
## Spectrum of Components
|
|
30
|
+
|
|
31
|
+
### Launchers
|
|
32
|
+
|
|
33
|
+
Start one subagent or branch with a caller-provided prompt and model. Launchers do not judge or merge output.
|
|
34
|
+
|
|
35
|
+
Example: `recipes/subagent-prompt.json`.
|
|
36
|
+
|
|
37
|
+
### Reviewers
|
|
38
|
+
|
|
39
|
+
Inspect a scope through a declared lens and return evidence-grounded findings.
|
|
40
|
+
|
|
41
|
+
Seed example: `recipes/subagent-review.json`.
|
|
42
|
+
|
|
43
|
+
### Critics
|
|
44
|
+
|
|
45
|
+
Attack assumptions, edge cases, and failure modes. Critics should not rewrite the plan unless asked.
|
|
46
|
+
|
|
47
|
+
Seed example: `recipes/subagent-critic.json`.
|
|
48
|
+
|
|
49
|
+
### Planners
|
|
50
|
+
|
|
51
|
+
Turn a goal into bounded slices, validation gates, risks, and stop conditions.
|
|
52
|
+
|
|
53
|
+
Seed example: `recipes/subagent-plan.json`.
|
|
54
|
+
|
|
55
|
+
### Verifiers
|
|
56
|
+
|
|
57
|
+
Check a claim, artifact, or proposed result against evidence. Verifiers should separate proven, disproven, and unknown.
|
|
58
|
+
|
|
59
|
+
Seed example: `recipes/subagent-verify.json`.
|
|
60
|
+
|
|
61
|
+
### Evidence and Contradiction Mappers
|
|
62
|
+
|
|
63
|
+
Map source support, weak evidence, contradictions, unresolved assumptions, and missing source classes before synthesis.
|
|
64
|
+
|
|
65
|
+
Seed examples: `recipes/subagent-evidence-map.json` and `recipes/subagent-contradiction-map.json`.
|
|
66
|
+
|
|
67
|
+
### Mergers
|
|
68
|
+
|
|
69
|
+
Combine multiple branch outputs into a single synthesis. Mergers preserve minority findings and mark unsupported additions.
|
|
70
|
+
|
|
71
|
+
Seed example: `recipes/subagent-merge.json`.
|
|
72
|
+
|
|
73
|
+
### Normalizers, Artifacts, and Events
|
|
74
|
+
|
|
75
|
+
Convert variable branch output into stable JSON, Markdown sections, file artifacts, or actor-message records for downstream recipes.
|
|
76
|
+
|
|
77
|
+
Seed examples: `recipes/subagent-normalize.json`, `recipes/subagent-artifact.json`, and `recipes/subagent-message.json`.
|
|
78
|
+
|
|
79
|
+
### Quorum Operators
|
|
80
|
+
|
|
81
|
+
Run the same task across several independent models or instances and preserve vote shape for a later merger.
|
|
82
|
+
|
|
83
|
+
Seed example: `recipes/subagent-quorum.json`.
|
|
84
|
+
|
|
85
|
+
### Tasking and Conflict Handoffs
|
|
86
|
+
|
|
87
|
+
Produce bounded task cards or conflict reports for development swarms and integrator workflows.
|
|
88
|
+
|
|
89
|
+
Seed examples: `recipes/subagent-task-card.json` and `recipes/subagent-conflict-report.json`.
|
|
90
|
+
|
|
91
|
+
### Checkpoint Emitters
|
|
92
|
+
|
|
93
|
+
Emit bounded coordinator questions, partial state, or branch decisions as coordinator-bound actor messages. Checkpoint components should not pretend same-context resume exists unless the adapter can prove it.
|
|
94
|
+
|
|
95
|
+
Seed example: `recipes/subagent-checkpoint.json`.
|
|
96
|
+
|
|
97
|
+
### Follow-up Continuations
|
|
98
|
+
|
|
99
|
+
Resume or continue a branch with a bounded reply. If same-context continuation is unavailable, the component must declare a degraded mode such as creating a new branch with the checkpoint artifact included.
|
|
100
|
+
|
|
101
|
+
Seed example: `recipes/subagent-followup.json`.
|
|
102
|
+
|
|
103
|
+
### Judges
|
|
104
|
+
|
|
105
|
+
Evaluate report quality, evidence preservation, severity calibration, consensus purity, and internal consistency. Judges should not silently become another domain reviewer.
|
|
106
|
+
|
|
107
|
+
Seed example: `recipes/subagent-judge.json`.
|
|
108
|
+
|
|
109
|
+
## Composition Shape
|
|
110
|
+
|
|
111
|
+
A coordinator recipe can stay small by composing components:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
launch/review fanout → verify claims → merge → judge → final synthesis
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Seed example: `recipes/subagent-review-coordinator.json` composes reviewer fanout, verification, merge, judge, and normalization.
|
|
118
|
+
|
|
119
|
+
Higher-level examples:
|
|
120
|
+
|
|
121
|
+
- `recipes/pipeline-review-readiness.json`: Release/readiness gate over selected lenses.
|
|
122
|
+
- `recipes/pipeline-quorum-review.json`: Same prompt across a model pool, then merge, judge, and normalize vote shape.
|
|
123
|
+
- `recipes/pipeline-architect-coordinator.json`: Architecture direction synthesis with lens fanout, critique, verification, merge, and next-slice output.
|
|
124
|
+
- `recipes/pipeline-research-synthesis.json`: Plan, evidence map, contradiction map, verification, merge, and normalized research synthesis.
|
|
125
|
+
- `recipes/pipeline-checkpoint-continuation.json`: Checkpoint artifact, follow-up continuation, and normalized handoff with explicit degraded-mode handling.
|
|
126
|
+
- `recipes/pipeline-development-tasking.json`: Plan, task card, critique, and normalized integrator handoff for bounded implementation work.
|
|
127
|
+
- `recipes/pipeline-artifact-report.json`: Normalized report → durable artifact-shaped output → actor-message-shaped record.
|
|
128
|
+
|
|
129
|
+
For high-risk work, split breadth and confidence:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
lens swarm → quorum per lens → merger → post-merge judge
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For implementation work, combine coordination with local ownership policy:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
task cards → scoped branch agents → conflict reports → integrator merge → review
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Design Rules
|
|
142
|
+
|
|
143
|
+
- Prefer public args/defaults over baked-in local policy.
|
|
144
|
+
- Use `failure: "branch"` for independent fanout branches unless one failure invalidates the whole run.
|
|
145
|
+
- Keep model pools and provider aliases configurable.
|
|
146
|
+
- Use artifacts or actor messages for intermediate outputs that must survive compaction.
|
|
147
|
+
- Do not hide broad coordinator behavior inside a component named like a leaf.
|
|
148
|
+
- Do not introduce scheduler, goto, or workflow-only syntax; compose saved recipes and command templates.
|