@llblab/pi-actors 0.23.0 → 0.24.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.
Files changed (158) hide show
  1. package/AGENTS.md +125 -60
  2. package/BACKLOG.md +133 -176
  3. package/CHANGELOG.md +27 -0
  4. package/README.md +13 -0
  5. package/dist/fixtures/protocol/actor-message-branch.json +13 -0
  6. package/dist/fixtures/protocol/artifact-manifest.json +9 -0
  7. package/dist/fixtures/protocol/mailbox-contract.json +15 -0
  8. package/dist/fixtures/protocol/recipe-summary.json +16 -0
  9. package/dist/fixtures/protocol/room-message.json +11 -0
  10. package/dist/fixtures/protocol/room-roster.json +11 -0
  11. package/dist/fixtures/protocol/run-inbox-message.json +9 -0
  12. package/dist/fixtures/protocol/run-outbox-event.json +9 -0
  13. package/dist/fixtures/protocol/run-state.json +10 -0
  14. package/dist/index.js +3 -2
  15. package/dist/lib/actor-inspector-tui.js +5 -32
  16. package/dist/lib/actor-rooms.d.ts +8 -0
  17. package/dist/lib/actor-rooms.js +64 -20
  18. package/dist/lib/actor-worker.d.ts +13 -0
  19. package/dist/lib/actor-worker.js +86 -0
  20. package/dist/lib/async-runner.d.ts +5 -0
  21. package/dist/lib/async-runner.js +134 -0
  22. package/dist/lib/async-runs.js +9 -28
  23. package/dist/lib/conformance.d.ts +12 -0
  24. package/dist/lib/conformance.js +28 -0
  25. package/dist/lib/coordinator.d.ts +5 -0
  26. package/dist/lib/coordinator.js +574 -0
  27. package/dist/lib/locker.d.ts +5 -0
  28. package/dist/lib/locker.js +310 -0
  29. package/dist/lib/mailbox-loop.d.ts +41 -0
  30. package/dist/lib/mailbox-loop.js +62 -0
  31. package/dist/lib/observability.d.ts +2 -2
  32. package/dist/lib/observability.js +51 -53
  33. package/dist/lib/prompts.d.ts +1 -1
  34. package/dist/lib/prompts.js +1 -1
  35. package/dist/lib/recipe-references.js +25 -2
  36. package/dist/lib/recipe-utils.d.ts +5 -0
  37. package/dist/lib/recipe-utils.js +385 -0
  38. package/dist/lib/runtime-notifier.js +3 -7
  39. package/dist/lib/state-readers.d.ts +21 -0
  40. package/dist/lib/state-readers.js +74 -0
  41. package/dist/lib/tools.js +1 -1
  42. package/dist/lib/validate-recipe.d.ts +6 -0
  43. package/dist/lib/validate-recipe.js +104 -0
  44. package/dist/recipes/actor-worker.json +35 -0
  45. package/dist/recipes/coordinator-locker.json +45 -0
  46. package/dist/recipes/lens-swarm.json +66 -0
  47. package/dist/recipes/locker.json +45 -0
  48. package/dist/recipes/music-player.json +38 -0
  49. package/dist/recipes/pipeline-architect-coordinator.json +95 -0
  50. package/dist/recipes/pipeline-artifact-bundle.json +100 -0
  51. package/dist/recipes/pipeline-artifact-report.json +58 -0
  52. package/dist/recipes/pipeline-artifact-write.json +72 -0
  53. package/dist/recipes/pipeline-async-run-ops.json +70 -0
  54. package/dist/recipes/pipeline-checkpoint-continuation.json +67 -0
  55. package/dist/recipes/pipeline-development-tasking.json +81 -0
  56. package/dist/recipes/pipeline-docs-maintenance.json +80 -0
  57. package/dist/recipes/pipeline-media-library.json +59 -0
  58. package/dist/recipes/pipeline-quorum-review.json +79 -0
  59. package/dist/recipes/pipeline-release-readiness.json +110 -0
  60. package/dist/recipes/pipeline-release-summary.json +88 -0
  61. package/dist/recipes/pipeline-repo-health.json +89 -0
  62. package/dist/recipes/pipeline-research-synthesis.json +94 -0
  63. package/dist/recipes/pipeline-review-readiness.json +54 -0
  64. package/dist/recipes/pipeline-room-swarm.json +50 -0
  65. package/dist/recipes/subagent-artifact.json +32 -0
  66. package/dist/recipes/subagent-checkpoint.json +33 -0
  67. package/dist/recipes/subagent-conflict-report.json +32 -0
  68. package/dist/recipes/subagent-contradiction-map.json +33 -0
  69. package/dist/recipes/subagent-critic.json +35 -0
  70. package/dist/recipes/subagent-evidence-map.json +33 -0
  71. package/dist/recipes/subagent-followup.json +33 -0
  72. package/dist/recipes/subagent-judge.json +33 -0
  73. package/dist/recipes/subagent-merge.json +33 -0
  74. package/dist/recipes/subagent-message.json +34 -0
  75. package/dist/recipes/subagent-normalize.json +31 -0
  76. package/dist/recipes/subagent-plan.json +33 -0
  77. package/dist/recipes/subagent-prompt.json +28 -0
  78. package/dist/recipes/subagent-quorum.json +43 -0
  79. package/dist/recipes/subagent-review-coordinator.json +114 -0
  80. package/dist/recipes/subagent-review.json +37 -0
  81. package/dist/recipes/subagent-task-card.json +35 -0
  82. package/dist/recipes/subagent-tools.json +27 -0
  83. package/dist/recipes/subagent-verify.json +34 -0
  84. package/dist/recipes/subagents-prompts.json +51 -0
  85. package/dist/recipes/utility-actor-message.json +23 -0
  86. package/dist/recipes/utility-artifact-manifest.json +16 -0
  87. package/dist/recipes/utility-artifact-write.json +16 -0
  88. package/dist/recipes/utility-changelog-head.json +11 -0
  89. package/dist/recipes/utility-changelog-section.json +13 -0
  90. package/dist/recipes/utility-coordinator-lock-snapshot.json +13 -0
  91. package/dist/recipes/utility-git-log.json +11 -0
  92. package/dist/recipes/utility-git-status.json +9 -0
  93. package/dist/recipes/utility-jsonl-tail.json +10 -0
  94. package/dist/recipes/utility-markdown-index.json +14 -0
  95. package/dist/recipes/utility-package-summary.json +11 -0
  96. package/dist/recipes/utility-playlist-build.json +17 -0
  97. package/dist/recipes/utility-playlist-scan.json +11 -0
  98. package/dist/recipes/utility-run-ops-snapshot.json +17 -0
  99. package/dist/recipes/utility-run-state-files.json +13 -0
  100. package/dist/recipes/utility-run-summary.json +11 -0
  101. package/dist/recipes/utility-skill-summary.json +13 -0
  102. package/dist/recipes/utility-validate-recipe.json +13 -0
  103. package/dist/recipes/utility-validation-wrapper.json +13 -0
  104. package/dist/scripts/actor-worker.mjs +31 -0
  105. package/dist/scripts/async-runner.mjs +31 -0
  106. package/dist/scripts/build-dist.mjs +33 -0
  107. package/dist/scripts/conformance.mjs +33 -0
  108. package/dist/scripts/coordinator.mjs +31 -0
  109. package/dist/scripts/locker.mjs +33 -0
  110. package/dist/scripts/music-player.mjs +964 -0
  111. package/dist/scripts/recipe-utils.mjs +31 -0
  112. package/dist/scripts/validate-recipe.mjs +34 -0
  113. package/dist/skills/actors/SKILL.md +377 -0
  114. package/dist/skills/swarm/SKILL.md +467 -0
  115. package/dist/skills/swarm/references/development-swarm.md +596 -0
  116. package/docs/actor-messages.md +2 -2
  117. package/docs/async-runs.md +11 -0
  118. package/docs/template-recipes.md +1 -1
  119. package/fixtures/protocol/actor-message-branch.json +13 -0
  120. package/fixtures/protocol/artifact-manifest.json +9 -0
  121. package/fixtures/protocol/mailbox-contract.json +15 -0
  122. package/fixtures/protocol/recipe-summary.json +16 -0
  123. package/fixtures/protocol/room-message.json +11 -0
  124. package/fixtures/protocol/room-roster.json +11 -0
  125. package/fixtures/protocol/run-inbox-message.json +9 -0
  126. package/fixtures/protocol/run-outbox-event.json +9 -0
  127. package/fixtures/protocol/run-state.json +10 -0
  128. package/index.ts +3 -0
  129. package/lib/actor-inspector-tui.ts +11 -34
  130. package/lib/actor-rooms.ts +88 -18
  131. package/lib/actor-worker.ts +118 -0
  132. package/lib/async-runner.ts +173 -0
  133. package/lib/async-runs.ts +12 -22
  134. package/lib/conformance.ts +46 -0
  135. package/lib/coordinator.ts +664 -0
  136. package/lib/locker.ts +340 -0
  137. package/lib/mailbox-loop.ts +148 -0
  138. package/lib/observability.ts +24 -19
  139. package/lib/prompts.ts +1 -1
  140. package/lib/recipe-references.ts +37 -2
  141. package/lib/recipe-utils.ts +486 -0
  142. package/lib/runtime-notifier.ts +4 -6
  143. package/lib/state-readers.ts +93 -0
  144. package/lib/tools.ts +1 -1
  145. package/lib/validate-recipe.ts +110 -0
  146. package/package.json +10 -2
  147. package/recipes/actor-worker.json +35 -0
  148. package/recipes/pipeline-quorum-review.json +12 -7
  149. package/scripts/actor-worker.mjs +31 -0
  150. package/scripts/async-runner.mjs +11 -201
  151. package/scripts/build-dist.mjs +33 -0
  152. package/scripts/conformance.mjs +21 -35
  153. package/scripts/coordinator.mjs +15 -625
  154. package/scripts/locker.mjs +20 -332
  155. package/scripts/recipe-utils.mjs +17 -477
  156. package/scripts/validate-recipe.mjs +18 -121
  157. package/skills/actors/SKILL.md +8 -3
  158. package/skills/swarm/SKILL.md +3 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Detached async-runner entrypoint logic.
3
+ * Zones: async run process, command-template execution telemetry
4
+ */
5
+ import { appendFileSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { appendRecipeContextToPiArgs } from "./actor-recipe-context.js";
8
+ import { execCommandTemplate } from "./command-templates.js";
9
+ import { executeRegisteredTool } from "./execution.js";
10
+ import { writeJsonAtomic } from "./file-state.js";
11
+ function quoteCommandDetailPart(value) {
12
+ if (value === "")
13
+ return "''";
14
+ if (/^[A-Za-z0-9_/:=.,@%+\-]+$/.test(value))
15
+ return value;
16
+ return `'${String(value).replaceAll("'", "'\\''")}'`;
17
+ }
18
+ function formatCommandDetail(command, args) {
19
+ return [command, ...args].map(quoteCommandDetailPart).join(" ");
20
+ }
21
+ function summarizeCommandDetail(commandDetail) {
22
+ return commandDetail.length > 160
23
+ ? `${commandDetail.slice(0, 157)}...`
24
+ : commandDetail;
25
+ }
26
+ export async function runAsyncRunner(stateDir = process.argv[2]) {
27
+ if (!stateDir)
28
+ throw new Error("missing state dir");
29
+ const runPath = join(stateDir, "run.json");
30
+ const progressPath = join(stateDir, "progress.json");
31
+ const resultPath = join(stateDir, "result.json");
32
+ const eventsPath = join(stateDir, "events.jsonl");
33
+ const outboxPath = join(stateDir, "outbox.jsonl");
34
+ const stdoutPath = join(stateDir, "stdout.log");
35
+ const stderrPath = join(stateDir, "stderr.log");
36
+ const meta = JSON.parse(readFileSync(runPath, "utf8"));
37
+ function event(name, data = {}) {
38
+ appendFileSync(eventsPath, `${JSON.stringify({ event: name, ts: new Date().toISOString(), ...data })}\n`);
39
+ }
40
+ function outbox(name, summary, data = {}, delivery = "log", level = "info") {
41
+ appendFileSync(outboxPath, `${JSON.stringify({ body: data, data, delivery, event: name, from: `run:${meta.run}`, level, summary, to: "coordinator", ts: new Date().toISOString(), type: name })}\n`);
42
+ }
43
+ function progress(phase, extra = {}) {
44
+ writeJsonAtomic(progressPath, {
45
+ phase,
46
+ updatedAt: new Date().toISOString(),
47
+ ...extra,
48
+ });
49
+ }
50
+ let activeSubagents = 0;
51
+ let completedSubagents = 0;
52
+ const subagentFailures = [];
53
+ function getCommandDoneDelivery(result) {
54
+ return result.code !== 0 || activeSubagents > 0 ? "followup" : "log";
55
+ }
56
+ function progressRunning() {
57
+ progress("running", {
58
+ activeSubagents,
59
+ completed: completedSubagents,
60
+ failures: subagentFailures,
61
+ });
62
+ }
63
+ async function observedExec(command, args, options) {
64
+ const commandDetail = formatCommandDetail(command, args);
65
+ const execArgs = appendRecipeContextToPiArgs(command, args, meta.recipe_context_records, options?.actorRecipeContext);
66
+ activeSubagents += 1;
67
+ event("command.start", { activeSubagents, command: commandDetail });
68
+ progressRunning();
69
+ const result = await execCommandTemplate(command, execArgs, options);
70
+ activeSubagents = Math.max(0, activeSubagents - 1);
71
+ completedSubagents += 1;
72
+ if (result.code !== 0) {
73
+ subagentFailures.push({
74
+ code: result.code,
75
+ command: commandDetail,
76
+ killed: result.killed,
77
+ });
78
+ }
79
+ event("command.done", {
80
+ activeSubagents,
81
+ code: result.code,
82
+ command: commandDetail,
83
+ killed: result.killed,
84
+ });
85
+ outbox("command.done", `Command ${summarizeCommandDetail(commandDetail)} completed with code ${result.code}`, {
86
+ activeSubagents,
87
+ ...(meta.artifacts ? { artifacts: meta.artifacts } : {}),
88
+ run_files: [stdoutPath, stderrPath, resultPath, eventsPath, outboxPath],
89
+ code: result.code,
90
+ command: commandDetail,
91
+ killed: result.killed,
92
+ }, getCommandDoneDelivery(result), result.code === 0 ? "info" : "error");
93
+ progressRunning();
94
+ return result;
95
+ }
96
+ try {
97
+ event("run.runner.start", { pid: process.pid });
98
+ progressRunning();
99
+ const result = await executeRegisteredTool({
100
+ name: "run_actor",
101
+ description: "Detached command-template run actor",
102
+ template: meta.template,
103
+ args: [],
104
+ defaults: {},
105
+ }, meta.values || {}, observedExec, meta.cwd);
106
+ const text = result.content?.[0]?.text || "";
107
+ appendFileSync(stdoutPath, text);
108
+ writeJsonAtomic(resultPath, {
109
+ code: result.details.code,
110
+ command: result.details.command,
111
+ killed: result.details.killed,
112
+ truncated: result.details.truncated,
113
+ completedAt: new Date().toISOString(),
114
+ });
115
+ progress("done", {
116
+ completed: 1,
117
+ failures: result.details.nonCriticalFailures || [],
118
+ });
119
+ event("run.done", { code: result.details.code });
120
+ }
121
+ catch (error) {
122
+ const message = error instanceof Error ? error.message : String(error);
123
+ appendFileSync(stderrPath, `${message}\n`);
124
+ writeJsonAtomic(resultPath, {
125
+ code: 1,
126
+ error: message,
127
+ killed: false,
128
+ completedAt: new Date().toISOString(),
129
+ });
130
+ progress("failed", { completed: 0, failures: [{ message }] });
131
+ event("run.failed", { error: message });
132
+ throw error;
133
+ }
134
+ }
@@ -12,6 +12,7 @@ import { basename, dirname, extname, join, resolve } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { substituteCommandTemplateToken } from "./command-templates.js";
14
14
  import { writeJsonAtomic } from "./file-state.js";
15
+ import { readJsonFileResilient, readJsonlFileResilient } from "./state-readers.js";
15
16
  import * as Paths from "./paths.js";
16
17
  import * as RecipeReferences from "./recipe-references.js";
17
18
  import * as RecipeUsage from "./recipe-usage.js";
@@ -189,12 +190,7 @@ function resolveStartParams(params) {
189
190
  };
190
191
  }
191
192
  function readJson(path) {
192
- try {
193
- return JSON.parse(readFileSync(path, "utf8"));
194
- }
195
- catch {
196
- return undefined;
197
- }
193
+ return readJsonFileResilient(path, undefined).value;
198
194
  }
199
195
  function isAlive(pid) {
200
196
  try {
@@ -232,20 +228,12 @@ function tailLines(path, lines) {
232
228
  return content ? content.split("\n") : [];
233
229
  }
234
230
  function getInterruptedRunStatus(stateDir) {
235
- const events = tailFile(join(stateDir, "events.jsonl"), 200);
236
- if (!events)
237
- return undefined;
238
- for (const line of events.split("\n").reverse()) {
239
- try {
240
- const event = JSON.parse(line);
241
- if (event.event === "run.kill")
242
- return "killed";
243
- if (event.event === "run.cancel")
244
- return "cancelled";
245
- }
246
- catch {
247
- // Ignore malformed event lines.
248
- }
231
+ const events = readJsonlFileResilient(join(stateDir, "events.jsonl")).records.slice(-200);
232
+ for (const event of events.reverse()) {
233
+ if (event.event === "run.kill")
234
+ return "killed";
235
+ if (event.event === "run.cancel")
236
+ return "cancelled";
249
237
  }
250
238
  return undefined;
251
239
  }
@@ -655,14 +643,7 @@ function parseRunInboxLine(line) {
655
643
  }
656
644
  }
657
645
  function readRunInboxMessagesFromStateDir(stateDir) {
658
- const file = runInboxFile(stateDir);
659
- if (!existsSync(file))
660
- return [];
661
- return readFileSync(file, "utf8")
662
- .split("\n")
663
- .filter((line) => line.trim())
664
- .map(parseRunInboxLine)
665
- .filter((message) => Boolean(message));
646
+ return readJsonlFileResilient(runInboxFile(stateDir)).records;
666
647
  }
667
648
  function writeRunInboxMessages(stateDir, messages) {
668
649
  writeFileSync(runInboxFile(stateDir), messages.length
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Internal conformance runner logic.
3
+ * Zones: CI/release validation, protocol regression suite selection
4
+ */
5
+ export declare const conformanceSuites: string[];
6
+ export interface ConformanceReport {
7
+ code: number;
8
+ output: string;
9
+ summary: string;
10
+ suites: number;
11
+ }
12
+ export declare function runConformance(cwd?: URL): ConformanceReport;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Internal conformance runner logic.
3
+ * Zones: CI/release validation, protocol regression suite selection
4
+ */
5
+ import { spawnSync } from "node:child_process";
6
+ export const conformanceSuites = [
7
+ "tests/protocol-examples.test.ts",
8
+ "tests/recipe-discovery.test.ts",
9
+ "tests/registry.test.ts",
10
+ "tests/runtime-registry.test.ts",
11
+ "tests/async-runs.test.ts",
12
+ "tests/actor-rooms.test.ts",
13
+ "tests/tools.test.ts",
14
+ ];
15
+ export function runConformance(cwd = new URL("..", import.meta.url)) {
16
+ const result = spawnSync(process.execPath, ["--experimental-strip-types", "--test", ...conformanceSuites], { cwd, encoding: "utf8", stdio: "pipe" });
17
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
18
+ const summary = output
19
+ .split("\n")
20
+ .filter((line) => /^ℹ (tests|pass|fail|cancelled|skipped|todo|duration_ms) /.test(line))
21
+ .join("\n");
22
+ return {
23
+ code: result.status ?? 1,
24
+ output,
25
+ summary,
26
+ suites: conformanceSuites.length,
27
+ };
28
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Multi-actor coordinator helper entrypoint logic.
3
+ * Zones: swarm coordination, worker pool orchestration
4
+ */
5
+ export declare function runCoordinator(argv?: string[]): Promise<void>;