@llblab/pi-actors 0.30.1 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +13 -0
- package/README.md +20 -1
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +3 -3
- package/dist/lib/tools.js +24 -7
- package/dist/scripts/music-player.mjs +0 -1
- package/dist/skills/actors/SKILL.md +6 -3
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/async-runs.md +3 -1
- package/docs/recipe-library.md +1 -1
- package/lib/prompts.ts +3 -3
- package/lib/tools.ts +24 -7
- package/package.json +1 -1
- package/scripts/music-player.mjs +0 -1
- package/skills/actors/SKILL.md +6 -3
- package/skills/swarm/SKILL.md +1 -1
package/AGENTS.md
CHANGED
|
@@ -129,6 +129,8 @@ Pi host
|
|
|
129
129
|
- Preserve JSON envelope object shape across handoffs.
|
|
130
130
|
- Keep locker state generic and thin; orchestration strategy belongs in the coordinator.
|
|
131
131
|
- Graceful actor retirement is opt-in through recipe/run metadata and must not infer retirement for persistent services or backlog implementers.
|
|
132
|
+
- Script helpers that spawn long-lived child processes should keep those children inside the async run's owned process group unless they also provide an explicit termination bridge; `control.kill` must not leave detached playback/service descendants alive.
|
|
133
|
+
- True daemon recipes are allowed, but daemon ownership belongs to the recipe/script contract: persist a pid or service handle, verify ownership before signaling, expose status/stop semantics, and bridge `control.kill` to daemon cleanup instead of relying on the generic runner to discover detached services.
|
|
132
134
|
|
|
133
135
|
## Context And Planning Hygiene
|
|
134
136
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.31.0: Agent Adoption Ergonomics
|
|
6
|
+
|
|
7
|
+
- `[Adoption]` Added a compact actor-mode trigger rule to the injected prompt, actors skill, README, and async-run docs so models prefer `spawn → message → inspect` for long-lived, stateful, follow-up, artifact, service, fanout, and resumable work while keeping short foreground checks as ordinary tools.
|
|
8
|
+
- `[Skills]` Reframed the bundled actors skill as required practical guidance for non-trivial actor use or pi-actors changes, and made the injected prompt route agents to it before improvising actor workflows.
|
|
9
|
+
- `[Tools]` Strengthened `spawn`, `message`, and `inspect` descriptions with adoption cues that steer models away from ad hoc shell backgrounding, actor restarts, and polling loops.
|
|
10
|
+
- `[Spawn]` Added explicit next-action feedback to spawn results so newly created actors immediately suggest inspect/message chains instead of leaving agents to infer the next step.
|
|
11
|
+
- `[Docs]` Added a task-first “when to use actors” golden path to the README without adding public verbs or scheduler/service-manager concepts.
|
|
12
|
+
|
|
13
|
+
## 0.30.2: Music Player Kill Hotfix
|
|
14
|
+
|
|
15
|
+
- `[Music Player]` Kept backend player processes inside the async run process group so `control.kill` can terminate an active music-player run without leaving detached `cvlc`/player children alive.
|
|
16
|
+
- `[Docs]` Updated durable project guidance, actor skill guidance, async-run ownership docs, and recipe-library music-player notes to preserve the run-owned process-tree invariant while still allowing true daemon recipes through explicit termination bridges.
|
|
17
|
+
|
|
5
18
|
## 0.30.1: Backlog Curation Hotfix
|
|
6
19
|
|
|
7
20
|
- `[Backlog]` Added curation rules clarifying that completed work belongs only in the changelog, that cohesive ~1000-line domain files are acceptable, and that file splitting should follow real ownership boundaries rather than line count alone.
|
package/README.md
CHANGED
|
@@ -29,7 +29,26 @@ inspect intentionally read state, logs, messages, contracts, or artifacts
|
|
|
29
29
|
|
|
30
30
|
Everything else is an adapter until proven otherwise.
|
|
31
31
|
|
|
32
|
-
Use `spawn` when work may outlive the current turn. Use `message` when the actor should be steered rather than restarted. Use `inspect` at decision points, after actor follow-ups, or during diagnosis. Do not build polling loops as the default coordination pattern.
|
|
32
|
+
Use `spawn` when work may outlive the current turn. Use `message` when the actor should be steered rather than restarted. Use `inspect` at decision points, after actor follow-ups, or during diagnosis. For non-trivial actor use, load the bundled `actors` skill before improvising. Do not build polling loops as the default coordination pattern.
|
|
33
|
+
|
|
34
|
+
## When To Use Actors
|
|
35
|
+
|
|
36
|
+
Use actor-mode instead of ad hoc shell backgrounding when work is:
|
|
37
|
+
|
|
38
|
+
- Long-running or likely to outlive this agent turn.
|
|
39
|
+
- Stateful, resumable, or something you will need to inspect later.
|
|
40
|
+
- Expected to produce named artifacts or follow-up messages.
|
|
41
|
+
- A service, worker, media process, fanout, subagent, or pipeline.
|
|
42
|
+
- A repeatable local capability worth promoting into recipe memory.
|
|
43
|
+
|
|
44
|
+
Keep ordinary foreground tools for short checks such as `rg`, `ls`, quick tests, and one-shot transforms. The golden path is:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
create actor -> spawn
|
|
48
|
+
steer actor -> message
|
|
49
|
+
read state/results -> inspect
|
|
50
|
+
repeatable pattern -> promote to recipe/tool memory
|
|
51
|
+
```
|
|
33
52
|
|
|
34
53
|
## Install
|
|
35
54
|
|
package/dist/lib/prompts.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export declare const REGISTER_TOOL_DESCRIPTION: string;
|
|
7
7
|
export declare const REGISTER_TOOL_PROMPT_SNIPPET = "Register persistent command templates as agent-callable tools";
|
|
8
8
|
export declare const REGISTER_TOOL_GUIDELINES: string[];
|
|
9
|
-
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync: string leaf, array sequence, object node; flags include args/defaults, parallel, when, timeout, delay, retry, failure, recover, repeat, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.\n- Recipes own template directly and may declare metadata/defaults/imports/mailbox/artifacts; files >1 MiB or import depth >32 fail closed.\n- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.\n- Use spawn/message/inspect for actor-level start/send/observe; avoid runtime/FIFO/outbox vocabulary in public guidance.\n- Run state lives under ~/.pi/agent/tmp/pi-actors/runs; inspect status/tail/messages/mailbox/files/artifacts intentionally and avoid busy-polling.\n- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; packaged/ad hoc recipes are lower-priority components; offer to save successful recurring patterns only after confirmation.\n-
|
|
9
|
+
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync: string leaf, array sequence, object node; flags include args/defaults, parallel, when, timeout, delay, retry, failure, recover, repeat, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.\n- Recipes own template directly and may declare metadata/defaults/imports/mailbox/artifacts; files >1 MiB or import depth >32 fail closed.\n- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.\n- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.\n- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid runtime/FIFO/outbox vocabulary in public guidance.\n- Run state lives under ~/.pi/agent/tmp/pi-actors/runs; inspect status/tail/messages/mailbox/files/artifacts intentionally and avoid busy-polling.\n- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; packaged/ad hoc recipes are lower-priority components; offer to save successful recurring patterns only after confirmation.\n- Long fanout = parent async recipe wrapping template(parallel:true) and imports; packaged fanout recipes bubble branch completion messages; grow recurring multi-agent workflows as packaged recipes/pipelines, not ad hoc external scripts.\n- For any non-trivial actor use or pi-actors change, read the bundled actors skill first; for deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.";
|
|
10
10
|
export declare const REGISTER_TOOL_PARAM_DESCRIPTIONS: {
|
|
11
11
|
readonly name: "Tool name in snake_case (e.g., 'transcribe')";
|
|
12
12
|
readonly description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.";
|
package/dist/lib/prompts.js
CHANGED
|
@@ -21,12 +21,12 @@ export const ONBOARDING_SYSTEM_PROMPT = `pi-actors quick model:
|
|
|
21
21
|
- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.
|
|
22
22
|
- Recipes own template directly and may declare metadata/defaults/imports/mailbox/artifacts; files >1 MiB or import depth >32 fail closed.
|
|
23
23
|
- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.
|
|
24
|
-
-
|
|
24
|
+
- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.
|
|
25
|
+
- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid runtime/FIFO/outbox vocabulary in public guidance.
|
|
25
26
|
- Run state lives under ~/.pi/agent/tmp/pi-actors/runs; inspect status/tail/messages/mailbox/files/artifacts intentionally and avoid busy-polling.
|
|
26
27
|
- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; packaged/ad hoc recipes are lower-priority components; offer to save successful recurring patterns only after confirmation.
|
|
27
|
-
- Foreground tools/templates fit short work; async recipes/runs fit subagents, services, fanout, media, and long pipelines.
|
|
28
28
|
- Long fanout = parent async recipe wrapping template(parallel:true) and imports; packaged fanout recipes bubble branch completion messages; grow recurring multi-agent workflows as packaged recipes/pipelines, not ad hoc external scripts.
|
|
29
|
-
- For
|
|
29
|
+
- For any non-trivial actor use or pi-actors change, read the bundled actors skill first; for deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.`;
|
|
30
30
|
export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
|
|
31
31
|
name: "Tool name in snake_case (e.g., 'transcribe')",
|
|
32
32
|
description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.",
|
package/dist/lib/tools.js
CHANGED
|
@@ -128,12 +128,23 @@ function asRecord(value) {
|
|
|
128
128
|
function formatFailureCount(value) {
|
|
129
129
|
return Array.isArray(value) ? value.length : undefined;
|
|
130
130
|
}
|
|
131
|
+
function actorRunNextActions(run) {
|
|
132
|
+
const id = String(run ?? "").trim();
|
|
133
|
+
if (!id)
|
|
134
|
+
return [];
|
|
135
|
+
return [
|
|
136
|
+
`inspect target=run:${id} view=status`,
|
|
137
|
+
`inspect target=run:${id} view=messages`,
|
|
138
|
+
`message to=run:${id} type=<actor.action>`,
|
|
139
|
+
];
|
|
140
|
+
}
|
|
131
141
|
function compactAsyncRunStatus(value) {
|
|
132
142
|
const status = asRecord(value);
|
|
133
143
|
const progress = asRecord(status.progress);
|
|
134
144
|
const result = asRecord(status.result);
|
|
145
|
+
const run = String(status.run ?? "<unknown>");
|
|
135
146
|
const tokens = [
|
|
136
|
-
`run=${
|
|
147
|
+
`run=${run}`,
|
|
137
148
|
`status=${String(status.status ?? "unknown")}`,
|
|
138
149
|
];
|
|
139
150
|
if (status.tool)
|
|
@@ -159,6 +170,9 @@ function compactAsyncRunStatus(value) {
|
|
|
159
170
|
tokens.push("killed=true");
|
|
160
171
|
if (status.candidate_recipe)
|
|
161
172
|
tokens.push(`candidate_recipe=${String(status.candidate_recipe)}`);
|
|
173
|
+
const nextActions = actorRunNextActions(run);
|
|
174
|
+
if (nextActions.length > 0)
|
|
175
|
+
tokens.push(`next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`);
|
|
162
176
|
return `\n${tokens.join(" ")}`;
|
|
163
177
|
}
|
|
164
178
|
function compactRunMessages(messages) {
|
|
@@ -720,7 +734,7 @@ export function createSpawnToolDefinition() {
|
|
|
720
734
|
return {
|
|
721
735
|
name: "spawn",
|
|
722
736
|
label: "Spawn",
|
|
723
|
-
description: "Create an addressable actor from a recipe file or inline command template. Currently spawns run:<id> actors backed by async runs.",
|
|
737
|
+
description: "Create an addressable actor from a recipe file or inline command template. Use instead of ad hoc shell backgrounding for work that may outlive this turn, needs steering/follow-up/artifacts, runs as a service, fans out, or should be inspected later. Currently spawns run:<id> actors backed by async runs.",
|
|
724
738
|
parameters: objectSchema({
|
|
725
739
|
artifacts: looseObjectSchema("Optional named artifact paths for the spawned actor."),
|
|
726
740
|
as: stringSchema("Optional actor address for the spawned run, e.g. run:<id>."),
|
|
@@ -770,9 +784,12 @@ export function createSpawnToolDefinition() {
|
|
|
770
784
|
throw enhanceSpawnRecipeError(error, recipe);
|
|
771
785
|
}
|
|
772
786
|
const candidateRecipe = writeSpawnCandidateRecipe(input, meta);
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
787
|
+
const nextActions = actorRunNextActions(meta.run);
|
|
788
|
+
const details = {
|
|
789
|
+
...meta,
|
|
790
|
+
...(candidateRecipe ? { candidate_recipe: candidateRecipe } : {}),
|
|
791
|
+
next_actions: nextActions,
|
|
792
|
+
};
|
|
776
793
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
777
794
|
ActorRooms.writeCommunicationSnapshot(meta.state_dir, String(meta.run));
|
|
778
795
|
return {
|
|
@@ -818,7 +835,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
818
835
|
return {
|
|
819
836
|
name: "inspect",
|
|
820
837
|
label: "Inspect",
|
|
821
|
-
description: "Intentionally inspect an actor. Supports run:<id> views: status, tail, messages, artifacts, files, mailbox, communication; room:<run> status/messages/previews/roster/contacts; coordinator/session status; and tool:<name> status/schema.",
|
|
838
|
+
description: "Intentionally inspect an actor at decision points, after follow-ups, or during diagnosis instead of polling. Supports run:<id> views: status, tail, messages, artifacts, files, mailbox, communication; room:<run> status/messages/previews/roster/contacts; coordinator/session status; and tool:<name> status/schema.",
|
|
822
839
|
parameters: objectSchema({
|
|
823
840
|
lines: stringSchema("Line count for tail/messages views. Default 40."),
|
|
824
841
|
status: stringSchema("Optional session run filter: all, running, active, terminal, done, failed, cancelled, killed, or exited."),
|
|
@@ -1122,7 +1139,7 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
1122
1139
|
return {
|
|
1123
1140
|
name: "message",
|
|
1124
1141
|
label: "Message",
|
|
1125
|
-
description: "Send one typed addressed message. Routes to run:<id> mailboxes, branch:<run>/<branch> mailboxes, room:<run> timelines/rosters, tool:<name> calls, and coordinator/session-bound run messages.",
|
|
1142
|
+
description: "Send one typed addressed message to steer an existing actor instead of restarting it. Routes to run:<id> mailboxes, branch:<run>/<branch> mailboxes, room:<run> timelines/rosters, tool:<name> calls, and coordinator/session-bound run messages.",
|
|
1126
1143
|
parameters: objectSchema({
|
|
1127
1144
|
body: unionSchema([
|
|
1128
1145
|
stringSchema("Message body. For run:<id>, this is the run-local command line."),
|
|
@@ -804,7 +804,6 @@ function playOne(ctx, player, volume, track, index, count) {
|
|
|
804
804
|
const [command, args] = playerCommand(ctx, player, volume, track);
|
|
805
805
|
writeStatus(ctx, "playing", index, count, track, player, "");
|
|
806
806
|
const child = spawn(command, args, {
|
|
807
|
-
detached: process.platform !== "win32",
|
|
808
807
|
stdio: ["ignore", "inherit", "inherit"],
|
|
809
808
|
});
|
|
810
809
|
ctx.child = child;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: actors
|
|
3
|
-
description:
|
|
3
|
+
description: Required practical guide for non-trivial pi-actors use. 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.31.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
9
9
|
|
|
10
|
-
`pi-actors` turns trusted local capabilities into addressable actors. This skill is the
|
|
10
|
+
`pi-actors` turns trusted local capabilities into addressable actors. This skill is the required practical layer for any non-trivial pi-actors use or repo change: tools, nouns, lifecycle, message protocol, recipes, and common edge cases. It is not a multi-agent strategy guide; use a swarm skill for decomposition, quorum design, reviewer lenses, and consensus methodology.
|
|
11
11
|
|
|
12
12
|
Maintain this skill as the extension's agent-facing manual. When implementation changes reveal new durable mechanics, invariants, warnings, or safer operating patterns, update this skill alongside code/docs so future agents learn the current actor model instead of rediscovering it.
|
|
13
13
|
|
|
@@ -48,6 +48,8 @@ Trusted local capability
|
|
|
48
48
|
|
|
49
49
|
## Three Verbs
|
|
50
50
|
|
|
51
|
+
Actor-mode trigger: if work may outlive this turn, needs steering/follow-up/artifacts, runs as a service, fans out, or should be resumed/inspected later, use `spawn → message → inspect` instead of ad hoc shell backgrounding. Keep short foreground checks as normal tools.
|
|
52
|
+
|
|
51
53
|
### `spawn` — create a run actor
|
|
52
54
|
|
|
53
55
|
Use for long work, background services, subagents, fanout, pipelines, and reusable recipes.
|
|
@@ -87,6 +89,7 @@ Envelope fields:
|
|
|
87
89
|
- Addresses: `run:<id>`, `branch:<run>/<branch>`, `room:<run>`, `tool:<name>`, `coordinator`, `session:<id>`.
|
|
88
90
|
- Room posts require `from` from the same run (`run:<run>` or `branch:<run>/<branch>`).
|
|
89
91
|
- 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`.
|
|
92
|
+
- 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`.
|
|
90
93
|
|
|
91
94
|
Check `inspect view=mailbox` before domain-specific messages.
|
|
92
95
|
|
package/docs/async-runs.md
CHANGED
|
@@ -6,6 +6,8 @@ Async runs are detached executions of a template recipe or inline command templa
|
|
|
6
6
|
|
|
7
7
|
**Scope:** run id, state path, runner pid, process-group cancellation, logs, status, tail, list, script-authored actor messages, run-local control messages, cancel, force-kill, terminal result state, ambient activity indicators, and extension-owned temp storage. No scheduler, queue daemon, workflow DSL, distributed worker, or second execution language.
|
|
8
8
|
|
|
9
|
+
Actor-mode trigger: choose an async run when work may outlive the current turn, needs later steering or inspection, produces artifacts/follow-ups, runs as a service, fans out, or should become repeatable recipe memory. Keep short foreground checks in ordinary tools/templates.
|
|
10
|
+
|
|
9
11
|
Layer boundary: async-run configuration may inject lifecycle values such as `{run_id}` and `{state_dir}` and may choose detached execution through `async: true`, but it does not add command-template graph syntax. Recipe imports and recipe-local references belong to the template-recipe layer; status, control messages, actor messages, cancel, and kill belong to the async-run layer.
|
|
10
12
|
|
|
11
13
|
---
|
|
@@ -233,7 +235,7 @@ Use coordinator/session-bound messages for completion and decision points, not f
|
|
|
233
235
|
|
|
234
236
|
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.
|
|
235
237
|
|
|
236
|
-
On Unix-like systems, `control.kill` signals the runner process group when available, then falls back to the runner pid. 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. 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.
|
|
238
|
+
On Unix-like systems, `control.kill` signals the runner process group when available, then falls back to the runner pid. 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.
|
|
237
239
|
|
|
238
240
|
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.
|
|
239
241
|
|
package/docs/recipe-library.md
CHANGED
|
@@ -185,7 +185,7 @@ The wrapper also accepts control commands directly when a caller already has the
|
|
|
185
185
|
scripts/music-player.mjs next ~/.pi/agent/tmp/pi-actors/runs/music
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
-
Message body is queued in the recipe's run-local mailbox and reconciled by the player loop. The loop treats `wake.jsonl` and `fs.watch` as advisory signals, then verifies the durable inbox signature before taking the inbox lock so unchanged mailboxes are not reread on every tick.
|
|
188
|
+
Message body is queued in the recipe's run-local mailbox and reconciled by the player loop. The loop treats `wake.jsonl` and `fs.watch` as advisory signals, then verifies the durable inbox signature before taking the inbox lock so unchanged mailboxes are not reread on every tick. Backend players stay inside the async run process group so `control.kill` terminates active playback with the run instead of leaving detached player children alive; player-local pause/resume/next/stop controls still signal the current backend pid or process group when available. The script writes `status.txt`, `player.json`, and track-change actor messages in the same state dir. Track-change messages stay diagnostic by default; interactive recipes should define a small command vocabulary for addressed messages, emit semantic actor messages for decision points, and let the coordinator react to messages rather than sleep-polling state.
|
|
189
189
|
|
|
190
190
|
Cross-platform smoke checklist:
|
|
191
191
|
|
package/lib/prompts.ts
CHANGED
|
@@ -27,12 +27,12 @@ export const ONBOARDING_SYSTEM_PROMPT = `pi-actors quick model:
|
|
|
27
27
|
- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.
|
|
28
28
|
- Recipes own template directly and may declare metadata/defaults/imports/mailbox/artifacts; files >1 MiB or import depth >32 fail closed.
|
|
29
29
|
- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.
|
|
30
|
-
-
|
|
30
|
+
- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.
|
|
31
|
+
- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid runtime/FIFO/outbox vocabulary in public guidance.
|
|
31
32
|
- Run state lives under ~/.pi/agent/tmp/pi-actors/runs; inspect status/tail/messages/mailbox/files/artifacts intentionally and avoid busy-polling.
|
|
32
33
|
- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; packaged/ad hoc recipes are lower-priority components; offer to save successful recurring patterns only after confirmation.
|
|
33
|
-
- Foreground tools/templates fit short work; async recipes/runs fit subagents, services, fanout, media, and long pipelines.
|
|
34
34
|
- Long fanout = parent async recipe wrapping template(parallel:true) and imports; packaged fanout recipes bubble branch completion messages; grow recurring multi-agent workflows as packaged recipes/pipelines, not ad hoc external scripts.
|
|
35
|
-
- For
|
|
35
|
+
- For any non-trivial actor use or pi-actors change, read the bundled actors skill first; for deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.`;
|
|
36
36
|
|
|
37
37
|
export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
|
|
38
38
|
name: "Tool name in snake_case (e.g., 'transcribe')",
|
package/lib/tools.ts
CHANGED
|
@@ -187,12 +187,23 @@ function formatFailureCount(value: unknown): number | undefined {
|
|
|
187
187
|
return Array.isArray(value) ? value.length : undefined;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
function actorRunNextActions(run: unknown): string[] {
|
|
191
|
+
const id = String(run ?? "").trim();
|
|
192
|
+
if (!id) return [];
|
|
193
|
+
return [
|
|
194
|
+
`inspect target=run:${id} view=status`,
|
|
195
|
+
`inspect target=run:${id} view=messages`,
|
|
196
|
+
`message to=run:${id} type=<actor.action>`,
|
|
197
|
+
];
|
|
198
|
+
}
|
|
199
|
+
|
|
190
200
|
function compactAsyncRunStatus(value: unknown): string {
|
|
191
201
|
const status = asRecord(value);
|
|
192
202
|
const progress = asRecord(status.progress);
|
|
193
203
|
const result = asRecord(status.result);
|
|
204
|
+
const run = String(status.run ?? "<unknown>");
|
|
194
205
|
const tokens = [
|
|
195
|
-
`run=${
|
|
206
|
+
`run=${run}`,
|
|
196
207
|
`status=${String(status.status ?? "unknown")}`,
|
|
197
208
|
];
|
|
198
209
|
if (status.tool) tokens.push(`tool=${String(status.tool)}`);
|
|
@@ -213,6 +224,9 @@ function compactAsyncRunStatus(value: unknown): string {
|
|
|
213
224
|
if (result.killed === true) tokens.push("killed=true");
|
|
214
225
|
if (status.candidate_recipe)
|
|
215
226
|
tokens.push(`candidate_recipe=${String(status.candidate_recipe)}`);
|
|
227
|
+
const nextActions = actorRunNextActions(run);
|
|
228
|
+
if (nextActions.length > 0)
|
|
229
|
+
tokens.push(`next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`);
|
|
216
230
|
return `\n${tokens.join(" ")}`;
|
|
217
231
|
}
|
|
218
232
|
|
|
@@ -942,7 +956,7 @@ export function createSpawnToolDefinition<
|
|
|
942
956
|
name: "spawn",
|
|
943
957
|
label: "Spawn",
|
|
944
958
|
description:
|
|
945
|
-
"Create an addressable actor from a recipe file or inline command template. Currently spawns run:<id> actors backed by async runs.",
|
|
959
|
+
"Create an addressable actor from a recipe file or inline command template. Use instead of ad hoc shell backgrounding for work that may outlive this turn, needs steering/follow-up/artifacts, runs as a service, fans out, or should be inspected later. Currently spawns run:<id> actors backed by async runs.",
|
|
946
960
|
parameters: objectSchema(
|
|
947
961
|
{
|
|
948
962
|
artifacts: looseObjectSchema(
|
|
@@ -1023,9 +1037,12 @@ export function createSpawnToolDefinition<
|
|
|
1023
1037
|
throw enhanceSpawnRecipeError(error, recipe);
|
|
1024
1038
|
}
|
|
1025
1039
|
const candidateRecipe = writeSpawnCandidateRecipe(input, meta);
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1028
|
-
|
|
1040
|
+
const nextActions = actorRunNextActions(meta.run);
|
|
1041
|
+
const details = {
|
|
1042
|
+
...meta,
|
|
1043
|
+
...(candidateRecipe ? { candidate_recipe: candidateRecipe } : {}),
|
|
1044
|
+
next_actions: nextActions,
|
|
1045
|
+
};
|
|
1029
1046
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
1030
1047
|
ActorRooms.writeCommunicationSnapshot(meta.state_dir, String(meta.run));
|
|
1031
1048
|
return {
|
|
@@ -1103,7 +1120,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1103
1120
|
name: "inspect",
|
|
1104
1121
|
label: "Inspect",
|
|
1105
1122
|
description:
|
|
1106
|
-
"Intentionally inspect an actor. Supports run:<id> views: status, tail, messages, artifacts, files, mailbox, communication; room:<run> status/messages/previews/roster/contacts; coordinator/session status; and tool:<name> status/schema.",
|
|
1123
|
+
"Intentionally inspect an actor at decision points, after follow-ups, or during diagnosis instead of polling. Supports run:<id> views: status, tail, messages, artifacts, files, mailbox, communication; room:<run> status/messages/previews/roster/contacts; coordinator/session status; and tool:<name> status/schema.",
|
|
1107
1124
|
parameters: objectSchema(
|
|
1108
1125
|
{
|
|
1109
1126
|
lines: stringSchema("Line count for tail/messages views. Default 40."),
|
|
@@ -1557,7 +1574,7 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1557
1574
|
name: "message",
|
|
1558
1575
|
label: "Message",
|
|
1559
1576
|
description:
|
|
1560
|
-
"Send one typed addressed message. Routes to run:<id> mailboxes, branch:<run>/<branch> mailboxes, room:<run> timelines/rosters, tool:<name> calls, and coordinator/session-bound run messages.",
|
|
1577
|
+
"Send one typed addressed message to steer an existing actor instead of restarting it. Routes to run:<id> mailboxes, branch:<run>/<branch> mailboxes, room:<run> timelines/rosters, tool:<name> calls, and coordinator/session-bound run messages.",
|
|
1561
1578
|
parameters: objectSchema(
|
|
1562
1579
|
{
|
|
1563
1580
|
body: unionSchema([
|
package/package.json
CHANGED
package/scripts/music-player.mjs
CHANGED
|
@@ -804,7 +804,6 @@ function playOne(ctx, player, volume, track, index, count) {
|
|
|
804
804
|
const [command, args] = playerCommand(ctx, player, volume, track);
|
|
805
805
|
writeStatus(ctx, "playing", index, count, track, player, "");
|
|
806
806
|
const child = spawn(command, args, {
|
|
807
|
-
detached: process.platform !== "win32",
|
|
808
807
|
stdio: ["ignore", "inherit", "inherit"],
|
|
809
808
|
});
|
|
810
809
|
ctx.child = child;
|
package/skills/actors/SKILL.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: actors
|
|
3
|
-
description:
|
|
3
|
+
description: Required practical guide for non-trivial pi-actors use. 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.31.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
9
9
|
|
|
10
|
-
`pi-actors` turns trusted local capabilities into addressable actors. This skill is the
|
|
10
|
+
`pi-actors` turns trusted local capabilities into addressable actors. This skill is the required practical layer for any non-trivial pi-actors use or repo change: tools, nouns, lifecycle, message protocol, recipes, and common edge cases. It is not a multi-agent strategy guide; use a swarm skill for decomposition, quorum design, reviewer lenses, and consensus methodology.
|
|
11
11
|
|
|
12
12
|
Maintain this skill as the extension's agent-facing manual. When implementation changes reveal new durable mechanics, invariants, warnings, or safer operating patterns, update this skill alongside code/docs so future agents learn the current actor model instead of rediscovering it.
|
|
13
13
|
|
|
@@ -48,6 +48,8 @@ Trusted local capability
|
|
|
48
48
|
|
|
49
49
|
## Three Verbs
|
|
50
50
|
|
|
51
|
+
Actor-mode trigger: if work may outlive this turn, needs steering/follow-up/artifacts, runs as a service, fans out, or should be resumed/inspected later, use `spawn → message → inspect` instead of ad hoc shell backgrounding. Keep short foreground checks as normal tools.
|
|
52
|
+
|
|
51
53
|
### `spawn` — create a run actor
|
|
52
54
|
|
|
53
55
|
Use for long work, background services, subagents, fanout, pipelines, and reusable recipes.
|
|
@@ -87,6 +89,7 @@ Envelope fields:
|
|
|
87
89
|
- Addresses: `run:<id>`, `branch:<run>/<branch>`, `room:<run>`, `tool:<name>`, `coordinator`, `session:<id>`.
|
|
88
90
|
- Room posts require `from` from the same run (`run:<run>` or `branch:<run>/<branch>`).
|
|
89
91
|
- 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`.
|
|
92
|
+
- 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`.
|
|
90
93
|
|
|
91
94
|
Check `inspect view=mailbox` before domain-specific messages.
|
|
92
95
|
|
package/skills/swarm/SKILL.md
CHANGED