@llblab/pi-actors 0.41.1 → 0.42.1
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 +11 -6
- package/BACKLOG.md +1 -65
- package/CHANGELOG.md +25 -0
- package/README.md +24 -4
- package/dist/index.js +25 -121
- package/dist/lib/async-runs.d.ts +37 -5
- package/dist/lib/async-runs.js +181 -51
- package/dist/lib/automatic-review-runtime.d.ts +18 -0
- package/dist/lib/automatic-review-runtime.js +96 -0
- package/dist/lib/command-templates.js +1 -1
- package/dist/lib/draft-consolidation-transaction.d.ts +65 -0
- package/dist/lib/draft-consolidation-transaction.js +610 -0
- package/dist/lib/draft-consolidation.d.ts +35 -0
- package/dist/lib/draft-consolidation.js +126 -0
- package/dist/lib/draft-review.d.ts +56 -0
- package/dist/lib/draft-review.js +254 -0
- package/dist/lib/draft-sleep.d.ts +65 -0
- package/dist/lib/draft-sleep.js +468 -0
- package/dist/lib/file-state.d.ts +8 -1
- package/dist/lib/file-state.js +115 -18
- package/dist/lib/inspector-actions.d.ts +16 -0
- package/dist/lib/inspector-actions.js +57 -0
- package/dist/lib/inspector-command.d.ts +7 -0
- package/dist/lib/inspector-command.js +37 -0
- package/dist/lib/inspector-overlay.d.ts +23 -3
- package/dist/lib/inspector-overlay.js +327 -70
- package/dist/lib/inspector.d.ts +9 -0
- package/dist/lib/inspector.js +49 -1
- package/dist/lib/observability.d.ts +13 -0
- package/dist/lib/observability.js +160 -6
- package/dist/lib/paths.d.ts +7 -0
- package/dist/lib/paths.js +28 -0
- package/dist/lib/recipes-discovery.js +32 -24
- package/dist/lib/recipes-usage.d.ts +18 -6
- package/dist/lib/recipes-usage.js +445 -34
- package/dist/lib/review-control.d.ts +14 -0
- package/dist/lib/review-control.js +111 -0
- package/dist/lib/review-diagnostics.d.ts +11 -0
- package/dist/lib/review-diagnostics.js +148 -0
- package/dist/lib/review-projection.d.ts +14 -0
- package/dist/lib/review-projection.js +170 -0
- package/dist/lib/run-ui-runtime.d.ts +18 -0
- package/dist/lib/run-ui-runtime.js +123 -0
- package/dist/lib/runs-artifacts.d.ts +1 -1
- package/dist/lib/runs-artifacts.js +1 -1
- package/dist/lib/runs-control.d.ts +10 -2
- package/dist/lib/runs-control.js +37 -7
- package/dist/lib/runs-identity.d.ts +1 -1
- package/dist/lib/runs-identity.js +1 -1
- package/dist/lib/runs-index.d.ts +11 -2
- package/dist/lib/runs-index.js +46 -23
- package/dist/lib/runs-mailbox.d.ts +1 -1
- package/dist/lib/runs-mailbox.js +1 -1
- package/dist/lib/runs-messages.d.ts +1 -1
- package/dist/lib/runs-messages.js +1 -1
- package/dist/lib/runs-outbox.d.ts +1 -1
- package/dist/lib/runs-outbox.js +1 -1
- package/dist/lib/runs-ownership.d.ts +1 -1
- package/dist/lib/runs-ownership.js +18 -4
- package/dist/lib/runs-parent-teardown.d.ts +51 -0
- package/dist/lib/runs-parent-teardown.js +172 -0
- package/dist/lib/runs-process.d.ts +1 -1
- package/dist/lib/runs-process.js +5 -4
- package/dist/lib/runs-retention.d.ts +1 -1
- package/dist/lib/runs-retention.js +1 -1
- package/dist/lib/runs-start.d.ts +5 -3
- package/dist/lib/runs-start.js +7 -48
- package/dist/lib/runs-status.d.ts +5 -3
- package/dist/lib/runs-status.js +7 -6
- package/dist/lib/runtime.d.ts +7 -1
- package/dist/lib/runtime.js +32 -18
- package/dist/lib/tool-review-lineage-transaction.d.ts +27 -0
- package/dist/lib/tool-review-lineage-transaction.js +597 -0
- package/dist/lib/tool-review-lineage.d.ts +24 -0
- package/dist/lib/tool-review-lineage.js +98 -0
- package/dist/lib/tool-review-scheduler.d.ts +80 -0
- package/dist/lib/tool-review-scheduler.js +494 -0
- package/dist/lib/tool-review-transaction.d.ts +50 -0
- package/dist/lib/tool-review-transaction.js +362 -0
- package/dist/lib/tool-review.d.ts +56 -0
- package/dist/lib/tool-review.js +197 -0
- package/dist/lib/tools-inspect.js +28 -4
- package/dist/lib/tools-local.js +21 -4
- package/dist/lib/tools-message.d.ts +1 -0
- package/dist/lib/tools-message.js +29 -17
- package/dist/lib/tools-spawn.js +14 -2
- package/dist/lib/tools.d.ts +1 -0
- package/dist/lib/tools.js +1 -0
- package/dist/recipes/draft-review.json +24 -0
- package/dist/recipes/tool-review.json +24 -0
- package/dist/scripts/async-runner.mjs +9 -9
- package/dist/scripts/build-dist.mjs +6 -1
- package/dist/scripts/conformance.mjs +6 -1
- package/dist/scripts/recipe-utils.mjs +3 -3
- package/dist/scripts/release-gates.mjs +165 -0
- package/dist/skills/actors/SKILL.md +9 -8
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/actor-inspector.md +20 -11
- package/docs/async-runs.md +16 -2
- package/docs/recipe-library.md +7 -3
- package/docs/template-recipes.md +4 -11
- package/docs/tool-registry.md +13 -4
- package/index.ts +27 -142
- package/lib/async-runs.ts +283 -67
- package/lib/automatic-review-runtime.ts +135 -0
- package/lib/command-templates.ts +1 -1
- package/lib/draft-consolidation-transaction.ts +821 -0
- package/lib/draft-consolidation.ts +181 -0
- package/lib/draft-review.ts +325 -0
- package/lib/draft-sleep.ts +576 -0
- package/lib/file-state.ts +143 -19
- package/lib/inspector-actions.ts +79 -0
- package/lib/inspector-command.ts +54 -0
- package/lib/inspector-overlay.ts +377 -91
- package/lib/inspector.ts +73 -1
- package/lib/observability.ts +194 -5
- package/lib/paths.ts +43 -0
- package/lib/recipes-discovery.ts +34 -26
- package/lib/recipes-usage.ts +569 -40
- package/lib/review-control.ts +137 -0
- package/lib/review-diagnostics.ts +164 -0
- package/lib/review-projection.ts +200 -0
- package/lib/run-ui-runtime.ts +153 -0
- package/lib/runs-artifacts.ts +1 -1
- package/lib/runs-control.ts +69 -6
- package/lib/runs-identity.ts +1 -1
- package/lib/runs-index.ts +57 -21
- package/lib/runs-mailbox.ts +1 -1
- package/lib/runs-messages.ts +1 -1
- package/lib/runs-outbox.ts +1 -1
- package/lib/runs-ownership.ts +23 -4
- package/lib/runs-parent-teardown.ts +257 -0
- package/lib/runs-process.ts +5 -4
- package/lib/runs-retention.ts +1 -1
- package/lib/runs-start.ts +13 -68
- package/lib/runs-status.ts +17 -8
- package/lib/runtime.ts +34 -17
- package/lib/tool-review-lineage-transaction.ts +881 -0
- package/lib/tool-review-lineage.ts +145 -0
- package/lib/tool-review-scheduler.ts +635 -0
- package/lib/tool-review-transaction.ts +563 -0
- package/lib/tool-review.ts +270 -0
- package/lib/tools-inspect.ts +35 -4
- package/lib/tools-local.ts +29 -4
- package/lib/tools-message.ts +45 -30
- package/lib/tools-spawn.ts +22 -2
- package/lib/tools.ts +5 -0
- package/package.json +5 -3
- package/recipes/draft-review.json +24 -0
- package/recipes/tool-review.json +24 -0
- package/scripts/async-runner.mjs +9 -9
- package/scripts/build-dist.mjs +6 -1
- package/scripts/conformance.mjs +6 -1
- package/scripts/recipe-utils.mjs +3 -3
- package/scripts/release-gates.mjs +165 -0
- package/skills/actors/SKILL.md +9 -8
- package/skills/swarm/SKILL.md +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Required practical guide for non-trivial pi-actors use, including parallel actor launches, subagent fanout, and autonomous coordinator workflows. Read before using or changing spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.42.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -40,7 +40,7 @@ Trusted local capability
|
|
|
40
40
|
- **Command template**: portable execution graph. String leaf, sequence array, or object node with controls.
|
|
41
41
|
- **Recipe**: saved JSON definition wrapping a template with args, defaults, imports, mailbox, artifacts, metadata, and optional `async: true`.
|
|
42
42
|
- **Run actor**: one detached execution instance addressable as `run:<id>` with status, logs, messages, mailbox metadata, files, and artifacts.
|
|
43
|
-
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`. `tool:pi-actors` is reserved for runtime
|
|
43
|
+
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`. `tool:pi-actors` is reserved for runtime status and explicit automatic-review retry/reset control.
|
|
44
44
|
- **Artifact**: named durable output path declared by a recipe/run.
|
|
45
45
|
- **Mailbox**: interaction contract: message types the actor accepts/emits.
|
|
46
46
|
- **Advanced group/coordination surfaces**: `branch:<run>/<branch>`, `room:<run>`, `coordinator`, `session:<id>`, and communication snapshots exist for multi-actor workflows and diagnostics; do not make them the default mental model.
|
|
@@ -94,7 +94,8 @@ Envelope fields:
|
|
|
94
94
|
- Group posts to `room:<run>` require `from` from the same run (`run:<run>` or `branch:<run>/<branch>`).
|
|
95
95
|
- Runtime termination message: `control.kill` is the only documented actor message that kills a run. `control.stop` and `control.cancel` are actor-local mailbox vocabulary only when a recipe declares and handles them. Terminal retention messages: `control.archive`, `control.prune`.
|
|
96
96
|
- Long-lived child processes should remain in the run-owned process group unless the recipe implements an explicit daemon termination bridge; do not leave unowned detached services behind `control.kill`.
|
|
97
|
-
- Run controls revalidate a persisted cross-platform process identity proof
|
|
97
|
+
- Run controls revalidate a persisted cross-platform process identity proof at authorization and again immediately before signaling. Treat `dead pid`, `owner mismatch`, and `unsupported proof` as distinct fail-closed states; on Unix, only process-group `ESRCH` plus one more matching identity check permits exact-pid fallback, while permission/authorization errors remain terminal. Node exposes no portable pidfd/process-group handle, so retain the documented residual exit/reuse window instead of claiming atomic signaling or bypassing control with direct pid signals.
|
|
98
|
+
- Detached actors survive ordinary agent turns. On `session_shutdown` (quit, reload, or session replacement), pi-actors attempts canonical `control.kill` for each discovered readable still-running exact-owner run. Control compares immutable run generation inside the canonical boundary and serializes against same-directory restart; terminal, ambiguous, changed-generation, and other-session runs remain untouched. Teardown scans without the ordinary index depth cap, reports unreadable/corrupt state as failures, and persists a bounded summary under the run root. Descendant Pi sessions remain separate owners and rely on their own shutdown hooks; hard host termination can still leave an orphan requiring summary-guided OS/manual recovery, so never describe teardown as an absolute no-survivor guarantee.
|
|
98
99
|
|
|
99
100
|
Check `inspect view=mailbox` before domain-specific messages.
|
|
100
101
|
|
|
@@ -216,15 +217,15 @@ Only matching filename ids compete. Higher priority shadows lower priority; with
|
|
|
216
217
|
Muscle-memory lens: pi-actors has two durable executable-memory layers.
|
|
217
218
|
|
|
218
219
|
1. `~/.pi/agent/recipes/*.json` and `*.md` are the agent's active capability memory. Every recipe in that directory becomes an easy-to-call tool automatically and survives into later sessions. Descriptions matter here because they become the tool's operator-facing title/context.
|
|
219
|
-
2. `~/.pi/agent/recipes/drafts/*.json` is draft memory captured from successful inline `spawn template=...` runs. Drafts do not enter the injected tool surface
|
|
220
|
+
2. `~/.pi/agent/recipes/drafts/*.json` is draft memory captured from successful inline `spawn template=...` runs. Drafts do not enter the injected tool surface and remain reusable by explicit path, e.g. `spawn file="~/.pi/agent/recipes/drafts/<name>.json"`.
|
|
220
221
|
|
|
221
|
-
Agents grow
|
|
222
|
+
Agents grow draft memory by trying ad hoc actors successfully. Active memory grows through explicit `register_tool`, deliberate operator recipe edits, or the bounded automatic review cycle. Treat drafts as the workbench and root recipes as active muscle memory.
|
|
222
223
|
|
|
223
|
-
Usage lens: user recipe launches update extension-maintained `.usage/<recipe-
|
|
224
|
+
Usage lens: user recipe launches update an extension-maintained canonical name-and-priority lineage ledger under `.usage/recipes/<recipe-name>.json`; authored recipe files are not rewritten for telemetry. Accounting briefly shares the portfolio mutation fence so source quarantine cannot erase an authorized launch; if another session already changed the source, the stale invocation rejects and requests reload rather than executing without evidence. Lifetime calls survive rename, revision, promotion, and demotion, while revision-local calls restart when executable content changes. The bounded unversioned ledger retains former names/paths, revision ancestry, transition events, and review epochs. Discovery merges lineage usage into inspection. Agents should not hand-edit counters; usage remains evidence rather than a sufficient usefulness verdict.
|
|
224
225
|
|
|
225
|
-
|
|
226
|
+
Automatic review lens: successful transient/ad hoc actor runs leave replayable drafts rather than active tools. At twelve eligible drafts, pi-actors captures one exact trusted batch and attaches only its identity-opaque value-free structural projection to a silent no-tools reviewer after the foreground turn and active actors finish. Its complete quota-free `promote`/`discard` result contains no recipe content: the deterministic executor derives promotions from exact captured sources and revalidates source/target CAS, complete recipes, root identity, quarantine hashes, and recovery state before commit. Newer drafts remain for a later batch; malformed, stale, unsafe, or incomplete decisions fail closed. Unchanged automatic demotions remain in cooldown until their executable fingerprint changes. Prefer fenced `register_tool draft=...` for an explicit single-draft promotion. A deliberate move/copy into the recipe root also remains valid, but may invalidate and defer an already captured batch; do not reconstruct removed batch commands or ask the operator to drive an automatic batch.
|
|
226
227
|
|
|
227
|
-
|
|
228
|
+
Portfolio lens: thirty-six eligible non-sensitive active revisions trigger a no-tools review of an attached value-free structural projection; canonical names, draft basenames, raw hashes, recipe bodies, template/default values, authored prose, and filesystem paths remain in the separate trusted capture; batch-local occurrence IDs and equality-only content groups preserve correlation and deduplication. Set `PI_ACTORS_AUTOMATIC_REVIEW=off` before Pi starts to disable both reviewer scheduling and safe-boundary portfolio activation; verify the effective value with `inspect target=tool:pi-actors view=status`. The reviewer may select keep, unchanged-source rename, unchanged-source demotion, or deduplication of canonically identical captured recipes; it cannot return recipe content. Replacement, split, and executable contract changes require explicit operator authoring. Approval remains immutable until the next safe session boundary, where journaled filesystem and lineage executors apply only the captured recipe bytes. Use `inspect target=recipes view=reviews` for bounded evidence including failed stage/error/next action. Recover a failed cycle through `message to=tool:pi-actors type=review.retry body={"scope":"draft"|"tool"}`. Draft retry resumes an existing authenticated transaction plan and original reviewer run rather than generating decisions after filesystem commit; `review.reset` clears only disposable terminal admission state and rejects tool recovery evidence that must roll forward.
|
|
228
229
|
|
|
229
230
|
## Registered Tools
|
|
230
231
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: swarm
|
|
3
3
|
description: Subagent and actor orchestration with scoped locks, fanout, and quorum consensus. Use before launching multiple parallel actors or subagents for independent implementation, artifact generation, review, delegated audit, coordinated execution, or any workflow that needs autonomous coordinator decomposition and integration.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.42.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Swarm
|
package/docs/actor-inspector.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Actor Inspector
|
|
2
2
|
|
|
3
|
-
The actor inspector is a manually opened
|
|
3
|
+
The actor inspector is a manually opened TUI navigator for owned actor runs. Evidence remains read-only; its one explicit lifecycle action can send canonical `control.kill` to the selected running run after confirmation. It keeps recipe/launch identity, communication evidence, and persisted subagent execution evidence in one hierarchy without merging their meanings.
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
owned run
|
|
7
|
+
→ recipe
|
|
7
8
|
→ messages | turns
|
|
8
9
|
→ filtered timeline
|
|
9
|
-
→ bounded detail
|
|
10
|
+
→ one bounded detail level
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
## Navigation
|
|
@@ -16,18 +17,20 @@ owned run
|
|
|
16
17
|
The overlay exposes an explicit focus hierarchy:
|
|
17
18
|
|
|
18
19
|
```text
|
|
19
|
-
Run ←/→ chooses the previous/next owned run, Enter opens runs, ↓ enters tabs
|
|
20
|
-
Tabs ←/→ chooses Messages or Turns
|
|
21
|
-
|
|
20
|
+
Run ←/→ chooses the previous/next owned run, Enter opens runs, K asks to Kill a running run, ↓ enters tabs
|
|
21
|
+
Tabs ←/→ chooses Recipe, Messages, or Turns
|
|
22
|
+
Recipe ↑/↓ scroll; PageUp/PageDown jumps by viewport; ↑ at top, Escape, or ← returns to tabs
|
|
23
|
+
Filters Enter on Messages/Turns opens Channel/State or Subagent; Enter opens values
|
|
22
24
|
Values ↑/↓ hovers, Enter applies, Escape returns one menu level
|
|
23
|
-
List ↑/↓ chooses, Enter/→ opens detail
|
|
24
|
-
Detail ↑/↓ scroll,
|
|
25
|
-
Readable ↑/↓ scroll, Escape/← returns to evidence detail
|
|
25
|
+
List ↑/↓ chooses, PageUp/PageDown jumps by viewport, Enter/→ opens detail, ← returns to tabs
|
|
26
|
+
Detail ↑/↓ scroll, PageUp/PageDown jumps by viewport, Escape/← returns to the list
|
|
26
27
|
Escape Close (or cancel the active options popup)
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
Navigation stays bounded by available actions. `↑` on Run does nothing because no higher control exists. `↓` on Tabs enters the timeline only when it contains rows. Empty timelines therefore never receive focus.
|
|
30
31
|
|
|
32
|
+
`K` appears only while Run is focused and the selected owned run reports `running`. It replaces the Inspector with a dedicated responsive `Confirm Actor Kill` overlay that names the exact `run:<id>`, shows its current status, and states that canonical `control.kill` is destructive and irreversible. Cancel owns initial focus; ←/→/Tab moves between Cancel and Kill actor, Enter activates the focused choice, `Y` confirms directly, and `N`/Escape cancels. Confirmation captures the immutable run generation and routes expected owner/generation through canonical `control.kill`; control compares owner, generation, and running status while serialized against same-directory restart, so terminal, ownership, or replacement-generation races reject without signaling. After the dialog closes, success, cancellation, rejection, and failure remain bounded in the Inspector content area; terminal runs expose no Kill hint and reject a stale keypress.
|
|
33
|
+
|
|
31
34
|
Selection and focus remain separate visual states. Accent-blue text marks the current tab, active filter popup, and applied option. The Run control uses `← … →` markers plus a light neutral background to show both focus and horizontal cycling; menus and timeline rows retain the single `▶` focus marker, while selected tabs retain brackets. Opening a popup keeps its parent filter blue so the relationship remains visible. The footer uses accent color only for key names and arrows; descriptions remain muted.
|
|
32
35
|
|
|
33
36
|
The top Run control aligns vertically with the tab labels, names the selected owned run, and colors its textual lifecycle status semantically. ←/→ cycles owned runs directly with wraparound, while Enter opens the complete owned-run list immediately beneath the control. That run list starts one cell farther left than the filter menus so its border aligns with the Run control rather than the tab/filter grid. It still overlays the tab row rather than leaving a detached gap. The timeline no longer renders run metadata as a data row.
|
|
@@ -36,7 +39,13 @@ Filters live behind their tab rather than occupying a permanent row. Non-default
|
|
|
36
39
|
|
|
37
40
|
Nested menus overlay rather than replace the timeline. Only rows and columns containing menu borders or values occlude underlying cells. When adjacent menus have different heights, the unused corner remains transparent and preserves the separator, striped background, and timeline data beneath it. Every run, filter, and nested value menu is viewport-bounded: ↑/↓ moves through the complete option set, the visible window follows focus, and `↑`/`↓` border markers disclose hidden options above or below without growing past the available inspector rows.
|
|
38
41
|
|
|
39
|
-
The overlay uses most of the available terminal width and height and reduces its content/menu viewport on shorter terminals. The bordered header keeps
|
|
42
|
+
The overlay uses most of the available terminal width and height and reduces its content/menu viewport on shorter terminals. The bordered header keeps all three tabs visible, while the body shows the selected run and its current status above the active document or evidence rows. Run, Message, and Turn lists place the newest retained item directly below their control; a newly opened Inspector therefore selects the latest owned run, and ↓ moves backward in time toward older entries. Run options, Messages, and Turns all use compact descending `#N` labels, providing one timestamp-free time axis without repeating type words on every row. Evidence rows retain stable alternating backgrounds based on their absolute timeline position, including while scrolling: even rows keep the dark overlay background, while odd rows use the neutral `customMessageBg` stripe. Unused viewport padding stays on the plain overlay background instead of drawing fake striped rows beneath the last item. The footer exposes the active keys. Messages retain attention markers and unread filtering and open into bounded detail without leaving the overlay. The overlay refreshes while visible and distinguishes true empty timelines from filtered-empty results; filtered-empty copy points back to Enter on the active tab without moving focus.
|
|
43
|
+
|
|
44
|
+
## Recipe Document
|
|
45
|
+
|
|
46
|
+
`Recipe` is the first and initially selected tab. It reads only persisted owned-run evidence from `run.json`: recipe identity and source, the authored recipe context captured at launch, the resolved executable template and runtime values, composition records, model policy, mailbox, artifacts, notification/retirement policy, and bounded read diagnostics. It never follows a mutable external recipe path while the Inspector is open.
|
|
47
|
+
|
|
48
|
+
The document renders as labeled, indented terminal text rather than raw JSON and scrolls as one level. Secret-bearing values receive the same redaction as turn evidence. Recipe context lives here rather than repeating inside every Turn.
|
|
40
49
|
|
|
41
50
|
## Communication Timeline
|
|
42
51
|
|
|
@@ -64,9 +73,9 @@ Each turn groups:
|
|
|
64
73
|
- Tool calls in assistant source order;
|
|
65
74
|
- Tool results correlated by `toolCallId`, regardless of completion order.
|
|
66
75
|
|
|
67
|
-
Enter/→ opens the selected turn as structured
|
|
76
|
+
Enter/→ opens the selected turn as one structured, scrollable detail document inside the overlay. A compact `Subagent N` heading with an optional meaningful role leads into meaning-first sections: User, persisted Thinking, Assistant, Tools, Execution, and Diagnostics. A final Provenance section retains session/prompt paths and truncation state without duplicating recipe context from the Recipe tab. Generic internal stages such as `command` and `subagent` stay hidden; technical `command-NNN` provenance remains available through the session and prompt paths without producing a redundant `Command / command-NNN (command)` block. Secondary qualifiers use parentheses rather than centered-dot separators. Long text, paths, and structured values wrap to subsequent terminal rows instead of receiving visual ellipsis; lines that already fit the available inner width remain intact, leading indentation is reserved before wrapping long unbroken paths so it cannot become a whitespace-only row, and every section plus all of its explicit or wrapped continuations keeps one background stripe. Blank-only source lines and trailing line breaks are omitted from both evidence and readable rendering. Section boundaries change the stripe without inserting separator rows, so the next heading follows the previous value immediately. ↑/↓ scrolls the resulting visual-row document while the footer remains visible. Source evidence remains bounded by the persisted session reader, but the detail view no longer truncates that retained evidence to one terminal row per field.
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
The detail view removes a single enclosing `<file name="…">…</file>` prompt transport wrapper and renders structured values as indented key/value text rather than one-line JSON. It has no nested transcript mode: Escape/← returns directly to the Turns list.
|
|
70
79
|
|
|
71
80
|
## Evidence And Privacy Boundary
|
|
72
81
|
|
package/docs/async-runs.md
CHANGED
|
@@ -83,12 +83,16 @@ Use `run_id` on async recipe tools or `as: "run:<id>"` on `spawn` when the calle
|
|
|
83
83
|
|
|
84
84
|
Review commands that require semantic evidence apply marker acceptance before command completion accounting. Rejected code-zero output is reported consistently as a failed command in events, progress, evidence, and outbox delivery; it cannot emit a success-level completion notification. Evidence records are written before command launch and lifecycle cancellation or kill finalizes any running record with its interrupted state, effective exit code, and attempt capture paths. Async attempt stdout/stderr files exist from attempt start, so even small partial streams remain auditable when a command never returns.
|
|
85
85
|
|
|
86
|
+
Terminal follow-ups carry one bounded semantic result in addition to run-file and artifact references. Direct `spawn` and saved async tools persist the originating tool-call correlation; callers may also provide `correlation_id` and a bounded scalar `transport_context`. A transport adapter can preserve an exact route such as `{ "transport": "telegram", "chat_id": 123456, "thread_id": 77 }`, and the same metadata is returned inside terminal follow-up details after detached completion. When a recipe advertises `review.completed`, an explicit matching outbox envelope wins; otherwise a successful accepted review result deterministically synthesizes one from the bounded beginning of `stdout.log`. Failed runs carry their bounded terminal error as `run.failed`.
|
|
87
|
+
|
|
88
|
+
Watcher acceleration and periodic reconciliation share one live in-flight guard. Delivery remains at-least-once across the send/handled-marker crash window, but reentrant watcher/reconciliation races do not create parallel sends. A send failure leaves the run unhandled for retry, notifies the active operator, and persists bounded attempts/error/status evidence in `terminal-delivery-failure.json`; `getRunStatus` exposes the latest record as `terminal_delivery_failure`.
|
|
89
|
+
|
|
86
90
|
## State Files
|
|
87
91
|
|
|
88
92
|
Use ordinary files under the extension temp directory so status tools stay simple and inspectable:
|
|
89
93
|
|
|
90
94
|
- `.pi-actors-run-state.json`: runtime ownership marker binding the run id to the canonical state directory; launch reuse and destructive retention fail closed when it is absent, invalid, mismatched, or reached through a symlink alias. State reuse also fails closed whenever the persisted process identity mismatches a still-live pid, preventing corrupted metadata from admitting overlapping runners.
|
|
91
|
-
- `run.json`: pid, cross-platform `process_identity` proof (start time, command, and canonical cwd where available), optional source metadata (`launch_source`, `tool`, `recipe`, `recipe_file`), command-template config, cwd, coordinator owner id, values, named `artifacts`, mailbox metadata, created time, and state dir. Existing launch cwd aliases are resolved through native `realpath` before proof matching, so symlinked working directories do not degrade control to `unsupported_proof`.
|
|
95
|
+
- `run.json`: pid, cross-platform `process_identity` proof (start time, command, and canonical cwd where available), optional source metadata (`launch_source`, `tool`, `recipe`, `recipe_file`), `launch_correlation`, bounded scalar `transport_context`, command-template config, cwd, coordinator owner id, values, named `artifacts`, mailbox metadata, created time, and state dir. Existing launch cwd aliases are resolved through native `realpath` before proof matching, so symlinked working directories do not degrade control to `unsupported_proof`.
|
|
92
96
|
- `communication.json`: compact actor communication snapshot with self/root/parent, default-room, member, and contact hints for room-aware scripts and agents.
|
|
93
97
|
- `progress.json`: phase, active command count, completed count, failures, updated time, and optional `model_policy` provenance for inherited/explicit model and thinking values.
|
|
94
98
|
- `events.jsonl`: append-only implementation lifecycle log.
|
|
@@ -98,6 +102,8 @@ Use ordinary files under the extension temp directory so status tools stay simpl
|
|
|
98
102
|
- `captures/command-NNN/attempt-NNN/{stdout,stderr}.log`: complete byte-exact command streams, retained even below the bounded in-memory capture limit and separated across retries.
|
|
99
103
|
- `review-evidence.json`: stable command/stage manifest linking prompts, repeated branches, capture attempts, byte counts, exit state, semantic marker acceptance, recipe context, and model/thinking policy; terminal status aligns with the run. Review pipelines inject prior-stage `ACTOR_EVIDENCE_REF` values into downstream prompts, record cited/missing report sources, and fail closed if a normalized report claims `complete` without every required reviewer, verifier, merger, and judge reference.
|
|
100
104
|
- `result.json`: final code, killed flag, output selector, and optional full-output path.
|
|
105
|
+
- `terminal-delivery-failure.json`: latest bounded failed follow-up attempt count, status, error, and timestamp; a later successful retry writes `terminal-handled.json`.
|
|
106
|
+
- `terminal-handled.json`: durable proof that terminal follow-up delivery or an explicit terminal control completed; notification delivery writes it only after the follow-up send returns successfully.
|
|
101
107
|
|
|
102
108
|
Public `spawn` always uses the runtime-owned run root; caller-selected state directories are rejected so `run:<id>` addressing and retention share one boundary. Internal adapters may still supply isolated state directories for deterministic fixtures, but those are not part of the public actor contract. Every launched runner also persists a process identity proof and revalidates it for status, state reuse, message delivery, cancellation, kill, and retirement; dead pids, reused-pid owner mismatches, and unavailable platform proofs remain distinct diagnostics and destructive controls fail closed.
|
|
103
109
|
|
|
@@ -246,7 +252,7 @@ Use coordinator/session-bound messages for completion and decision points, not f
|
|
|
246
252
|
|
|
247
253
|
An async run belongs to the current user, cwd, and launching agent session at start time. Send, cancellation, and force-kill target only the recorded runner pid when command line and cwd still match the recorded owner data. Stale pid reuse must fail closed.
|
|
248
254
|
|
|
249
|
-
On Unix-like systems, `control.kill` signals the runner process group when available
|
|
255
|
+
Immediately before signaling, control revalidates the persisted process identity a second time inside the state-directory lifecycle lock. On Unix-like systems, `control.kill` signals the runner process group when available and falls back to the exact runner pid only when group signaling returns `ESRCH` and one additional identity revalidation still matches; authorization and permission errors fail closed without fallback. On native Windows, `control.kill` uses Windows process-tree termination through the platform adapter. The runner starts command-template children in the owned process tree, so long-running descendants such as audio players should stop with the run instead of becoming orphaned background processes. A recipe may manage a true detached daemon, but then daemon ownership is recipe-local: the script must persist and verify a pid or service handle, expose status/stop behavior, and bridge `control.kill` to daemon cleanup. The generic runner does not scan for or guess detached services. After the process exits, status reflects the operator action as `killed` instead of a generic `exited`. Programmatic `cancelRun()` remains an internal lifecycle helper for retirement and tests, but it is not a documented actor-message action. Node does not expose one portable identity-stable process-group handle across Linux, macOS, and Windows, so a runner can theoretically exit and its PID/PGID can be reused after the final identity read but before the OS signal call. Generation fencing, lifecycle serialization, immediate revalidation, and error-specific fallback minimize this residual platform window; docs and evidence must not claim pidfd/handle-level atomic signaling where the host cannot provide it.
|
|
250
256
|
|
|
251
257
|
State is append-only where practical. Final result writes should be atomic. Recipe-local control endpoints and actor-message logs may live in the state dir. pi-actors core owns the generic run-local message adapter and runtime attention policy; command and message vocabularies belong to the recipe/script.
|
|
252
258
|
|
|
@@ -270,6 +276,14 @@ Rules:
|
|
|
270
276
|
- The `runs` state root is preserved by startup cleanup; run lifecycle cleanup must be explicit and run-aware.
|
|
271
277
|
- State that must survive restarts belongs in the agent root, not in `tmp`.
|
|
272
278
|
|
|
279
|
+
## Parent Session Teardown
|
|
280
|
+
|
|
281
|
+
Async actors may outlive individual agent turns. Every Pi `session_shutdown` reason (`quit`, `reload`, `new`, `resume`, or `fork`) scans persisted run state and attempts teardown for discovered readable `running` runs whose exact `ownerId` matches the retiring coordinator session. Each new run persists immutable `run_instance_id`; teardown carries expected owner/generation into canonical `control.kill`, which compares both while holding the state-directory lifecycle lock shared with restart. Missing ownership or generation fails closed; terminal, ambiguous, changed-generation, and other-session runs remain untouched. Teardown never signals processes directly.
|
|
282
|
+
|
|
283
|
+
Teardown remains idempotent and best-effort across discovered siblings: one signal, process-proof, or evidence-write failure does not block later candidates. A `run.parent_teardown` event is written only while the selected generation still owns that state directory; replacement generations cannot receive stale teardown evidence. Successful kills retain `run.kill`, terminal progress, process-identity fencing, and handled-marker evidence.
|
|
284
|
+
|
|
285
|
+
This boundary intentionally does not run at ordinary `agent_end`. Teardown uses unbounded directory discovery rather than the ordinary index depth cap. Unreadable directories and corrupt run state become explicit failures, and every invocation persists a bounded summary under `<run-root>/teardown/`; shutdown warnings include that path when failures remain. Actors launched by descendant Pi sessions deliberately remain outside the exact-owner contract and rely on their own session shutdown hook. A hard OS/process kill can still prevent either hook; use the persisted summary plus OS-level/manual recovery for an orphan that a replacement session cannot control safely.
|
|
286
|
+
|
|
273
287
|
## Ambient Observability
|
|
274
288
|
|
|
275
289
|
Interactive sessions expose compact activity with minimal screen cost:
|
package/docs/recipe-library.md
CHANGED
|
@@ -13,14 +13,16 @@ Helper scripts that belong to library recipes live in root `scripts/`. The music
|
|
|
13
13
|
|
|
14
14
|
## Install Locally
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Select only the operator-facing recipe or wrapper you intend to own locally. For example:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
mkdir -p ~/.pi/agent/recipes
|
|
20
|
-
cp <repo>/recipes
|
|
20
|
+
cp <repo>/recipes/pipeline-review-readiness.json ~/.pi/agent/recipes/
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Do not bulk-copy `recipes/*.json`. The packaged library also contains internal composition stages, including `draft-review.json` and `tool-review.json`; the automatic-review runtime launches those selectors with fenced inputs and they must not become user-installed callable tools.
|
|
24
|
+
|
|
25
|
+
A registered tool can instead point at one selected recipe path when a durable operator-facing name is useful. Prefer a thin wrapper for public defaults or policy rather than copying the wrapper's internal imports.
|
|
24
26
|
|
|
25
27
|
## Async Subagent Components
|
|
26
28
|
|
|
@@ -32,6 +34,8 @@ Core subagent recipes:
|
|
|
32
34
|
- `recipes/subagent-preflight.json`: Tiny model/thinking/tool-policy smoke check before expensive fanout; failures surface `ACTOR_PREFLIGHT_FAILED` with stage, selected policy, provider error class, prompt file, and override args.
|
|
33
35
|
- Packaged reviewer, verifier, merger, judge, and normalizer stages use `accept_output: review_evidence` and require `ACTOR_REVIEW_RESULT` as the exact first non-whitespace output line. Marker prefixes, format acknowledgements, and input requests therefore remain rejected branch diagnostics rather than usable quorum evidence.
|
|
34
36
|
- `recipes/subagent-review.json`: Evidence-grounded review lens.
|
|
37
|
+
- `recipes/draft-review.json`: Internal no-tools selector for one immutable automatic draft batch. It receives an attached value-free structural projection with batch-local opaque occurrence/content-group identities, counts, risk labels, and usage—not canonical names, draft basenames, raw hashes, recipe bodies, template text, defaults, authored prose, or filesystem paths—then emits one terminal `DRAFT_REVIEW_RESULT` with quota-free promote/discard decisions. The executor derives any promotion from the separate trusted captured source.
|
|
38
|
+
- `recipes/tool-review.json`: Internal no-tools selector for one immutable 36-tool portfolio. It receives the same identity-opaque value-free structural projection and may recommend quota-free keep, unchanged-source rename (`evolve`), unchanged-source demote, or identical-source merge decisions. `replace`, `split`, and returned recipe content fail mechanically; deterministic executors alone read trusted captured recipes and own validated safe-boundary activation.
|
|
35
39
|
- `recipes/subagent-critic.json`: Assumption and failure-mode critique.
|
|
36
40
|
- `recipes/subagent-plan.json`: Bounded plan slices and validation gates.
|
|
37
41
|
- `recipes/subagent-evidence-map.json`: Evidence and confidence map.
|
package/docs/template-recipes.md
CHANGED
|
@@ -107,20 +107,13 @@ The high-priority user recipe directory is also the default tool set: recipes pl
|
|
|
107
107
|
|
|
108
108
|
Higher-priority files shadow lower-priority files with the same basename. Within one priority layer, same-id JSON shadows Markdown because JSON is the canonical precise format. A highest-priority invalid recipe is still visible and blocks fallback so operators do not accidentally run packaged behavior when a user override is broken. A highest-priority recipe with `disabled: true` also blocks fallback, is not launchable, and intentionally disables that id. Healthy overrides are silent; failed bare-name launches caused by invalid or disabled shadowing include compact `reason=shadowed_invalid` or `reason=shadowed_disabled` diagnostics with the active path, blocked candidate, and recipe-doctor hint.
|
|
109
109
|
|
|
110
|
-
## Usage Metadata
|
|
110
|
+
## Usage And Lineage Metadata
|
|
111
111
|
|
|
112
|
-
User-owned recipe launches
|
|
112
|
+
User-owned recipe launches update extension-maintained lineage ledgers under `.usage/recipes/<recipe-name>.json` plus a path index. Authored recipes remain untouched. The ledger keeps lifetime and revision-local launch counts, executable fingerprints, former names and paths, bounded revision ancestry, transition events, and review epochs.
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
"calls": 12,
|
|
117
|
-
"last_called": "2026-05-22T10:30:00.000Z"
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
The extension increments `calls` and updates `last_called` when it starts that concrete recipe, either through a recipe-backed tool call or a direct async recipe-file run. The sidecar also stores a content `fingerprint`; if authored recipe content changes, the next launch resets `calls` before counting the new launch and records `reset_at`. Keeping telemetry outside the recipe prevents usage writes from replacing concurrent operator edits; discovery merges sidecar usage into inspection. Agents should treat these fields as cleanup evidence, not as authored recipe contract. Packaged standard-library recipes do not receive usage metadata.
|
|
114
|
+
Lifetime usage survives rename, promotion, demotion, and executable revision. Revision-local counters restart when executable content changes, making the new fingerprint eligible for portfolio review without erasing prior evidence. Discovery merges current lineage evidence into inspection; packaged standard-library recipes receive no mutable usage ledger. The unreleased format stays intentionally unversioned until a public compatibility boundary exists.
|
|
122
115
|
|
|
123
|
-
There is intentionally no failure counter
|
|
116
|
+
There is intentionally no failure counter: a failed launch can reflect caller misuse, missing values, or environment state rather than recipe quality. Usage remains evidence, not an automatic usefulness verdict. Automatic review combines it with contract quality, portability, duplication, safety, and likely future value. `register_tool draft=...` is the preferred fenced single-draft override; a deliberate move/copy from `drafts/` into the recipe root also remains valid, though it may defer an already captured automatic batch.
|
|
124
117
|
|
|
125
118
|
For object form, keep `template` last. Recipe metadata comes first; executable content stays last.
|
|
126
119
|
|
package/docs/tool-registry.md
CHANGED
|
@@ -10,16 +10,20 @@ The registry source is location-discovered recipes, not a live tool-only JSON fi
|
|
|
10
10
|
|
|
11
11
|
- `~/.pi/agent/recipes/*.json` and `*.md` are the highest-priority user recipe root and the operator-managed tool set.
|
|
12
12
|
- Recipes in that root are tools by location.
|
|
13
|
-
- `~/.pi/agent/recipes/drafts/*.json` stores captured inline-spawn draft recipes, not registered tools. Promote one with `register_tool name=<tool_name> draft=<draft_path>` or
|
|
13
|
+
- `~/.pi/agent/recipes/drafts/*.json` stores captured inline-spawn draft recipes, not registered tools. Twelve drafts trigger one silent automatic exact-batch review after the foreground turn and active actors finish; the deterministic executor promotes or discards every reviewed source while preserving newer drafts for the next batch. Promote one earlier with the fenced `register_tool name=<tool_name> draft=<draft_path>` override or a deliberate move/copy into the recipe root. A direct filesystem promotion remains legitimate, but it can shrink or invalidate a captured batch and defer its automatic cleanup; lineage reattaches on the next launch when the move remains unambiguous. No manual batch-consolidation command exists. `inspect target=recipes view=summary` reports draft count, and verbose output lists paths, timestamps, fingerprints, validation state, source run when known, descriptions, and template previews.
|
|
14
14
|
- Packaged pi-actors recipes are the lower-priority standard library of declarative actor config components, not automatically registered tools.
|
|
15
15
|
- Ad hoc recipe files outside the user recipe root are components unless explicitly registered/copied into `~/.pi/agent/recipes`.
|
|
16
|
-
-
|
|
16
|
+
- The current tool name is the filename basename; `~/.pi/agent/recipes/docs_review.json` and `docs_review.md` both expose `docs_review`. A canonical name-and-priority lineage ledger preserves usage and revision history as draft/active location changes; controlled rename transfers that history to the new name and retains the former name.
|
|
17
|
+
- Set `PI_ACTORS_AUTOMATIC_REVIEW=off` (also accepts `false` or `0`) before starting Pi to disable draft/tool reviewer scheduling and safe-boundary portfolio activation; runtime status exposes the effective policy.
|
|
18
|
+
- Active user recipes receive zero-call lineage without fabricating launches. Once thirty-six non-sensitive current revisions lack a review fingerprint, pi-actors captures the oldest exact portfolio and silently attaches an identity-opaque value-free structural projection with batch-local equality-only content groups to the no-tools `tool-review` actor after a foreground turn and only while no other actor runs. A content revision resets revision-local usage and becomes eligible again while lifetime usage remains continuous. The reviewer may select only `keep`, unchanged-source rename (`evolve`), unchanged-source `demote`, or `merge` for canonically identical captured recipes; it never returns recipe content. `replace`, `split`, and executable contract changes require explicit operator authoring. Completed output becomes an immutable size-bounded approval plan only after exact result, source-hash, target-collision, and lineage-projection validation; approval itself never mutates active recipes. At the next `session_start`, filesystem commit persists `lineage_pending`, journaled lineage records roll forward, `completed` persists, and only then may quarantine be removed before runtime tool discovery.
|
|
17
19
|
- Same-id JSON shadows Markdown in the same priority layer.
|
|
18
20
|
|
|
19
|
-
Because the user recipe directory is sticky agent muscle memory, runtime launches update
|
|
21
|
+
Because the user recipe directory is sticky agent muscle memory, runtime launches update a stable lineage ledger under `.usage/recipes/<recipe-name>.json` plus a priority-compatible path index rather than rewriting authored recipe files. Launch accounting briefly shares the canonical recipe-root fence used by portfolio activation before taking index/ledger locks; activation therefore cannot quarantine a source between launch authorization and accounting. If activation already changed the loaded source, the stale invocation rejects with a reload-and-retry error instead of executing without usage evidence. `lifetime_calls` and the compatibility `calls` view survive rename, promotion, demotion, and content revision; `revision_calls` restarts only when the executable fingerprint changes. The bounded ledger retains former paths/names, revision ancestry, promotion/demotion events, and review epochs. An unambiguous external rename follows its prior lineage by fingerprint. Because automatic review has not shipped publicly, its inputs, results, admission state, plans, journals, evidence, lineage storage, and snapshots remain unversioned rather than carrying migration branches for discarded internal iterations. Discovery and file-watcher refresh merge ledger usage into inspect summaries. `inspect target=recipes view=summary verbose=true` includes usage metadata and operator-gated cleanup recommendations for invalid, shadowed, disabled, component-only, unused, or overriding recipes. The extension does not maintain a failure counter.
|
|
20
22
|
|
|
21
23
|
`register_tool` is the preferred agent-facing mutation API. It creates, updates, and deletes recipe files in `~/.pi/agent/recipes`; agents do not need to edit the files directly for normal registration. Extension-authored register, update, delete, draft-promotion, and usage-metadata mutations hold a cross-process lock keyed by filesystem-canonical recipe identity across the complete check/read/write/runtime-update window. Existing targets or the nearest existing parent are resolved through `realpath`, so real and symlink aliases serialize while unrelated recipes remain independent; stale locks are reclaimed only after their owner is proven dead. Direct file edits are still valid for operators and advanced agents. Runtime behavior is reactive: file creation, deletion, or edits in the user recipe root trigger validation and tool-set refresh, with invalid recipes surfaced as diagnostics rather than silently ignored. If the recipe root does not exist at session start, an advisory parent watcher detects its creation and switches to the normal root watcher; deletion or rename rearms the parent watcher without polling.
|
|
22
24
|
|
|
25
|
+
Draft-consolidation journals capture root `dev` and `ino` from Node bigint stats and retain them as lossless decimal strings alongside lexical and native-real paths. Some network, virtual, or compatibility filesystems may report weak or zero device/inode identity; those values remain evidence but not a standalone trust claim because recovery also requires unchanged lexical paths, native realpaths, non-reparse directory roots, source/target hashes, and journal CAS. Native Windows regressions use unprivileged NTFS directory junctions to verify canonical mutation/lifecycle locks and fail-closed recovery after draft-root or trusted-root reparse substitution. This evidence supports the current portable process-crash and trusted-state-tree contract; a native handle-relative mutation layer is not justified unless real Windows runs expose a residual substitution window that these independent checks cannot fence.
|
|
26
|
+
|
|
23
27
|
Inspect the loaded pi-actors runtime and discovered registry with:
|
|
24
28
|
|
|
25
29
|
```text
|
|
@@ -27,10 +31,15 @@ inspect target=tool:pi-actors view=status
|
|
|
27
31
|
inspect target=tool:pi-actors view=triage
|
|
28
32
|
inspect target=recipes view=status
|
|
29
33
|
inspect target=recipes view=doctor
|
|
34
|
+
inspect target=recipes view=reviews
|
|
30
35
|
inspect target=recipes view=summary verbose=true
|
|
31
36
|
```
|
|
32
37
|
|
|
33
|
-
`
|
|
38
|
+
`inspect target=recipes view=reviews` returns bounded read-only evidence for automatic draft/tool review phases, decision counts, garbage collection, lineage revisions, demotions, rollback provenance, retained revision snapshots, and bounded `failed_stage`/`last_error`/`next_action` fields. It never starts a review or generates a follow-up turn. Automatic reviewers receive only an attached value-free projection—counts, risk labels, bounded usage, and command-graph shape without recipe bodies, template/default values, authored prose, or filesystem paths—and no general filesystem or mutation tools. Internal snapshot rollback writes one CAS-authenticated journal before changing either recipe or lineage state; interruption after either write rolls forward on the next identical rollback request instead of returning a permanently split recipe/ledger state.
|
|
39
|
+
|
|
40
|
+
Explicit recovery stays inside the existing actor-message surface: send `review.retry` or `review.reset` to `tool:pi-actors` with `body={"scope":"draft"}` or `body={"scope":"tool"}`. Retry resets bounded launch/processing counters and reuses the immutable batch. If a draft transaction journal already exists, retry preserves the original reviewer run and resumes that authenticated journal plan; even changed reviewer stdout cannot redirect committed recipe or lineage targets. When a tool transaction already committed, retry preserves approval/transaction evidence and returns to the safe activation/lineage boundary rather than launching another reviewer. Reset removes only disposable failed/completed admission state and rejects tool cycles that still carry recovery evidence.
|
|
41
|
+
|
|
42
|
+
`tool:pi-actors` is a reserved runtime-status/control actor. `view=status` reports the loaded package version, package root, source/dist mode, entrypoint path, recipe roots, automatic-review policy, and git commit when available. Use it after reloads to confirm which extension code is actually live. `view=triage` adds a compact attention surface for active runs, other-session runs, invalid or blocking recipes, exposed tool recipes with non-lifecycle risk labels, drafts, stale claims, failed runs, attention messages, and next inspect actions without repairing anything. Packaged components and recipes whose only label is `risk.long_running` stay in recipe doctor/summary evidence rather than triage attention.
|
|
34
43
|
|
|
35
44
|
The recipe summary reports active, shadowed, invalid, disabled, and diagnostic entries so operators can answer why a tool is present, hidden, broken, or disabled. The doctor view keeps the same registry evidence but promotes an advisory action surface: compact output includes the highest-priority `top` remediation, risk-label counts, and ordered actions for invalid/blocking, disabled, risky shell-boundary, and shadowed recipes. Verbose inspection keeps per-recipe `risk_labels`, the structured `risk_summary`, `remediations`, `top_action`, diagnostic details, and blocked lower-priority fallback paths when a broken or disabled higher-priority recipe masks a fallback. Risk labels are deterministic review aids, not execution blockers or sandbox claims.
|
|
36
45
|
|
package/index.ts
CHANGED
|
@@ -5,118 +5,32 @@
|
|
|
5
5
|
* Wraps command templates as callable pi tools, stores durable user tools as recipe files, and exposes actor orchestration across reloads and sessions.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import * as
|
|
8
|
+
import * as AutomaticReviewRuntime from "./lib/automatic-review-runtime.ts";
|
|
9
9
|
import * as CommandTemplates from "./lib/command-templates.ts";
|
|
10
|
-
import * as
|
|
11
|
-
import * as Observability from "./lib/observability.ts";
|
|
10
|
+
import * as InspectorCommand from "./lib/inspector-command.ts";
|
|
12
11
|
import * as Paths from "./lib/paths.ts";
|
|
13
12
|
import * as Pi from "./lib/pi.ts";
|
|
14
13
|
import * as Prompts from "./lib/prompts.ts";
|
|
14
|
+
import * as RunUiRuntime from "./lib/run-ui-runtime.ts";
|
|
15
15
|
import * as Runtime from "./lib/runtime.ts";
|
|
16
16
|
import * as Temp from "./lib/temp.ts";
|
|
17
17
|
import * as Tools from "./lib/tools.ts";
|
|
18
18
|
import * as ToolsResponse from "./lib/tools-response.ts";
|
|
19
19
|
|
|
20
20
|
export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
|
|
21
|
-
let runsAnimationInterval: NodeJS.Timeout | undefined;
|
|
22
|
-
let runsNotifyTimeout: NodeJS.Timeout | undefined;
|
|
23
21
|
let activeRunContext: Pi.ExtensionContext | undefined;
|
|
24
|
-
let lastRunWatcherDiagnosticId = 0;
|
|
25
|
-
const runUi = Observability.createRunUiObservationState();
|
|
26
|
-
const retirementAttempts = new Set<string>();
|
|
27
|
-
const terminalNotificationsInFlight = new Set<string>();
|
|
28
22
|
const getRunOwnerId = Pi.getSessionId;
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
const updateRunUi = (
|
|
42
|
-
ctx: Pi.ExtensionContext,
|
|
43
|
-
notify = false,
|
|
44
|
-
terminalOnly = false,
|
|
45
|
-
): void => {
|
|
46
|
-
const ownerId = getRunOwnerId(ctx);
|
|
47
|
-
const snapshot = Observability.readRunUiSnapshot(runUi, ownerId);
|
|
48
|
-
ctx.ui.setStatus(
|
|
49
|
-
"zz-pi-actors-runs",
|
|
50
|
-
snapshot.status ? ctx.ui.theme.fg("dim", snapshot.status) : undefined,
|
|
51
|
-
);
|
|
52
|
-
if (!notify) return;
|
|
53
|
-
const notificationSink = Pi.createNotificationSink(pi, ctx);
|
|
54
|
-
retireCandidateRuns(ctx, snapshot.summary);
|
|
55
|
-
Observability.deliverRunTransitionNotifications(
|
|
56
|
-
snapshot.transitions,
|
|
57
|
-
notificationSink,
|
|
58
|
-
terminalNotificationsInFlight,
|
|
59
|
-
);
|
|
60
|
-
Observability.pruneRunUiObservationState(runUi, snapshot);
|
|
61
|
-
if (!terminalOnly) {
|
|
62
|
-
Observability.deliverRunOutboxNotifications(
|
|
63
|
-
snapshot.outboxEvents,
|
|
64
|
-
notificationSink,
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const closeRunWatchers = (): void => {
|
|
69
|
-
runWatcher.close();
|
|
70
|
-
terminalReconciliation.close();
|
|
71
|
-
if (runsNotifyTimeout) clearTimeout(runsNotifyTimeout);
|
|
72
|
-
runsNotifyTimeout = undefined;
|
|
73
|
-
};
|
|
74
|
-
const reportRunWatcherDiagnostics = (ctx: Pi.ExtensionContext): void => {
|
|
75
|
-
for (const diagnostic of runWatcher.getDiagnostics()) {
|
|
76
|
-
if (diagnostic.id <= lastRunWatcherDiagnosticId) continue;
|
|
77
|
-
lastRunWatcherDiagnosticId = diagnostic.id;
|
|
78
|
-
ctx.ui.notify(
|
|
79
|
-
diagnostic.message,
|
|
80
|
-
diagnostic.code === "rearmed" ? "info" : "warning",
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
const scheduleRunEventUpdate = (): void => {
|
|
85
|
-
if (runsNotifyTimeout) clearTimeout(runsNotifyTimeout);
|
|
86
|
-
runsNotifyTimeout = setTimeout(() => {
|
|
87
|
-
const ctx = activeRunContext;
|
|
88
|
-
if (!ctx) return;
|
|
89
|
-
runWatcher.refresh();
|
|
90
|
-
updateRunUi(ctx, true);
|
|
91
|
-
reportRunWatcherDiagnostics(ctx);
|
|
92
|
-
}, 50);
|
|
93
|
-
runsNotifyTimeout.unref?.();
|
|
94
|
-
};
|
|
95
|
-
const runWatcher = Observability.createRunStateWatcher({
|
|
96
|
-
stateRoot: Paths.EXTENSION_RUNTIME_PATHS.runStateRoot,
|
|
97
|
-
onChange: scheduleRunEventUpdate,
|
|
23
|
+
const automaticReview = AutomaticReviewRuntime.createAutomaticReviewRuntime({
|
|
24
|
+
getActiveContext: () => activeRunContext,
|
|
25
|
+
getRunOwnerId,
|
|
26
|
+
getThinkingLevel: () => pi.getThinkingLevel(),
|
|
27
|
+
});
|
|
28
|
+
const runUiRuntime = RunUiRuntime.createRunUiRuntime({
|
|
29
|
+
getActiveContext: () => activeRunContext,
|
|
30
|
+
getRunOwnerId,
|
|
31
|
+
onRunEvent: automaticReview.schedule,
|
|
32
|
+
pi,
|
|
98
33
|
});
|
|
99
|
-
const terminalReconciliation =
|
|
100
|
-
Observability.createRunTerminalReconciliationLoop({
|
|
101
|
-
onError: (error) => {
|
|
102
|
-
const ctx = activeRunContext;
|
|
103
|
-
if (!ctx) return;
|
|
104
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
-
ctx.ui.notify(`Actor terminal reconciliation failed: ${message}`, "error");
|
|
106
|
-
},
|
|
107
|
-
reconcile: () => {
|
|
108
|
-
const ctx = activeRunContext;
|
|
109
|
-
if (!ctx) return;
|
|
110
|
-
Observability.reconcileRunTerminalNotifications({
|
|
111
|
-
inFlight: terminalNotificationsInFlight,
|
|
112
|
-
ownerId: getRunOwnerId(ctx),
|
|
113
|
-
sink: Pi.createNotificationSink(pi, ctx),
|
|
114
|
-
state: runUi,
|
|
115
|
-
});
|
|
116
|
-
reportRunWatcherDiagnostics(ctx);
|
|
117
|
-
},
|
|
118
|
-
refreshWatcher: () => runWatcher.refresh(),
|
|
119
|
-
});
|
|
120
34
|
const actorToolDefinitions = new Map<string, Tools.ActorToolDefinition>();
|
|
121
35
|
const withCurrentThinkingContext = <T extends Tools.ActorToolDefinition>(
|
|
122
36
|
definition: T,
|
|
@@ -164,54 +78,26 @@ export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
|
|
|
164
78
|
// Clear the pre-overlay widget after hot reloads from older pi-actors builds.
|
|
165
79
|
ctx.ui.setWidget("zz-pi-actors-comms", undefined);
|
|
166
80
|
activeRunContext = ctx;
|
|
167
|
-
|
|
81
|
+
runUiRuntime.close();
|
|
82
|
+
automaticReview.close();
|
|
168
83
|
recipeReload.close();
|
|
169
84
|
await Temp.prepareExtensionTempDir(Paths.EXTENSION_RUNTIME_PATHS.tempDir);
|
|
170
85
|
if (activeRunContext !== ctx) return;
|
|
86
|
+
automaticReview.start(ctx);
|
|
171
87
|
runtime.loadTools(ctx);
|
|
172
|
-
|
|
173
|
-
runWatcher.refresh();
|
|
174
|
-
terminalReconciliation.start();
|
|
88
|
+
runUiRuntime.start(ctx);
|
|
175
89
|
recipeReload.watch(ctx);
|
|
176
|
-
if (runsAnimationInterval) clearInterval(runsAnimationInterval);
|
|
177
|
-
runsAnimationInterval = setInterval(() => {
|
|
178
|
-
if (activeRunContext === ctx) updateRunUi(ctx, false);
|
|
179
|
-
}, 1000);
|
|
180
|
-
runsAnimationInterval.unref?.();
|
|
181
90
|
});
|
|
182
|
-
pi.on("
|
|
183
|
-
if (
|
|
184
|
-
|
|
91
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
92
|
+
if (activeRunContext === ctx) automaticReview.schedule();
|
|
93
|
+
});
|
|
94
|
+
pi.on("session_shutdown", async (event, ctx) => {
|
|
185
95
|
activeRunContext = undefined;
|
|
186
|
-
|
|
96
|
+
automaticReview.close();
|
|
187
97
|
recipeReload.close();
|
|
98
|
+
runUiRuntime.shutdown(event.reason, ctx);
|
|
188
99
|
});
|
|
189
|
-
|
|
190
|
-
description: "Open the keyboard-driven actor inspector overlay",
|
|
191
|
-
handler: async (_args, ctx) => {
|
|
192
|
-
ctx.ui.setWidget("zz-pi-actors-comms", undefined);
|
|
193
|
-
await ctx.ui.custom<void>(
|
|
194
|
-
(tui, theme, _keybindings, done) =>
|
|
195
|
-
new InspectorOverlay.ActorInspectorOverlay({
|
|
196
|
-
done,
|
|
197
|
-
ownerId: getRunOwnerId(ctx),
|
|
198
|
-
stateRoot: Paths.EXTENSION_RUNTIME_PATHS.runStateRoot,
|
|
199
|
-
theme,
|
|
200
|
-
tui,
|
|
201
|
-
}),
|
|
202
|
-
{
|
|
203
|
-
overlay: true,
|
|
204
|
-
overlayOptions: {
|
|
205
|
-
anchor: "center",
|
|
206
|
-
width: "94%",
|
|
207
|
-
minWidth: 72,
|
|
208
|
-
maxHeight: "94%",
|
|
209
|
-
margin: 1,
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
);
|
|
213
|
-
},
|
|
214
|
-
});
|
|
100
|
+
InspectorCommand.registerActorInspectorCommand(pi, getRunOwnerId);
|
|
215
101
|
pi.on("before_agent_start", async (event) => ({
|
|
216
102
|
systemPrompt: `${event.systemPrompt}\n\n${Prompts.ONBOARDING_SYSTEM_PROMPT}`,
|
|
217
103
|
}));
|
|
@@ -221,11 +107,10 @@ export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
|
|
|
221
107
|
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
222
108
|
getActiveTools: () => pi.getActiveTools(),
|
|
223
109
|
getRuntimeTool: (name) =>
|
|
224
|
-
Tools.resolveActiveRuntimeTool(
|
|
225
|
-
|
|
226
|
-
runtime.getTools(),
|
|
227
|
-
(activeName) => actorToolDefinitions.get(activeName),
|
|
110
|
+
Tools.resolveActiveRuntimeTool(name, runtime.getTools(), (activeName) =>
|
|
111
|
+
actorToolDefinitions.get(activeName),
|
|
228
112
|
),
|
|
113
|
+
handleRuntimeMessage: automaticReview.handleMessage,
|
|
229
114
|
registryRuntime: runtime,
|
|
230
115
|
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
231
116
|
}).map(withCurrentThinkingContext),
|