@llblab/pi-actors 0.43.0 → 0.44.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.
Files changed (109) hide show
  1. package/AGENTS.md +16 -10
  2. package/CHANGELOG.md +425 -536
  3. package/README.md +13 -11
  4. package/dist/index.js +1 -1
  5. package/dist/lib/async-runs.d.ts +2 -1
  6. package/dist/lib/async-runs.js +24 -34
  7. package/dist/lib/automatic-review-runtime.d.ts +1 -1
  8. package/dist/lib/automatic-review-runtime.js +5 -5
  9. package/dist/lib/command-templates.d.ts +2 -0
  10. package/dist/lib/command-templates.js +38 -4
  11. package/dist/lib/control-projection.d.ts +20 -0
  12. package/dist/lib/control-projection.js +66 -0
  13. package/dist/lib/control.d.ts +3 -0
  14. package/dist/lib/control.js +27 -14
  15. package/dist/lib/draft-sleep.js +3 -3
  16. package/dist/lib/file-state.d.ts +4 -1
  17. package/dist/lib/file-state.js +118 -44
  18. package/dist/lib/inspector-overlay.d.ts +2 -0
  19. package/dist/lib/inspector-overlay.js +124 -69
  20. package/dist/lib/limits.d.ts +15 -3
  21. package/dist/lib/limits.js +15 -3
  22. package/dist/lib/observability.d.ts +4 -2
  23. package/dist/lib/observability.js +43 -36
  24. package/dist/lib/prompts.d.ts +1 -1
  25. package/dist/lib/prompts.js +1 -1
  26. package/dist/lib/recipe-control.js +6 -2
  27. package/dist/lib/review-control.d.ts +1 -1
  28. package/dist/lib/review-control.js +4 -5
  29. package/dist/lib/run-evidence-policy.d.ts +95 -0
  30. package/dist/lib/run-evidence-policy.js +177 -0
  31. package/dist/lib/run-ui-runtime.js +2 -0
  32. package/dist/lib/runs-control-delivery.d.ts +8 -1
  33. package/dist/lib/runs-control-delivery.js +38 -15
  34. package/dist/lib/runs-controls.d.ts +8 -4
  35. package/dist/lib/runs-controls.js +189 -50
  36. package/dist/lib/runs-retention.js +27 -14
  37. package/dist/lib/runs-trace.d.ts +26 -2
  38. package/dist/lib/runs-trace.js +411 -18
  39. package/dist/lib/runtime-identity.d.ts +7 -0
  40. package/dist/lib/runtime-identity.js +35 -0
  41. package/dist/lib/runtime-triage.d.ts +29 -0
  42. package/dist/lib/runtime-triage.js +60 -0
  43. package/dist/lib/tool-review-scheduler.js +7 -7
  44. package/dist/lib/tools-inspect.js +91 -18
  45. package/dist/lib/tools-message.d.ts +1 -2
  46. package/dist/lib/tools-message.js +6 -6
  47. package/dist/lib/tools-response.d.ts +0 -1
  48. package/dist/lib/tools-response.js +0 -9
  49. package/dist/lib/tools.d.ts +1 -1
  50. package/dist/lib/tools.js +1 -1
  51. package/dist/lib/trace-projection.js +107 -41
  52. package/dist/scripts/conformance.mjs +5 -0
  53. package/dist/scripts/locker.mjs +40 -90
  54. package/dist/scripts/music-player.mjs +48 -142
  55. package/dist/scripts/release-gates.mjs +56 -3
  56. package/dist/scripts/validate-recipe.mjs +5 -4
  57. package/dist/skills/actors/SKILL.md +17 -11
  58. package/dist/skills/swarm/SKILL.md +2 -4
  59. package/docs/README.md +1 -4
  60. package/docs/actor-inspector.md +6 -5
  61. package/docs/async-runs.md +11 -9
  62. package/docs/command-templates.md +6 -116
  63. package/docs/recipe-library.md +4 -6
  64. package/docs/releasing.md +28 -0
  65. package/docs/template-recipes.md +1 -1
  66. package/docs/tool-registry.md +2 -2
  67. package/index.ts +1 -1
  68. package/lib/async-runs.ts +26 -50
  69. package/lib/automatic-review-runtime.ts +7 -7
  70. package/lib/command-templates.ts +44 -4
  71. package/lib/control-projection.ts +105 -0
  72. package/lib/control.ts +33 -18
  73. package/lib/draft-sleep.ts +3 -3
  74. package/lib/file-state.ts +91 -63
  75. package/lib/inspector-overlay.ts +108 -61
  76. package/lib/limits.ts +15 -3
  77. package/lib/observability.ts +55 -57
  78. package/lib/prompts.ts +1 -1
  79. package/lib/recipe-control.ts +9 -2
  80. package/lib/review-control.ts +4 -5
  81. package/lib/run-evidence-policy.ts +242 -0
  82. package/lib/run-ui-runtime.ts +2 -0
  83. package/lib/runs-control-delivery.ts +45 -17
  84. package/lib/runs-controls.ts +180 -102
  85. package/lib/runs-retention.ts +28 -20
  86. package/lib/runs-trace.ts +499 -20
  87. package/lib/runtime-identity.ts +39 -0
  88. package/lib/runtime-triage.ts +106 -0
  89. package/lib/tool-review-scheduler.ts +7 -7
  90. package/lib/tools-inspect.ts +94 -20
  91. package/lib/tools-message.ts +7 -8
  92. package/lib/tools-response.ts +0 -12
  93. package/lib/tools.ts +4 -4
  94. package/lib/trace-projection.ts +156 -71
  95. package/package.json +1 -1
  96. package/scripts/conformance.mjs +5 -0
  97. package/scripts/locker.mjs +40 -90
  98. package/scripts/music-player.mjs +48 -142
  99. package/scripts/release-gates.mjs +56 -3
  100. package/scripts/validate-recipe.mjs +5 -4
  101. package/skills/actors/SKILL.md +17 -11
  102. package/skills/swarm/SKILL.md +2 -4
  103. package/dist/lib/runtime-notifier.d.ts +0 -48
  104. package/dist/lib/runtime-notifier.js +0 -138
  105. package/docs/0.43-baseline.md +0 -44
  106. package/docs/actors-deep-reference.md +0 -108
  107. package/docs/component-recipes.md +0 -45
  108. package/docs/task-first-recipes.md +0 -261
  109. package/lib/runtime-notifier.ts +0 -211
package/docs/README.md CHANGED
@@ -4,16 +4,13 @@ Living index of all documentation in the `/docs` directory.
4
4
 
5
5
  ## Documents
6
6
 
7
- - [0.43-baseline.md](./0.43-baseline.md) — Frozen `0.42.3` line counts and retained-invariant preservation gate
8
- - [actors-deep-reference.md](./actors-deep-reference.md) — Recipe navigator, operating patterns, lifecycle discipline, and pitfalls
9
7
  - [command-templates.md](./command-templates.md) — Portable synchronous command execution standard
10
8
  - [template-recipes.md](./template-recipes.md) — Saved JSON/Markdown recipe standard, imports, and reusable command-template graph composition
11
9
  - [async-runs.md](./async-runs.md) — Run lifecycle, state, Control, Trace, cancellation, and terminal reconciliation
12
10
  - [actor-inspector.md](./actor-inspector.md) — Owner-filtered actor-instance navigation through Recipe, Trace, and Control
13
11
  - [tool-registry.md](./tool-registry.md) — Local `pi-actors` registry storage and `register_tool` adaptation
14
12
  - [recipe-library.md](./recipe-library.md) — Packaged standard recipe library such as async subagents, coordinator pipelines, utilities, and music playback
15
- - [task-first-recipes.md](./task-first-recipes.md) — Task-first design map for deriving high-level recipes and missing component cells
16
- - [component-recipes.md](./component-recipes.md) — Weak component-recipe contract for composing subagent coordinator building blocks
13
+ - [releasing.md](./releasing.md) — Guarded tag validation, npm Trusted Publisher setup, registry verification, and GitHub Release convergence
17
14
 
18
15
  ## Root Context
19
16
 
@@ -18,13 +18,13 @@ Shows captured execution provenance:
18
18
  - declared artifacts and actor-local actions;
19
19
  - model/thinking policy and launch source.
20
20
 
21
- Captured Recipe evidence belongs to the Run generation and does not change when an active Recipe file later changes.
21
+ Captured Recipe evidence belongs to the Run generation and does not change when an active Recipe file later changes. Non-empty object values render as indented brace-delimited property lists rather than flattened inline strings.
22
22
 
23
23
  ## Trace
24
24
 
25
25
  Shows the unified bounded Trace projection. Sources include lifecycle/runtime observations, Controls, owned Pi turns, command-log tails, results, artifacts, and diagnostics. Filter by source and open a row for structured detail.
26
26
 
27
- Trace ordering stays deterministic and newest-first. The projection applies path containment and redaction before rendering.
27
+ Trace ordering stays deterministic and newest-first: timestamp descending, same-source physical ordinal descending, fixed internal source rank, then stable id. Internal ordinals are never displayed or interpreted as cross-source causality. Row numbers still read chronologically from bottom to top: the oldest visible event is `#1` and the newest carries the highest number. The summary states whether retained history is complete; `runtime.trace_compacted` means older history was discarded and shows bounded cumulative drop evidence. Terminal/result/execution/artifact evidence keeps its own authority. The projection applies path containment and redaction before rendering.
28
28
 
29
29
  ## Control
30
30
 
@@ -33,16 +33,17 @@ Shows:
33
33
  - Recipe-declared actor-local actions;
34
34
  - runtime-owned lifecycle actions;
35
35
  - generation-fenced endpoint readiness;
36
+ - pending capacity, saturation, journal bytes, stale count, and diagnostics;
36
37
  - recent durable Control records and outcomes.
37
38
 
38
- A service endpoint counts as ready only when `control-endpoint.json` matches the Run's immutable `run_instance_id`.
39
+ A service endpoint counts as ready only when `control-endpoint.json` matches the Run's immutable `run_instance_id`. Capacity reaches zero at 64 pending Controls; further requests are rejected before admission, while admitted nonterminal Controls never expire automatically. Runtime-owned kill remains available for a stuck saturated Run. Recent Control input and errors use the same bounded structured redaction as tool inspection. The durable `controls.jsonl` journal remains raw and local; rendering never mutates it or attaches an unredacted copy.
39
40
 
40
41
  ## Keys
41
42
 
42
43
  The footer displays current bindings. Use tab navigation to switch Recipe/Trace/Control, movement keys to select rows, detail navigation to inspect evidence, refresh to reconcile disk state, and the documented kill key for lifecycle termination.
43
44
 
44
- Run kill revalidates owner and generation through the canonical lifecycle path. The Inspector never edits state directly and never derives authority from displayed data.
45
+ Run kill revalidates owner and generation through the canonical lifecycle path. After success, the Run status header is the sole confirmation; the content area does not duplicate it. The Inspector never edits state directly and never derives authority from displayed data.
45
46
 
46
47
  ## Scope
47
48
 
48
- The Actor Inspector treats each Run as a concrete actor instance. It does not expose group conversations, peer addresses, routing, or communication topology. Use `inspect target=runtime`, `inspect target=recipes`, and `inspect target=tool:<name>` for non-Run management targets.
49
+ The Actor Inspector treats each Run as a concrete actor instance. It does not expose group conversations, peer addresses, routing, or communication topology. Use `inspect target=runtime view=status`, `inspect target=recipes view=status`, and `inspect target=tool:<name> view=status` for non-Run management targets.
@@ -55,9 +55,9 @@ Trace records strict bounded events:
55
55
  {"id":"…","ts":"…","kind":"command.done","summary":"Command completed","data":{"code":0},"level":"info","attention":"followup"}
56
56
  ```
57
57
 
58
- Required fields: `id`, `ts`, `kind`. Optional fields: `summary`, `data`, `level`, `attention`. Trace rejects addressed-envelope fields and malformed or oversized data.
58
+ Required fields: `id`, `ts`, `kind`. Optional fields: `summary`, `data`, `level`, `attention`. Trace rejects addressed-envelope fields and malformed or oversized data. It retains a recent suffix within 2,048 events and 4 MiB. When either bound would be exceeded, the canonical lock atomically keeps a newest suffix near the lower targets, the new event, and one cumulative warning-only `runtime.trace_compacted` marker. The marker means older history was discarded; it reports cumulative drop evidence and never requests attention.
59
59
 
60
- Runtime lifecycle, runner progress, command completion, cancellation, kill, parent teardown, and controlled-service observations use Trace. `inspect view=trace` projects these events with Controls, owned Pi turns, logs, results, artifacts, and diagnostics under a deterministic global bound.
60
+ Runtime lifecycle, runner progress, command completion, cancellation, kill, parent teardown, and controlled-service observations use Trace. Bounded reads preserve complete UTF-8 lines and disclose omitted legacy prefixes. `inspect view=trace` reports retained-history completeness and projects events with Controls, owned Pi turns, logs, results, artifacts, and diagnostics newest-first. Equal timestamps use same-source physical order, then fixed source rank and stable id without claiming cross-source causality. Terminal state, `result.json`, `execution.json`, and artifacts remain authoritative even when old Trace has compacted.
61
61
 
62
62
  ## Control
63
63
 
@@ -82,15 +82,15 @@ The runtime:
82
82
  5. writes the exact `{id, action, input?}` wire document to FIFO or named pipe;
83
83
  6. records delivered or failed outcome.
84
84
 
85
- Unix services may publish a FIFO; native Windows services publish a Windows named pipe. Native Windows FIFO delivery fails before transport rather than degrading to another protocol. FIFO wire documents above the portable 512-byte atomic-write bound fail before writing; named pipes retain the general Control input bound.
85
+ Unix services may publish a FIFO; native Windows services publish a Windows named pipe. Native Windows FIFO delivery fails before transport rather than degrading to another protocol. Both transports admit the same portable envelope: action is at most 64 lowercase ASCII characters, serialized JSON input is at most 380 bytes, and the newline-terminated wire record is at most 512 bytes. Invalid envelopes fail before journal admission or transport. Put larger data in a declared artifact/path and send only a bounded reference or instruction through Control.
86
86
 
87
- A service claims queued or transport-delivered Controls and records handled/failed outcomes under the token-owned Control journal lock. Journal snapshots replace atomically, and expected-status fencing prevents delivery failure evidence from regressing a Control already claimed or completed by a fast consumer. Terminal compaction remains bounded. Services capture their startup generation, so stale-generation Controls never execute.
87
+ A service exact-id claims queued or transport-delivered Controls and records handled/failed outcomes through the canonical Control journal authority. Admission performs one locked read, integrity/generation check, terminal-tail compaction, capacity decision, and atomic write. The 65th pending Control and a rewrite exceeding 1 MiB fail as bounded `control_backpressure` before admission; malformed, unreadable, oversized, or stale-generation journals fail with an integrity reason and no rejected record. `inspect view=control` reports pending capacity, saturation, stale work, journal bytes, and diagnostics. Every transition uses the same lock, expected-state fence, 128-terminal compaction, and atomic bounded rewrite; persisted errors truncate inside the string at 4 KiB. Admitted nonterminal Controls never expire automatically. Delivery failure evidence cannot regress a Control already claimed or completed by a fast consumer. Services capture their startup generation, so stale-generation Controls never execute.
88
88
 
89
- Runtime lifecycle `kill`, retention actions, and review retry/reset remain runtime-owned rather than Recipe-declared.
89
+ Runtime lifecycle `kill`, retention actions, and review retry/reset remain runtime-owned rather than Recipe-declared. Kill is the recovery path for a stuck saturated Run: it bypasses actor-local Control capacity and creates no synthetic Control. Same-directory restart clears all generation-local evidence before the new `run_instance_id`; archive moves the exact bounded terminal tree, while prune removes kernel state and preserves only explicitly requested artifacts.
90
90
 
91
91
  ## Execution Evidence
92
92
 
93
- `execution.json` stores general command/session provenance. The async runner keeps bounded stdout/stderr logs plus bounded complete captures when semantic validation requires untruncated evidence. Pi command execution also records owned session provenance for later Trace projection and review checks.
93
+ `execution.json` stores general command/session provenance. The async runner keeps bounded stdout/stderr logs plus complete capture artifacts when semantic validation requires untruncated evidence. Pi command execution also records owned session provenance for later Trace projection and review checks. Trace and Control quotas do not constrain declared user artifacts, repositories or media sources, complete execution captures, or actor-owned queue/workload state; each remains governed by its own lifecycle and policy.
94
94
 
95
95
  Review acceptance remains a command-stage concern. General execution evidence does not imply review approval.
96
96
 
@@ -98,7 +98,7 @@ Review acceptance remains a command-stage concern. General execution evidence do
98
98
 
99
99
  Statuses include `running`, `done`, `failed`, `exited`, `cancelled`, and `killed`. Status resolution combines persisted metadata, result/terminal evidence, and verified process state.
100
100
 
101
- Ambient observation detects terminal transitions and Trace attention. Terminal follow-up delivery persists handled/failure evidence so reloads retry unhandled transitions without duplicating completed notifications.
101
+ Ambient observation detects terminal transitions and retained Trace attention. Canonical attention is an in-memory wake hint, not a durable queue: observers prime retained ids at startup, deliver each later retained unseen id once, and bound memory to the current retained set across compaction. Persist durable recovery state or an artifact before emitting attention; compaction may discard older hints and its marker makes that history loss explicit. Terminal follow-up delivery persists handled/failure evidence so reloads retry unhandled transitions without duplicating completed notifications.
102
102
 
103
103
  Large semantic results stay outside compact visible follow-up text and remain available in structured details, execution captures, or artifacts.
104
104
 
@@ -124,7 +124,9 @@ Archive and prune apply only to terminal Runs and enforce path containment. Rete
124
124
  Packaged controlled services demonstrate the endpoint protocol:
125
125
 
126
126
  - `music-player` consumes playback Controls and emits playback Trace;
127
- - `resource-locker` consumes queue/lease actions and emits lock Trace.
127
+ - `resource-locker` consumes queue/lease actions, emits lock Trace, and atomically retains at most 512 valid journal records within 1 MiB.
128
+
129
+ Shared archive/prune evidence similarly retains at most 256 valid records within 1 MiB under its canonical lock. The obsolete advisory `wake.jsonl` notifier was removed; filesystem watchers and bounded reconciliation observe authoritative state directly.
128
130
 
129
131
  One-shot pipelines omit Control and terminate through their command graph.
130
132
 
@@ -136,4 +138,4 @@ inspect target=run:<id> view=trace source=lifecycle lines=40
136
138
  inspect target=run:<id> view=control
137
139
  ```
138
140
 
139
- Use `/actor-inspector` to inspect Runs as concrete actor instances in the live TUI. Runtime, Recipe registry, and tool definitions remain separate management targets.
141
+ Use `/actor-inspector` to inspect Runs as concrete actor instances in the live TUI. Runtime, Recipe registry, and tool definitions remain separate management targets. No public noun, tool, target, or view is added by bounded retention.
@@ -16,7 +16,7 @@ Layer boundary: command templates own only the synchronous execution graph. Reci
16
16
 
17
17
  Command-template standard owns:
18
18
 
19
- - Command string splitting and direct argv execution.
19
+ - Command string splitting, portable script-interpreter inference, and direct argv execution.
20
20
  - Placeholder resolution, typed public args, defaults, `??`, ternary string selection, and array-index placeholders.
21
21
  - Synchronous graph shape: sequence, `parallel`, `when`, `repeat`, stdin flow, stdout joins, and output selection.
22
22
  - Per-node execution controls: `timeout`, `delay`, `retry`, `failure`, and `recover`.
@@ -72,7 +72,7 @@ A runtime must:
72
72
 
73
73
  1. Split the template into shell-like words with simple single quotes, double quotes, and backslash escapes
74
74
  2. Substitute placeholders inside each split word
75
- 3. Execute command + args directly, without shell evaluation
75
+ 3. Infer a first-word `.js` or `.mjs` script through the first available `node`, `bun`, or `deno run` runtime, infer `.sh` through `bash`, and otherwise execute command + args directly; explicit interpreters remain unchanged and no shell evaluates the resulting argv
76
76
  4. Treat exit code `0` as success and non-zero as failure
77
77
  5. Use stdout as the default result channel and stderr only for diagnostics
78
78
 
@@ -106,35 +106,11 @@ With runtime values `{ "text": "hello" }`, argv is:
106
106
 
107
107
  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.
108
108
 
109
- Fallback values can be selected with nullish coalescing:
110
-
111
- ```json
112
- {
113
- "template": "deploy --env {env??dev} --region {region??local}"
114
- }
115
- ```
116
-
117
- Optional flags can be mapped from boolean args with a ternary:
118
-
119
- ```json
120
- {
121
- "args": ["target:path", "all:bool"],
122
- "defaults": { "all": "true" },
123
- "template": "validate-recipe {target} {all?--all:}"
124
- }
125
- ```
109
+ Use `{env??dev}` for fallback values and `{all?--all:}` to map boolean args to optional text.
126
110
 
127
111
  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.
128
112
 
129
- Node control fields can also read public args. Use distinct arg names so execution controls stay visually separate from public inputs:
130
-
131
- ```json
132
- {
133
- "args": ["timeout_ms:int"],
134
- "timeout": "{timeout_ms}",
135
- "template": "npm test"
136
- }
137
- ```
113
+ Node control fields can also read public args, for example `"timeout": "{timeout_ms}"`; use distinct names so execution controls stay visually separate from public inputs.
138
114
 
139
115
  ## Quoting
140
116
 
@@ -194,21 +170,6 @@ Composition rules:
194
170
  - `min_successful` adds a join header with `complete`, `degraded`, or `insufficient_data`; with `failure: "branch"` or `"root"`, an unmet threshold fails at that scope
195
171
  - Each leaf still applies its own inline defaults
196
172
 
197
- ```json
198
- {
199
- "template": [
200
- "/path/to/tts --text {text} --lang {lang} --out {mp3}",
201
- {
202
- "defaults": { "codec": "libopus" },
203
- "template": "ffmpeg -y -i {mp3} -c:a {codec} {ogg}"
204
- }
205
- ],
206
- "args": ["text", "lang", "mp3", "ogg"],
207
- "defaults": { "lang": "en" },
208
- "output": "ogg"
209
- }
210
- ```
211
-
212
173
  `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.
213
174
 
214
175
  ### Repeat
@@ -245,52 +206,7 @@ Repeat expressions support only integers, `index`, `prev`, `next`, `repeat`, par
245
206
 
246
207
  Repeat placeholders are local generated values. Call-time args should not use these reserved names to override the repeat index.
247
208
 
248
- Parallel nodes use the same object shape. Flags come first and `template` stays last:
249
-
250
- ```json
251
- {
252
- "template": [
253
- "prepare {out_dir}",
254
- {
255
- "parallel": true,
256
- "template": [
257
- {
258
- "label": "reviewer-a",
259
- "timeout": 300000,
260
- "template": "review-gpt {scope}"
261
- },
262
- {
263
- "label": "reviewer-b",
264
- "timeout": 300000,
265
- "template": "review-deepseek {scope}"
266
- },
267
- {
268
- "label": "kimi",
269
- "timeout": 300000,
270
- "template": "review-kimi {scope}"
271
- }
272
- ]
273
- },
274
- "merge {out_dir}"
275
- ]
276
- }
277
- ```
278
-
279
- A degraded parallel join is still usable when at least one branch succeeds:
280
-
281
- ```text
282
- --- branch: reviewer-a status: done ---
283
- review text
284
- --- branch: reviewer-b status: failed ---
285
- exit: 1
286
- stderr: provider balance exhausted
287
- ```
288
-
289
- Some local schemas may accept `pipe` as an alias, but the portable standard is `template: [...]`.
290
-
291
- ## Fail-Open Default Policy
292
-
293
- 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.
209
+ Parallel children use the same object shape: flags come first and `template` stays last. A join remains usable when at least one branch succeeds and reports each branch label/status. Some local schemas may accept `pipe`, but the portable standard is `template: [...]`.
294
210
 
295
211
  ## Failure Propagation
296
212
 
@@ -302,33 +218,7 @@ Use `failure` when a node should stop more aggressively:
302
218
  - `"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.
303
219
  - `"root"`: abort the outermost composition.
304
220
 
305
- ```json
306
- {
307
- "parallel": true,
308
- "template": [
309
- {
310
- "label": "agent-a",
311
- "failure": "branch",
312
- "template": [
313
- "agent-a-work {scope}",
314
- "agent-a-validate {scope}",
315
- "agent-a-push {scope}"
316
- ]
317
- },
318
- {
319
- "label": "agent-b",
320
- "failure": "branch",
321
- "template": [
322
- "agent-b-work {scope}",
323
- "agent-b-validate {scope}",
324
- "agent-b-push {scope}"
325
- ]
326
- }
327
- ]
328
- }
329
- ```
330
-
331
- If `agent-a-validate` fails, `agent-a-push` is skipped, `agent-b` can still finish, and the parallel join reports degraded branch coverage.
221
+ A branch failure skips the remainder of that branch while parallel siblings can finish; their join reports degraded coverage.
332
222
 
333
223
  ## Retry
334
224
 
@@ -35,9 +35,9 @@ Artifact pipelines terminate in files/manifests and result evidence; they do not
35
35
  ### Controlled services
36
36
 
37
37
  - `music-player.json` — playback service with declared playback actions, `controls.jsonl`, generation-fenced endpoint readiness, state artifact, and playback Trace. Player selection is `player:enum(auto,mpv,afplay,ffplay,cvlc,play,wmp)=auto`.
38
- - `resource-locker.json` — optional queue/lease-lock service with explicit owner/resource input and lock Trace.
38
+ - `resource-locker.json` — optional queue/lease-lock service with explicit owner/resource input, lock Trace, and a 512-record/1 MiB atomically retained journal.
39
39
 
40
- These are the packaged Recipes that declare actor-local Control. Ordinary one-shot Recipes omit it.
40
+ These are the packaged Recipes that declare actor-local Control. Ordinary one-shot Recipes omit it. Helper-backed packaged Recipes self-locate their installed package root when `repo` is omitted; an explicit caller value still wins for development or custom layouts.
41
41
 
42
42
  ## Component Recipes
43
43
 
@@ -64,7 +64,7 @@ Use utilities as imported cells or registered tools where their contract fits.
64
64
  3. Use inline templates for genuinely one-off trusted work.
65
65
  4. Declare artifacts for outputs that callers must retain.
66
66
  5. Declare Control only when a service process actually consumes it.
67
- 6. Keep large semantic evidence in artifacts or execution captures, not Trace summaries.
67
+ 6. Keep large semantic evidence in artifacts or execution captures, not Trace summaries; Trace/Control quotas do not bound user artifacts or actor-owned workload state.
68
68
 
69
69
  ## Installation Safety
70
70
 
@@ -76,12 +76,10 @@ Do not bulk-copy `recipes/*.json` into the user Recipe root. Internal `draft-rev
76
76
  npm run recipes:qa
77
77
  ```
78
78
 
79
- Recipe QA validates syntax, imports, Control declarations, artifact paths, helper references, and platform documentation. Removed mailbox declarations fail with the migration diagnostic rather than receiving automatic conversion.
79
+ Recipe QA validates syntax, imports, Control declarations, artifact paths, helper references, and platform documentation. Recipe descriptions are optional because discovery supplies stable fallback tool copy; internal component Recipes do not need boilerplate. The packaged baseline requires zero diagnostics and zero warnings, and any future warning is release-blocking with its concrete file and repair. Removed mailbox declarations fail with the migration diagnostic rather than receiving automatic conversion.
80
80
 
81
81
  ## Related
82
82
 
83
83
  - [Template Recipes](./template-recipes.md)
84
84
  - [Command templates](./command-templates.md)
85
85
  - [Runs](./async-runs.md)
86
- - [Component Recipes](./component-recipes.md)
87
- - [Task-first design](./task-first-recipes.md)
@@ -0,0 +1,28 @@
1
+ # Release Operations
2
+
3
+ Stable releases use one immutable tag workflow. The workflow runs the complete reusable Ubuntu, macOS, Windows, and dependency-audit boundary before any publication, publishes and verifies the exact npm package through Trusted Publisher, then creates or converges the GitHub Release from the matching changelog section.
4
+
5
+ ## One-time npm Trusted Publisher setup
6
+
7
+ Configure the existing public package `@llblab/pi-actors` on npmjs.com with a GitHub Actions Trusted Publisher using these exact values:
8
+
9
+ - **Owner:** `llblab`
10
+ - **Repository:** `pi-actors`
11
+ - **Workflow filename:** `release.yml`
12
+ - **Environment:** Leave empty unless the workflow and npm configuration later adopt the same named GitHub environment in one reviewed change.
13
+
14
+ The binding must target `.github/workflows/release.yml`; npm asks for the filename rather than the repository-relative path. npm does not verify this identity when the setting is saved, so the first tagged publication provides the decisive proof.
15
+
16
+ Do not create `NPM_TOKEN`, `NODE_AUTH_TOKEN`, or another long-lived npm publish secret. The publication job runs on a GitHub-hosted Ubuntu runner with `id-token: write`, Node 24, npm 11.5.1 or newer, the public npm registry, and package-manager caching disabled at the credential-bearing boundary.
17
+
18
+ ## Release sequence
19
+
20
+ 1. Merge the validated release tree through the repository's guarded `dev` to `main` flow.
21
+ 2. Create one immutable `v<package.version>` tag on the verified `main` commit.
22
+ 3. Let `.github/workflows/release.yml` invoke the complete reusable validation workflow.
23
+ 4. Let the publication job verify the tag commit, package manifests, and non-empty changelog section.
24
+ 5. Publish the exact public npm package through OIDC when the version does not exist.
25
+ 6. Verify npm version, `gitHead`, Pi extension/skill metadata, and packed runtime manifests.
26
+ 7. Create or update the GitHub Release only after npm verification succeeds.
27
+
28
+ A rerun skips `npm publish` only when the exact existing version reports the same tagged `gitHead`; contradictory identity fails closed because npm versions are immutable. Registry lookup retries remain bounded. A missing or mismatched Trusted Publisher usually surfaces as npm authentication or not-found failure and must be corrected in npm package settings—never by adding a token fallback.
@@ -87,7 +87,7 @@ Only a process that consumes actor-local input declares actions:
87
87
  }
88
88
  ```
89
89
 
90
- Actions must be lowercase, unique, and non-reserved. One-shot Recipes omit Control. Outputs belong in Trace, artifacts, execution evidence, or the command result.
90
+ Actions must be lowercase ASCII, unique, non-reserved, and at most 64 characters. Serialized Control input is at most 380 bytes so every admitted wire record remains within 512 bytes on FIFO and named pipe. One-shot Recipes omit Control. Larger data belongs in a declared artifact/path; outputs belong in Trace, artifacts, execution evidence, or the command result.
91
91
 
92
92
  ## Artifacts
93
93
 
@@ -27,8 +27,8 @@ User Recipes take priority over packaged Recipes. Active invalid or disabled sha
27
27
  Inspect registry state with:
28
28
 
29
29
  ```text
30
- inspect target=recipes
31
- inspect target=tool:<name>
30
+ inspect target=recipes view=status
31
+ inspect target=tool:<name> view=status
32
32
  ```
33
33
 
34
34
  Recipe inspection reports active, shadowed, invalid, disabled, diagnostic, risk, usage, and review evidence. Tool inspection reports the current capability definition/schema; a registered tool is not a running actor.
package/index.ts CHANGED
@@ -110,7 +110,7 @@ export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
110
110
  Tools.resolveActiveRuntimeTool(name, runtime.getTools(), (activeName) =>
111
111
  actorToolDefinitions.get(activeName),
112
112
  ),
113
- handleRuntimeMessage: automaticReview.handleMessage,
113
+ handleRuntimeControl: automaticReview.handleControl,
114
114
  registryRuntime: runtime,
115
115
  setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
116
116
  }).map(withCurrentThinkingContext),
package/lib/async-runs.ts CHANGED
@@ -59,14 +59,11 @@ import {
59
59
  verifyRunProcessIdentity,
60
60
  type RunProcessIdentity,
61
61
  } from "./runs-process.ts";
62
+ import * as RuntimeIdentity from "./runtime-identity.ts";
62
63
  import * as RunsStart from "./runs-start.ts";
63
64
  import { appendRunTraceEvent } from "./runs-trace.ts";
64
65
  import * as RunsIndex from "./runs-index.ts";
65
66
  import * as RunsParentTeardown from "./runs-parent-teardown.ts";
66
- import {
67
- appendRunControlInStateDir,
68
- updateRunControlStatusInStateDir,
69
- } from "./runs-controls.ts";
70
67
  import {
71
68
  deliverRunControl,
72
69
  type DeliverRunControlOptions,
@@ -173,7 +170,7 @@ export interface AsyncRunMeta {
173
170
  run: string;
174
171
  run_instance_id: string;
175
172
  state_dir: string;
176
- state_schema: "run-kernel-v1";
173
+ state_schema: typeof RuntimeIdentity.RUN_STATE_SCHEMA;
177
174
  status: AsyncRunStatus;
178
175
  tool?: string;
179
176
  template: CommandTemplateValue;
@@ -466,7 +463,22 @@ export function startRun(
466
463
  const resolved = resolveRunTemplate(startParams);
467
464
  const run = safeRunId(startParams.run_id);
468
465
  const stateDir = resolveStateDir(startParams, run);
466
+ const recipeFile = startParams.file
467
+ ? resolveRecipeFile(startParams.file)
468
+ : undefined;
469
+ const packagedRecipeRoot = resolve(Paths.getPackagedRecipeRoot());
470
+ const recipeRelation = recipeFile
471
+ ? relative(packagedRecipeRoot, recipeFile)
472
+ : undefined;
473
+ const packagedRepo =
474
+ startParams.defaults?.repo === "~/.pi/agent/extensions/pi-actors" &&
475
+ recipeRelation &&
476
+ !recipeRelation.startsWith("..") &&
477
+ !isAbsolute(recipeRelation)
478
+ ? dirname(packagedRecipeRoot)
479
+ : undefined;
469
480
  const values = {
481
+ ...(packagedRepo ? { repo: packagedRepo } : {}),
470
482
  ...(startParams.values || {}),
471
483
  run_id: run,
472
484
  state_dir: stateDir,
@@ -494,9 +506,6 @@ export function startRun(
494
506
  prepareStateDirForStart(stateDir);
495
507
  const stdout = join(stateDir, "stdout.log");
496
508
  const stderr = join(stateDir, "stderr.log");
497
- const recipeFile = startParams.file
498
- ? resolveRecipeFile(startParams.file)
499
- : undefined;
500
509
  const recipe = startParams.name || getRunIdFromFile(recipeFile);
501
510
  const includeActorRecipeContext =
502
511
  startParams.actor_context !== false &&
@@ -545,7 +554,7 @@ export function startRun(
545
554
  run,
546
555
  run_instance_id: randomUUID(),
547
556
  state_dir: stateDir,
548
- state_schema: "run-kernel-v1",
557
+ state_schema: RuntimeIdentity.RUN_STATE_SCHEMA,
549
558
  status: "running",
550
559
  ...(startParams.tool ? { tool: startParams.tool } : {}),
551
560
  template: resolved.template,
@@ -920,37 +929,9 @@ function stopRun(
920
929
  ) {
921
930
  return { stopped: false, reason: "run generation changed", status };
922
931
  }
923
- const control =
924
- event === "run.kill" && typeof status.run_instance_id === "string"
925
- ? appendRunControlInStateDir(stateDir, {
926
- action: "kill",
927
- run_instance_id: status.run_instance_id,
928
- })
929
- : undefined;
930
- if (control) {
931
- updateRunControlStatusInStateDir(
932
- stateDir,
933
- control.id,
934
- "claimed",
935
- {},
936
- ["queued"],
937
- );
938
- }
939
- const finish = (result: Record<string, unknown>): Record<string, unknown> => {
940
- if (!control) return result;
941
- const handled = result.stopped === true;
942
- updateRunControlStatusInStateDir(
943
- stateDir,
944
- control.id,
945
- handled ? "handled" : "failed",
946
- handled ? {} : { error: String(result.reason ?? "kill rejected") },
947
- ["claimed"],
948
- );
949
- return { ...result, control_id: control.id };
950
- };
951
932
  const pid = Number(status.pid || 0);
952
933
  if (status.status !== "running" && status.status !== "exited") {
953
- return finish({ stopped: false, reason: "not running", status });
934
+ return { stopped: false, reason: "not running", status };
954
935
  }
955
936
  const identity = verifyRunProcessIdentity(
956
937
  pid,
@@ -961,22 +942,22 @@ function stopRun(
961
942
  identity.status === "owner_mismatch" ||
962
943
  identity.status === "unsupported_proof"
963
944
  ) {
964
- return finish({
945
+ return {
965
946
  stopped: false,
966
947
  reason: identity.status.replaceAll("_", " "),
967
948
  process_identity_status: identity.status,
968
949
  status,
969
- });
950
+ };
970
951
  }
971
- return finish({ stopped: false, reason: "not running", status });
952
+ return { stopped: false, reason: "not running", status };
972
953
  }
973
954
  if (!identity.valid) {
974
- return finish({
955
+ return {
975
956
  stopped: false,
976
957
  reason: identity.status.replaceAll("_", " "),
977
958
  process_identity_status: identity.status,
978
959
  status,
979
- });
960
+ };
980
961
  }
981
962
  let signalResult: RunProcessSignalPlan;
982
963
  try {
@@ -986,11 +967,6 @@ function stopRun(
986
967
  status.process_identity as RunProcessIdentity,
987
968
  );
988
969
  } catch (error) {
989
- if (control) {
990
- updateRunControlStatusInStateDir(stateDir, control.id, "failed", {
991
- error: error instanceof Error ? error.message : String(error),
992
- });
993
- }
994
970
  throw error;
995
971
  }
996
972
  appendRunTraceEvent(stateDir, {
@@ -1007,13 +983,13 @@ function stopRun(
1007
983
  finalizeInterruptedExecution(stateDir, "cancelled", signal);
1008
984
  markTerminalProgress(stateDir, "cancelled");
1009
985
  }
1010
- return finish({
986
+ return {
1011
987
  stopped: true,
1012
988
  pid,
1013
989
  signal,
1014
990
  ...signalResult,
1015
991
  state_dir: stateDir,
1016
- });
992
+ };
1017
993
  } finally {
1018
994
  releaseControlLock();
1019
995
  }
@@ -16,7 +16,7 @@ import * as ToolReviewScheduler from "./tool-review-scheduler.ts";
16
16
 
17
17
  export interface AutomaticReviewRuntime {
18
18
  close(): void;
19
- handleMessage(type: string, body: unknown): Record<string, unknown>;
19
+ handleControl(action: string, input: unknown): Record<string, unknown>;
20
20
  schedule(): void;
21
21
  start(ctx: Pi.ExtensionContext): void;
22
22
  }
@@ -52,18 +52,18 @@ export function createAutomaticReviewRuntime(
52
52
 
53
53
  return {
54
54
  close,
55
- handleMessage(type, body) {
56
- if (type !== "review.retry" && type !== "review.reset") {
57
- throw new Error("tool:pi-actors accepts review.retry or review.reset messages.");
55
+ handleControl(action, input) {
56
+ if (action !== "review.retry" && action !== "review.reset") {
57
+ throw new Error("runtime accepts review.retry or review.reset Controls.");
58
58
  }
59
- if (type === "review.retry" && !Paths.isAutomaticRecipeReviewEnabled()) {
59
+ if (action === "review.retry" && !Paths.isAutomaticRecipeReviewEnabled()) {
60
60
  throw new Error(
61
61
  "Automatic recipe review is disabled by PI_ACTORS_AUTOMATIC_REVIEW.",
62
62
  );
63
63
  }
64
64
  return ReviewControl.controlAutomaticReview(
65
- type,
66
- ReviewControl.parseAutomaticReviewScope(body),
65
+ action,
66
+ ReviewControl.parseAutomaticReviewScope(input),
67
67
  {
68
68
  scheduleDraft: () => draftScheduler?.schedule(),
69
69
  scheduleTool: () => toolScheduler?.schedule(),