@llblab/pi-actors 0.25.0 → 0.26.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/BACKLOG.md +4 -8
- package/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/dist/lib/actor-worker.d.ts +3 -0
- package/dist/lib/actor-worker.js +54 -6
- package/dist/recipes/actor-worker.json +10 -6
- package/dist/recipes/lens-swarm.json +0 -2
- package/dist/recipes/pipeline-architect-coordinator.json +0 -2
- package/dist/recipes/pipeline-artifact-bundle.json +0 -2
- package/dist/recipes/pipeline-artifact-report.json +0 -2
- package/dist/recipes/pipeline-artifact-write.json +0 -2
- package/dist/recipes/pipeline-async-run-ops.json +0 -2
- package/dist/recipes/pipeline-checkpoint-continuation.json +0 -2
- package/dist/recipes/pipeline-development-tasking.json +0 -2
- package/dist/recipes/pipeline-docs-maintenance.json +0 -2
- package/dist/recipes/pipeline-media-library.json +0 -2
- package/dist/recipes/pipeline-quorum-review.json +0 -2
- package/dist/recipes/pipeline-release-readiness.json +0 -2
- package/dist/recipes/pipeline-release-summary.json +0 -2
- package/dist/recipes/pipeline-repo-health.json +0 -2
- package/dist/recipes/pipeline-research-synthesis.json +0 -2
- package/dist/recipes/pipeline-review-readiness.json +0 -2
- package/dist/recipes/pipeline-room-swarm.json +0 -2
- package/dist/recipes/subagent-artifact.json +0 -2
- package/dist/recipes/subagent-checkpoint.json +0 -2
- package/dist/recipes/subagent-conflict-report.json +0 -2
- package/dist/recipes/subagent-contradiction-map.json +0 -2
- package/dist/recipes/subagent-critic.json +0 -2
- package/dist/recipes/subagent-evidence-map.json +0 -2
- package/dist/recipes/subagent-followup.json +0 -2
- package/dist/recipes/subagent-judge.json +0 -2
- package/dist/recipes/subagent-merge.json +0 -2
- package/dist/recipes/subagent-message.json +0 -2
- package/dist/recipes/subagent-normalize.json +0 -2
- package/dist/recipes/subagent-plan.json +0 -2
- package/dist/recipes/subagent-prompt.json +0 -2
- package/dist/recipes/subagent-quorum.json +0 -2
- package/dist/recipes/subagent-review-coordinator.json +0 -2
- package/dist/recipes/subagent-review.json +0 -2
- package/dist/recipes/subagent-task-card.json +0 -2
- package/dist/recipes/subagent-tools.json +0 -2
- package/dist/recipes/subagent-verify.json +0 -2
- package/dist/recipes/subagents-prompts.json +0 -2
- package/dist/skills/actors/SKILL.md +2 -2
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/actor-messages.md +1 -1
- package/docs/template-recipes.md +2 -2
- package/lib/actor-worker.ts +62 -6
- package/package.json +1 -1
- package/recipes/actor-worker.json +10 -6
- package/recipes/lens-swarm.json +0 -2
- package/recipes/pipeline-architect-coordinator.json +0 -2
- package/recipes/pipeline-artifact-bundle.json +0 -2
- package/recipes/pipeline-artifact-report.json +0 -2
- package/recipes/pipeline-artifact-write.json +0 -2
- package/recipes/pipeline-async-run-ops.json +0 -2
- package/recipes/pipeline-checkpoint-continuation.json +0 -2
- package/recipes/pipeline-development-tasking.json +0 -2
- package/recipes/pipeline-docs-maintenance.json +0 -2
- package/recipes/pipeline-media-library.json +0 -2
- package/recipes/pipeline-quorum-review.json +0 -2
- package/recipes/pipeline-release-readiness.json +0 -2
- package/recipes/pipeline-release-summary.json +0 -2
- package/recipes/pipeline-repo-health.json +0 -2
- package/recipes/pipeline-research-synthesis.json +0 -2
- package/recipes/pipeline-review-readiness.json +0 -2
- package/recipes/pipeline-room-swarm.json +0 -2
- package/recipes/subagent-artifact.json +0 -2
- package/recipes/subagent-checkpoint.json +0 -2
- package/recipes/subagent-conflict-report.json +0 -2
- package/recipes/subagent-contradiction-map.json +0 -2
- package/recipes/subagent-critic.json +0 -2
- package/recipes/subagent-evidence-map.json +0 -2
- package/recipes/subagent-followup.json +0 -2
- package/recipes/subagent-judge.json +0 -2
- package/recipes/subagent-merge.json +0 -2
- package/recipes/subagent-message.json +0 -2
- package/recipes/subagent-normalize.json +0 -2
- package/recipes/subagent-plan.json +0 -2
- package/recipes/subagent-prompt.json +0 -2
- package/recipes/subagent-quorum.json +0 -2
- package/recipes/subagent-review-coordinator.json +0 -2
- package/recipes/subagent-review.json +0 -2
- package/recipes/subagent-task-card.json +0 -2
- package/recipes/subagent-tools.json +0 -2
- package/recipes/subagent-verify.json +0 -2
- package/recipes/subagents-prompts.json +0 -2
- package/skills/actors/SKILL.md +2 -2
- package/skills/swarm/SKILL.md +1 -1
package/docs/template-recipes.md
CHANGED
|
@@ -80,7 +80,7 @@ defaults:
|
|
|
80
80
|
scope: docs
|
|
81
81
|
mailbox:
|
|
82
82
|
accepts:
|
|
83
|
-
- control.
|
|
83
|
+
- control.kill
|
|
84
84
|
---
|
|
85
85
|
|
|
86
86
|
Human notes can explain intent, examples, or review guidance.
|
|
@@ -155,7 +155,7 @@ Use recipe-level `mailbox` to document the semantic messages a recipe actor acce
|
|
|
155
155
|
"control.continue",
|
|
156
156
|
"control.revise",
|
|
157
157
|
"control.approve",
|
|
158
|
-
"control.
|
|
158
|
+
"control.kill"
|
|
159
159
|
],
|
|
160
160
|
"emits": ["checkpoint.needs_scope", "branch.done", "run.done"]
|
|
161
161
|
}
|
package/lib/actor-worker.ts
CHANGED
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
* Zones: worker demo, mailbox loop recipe runtime
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { appendFileSync } from "node:fs";
|
|
6
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
|
-
import { appendRoomMessage, ensureRoomMember } from "./actor-rooms.ts";
|
|
9
|
+
import { appendRoomMessage, ensureRoomMember, readBranchInboxMessages } from "./actor-rooms.ts";
|
|
10
10
|
import { handleMailboxLoopOnce, isMailboxLoopStopMessage } from "./mailbox-loop.ts";
|
|
11
11
|
|
|
12
12
|
interface ActorWorkerArgs {
|
|
13
|
+
artifact_dir?: string;
|
|
13
14
|
branch?: string;
|
|
14
15
|
poll_ms?: string | number;
|
|
15
16
|
run?: string;
|
|
17
|
+
stale_claim_ms?: string | number;
|
|
16
18
|
state_dir?: string;
|
|
19
|
+
write_artifacts?: string | boolean;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export function parseActorWorkerArgs(argv: string[]): ActorWorkerArgs {
|
|
@@ -41,12 +44,23 @@ function text(value: unknown): string {
|
|
|
41
44
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
function enabled(value: unknown): boolean {
|
|
48
|
+
return value !== false && value !== "false" && value !== "0";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function safeName(value: unknown): string {
|
|
52
|
+
return String(value ?? "unknown").replaceAll(/[^A-Za-z0-9_.-]+/g, "_");
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
export async function runActorWorker(argv = process.argv.slice(2)): Promise<void> {
|
|
45
56
|
const args = parseActorWorkerArgs(argv);
|
|
46
57
|
const stateDir = String(args.state_dir ?? "");
|
|
47
58
|
const run = String(args.run ?? "");
|
|
48
59
|
const branch = String(args.branch ?? "worker");
|
|
49
60
|
const pollMs = Math.max(50, Number(args.poll_ms ?? 1000));
|
|
61
|
+
const staleClaimMs = Math.max(0, Number(args.stale_claim_ms ?? 0));
|
|
62
|
+
const artifactDir = String(args.artifact_dir ?? join(stateDir, "worker-artifacts"));
|
|
63
|
+
const writeArtifacts = enabled(args.write_artifacts ?? true);
|
|
50
64
|
|
|
51
65
|
if (!stateDir || !run) {
|
|
52
66
|
throw new Error("usage: actor-worker.mjs --state-dir <dir> --run <id> [--branch worker] [--poll-ms 1000]");
|
|
@@ -55,6 +69,8 @@ export async function runActorWorker(argv = process.argv.slice(2)): Promise<void
|
|
|
55
69
|
const branchAddress = `branch:${run}/${branch}`;
|
|
56
70
|
const roomAddress = `room:${run}`;
|
|
57
71
|
const journalPath = join(stateDir, "worker-events.jsonl");
|
|
72
|
+
const statusPath = join(stateDir, "worker-status.json");
|
|
73
|
+
if (writeArtifacts) mkdirSync(artifactDir, { recursive: true });
|
|
58
74
|
|
|
59
75
|
function journal(event: string, data: Record<string, unknown> = {}): void {
|
|
60
76
|
appendFileSync(
|
|
@@ -63,6 +79,30 @@ export async function runActorWorker(argv = process.argv.slice(2)): Promise<void
|
|
|
63
79
|
);
|
|
64
80
|
}
|
|
65
81
|
|
|
82
|
+
function staleClaims(): number {
|
|
83
|
+
if (staleClaimMs <= 0) return 0;
|
|
84
|
+
const cutoff = Date.now() - staleClaimMs;
|
|
85
|
+
return readBranchInboxMessages(stateDir, run, branchAddress).filter((record) => {
|
|
86
|
+
if (record.status !== "claimed" || !record.claimed_at) return false;
|
|
87
|
+
const claimedAt = Date.parse(record.claimed_at);
|
|
88
|
+
return Number.isFinite(claimedAt) && claimedAt < cutoff;
|
|
89
|
+
}).length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function status(state: string, data: Record<string, unknown> = {}): void {
|
|
93
|
+
writeFileSync(
|
|
94
|
+
statusPath,
|
|
95
|
+
`${JSON.stringify({ artifact_dir: artifactDir, branch, run, stale_claims: staleClaims(), state, ts: new Date().toISOString(), ...data }, null, 2)}\n`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function writeResultArtifact(id: unknown, result: string): string | undefined {
|
|
100
|
+
if (!writeArtifacts) return undefined;
|
|
101
|
+
const path = join(artifactDir, `${safeName(id)}.txt`);
|
|
102
|
+
writeFileSync(path, `${result}\n`);
|
|
103
|
+
return path;
|
|
104
|
+
}
|
|
105
|
+
|
|
66
106
|
function room(type: string, summary: string, body: Record<string, unknown> = {}): void {
|
|
67
107
|
appendRoomMessage(stateDir, "main", {
|
|
68
108
|
body,
|
|
@@ -82,7 +122,10 @@ export async function runActorWorker(argv = process.argv.slice(2)): Promise<void
|
|
|
82
122
|
`${branch} joined as mailbox worker`,
|
|
83
123
|
);
|
|
84
124
|
room("awaiting_assignment", `${branch} awaiting assignment`, { branch });
|
|
85
|
-
journal("worker.started", { branch, run });
|
|
125
|
+
journal("worker.started", { artifact_dir: artifactDir, branch, run, stale_claim_ms: staleClaimMs });
|
|
126
|
+
status("awaiting_assignment", { handled: 0, stale_claim_ms: staleClaimMs });
|
|
127
|
+
|
|
128
|
+
let handled = 0;
|
|
86
129
|
|
|
87
130
|
let stopping = false;
|
|
88
131
|
while (!stopping) {
|
|
@@ -93,6 +136,7 @@ export async function runActorWorker(argv = process.argv.slice(2)): Promise<void
|
|
|
93
136
|
stopping = true;
|
|
94
137
|
room("actor.leave", `${branch} stopping`, { branch, reason: message.type });
|
|
95
138
|
journal("worker.stopping", { id: message.id, type: message.type });
|
|
139
|
+
status("stopping", { handled, reason: message.type });
|
|
96
140
|
return;
|
|
97
141
|
}
|
|
98
142
|
room("task.claim", `${branch} claimed ${message.type}`, {
|
|
@@ -100,19 +144,31 @@ export async function runActorWorker(argv = process.argv.slice(2)): Promise<void
|
|
|
100
144
|
id: message.id,
|
|
101
145
|
type: message.type,
|
|
102
146
|
});
|
|
147
|
+
const result = text(message.body);
|
|
148
|
+
const artifact = writeResultArtifact(message.id, result);
|
|
149
|
+
handled += 1;
|
|
103
150
|
room("task.result", `${branch} handled ${message.type}`, {
|
|
151
|
+
artifact,
|
|
104
152
|
branch,
|
|
105
153
|
id: message.id,
|
|
106
|
-
result
|
|
154
|
+
result,
|
|
107
155
|
type: message.type,
|
|
108
156
|
});
|
|
109
157
|
room("awaiting_assignment", `${branch} awaiting assignment`, { branch });
|
|
110
|
-
journal("task.handled", { id: message.id, type: message.type });
|
|
158
|
+
journal("task.handled", { artifact, id: message.id, type: message.type });
|
|
159
|
+
status("awaiting_assignment", {
|
|
160
|
+
handled,
|
|
161
|
+
last_artifact: artifact,
|
|
162
|
+
last_id: message.id,
|
|
163
|
+
last_type: message.type,
|
|
164
|
+
stale_claim_ms: staleClaimMs,
|
|
165
|
+
});
|
|
111
166
|
},
|
|
112
167
|
{ owner: branchAddress },
|
|
113
168
|
);
|
|
114
169
|
if (!result.handled) await sleep(pollMs);
|
|
115
170
|
}
|
|
116
171
|
|
|
117
|
-
journal("worker.done", { branch, run });
|
|
172
|
+
journal("worker.done", { branch, handled, run });
|
|
173
|
+
status("done", { handled });
|
|
118
174
|
}
|
package/package.json
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
"args": [
|
|
5
5
|
"repo:path",
|
|
6
6
|
"branch:string",
|
|
7
|
-
"poll_ms:int"
|
|
7
|
+
"poll_ms:int",
|
|
8
|
+
"stale_claim_ms:int",
|
|
9
|
+
"write_artifacts:bool"
|
|
8
10
|
],
|
|
9
11
|
"defaults": {
|
|
10
12
|
"repo": "~/.pi/agent/extensions/pi-actors",
|
|
11
13
|
"branch": "worker",
|
|
12
|
-
"poll_ms": "1000"
|
|
14
|
+
"poll_ms": "1000",
|
|
15
|
+
"stale_claim_ms": "0",
|
|
16
|
+
"write_artifacts": "true"
|
|
13
17
|
},
|
|
14
18
|
"mailbox": {
|
|
15
19
|
"accepts": [
|
|
16
20
|
"task.assign",
|
|
17
|
-
"control.stop",
|
|
18
|
-
"control.cancel",
|
|
19
21
|
"control.kill"
|
|
20
22
|
],
|
|
21
23
|
"emits": [
|
|
@@ -29,7 +31,9 @@
|
|
|
29
31
|
]
|
|
30
32
|
},
|
|
31
33
|
"artifacts": {
|
|
32
|
-
"journal": "{state_dir}/worker-events.jsonl"
|
|
34
|
+
"journal": "{state_dir}/worker-events.jsonl",
|
|
35
|
+
"status": "{state_dir}/worker-status.json",
|
|
36
|
+
"results": "{state_dir}/worker-artifacts"
|
|
33
37
|
},
|
|
34
|
-
"template": "{repo}/scripts/actor-worker.mjs --state-dir {state_dir} --run {run_id} --branch {branch} --poll-ms {poll_ms}"
|
|
38
|
+
"template": "{repo}/scripts/actor-worker.mjs --state-dir {state_dir} --run {run_id} --branch {branch} --poll-ms {poll_ms} --stale-claim-ms {stale_claim_ms} --write-artifacts {write_artifacts}"
|
|
35
39
|
}
|
package/recipes/lens-swarm.json
CHANGED
package/skills/actors/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: actors
|
|
3
3
|
description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for 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.26.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Actors (pi-actors)
|
|
@@ -277,7 +277,7 @@ Use packaged recipes by name with `spawn file=<name>` for async actors, or regis
|
|
|
277
277
|
- [`locker`](../../recipes/locker.json): modular queue + acquire/renew/release lease locks + journaled locker messages + platform-adapted control metadata.
|
|
278
278
|
- [`utility-coordinator-lock-snapshot`](../../recipes/utility-coordinator-lock-snapshot.json): one-shot JSON snapshot of a coordinator-locker state directory.
|
|
279
279
|
- [`music-player`](../../recipes/music-player.json): background local/URL/directory/playlist playback actor controlled by messages.
|
|
280
|
-
- [`actor-worker`](../../recipes/actor-worker.json): canonical mailbox-backed branch worker
|
|
280
|
+
- [`actor-worker`](../../recipes/actor-worker.json): canonical mailbox-backed branch worker reference that claims branch inbox work, emits room-visible task lifecycle messages, writes compact `worker-status.json`, optionally writes per-task result artifacts under `worker-artifacts/`, surfaces stale-claim counts when `stale_claim_ms` is set, and terminates on `control.kill`.
|
|
281
281
|
|
|
282
282
|
### Subagent Atoms
|
|
283
283
|
|
package/skills/swarm/SKILL.md
CHANGED