@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
package/README.md
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# pi-actors
|
|
2
|
+
|
|
3
|
+
Actor runtime and persistent local tool registry extension for the pi coding agent.
|
|
4
|
+
|
|
5
|
+
`pi-actors` is a local-first, cybernetic actor layer for agents. It is MCP-adjacent in spirit, but instead of waiting for external servers to define every capability, the agent can turn trusted local commands, scripts, and recipes into durable tools itself. Those tools persist across reloads as a kind of operational muscle memory: short semantic names, typed args, reusable recipes, and async runs replace repeated shell reconstruction.
|
|
6
|
+
|
|
7
|
+
## Start Here
|
|
8
|
+
|
|
9
|
+
- [Project Context](./AGENTS.md)
|
|
10
|
+
- [Open Backlog](./BACKLOG.md)
|
|
11
|
+
- [Changelog](./CHANGELOG.md)
|
|
12
|
+
- [Documentation](./docs/README.md)
|
|
13
|
+
|
|
14
|
+
## Key Features
|
|
15
|
+
|
|
16
|
+
- **Local-First Tool Memory**: Lets agents create and persist their own trusted local tools, forming durable operational muscle memory instead of one-off shell commands.
|
|
17
|
+
- **Commands Become Capabilities**: Turns stable local workflows into semantic agent tools, so the agent chooses what it can do instead of reconstructing how to run shell commands.
|
|
18
|
+
- **Persistent Tool Registry**: Stores tool definitions in `~/.pi/agent/tools.json` and registers them automatically on session start.
|
|
19
|
+
- **Compact Semantic Interface**: Exposes short tool names, descriptions, named args, and defaults instead of long paths, positional command-arg order, and repeated command boilerplate.
|
|
20
|
+
- **Safer Local Automation**: Wraps trusted command templates as narrow tools using split-first command-arg construction, placeholder substitution, and no shell evaluation.
|
|
21
|
+
- **Reusable Building Blocks**: Makes skill scripts, sub-agent wrappers, diagnostics, and project workflows available as composable agent capabilities.
|
|
22
|
+
- **Immediate Updates**: Registered and updated tools become callable in the active session; deleted tools are removed from active tools and fully disappear after reload.
|
|
23
|
+
- **Bounded Output**: Tool stdout is returned to the agent with truncation safeguards; full oversized output is saved to a temp file.
|
|
24
|
+
- **Template Recipes**: Stores reusable command-template JSON under `~/.pi/agent/recipes/*.json`; recipes can import other recipes, reuse defaults, declare ordered named `artifacts`, and run foreground or declare `async: true` for detached lifecycle.
|
|
25
|
+
- **Actor Runs**: Starts detached recipe or inline-template runs as addressable actors backed by state files under `~/.pi/agent/tmp/pi-actors`.
|
|
26
|
+
- **Context Onboarding**: Injects a compact system-prompt note explaining templates, recipes, async runs, tasks, and agent fanout so installed sessions have the mental model available by default.
|
|
27
|
+
- **Coordinator-Scoped Run Observability**: Shows at least one stable triangle per running async run started by the current agent session, adds triangles for active parallel branches, then injects compact completion events only back to the launching coordinator when attention is needed.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
From npm:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pi install npm:@llblab/pi-actors
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
From git:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pi install git:github.com/llblab/pi-actors
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Mental Model
|
|
44
|
+
|
|
45
|
+
`pi-actors` has one execution idea that grows in place:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
command
|
|
49
|
+
→ command template
|
|
50
|
+
→ template recipe
|
|
51
|
+
→ registered tool
|
|
52
|
+
→ async run
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- A **command** is one concrete local process.
|
|
56
|
+
- A **command template** is the reusable shape of that process, with named placeholders.
|
|
57
|
+
- A **template recipe** is saved JSON containing a template plus defaults and run mode.
|
|
58
|
+
- A **registered tool** gives a command template or recipe a stable agent-facing name.
|
|
59
|
+
- An **async run** is one execution instance with state, logs, script-authored events, status, tail, message send, cancel, and kill.
|
|
60
|
+
|
|
61
|
+
The template remains the execution language. The recipe is saved actor configuration. `async: true` is the detached lifecycle switch. The extension injects this compact mental model into the system prompt on each agent turn, including where to look first (`README.md`, `docs/README.md`, recipe/async docs, and `recipes/`) so an agent asked to inspect pi-actors can quickly understand the model and start composing async subagents or other long-running recipes.
|
|
62
|
+
|
|
63
|
+
## Operator Onboarding
|
|
64
|
+
|
|
65
|
+
Start with foreground templates for short deterministic work:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
register_tool name=lint_docs description="Lint docs" template="npm run lint:docs"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Move to async recipes when work is long-running, parallel, or agentic:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"name": "shader-ring-8-parallel",
|
|
76
|
+
"async": true,
|
|
77
|
+
"parallel": true,
|
|
78
|
+
"template": ["..."]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Expose a reusable recipe as a normal capability:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
register_tool name=shader_ring description="Start shader ring" template="shader-ring-8-parallel.json" args="theme,out_dir"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`Task` is the user's work item. `Template` is the execution graph. `Recipe` is saved JSON. `Run` is one execution instance with status, logs, cancellation, and ambient triangles.
|
|
89
|
+
|
|
90
|
+
## Compose Recipes With Imports
|
|
91
|
+
|
|
92
|
+
Recipes can import other recipe files and reuse them as named nodes. This keeps reusable steps small while letting a parent recipe decide whether the combined graph runs foreground or as one async run.
|
|
93
|
+
|
|
94
|
+
`review-one.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"name": "review-one",
|
|
99
|
+
"args": ["scope:string", "model:string"],
|
|
100
|
+
"defaults": { "model": "openai-codex/gpt-5.5" },
|
|
101
|
+
"template": "pi -p --model {model} --no-tools \"Review {scope}\""
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`review-pair.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"name": "review-pair",
|
|
110
|
+
"async": true,
|
|
111
|
+
"imports": {
|
|
112
|
+
"review": "review-one.json"
|
|
113
|
+
},
|
|
114
|
+
"parallel": true,
|
|
115
|
+
"failure": "branch",
|
|
116
|
+
"template": [
|
|
117
|
+
{ "name": "review", "values": { "scope": "README.md" } },
|
|
118
|
+
{ "name": "review", "values": { "scope": "docs/template-recipes.md" } }
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Register only the parent when that is the operator-facing capability:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
register_tool name=review_pair \
|
|
127
|
+
description="Start a parallel async docs review" \
|
|
128
|
+
template="review-pair.json"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Imported recipes are recipe definitions, not nested async runs. The parent recipe's `async: true` creates one run with one state dir; imported recipes contribute command-template graph, args, defaults, and values.
|
|
132
|
+
|
|
133
|
+
## Register Tools
|
|
134
|
+
|
|
135
|
+
`register_tool` lists, registers, updates, or deletes persistent tools. Call it without arguments to list the current registry.
|
|
136
|
+
|
|
137
|
+
### Local command: transcription
|
|
138
|
+
|
|
139
|
+
`pi-actors` is useful for exposing stable local commands as normal tools. For example, register an STT command:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
register_tool name=transcribe \
|
|
143
|
+
description="Transcribe a local audio file" \
|
|
144
|
+
template="/path/to/stt --file {file} --lang {lang=ru}"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Template recipe
|
|
148
|
+
|
|
149
|
+
For reusable workflows, keep the large template in a recipe file and register a small tool:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
register_tool name=shader_ring \
|
|
153
|
+
description="Start the shader ring recipe" \
|
|
154
|
+
template="shader-ring-8-parallel.json" \
|
|
155
|
+
args="theme,out_dir"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
If the recipe file contains `async: true`, calling `shader_ring` starts a detached run and returns metadata immediately. If `async` is omitted or false, the same recipe runs foreground and returns normal tool output.
|
|
159
|
+
|
|
160
|
+
A recipe can also be co-located in `tools.json` when keeping metadata and the recipe body together is clearer:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"review_docs": {
|
|
165
|
+
"description": "Start an async docs review",
|
|
166
|
+
"name": "review-docs",
|
|
167
|
+
"async": true,
|
|
168
|
+
"template": "pi -p --model openai-codex/gpt-5.5 --tools read,bash \"Review {scope}\""
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A co-located recipe entry still cannot define `tool`; it must own `template` directly.
|
|
174
|
+
|
|
175
|
+
### Sub-agent
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
register_tool name=call_subagent \
|
|
179
|
+
description="Run pi as a non-interactive sub-agent" \
|
|
180
|
+
template="pi -p --model {model=openai-codex/gpt-5.5} --no-tools {prompt}"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use `update=true` to overwrite an existing tool. Omit `template` during update to keep the previous template:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
register_tool name=call_subagent \
|
|
187
|
+
description="Run a focused pi sub-agent without tools" \
|
|
188
|
+
update=true
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Delete a tool:
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
register_tool name=call_subagent template=null
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Resulting Config
|
|
198
|
+
|
|
199
|
+
The commands above persist entries like this in `~/.pi/agent/tools.json`; tool names come from the top-level keys. Stored entries keep `template` last so flags and metadata are read before executable content:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"transcribe": {
|
|
204
|
+
"description": "Transcribe a local audio file",
|
|
205
|
+
"template": "/path/to/stt --file {file} --lang {lang=ru}"
|
|
206
|
+
},
|
|
207
|
+
"call_subagent": {
|
|
208
|
+
"description": "Run pi as a non-interactive sub-agent",
|
|
209
|
+
"template": "pi -p --model {model=openai-codex/gpt-5.5} --no-tools {prompt}"
|
|
210
|
+
},
|
|
211
|
+
"shader_ring": {
|
|
212
|
+
"description": "Start the shader ring recipe",
|
|
213
|
+
"args": ["theme", "out_dir"],
|
|
214
|
+
"template": "shader-ring-8-parallel.json"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
This file is the durable registry. `register_tool` is the interactive API; `tools.json` is the persisted state that is loaded on future sessions.
|
|
220
|
+
|
|
221
|
+
## Manage Run Actors
|
|
222
|
+
|
|
223
|
+
Use `spawn` when a command template may outlive the current turn. It starts the work now as an addressable actor, returns immediately with state metadata, and keeps ordinary files under `~/.pi/agent/tmp/pi-actors/runs/<run>` for later inspection.
|
|
224
|
+
|
|
225
|
+
Start from an inline template as an addressable run actor:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"as": "run:docs-review",
|
|
230
|
+
"template": "pi -p --model openai-codex/gpt-5.5 --no-tools {prompt}",
|
|
231
|
+
"values": {
|
|
232
|
+
"prompt": "Review docs/spec.md for contradictions."
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Do not check it on a timer. Let follow-up events arrive from the run, then react to a run-local request or redirect a long-lived recipe without polling/restarting it:
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{ "to": "run:docs-review", "type": "control.continue", "body": "continue" }
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Read recent events or logs only after a follow-up asks for inspection, at a real decision point, or during diagnosis:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{ "target": "run:docs-review", "view": "tail", "lines": "80" }
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Reusable local recipes live in `~/.pi/agent/recipes/*.json`; recipe tools honor each file's `async` flag. Use `spawn` for explicit detached starts from a file or inline template, and `inspect target=session:<id> view=runs status=running` or `inspect target=session:all view=runs` for explicit inventory/diagnosis. List output includes `tool` and `recipe` when the launcher recorded that source context.
|
|
250
|
+
|
|
251
|
+
## Recipe Library
|
|
252
|
+
|
|
253
|
+
Packaged standard recipes live under root `recipes/` with helper scripts under root `scripts/`. They are reusable library definitions, not automatically installed operator policy.
|
|
254
|
+
|
|
255
|
+
The subagent component recipes start non-interactive pi subagents as async runs or compose component recipes into higher-level coordinator pipelines. Use the no-tools recipe for the safest default, the explicit-tool variant when a bounded tool allowlist is needed, or the prompts fanout parent recipe to see imported subagent recipe nodes composed into one async run:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
register_tool name=subagent_prompt \
|
|
259
|
+
description="Start an async no-tools pi subagent" \
|
|
260
|
+
template="subagent-prompt.json"
|
|
261
|
+
|
|
262
|
+
register_tool name=subagent_tools \
|
|
263
|
+
description="Start an async pi subagent with an explicit tool allowlist" \
|
|
264
|
+
template="subagent-tools.json"
|
|
265
|
+
|
|
266
|
+
register_tool name=subagents_prompts \
|
|
267
|
+
description="Start parallel no-tools subagents from a prompt array as one async run" \
|
|
268
|
+
template="subagents-prompts.json"
|
|
269
|
+
|
|
270
|
+
subagent_prompt prompt="Review docs/async-runs.md for unclear wording." run_id=docs-review
|
|
271
|
+
subagent_tools prompt="Inspect package metadata and report risks." tools="read,bash" run_id=package-review
|
|
272
|
+
subagents_prompts \
|
|
273
|
+
prompts='["Review README.md for unclear release-onboarding wording. Return concise findings.","Review docs/template-recipes.md for unclear recipe-import wording. Return concise findings."]' \
|
|
274
|
+
run_id=review-prompts
|
|
275
|
+
inspect target=run:review-prompts view=tail
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The music player recipe starts a local file, URL, directory, or playlist as an async run, keeps the agent unblocked, shows the ambient triangle indicator in the launching coordinator, and can be controlled with addressed `message` calls. The standard library ships one Node.js wrapper recipe:
|
|
279
|
+
|
|
280
|
+
```text
|
|
281
|
+
register_tool name=music_player \
|
|
282
|
+
description="Start async music player playback through the Node.js wrapper" \
|
|
283
|
+
template="music-player.json" \
|
|
284
|
+
args="source:string,loop:bool=true,volume:int=70,player:enum(auto,mpv,ffplay,cvlc,play)=auto"
|
|
285
|
+
|
|
286
|
+
music_player source="~/Music" volume=55 run_id=music
|
|
287
|
+
message to=run:music type=player.next body=next
|
|
288
|
+
message to=run:music type=player.pause body=pause
|
|
289
|
+
message to=run:music type=player.play body=play
|
|
290
|
+
message to=run:music type=player.stop body=stop
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
See [`docs/recipe-library.md`](./docs/recipe-library.md) for install notes and recipe requirements.
|
|
294
|
+
|
|
295
|
+
## Runtime Contract
|
|
296
|
+
|
|
297
|
+
- Tool names are normalized to snake_case.
|
|
298
|
+
- Reserved built-in names are blocked.
|
|
299
|
+
- Templates are split into shell-like words first, then placeholders are substituted per command arg.
|
|
300
|
+
- Tool args are derived from placeholders when `args` is omitted.
|
|
301
|
+
- Typed arg declarations are progressive: `file:path`, `request_timeout:int=60000`, `speed:number=1.5`, `dry_run:bool=true`, `prompts:array`, and `mode:enum(check,fix)=check` can live in `args` or inline placeholders such as `{request_timeout:int=60000}`. They generate narrower tool schemas and runtime validation while existing untyped `args` and placeholders keep working.
|
|
302
|
+
- `{arg=default}` inline defaults resolve after runtime values and stored `defaults`; `{arg??fallback}` handles empty/null fallback values; `{flag?--flag:}` ternaries map small truthy/falsy values to strings such as optional CLI flags.
|
|
303
|
+
- `template: [...]` sequences execute left to right; each successful step passes stdout to the next step on stdin.
|
|
304
|
+
- Object nodes may set `parallel: true`; children receive the same stdin and joined stdout flows to the next sequence step.
|
|
305
|
+
- Parallel nodes use soft-quorum semantics: failed branches are reported as degraded coverage unless failure propagation escalates to the root.
|
|
306
|
+
- For long-running work or agentic fanout, prefer `async: true` recipes or `spawn` so lifecycle and ambient activity status remain visible.
|
|
307
|
+
- Timeout is disabled by default; set a positive `timeout` on bounded commands that should fail closed. Numeric node fields may read placeholders such as `timeout: "{timeout_ms}"`.
|
|
308
|
+
- Nodes may set `when` to skip conditional work and `delay` in milliseconds to wait before launch; delay is not inherited.
|
|
309
|
+
- Failed steps default to `failure: "continue"`, which records the failure and continues with empty stdin.
|
|
310
|
+
- `failure: "branch"` stops the current sequence/subtree without cancelling sibling parallel branches; `failure: "root"` aborts the composition.
|
|
311
|
+
- `retry` retries a leaf or whole node on non-zero exit; default attempts is `1`.
|
|
312
|
+
- `recover` runs a cleanup command template between failed retry attempts and stops retries if cleanup fails.
|
|
313
|
+
- Commands execute directly without shell evaluation, but trusted executables still run with the same permissions as pi.
|
|
314
|
+
- Obvious high-risk templates such as shells, interpreter eval modes, and broad filesystem mutation surface lightweight warnings without blocking existing tools.
|
|
315
|
+
- `async: true` on a recipe selects detached run lifecycle; omitted or false async runs the recipe foreground through registered tools.
|
|
316
|
+
- Layer boundaries stay explicit: command templates define synchronous execution graphs; template recipes add saved JSON metadata/import resolution and named `artifacts`; async runs add detached lifecycle, state, IPC, and observability.
|
|
317
|
+
- `spawn`, `message`, and `inspect` are high-level actor adapters. `spawn` creates `run:<id>` actors from recipes or inline templates with optional state/artifact metadata, `message` sends one typed envelope to `run:<id>` mailboxes, `branch:<run>/<branch>` mailboxes, `tool:<name>` calls, or the coordinator attention path, and `inspect` intentionally reads `run:<id>` status/tail/events/mailbox metadata or `session:<id>` run status while the broader actor/message protocol is refined.
|
|
318
|
+
- `spawn`, `message`, and `inspect` are the public async coordination vocabulary. Low-level async actions map to this actor API: start belongs to `spawn`; send/control belongs to `message`; status/tail/events/list belong to `inspect`; stop/kill are runtime control messages with synchronous results.
|
|
319
|
+
- Actor management returns compact text by default; pass `verbose: true` to `inspect` when full JSON state is needed.
|
|
320
|
+
- Detached runs inject `{run_id}` and `{state_dir}` into template values for run-local artifacts or recipe-specific control endpoints.
|
|
321
|
+
- Runtime actor messages are stored in `<state_dir>/outbox.jsonl`; coordinator attention is inferred by the runtime, not exposed as recipe or message-envelope input.
|
|
322
|
+
- Native Windows should use WSL or a recipe-specific transport for FIFO-controlled recipes.
|
|
323
|
+
- Registered tools may set `template` to a recipe JSON path/name; calling them follows that recipe's `async` mode.
|
|
324
|
+
- File-backed recipes may declare `imports` and embed imported recipes with `{ "name": "alias" }` nodes, or read `{alias.defaults.key}`, `{alias.defaults.key=fallback}`, and `{alias.values.key?yes:no}` references before command-template execution.
|
|
325
|
+
- Interactive sessions show ambient async activity as stable `▷` triangles aggregated across runs started by the current agent session. Each running async run contributes at least one triangle; parallel active branches can contribute more. One `▶` wave moves over the active set; terminal `done`/`failed`/unhandled `killed`/`exited` messages are delivered as compact follow-up context only to the launching coordinator agent, while intentional `cancel`, `kill`, and `stop` actions stay silent because the action already reports synchronously. Packaged multi-agent fanout recipes emit `command.done` messages by default so parallel subtask completions reach the coordinator.
|
|
326
|
+
- Use `{file}` as the canonical local file path arg.
|
|
327
|
+
- Stored `script` entries are rejected with migration guidance.
|
|
328
|
+
|
|
329
|
+
See [`docs/command-templates.md`](./docs/command-templates.md) for the portable synchronous command-template contract; [`docs/template-recipes.md`](./docs/template-recipes.md) for saved recipe JSON; [`docs/async-runs.md`](./docs/async-runs.md) for detached lifecycle, state files, cancellation, and observability; [`docs/tool-registry.md`](./docs/tool-registry.md) for registry storage; and [`docs/recipe-library.md`](./docs/recipe-library.md) for the packaged standard recipe library.
|
|
330
|
+
|
|
331
|
+
## Notes
|
|
332
|
+
|
|
333
|
+
- Only register trusted local commands. Registered tools run with the same system permissions as pi.
|
|
334
|
+
- `index.ts` is a small composition root; reusable behavior lives in flat `/lib` domains covered by focused tests.
|
|
335
|
+
|
|
336
|
+
## License
|
|
337
|
+
|
|
338
|
+
MIT
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Documentation Index
|
|
2
|
+
|
|
3
|
+
Living index of all documentation in the `/docs` directory.
|
|
4
|
+
|
|
5
|
+
## Documents
|
|
6
|
+
|
|
7
|
+
- [command-templates.md](./command-templates.md) — Portable synchronous command execution standard
|
|
8
|
+
- [template-recipes.md](./template-recipes.md) — Saved JSON recipe standard, imports, and reusable command-template graph composition
|
|
9
|
+
- [async-runs.md](./async-runs.md) — Detached run lifecycle, state files, actor messages, cancellation, and ambient indicators
|
|
10
|
+
- [actor-messages.md](./actor-messages.md) — Actor/message protocol for symmetric communication primitives
|
|
11
|
+
- [tool-registry.md](./tool-registry.md) — Local `pi-actors` registry storage and `register_tool` adaptation
|
|
12
|
+
- [recipe-library.md](./recipe-library.md) — Packaged standard recipe library such as async subagents, coordinator pipelines, utilities, and music playback
|
|
13
|
+
- [task-first-recipes.md](./task-first-recipes.md) — Task-first design map for deriving high-level recipes and missing component cells
|
|
14
|
+
- [component-recipes.md](./component-recipes.md) — Weak component-recipe contract for composing subagent coordinator building blocks
|
|
15
|
+
|
|
16
|
+
## Root Context
|
|
17
|
+
|
|
18
|
+
- [Project Context](../AGENTS.md)
|
|
19
|
+
- [Open Backlog](../BACKLOG.md)
|
|
20
|
+
- [Changelog](../CHANGELOG.md)
|
|
21
|
+
- [Root README](../README.md)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Actor Messages
|
|
2
|
+
|
|
3
|
+
Protocol target for organic communication across pi-actors.
|
|
4
|
+
|
|
5
|
+
## Contract
|
|
6
|
+
|
|
7
|
+
Compress communication to three durable verbs:
|
|
8
|
+
|
|
9
|
+
- `spawn`: create an addressable actor from a recipe, template, or tool.
|
|
10
|
+
- `message`: send one typed message to one address.
|
|
11
|
+
- `inspect`: intentionally observe state, logs, events, or artifacts.
|
|
12
|
+
|
|
13
|
+
Everything else is an adapter until proven otherwise.
|
|
14
|
+
|
|
15
|
+
## Nouns
|
|
16
|
+
|
|
17
|
+
- **Actor**: any addressable execution or coordination endpoint.
|
|
18
|
+
- **Address**: stable route string for an actor or sub-actor.
|
|
19
|
+
- **Message**: typed envelope flowing between addresses.
|
|
20
|
+
- **Artifact**: durable result path declared by recipe or produced by actor.
|
|
21
|
+
- **Inspection**: explicit diagnostic/read operation, not a coordination loop.
|
|
22
|
+
|
|
23
|
+
## Addresses
|
|
24
|
+
|
|
25
|
+
Initial address forms:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
run:<id>
|
|
29
|
+
branch:<run>/<branch>
|
|
30
|
+
coordinator
|
|
31
|
+
session:<id>
|
|
32
|
+
tool:<name>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Future forms may include `chat:<id>` or package-specific endpoints, but the envelope stays the same.
|
|
36
|
+
|
|
37
|
+
## Message Envelope
|
|
38
|
+
|
|
39
|
+
One shape covers upward, downward, lateral, parent-to-branch, and branch-to-parent traffic:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"to": "run:review",
|
|
44
|
+
"from": "coordinator",
|
|
45
|
+
"type": "control.approve",
|
|
46
|
+
"summary": "Approve checkpoint",
|
|
47
|
+
"body": "approve",
|
|
48
|
+
"reply_to": "msg_123",
|
|
49
|
+
"correlation_id": "task_456",
|
|
50
|
+
"metadata": {}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Field rules:
|
|
55
|
+
|
|
56
|
+
- `to`: required address.
|
|
57
|
+
- `from`: optional address; runtime fills when known.
|
|
58
|
+
- `type`: required semantic message type.
|
|
59
|
+
- `summary`: short human-facing line for notifications/follow-ups.
|
|
60
|
+
- `body`: string or JSON payload.
|
|
61
|
+
- routing/delivery is inferred from `to`, actor ownership, and coordinator runtime policy; recipes should not expose delivery knobs.
|
|
62
|
+
- `reply_to`: optional message id for conversational checkpoints.
|
|
63
|
+
- `correlation_id`: optional task/run/workflow id.
|
|
64
|
+
- `metadata`: optional structured routing or domain hints.
|
|
65
|
+
|
|
66
|
+
## Symmetry
|
|
67
|
+
|
|
68
|
+
The same `message` primitive must represent:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
coordinator -> run
|
|
72
|
+
run -> coordinator
|
|
73
|
+
run -> run
|
|
74
|
+
parent -> branch
|
|
75
|
+
branch -> parent
|
|
76
|
+
coordinator -> tool
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Transports differ, but the public contract does not:
|
|
80
|
+
|
|
81
|
+
- `to: run:<id>` may route to FIFO, mailbox file, socket, or process stdin.
|
|
82
|
+
- `to: coordinator` routes to outbox/watch/follow-up delivery when `from` names a run actor. Generic async-runner `command.done` events and explicit coordinator-bound messages include the actor envelope fields alongside the runtime event fields.
|
|
83
|
+
- `to: branch:<run>/<branch>` routes through the parent run mailbox with the full envelope preserved so the run can dispatch branch-local control.
|
|
84
|
+
- `to: tool:<name>` invokes an executable pi tool by name. Object bodies become tool parameters; primitive bodies are passed as `{ "input": body }`.
|
|
85
|
+
|
|
86
|
+
Transport is not public API unless a recipe explicitly documents a custom endpoint.
|
|
87
|
+
|
|
88
|
+
## Mailbox Declaration
|
|
89
|
+
|
|
90
|
+
Recipes can declare their conversational surface:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mailbox": {
|
|
95
|
+
"accepts": ["control.continue", "control.revise", "control.approve", "control.stop"],
|
|
96
|
+
"emits": ["checkpoint.needs_scope", "branch.done", "run.done"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`mailbox.accepts` is a contract for coordinator-to-actor messages. `mailbox.emits` is a contract for actor-to-coordinator or actor-to-actor messages. Packaged interactive and message-producing recipes declare mailbox metadata so coordinators can discover semantic message types without reading FIFO details. Message-producing recipes produce actor-message-envelope-shaped records with `to`, `from`, `type`, `summary`, `body`, optional `correlation_id`/`reply_to`, and optional `metadata` fields. Deterministic pipelines should prefer `utility-actor-message` for this wrapping so message shape is validated and guaranteed instead of delegated to a prompt; its recipe args intentionally mirror the envelope field names.
|
|
102
|
+
|
|
103
|
+
## Spawn
|
|
104
|
+
|
|
105
|
+
`spawn` creates an actor and returns its address:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"recipe": "subagents-prompts.json",
|
|
110
|
+
"as": "run:review",
|
|
111
|
+
"values": {},
|
|
112
|
+
"artifacts": { "report": "{state_dir}/report.md" }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`spawn` creates detached `run:<id>` actors from a recipe file/name or inline command template. Spawn metadata may include explicit `state_dir` and named `artifacts` for terminal follow-ups and inspection.
|
|
117
|
+
|
|
118
|
+
## Inspect
|
|
119
|
+
|
|
120
|
+
`inspect` reads state intentionally:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"target": "run:review",
|
|
125
|
+
"view": "status"
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The implementation supports `status`, `tail`, `events`, `artifacts`, `files`, and `mailbox` for `run:<id>` actors, plus `status`/`runs` for `session:<id>` and `session:all` actors with optional status filtering. `inspect` is for decision points and diagnosis only; examples must not teach sleep-then-inspect polling.
|
|
130
|
+
|
|
131
|
+
## Runtime Direction
|
|
132
|
+
|
|
133
|
+
Runtime operations use the actor/message vocabulary:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
create detached work -> spawn
|
|
137
|
+
run-local control -> message to run:<id>
|
|
138
|
+
coordinator signal -> message to coordinator
|
|
139
|
+
tool execution -> message to tool:<name>
|
|
140
|
+
intentional observe -> inspect
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Non-goals
|
|
144
|
+
|
|
145
|
+
- No generic expression language in templates.
|
|
146
|
+
- No public FIFO/outbox vocabulary in recipe args.
|
|
147
|
+
- No polling-first examples.
|
|
148
|
+
- No separate upward and downward message schemas.
|
|
149
|
+
- No broad facade that hides artifacts, logs, or ownership checks.
|