@llblab/pi-actors 0.22.5 → 0.24.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 +125 -59
- package/BACKLOG.md +88 -403
- package/CHANGELOG.md +39 -0
- package/README.md +14 -1
- package/dist/fixtures/protocol/actor-message-branch.json +13 -0
- package/dist/fixtures/protocol/artifact-manifest.json +9 -0
- package/dist/fixtures/protocol/mailbox-contract.json +15 -0
- package/dist/fixtures/protocol/recipe-summary.json +16 -0
- package/dist/fixtures/protocol/room-message.json +11 -0
- package/dist/fixtures/protocol/room-roster.json +11 -0
- package/dist/fixtures/protocol/run-inbox-message.json +9 -0
- package/dist/fixtures/protocol/run-outbox-event.json +9 -0
- package/dist/fixtures/protocol/run-state.json +10 -0
- package/dist/index.js +18 -2
- package/dist/lib/actor-inspector-tui.d.ts +4 -0
- package/dist/lib/actor-inspector-tui.js +58 -44
- package/dist/lib/actor-rooms.d.ts +16 -0
- package/dist/lib/actor-rooms.js +123 -28
- package/dist/lib/actor-worker.d.ts +13 -0
- package/dist/lib/actor-worker.js +86 -0
- package/dist/lib/async-runner.d.ts +5 -0
- package/dist/lib/async-runner.js +134 -0
- package/dist/lib/async-runs.d.ts +34 -2
- package/dist/lib/async-runs.js +224 -50
- package/dist/lib/build-dist.d.ts +5 -0
- package/dist/lib/build-dist.js +24 -0
- package/dist/lib/command-templates.js +5 -5
- package/dist/lib/conformance.d.ts +12 -0
- package/dist/lib/conformance.js +28 -0
- package/dist/lib/coordinator.d.ts +5 -0
- package/dist/lib/coordinator.js +557 -0
- package/dist/lib/limits.d.ts +11 -0
- package/dist/lib/limits.js +11 -0
- package/dist/lib/locker.d.ts +5 -0
- package/dist/lib/locker.js +310 -0
- package/dist/lib/mailbox-loop.d.ts +41 -0
- package/dist/lib/mailbox-loop.js +62 -0
- package/dist/lib/observability.d.ts +2 -2
- package/dist/lib/observability.js +57 -57
- package/dist/lib/output.js +4 -5
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +1 -1
- package/dist/lib/recipe-discovery.js +1 -0
- package/dist/lib/recipe-references.d.ts +11 -2
- package/dist/lib/recipe-references.js +29 -4
- package/dist/lib/recipe-usage.d.ts +2 -1
- package/dist/lib/recipe-usage.js +15 -3
- package/dist/lib/recipe-utils.d.ts +5 -0
- package/dist/lib/recipe-utils.js +385 -0
- package/dist/lib/runtime-notifier.js +3 -7
- package/dist/lib/runtime.js +12 -2
- package/dist/lib/state-readers.d.ts +21 -0
- package/dist/lib/state-readers.js +74 -0
- package/dist/lib/tools.js +198 -39
- package/dist/lib/validate-recipe.d.ts +6 -0
- package/dist/lib/validate-recipe.js +104 -0
- package/dist/recipes/actor-worker.json +35 -0
- package/dist/recipes/coordinator-locker.json +45 -0
- package/dist/recipes/lens-swarm.json +66 -0
- package/dist/recipes/locker.json +45 -0
- package/dist/recipes/music-player.json +38 -0
- package/dist/recipes/pipeline-architect-coordinator.json +95 -0
- package/dist/recipes/pipeline-artifact-bundle.json +100 -0
- package/dist/recipes/pipeline-artifact-report.json +58 -0
- package/dist/recipes/pipeline-artifact-write.json +72 -0
- package/dist/recipes/pipeline-async-run-ops.json +70 -0
- package/dist/recipes/pipeline-checkpoint-continuation.json +67 -0
- package/dist/recipes/pipeline-development-tasking.json +81 -0
- package/dist/recipes/pipeline-docs-maintenance.json +80 -0
- package/dist/recipes/pipeline-media-library.json +59 -0
- package/dist/recipes/pipeline-quorum-review.json +79 -0
- package/dist/recipes/pipeline-release-readiness.json +110 -0
- package/dist/recipes/pipeline-release-summary.json +88 -0
- package/dist/recipes/pipeline-repo-health.json +89 -0
- package/dist/recipes/pipeline-research-synthesis.json +94 -0
- package/dist/recipes/pipeline-review-readiness.json +54 -0
- package/dist/recipes/pipeline-room-swarm.json +50 -0
- package/dist/recipes/subagent-artifact.json +32 -0
- package/dist/recipes/subagent-checkpoint.json +33 -0
- package/dist/recipes/subagent-conflict-report.json +32 -0
- package/dist/recipes/subagent-contradiction-map.json +33 -0
- package/dist/recipes/subagent-critic.json +35 -0
- package/dist/recipes/subagent-evidence-map.json +33 -0
- package/dist/recipes/subagent-followup.json +33 -0
- package/dist/recipes/subagent-judge.json +33 -0
- package/dist/recipes/subagent-merge.json +33 -0
- package/dist/recipes/subagent-message.json +34 -0
- package/dist/recipes/subagent-normalize.json +31 -0
- package/dist/recipes/subagent-plan.json +33 -0
- package/dist/recipes/subagent-prompt.json +28 -0
- package/dist/recipes/subagent-quorum.json +43 -0
- package/dist/recipes/subagent-review-coordinator.json +114 -0
- package/dist/recipes/subagent-review.json +37 -0
- package/dist/recipes/subagent-task-card.json +35 -0
- package/dist/recipes/subagent-tools.json +27 -0
- package/dist/recipes/subagent-verify.json +34 -0
- package/dist/recipes/subagents-prompts.json +51 -0
- package/dist/recipes/utility-actor-message.json +23 -0
- package/dist/recipes/utility-artifact-manifest.json +16 -0
- package/dist/recipes/utility-artifact-write.json +16 -0
- package/dist/recipes/utility-changelog-head.json +11 -0
- package/dist/recipes/utility-changelog-section.json +13 -0
- package/dist/recipes/utility-coordinator-lock-snapshot.json +13 -0
- package/dist/recipes/utility-git-log.json +11 -0
- package/dist/recipes/utility-git-status.json +9 -0
- package/dist/recipes/utility-jsonl-tail.json +10 -0
- package/dist/recipes/utility-markdown-index.json +14 -0
- package/dist/recipes/utility-package-summary.json +11 -0
- package/dist/recipes/utility-playlist-build.json +17 -0
- package/dist/recipes/utility-playlist-scan.json +11 -0
- package/dist/recipes/utility-run-ops-snapshot.json +17 -0
- package/dist/recipes/utility-run-state-files.json +13 -0
- package/dist/recipes/utility-run-summary.json +11 -0
- package/dist/recipes/utility-skill-summary.json +13 -0
- package/dist/recipes/utility-validate-recipe.json +13 -0
- package/dist/recipes/utility-validation-wrapper.json +13 -0
- package/dist/scripts/actor-worker.mjs +31 -0
- package/dist/scripts/async-runner.mjs +31 -0
- package/dist/scripts/build-dist.mjs +25 -0
- package/dist/scripts/conformance.mjs +33 -0
- package/dist/scripts/coordinator.mjs +31 -0
- package/dist/scripts/locker.mjs +33 -0
- package/dist/scripts/music-player.mjs +964 -0
- package/dist/scripts/recipe-utils.mjs +31 -0
- package/dist/scripts/validate-recipe.mjs +34 -0
- package/dist/skills/actors/SKILL.md +375 -0
- package/dist/skills/swarm/SKILL.md +467 -0
- package/dist/skills/swarm/references/development-swarm.md +596 -0
- package/docs/actor-messages.md +2 -2
- package/docs/async-runs.md +13 -1
- package/docs/template-recipes.md +2 -2
- package/docs/tool-registry.md +0 -1
- package/fixtures/protocol/actor-message-branch.json +13 -0
- package/fixtures/protocol/artifact-manifest.json +9 -0
- package/fixtures/protocol/mailbox-contract.json +15 -0
- package/fixtures/protocol/recipe-summary.json +16 -0
- package/fixtures/protocol/room-message.json +11 -0
- package/fixtures/protocol/room-roster.json +11 -0
- package/fixtures/protocol/run-inbox-message.json +9 -0
- package/fixtures/protocol/run-outbox-event.json +9 -0
- package/fixtures/protocol/run-state.json +10 -0
- package/index.ts +21 -0
- package/lib/actor-inspector-tui.ts +138 -59
- package/lib/actor-rooms.ts +241 -60
- package/lib/actor-worker.ts +118 -0
- package/lib/async-runner.ts +173 -0
- package/lib/async-runs.ts +302 -53
- package/lib/build-dist.ts +30 -0
- package/lib/command-templates.ts +5 -5
- package/lib/conformance.ts +46 -0
- package/lib/coordinator.ts +649 -0
- package/lib/limits.ts +12 -0
- package/lib/locker.ts +340 -0
- package/lib/mailbox-loop.ts +148 -0
- package/lib/observability.ts +34 -23
- package/lib/output.ts +4 -6
- package/lib/prompts.ts +1 -1
- package/lib/recipe-discovery.ts +1 -0
- package/lib/recipe-references.ts +57 -6
- package/lib/recipe-usage.ts +31 -4
- package/lib/recipe-utils.ts +486 -0
- package/lib/runtime-notifier.ts +4 -6
- package/lib/runtime.ts +31 -7
- package/lib/state-readers.ts +93 -0
- package/lib/tools.ts +297 -58
- package/lib/validate-recipe.ts +110 -0
- package/package.json +11 -2
- package/recipes/actor-worker.json +35 -0
- package/recipes/pipeline-quorum-review.json +12 -7
- package/scripts/actor-worker.mjs +31 -0
- package/scripts/async-runner.mjs +11 -195
- package/scripts/build-dist.mjs +25 -0
- package/scripts/conformance.mjs +33 -0
- package/scripts/coordinator.mjs +19 -616
- package/scripts/locker.mjs +23 -322
- package/scripts/music-player.mjs +21 -2
- package/scripts/recipe-utils.mjs +20 -467
- package/scripts/validate-recipe.mjs +23 -113
- package/skills/actors/SKILL.md +7 -4
- package/skills/swarm/SKILL.md +3 -1
package/dist/lib/tools.js
CHANGED
|
@@ -7,6 +7,7 @@ import * as ActorMessages from "./actor-messages.js";
|
|
|
7
7
|
import * as ActorRooms from "./actor-rooms.js";
|
|
8
8
|
import * as AsyncRuns from "./async-runs.js";
|
|
9
9
|
import * as Execution from "./execution.js";
|
|
10
|
+
import * as Limits from "./limits.js";
|
|
10
11
|
import * as Paths from "./paths.js";
|
|
11
12
|
import * as Prompts from "./prompts.js";
|
|
12
13
|
import * as RecipeDiscovery from "./recipe-discovery.js";
|
|
@@ -154,7 +155,7 @@ function compactRunMessages(messages) {
|
|
|
154
155
|
].join(" "))
|
|
155
156
|
.join("\n")}`;
|
|
156
157
|
}
|
|
157
|
-
function compactPreview(value, maxLength =
|
|
158
|
+
function compactPreview(value, maxLength = Limits.COMPACT_PREVIEW_CHARS) {
|
|
158
159
|
if (value === undefined)
|
|
159
160
|
return undefined;
|
|
160
161
|
const text = typeof value === "string" ? value : JSON.stringify(value, undefined, 0);
|
|
@@ -173,7 +174,9 @@ function compactRoomPreviews(previews) {
|
|
|
173
174
|
`to=${preview.to}`,
|
|
174
175
|
`type=${preview.type}`,
|
|
175
176
|
preview.summary ? `summary=${compactPreview(preview.summary)}` : "",
|
|
176
|
-
preview.body_preview
|
|
177
|
+
preview.body_preview
|
|
178
|
+
? `body=${compactPreview(preview.body_preview)}`
|
|
179
|
+
: "",
|
|
177
180
|
]
|
|
178
181
|
.filter(Boolean)
|
|
179
182
|
.join(" "))
|
|
@@ -189,7 +192,9 @@ function compactRoomMessages(messages) {
|
|
|
189
192
|
`to=${message.to}`,
|
|
190
193
|
`type=${message.type}`,
|
|
191
194
|
`summary=${String(message.summary ?? "").replaceAll(/\s+/g, "_")}`,
|
|
192
|
-
compactPreview(message.body)
|
|
195
|
+
compactPreview(message.body)
|
|
196
|
+
? `body=${compactPreview(message.body)}`
|
|
197
|
+
: "",
|
|
193
198
|
]
|
|
194
199
|
.filter(Boolean)
|
|
195
200
|
.join(" "))
|
|
@@ -203,8 +208,12 @@ function compactRoomContacts(contacts) {
|
|
|
203
208
|
`address=${contact.address}`,
|
|
204
209
|
contact.role !== undefined ? `role=${String(contact.role)}` : "",
|
|
205
210
|
contact.parent !== undefined ? `parent=${String(contact.parent)}` : "",
|
|
206
|
-
contact.caps !== undefined
|
|
207
|
-
|
|
211
|
+
contact.caps !== undefined
|
|
212
|
+
? `caps=${Array.isArray(contact.caps) ? contact.caps.join(",") : String(contact.caps)}`
|
|
213
|
+
: "",
|
|
214
|
+
contact.claim !== undefined
|
|
215
|
+
? `claim=${String(contact.claim).replaceAll(/\s+/g, "_")}`
|
|
216
|
+
: "",
|
|
208
217
|
contact.status !== undefined ? `status=${String(contact.status)}` : "",
|
|
209
218
|
]
|
|
210
219
|
.filter(Boolean)
|
|
@@ -220,15 +229,19 @@ function compactRoomRoster(roster) {
|
|
|
220
229
|
`address=${member.address}`,
|
|
221
230
|
`role=${String(member.role ?? "")}`,
|
|
222
231
|
member.parent !== undefined ? `parent=${String(member.parent)}` : "",
|
|
223
|
-
member.caps !== undefined
|
|
224
|
-
|
|
232
|
+
member.caps !== undefined
|
|
233
|
+
? `caps=${Array.isArray(member.caps) ? member.caps.join(",") : String(member.caps)}`
|
|
234
|
+
: "",
|
|
235
|
+
member.claim !== undefined
|
|
236
|
+
? `claim=${String(member.claim).replaceAll(/\s+/g, "_")}`
|
|
237
|
+
: "",
|
|
225
238
|
`status=${String(member.status ?? "")}`,
|
|
226
239
|
`last_seen=${member.last_seen}`,
|
|
227
240
|
].join(" "))
|
|
228
241
|
.join("\n")}`;
|
|
229
242
|
}
|
|
230
243
|
function compactRoomStatus(status) {
|
|
231
|
-
return `\nroom=${status.room} messages=${status.message_count} roster=${status.roster_count}${status.last_message_at ? ` last_message_at=${status.last_message_at}` : ""}${status.last_message_from ? ` last_from=${status.last_message_from}` : ""}${status.last_message_type ? ` last_type=${status.last_message_type}` : ""}${status.last_message_summary ? ` last_summary=${compactPreview(status.last_message_summary)}` : ""}`;
|
|
244
|
+
return `\nroom=${status.room} messages=${status.message_count} roster=${status.roster_count}${status.diagnostics_count ? ` diagnostics=${status.diagnostics_count}` : ""}${status.last_message_at ? ` last_message_at=${status.last_message_at}` : ""}${status.last_message_from ? ` last_from=${status.last_message_from}` : ""}${status.last_message_type ? ` last_type=${status.last_message_type}` : ""}${status.last_message_summary ? ` last_summary=${compactPreview(status.last_message_summary)}` : ""}`;
|
|
232
245
|
}
|
|
233
246
|
function compactCommunicationSnapshot(snapshot) {
|
|
234
247
|
if (!snapshot)
|
|
@@ -245,19 +258,72 @@ function compactInboxMessages(messages, emptyLabel) {
|
|
|
245
258
|
`type=${String(message.type ?? "")}`,
|
|
246
259
|
`from=${String(message.from ?? "")}`,
|
|
247
260
|
`to=${String(message.to ?? "")}`,
|
|
248
|
-
...(message.queued_at
|
|
261
|
+
...(message.queued_at
|
|
262
|
+
? [`queued_at=${String(message.queued_at)}`]
|
|
263
|
+
: []),
|
|
249
264
|
...(message.sent_at ? [`sent_at=${String(message.sent_at)}`] : []),
|
|
250
|
-
...(message.claimed_at
|
|
251
|
-
|
|
252
|
-
|
|
265
|
+
...(message.claimed_at
|
|
266
|
+
? [`claimed_at=${String(message.claimed_at)}`]
|
|
267
|
+
: []),
|
|
268
|
+
...(message.handled_at
|
|
269
|
+
? [`handled_at=${String(message.handled_at)}`]
|
|
270
|
+
: []),
|
|
271
|
+
...(message.failed_at
|
|
272
|
+
? [`failed_at=${String(message.failed_at)}`]
|
|
273
|
+
: []),
|
|
253
274
|
].join(" "))
|
|
254
275
|
.join("\n")}`;
|
|
255
276
|
}
|
|
256
277
|
function compactBranchInbox(messages) {
|
|
257
278
|
return compactInboxMessages(messages, "branch inbox");
|
|
258
279
|
}
|
|
280
|
+
function normalizeMailboxEntry(value) {
|
|
281
|
+
if (typeof value === "string")
|
|
282
|
+
return { type: value };
|
|
283
|
+
const record = asRecord(value);
|
|
284
|
+
if (typeof record.type !== "string" || !record.type.trim())
|
|
285
|
+
return undefined;
|
|
286
|
+
return {
|
|
287
|
+
type: record.type.trim(),
|
|
288
|
+
...(record.body_schema !== undefined
|
|
289
|
+
? { body_schema: record.body_schema }
|
|
290
|
+
: {}),
|
|
291
|
+
...(record.ack !== undefined ? { ack: record.ack } : {}),
|
|
292
|
+
...(typeof record.idempotency === "string"
|
|
293
|
+
? { idempotency: record.idempotency }
|
|
294
|
+
: {}),
|
|
295
|
+
...(typeof record.level === "string" ? { level: record.level } : {}),
|
|
296
|
+
...(record.requires_response === true ? { requires_response: true } : {}),
|
|
297
|
+
...(typeof record.summary === "string" ? { summary: record.summary } : {}),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function normalizeMailboxContracts(mailbox) {
|
|
301
|
+
const accepts = Array.isArray(mailbox.accepts)
|
|
302
|
+
? mailbox.accepts
|
|
303
|
+
.map(normalizeMailboxEntry)
|
|
304
|
+
.filter((entry) => Boolean(entry))
|
|
305
|
+
: [];
|
|
306
|
+
const emits = Array.isArray(mailbox.emits)
|
|
307
|
+
? mailbox.emits
|
|
308
|
+
.map(normalizeMailboxEntry)
|
|
309
|
+
.filter((entry) => Boolean(entry))
|
|
310
|
+
: [];
|
|
311
|
+
return { accepts, emits };
|
|
312
|
+
}
|
|
313
|
+
function mailboxTypes(entries) {
|
|
314
|
+
return entries.flatMap((entry) => typeof asRecord(entry).type === "string"
|
|
315
|
+
? [String(asRecord(entry).type)]
|
|
316
|
+
: []);
|
|
317
|
+
}
|
|
259
318
|
function compactRunMailbox(run, mailbox, messages) {
|
|
260
|
-
|
|
319
|
+
const normalized = normalizeMailboxContracts(mailbox);
|
|
320
|
+
return `\nrun=${run} accepts=${mailboxTypes(normalized.accepts).join(",")} emits=${mailboxTypes(normalized.emits).join(",")}${compactInboxMessages(messages, "run inbox")}`;
|
|
321
|
+
}
|
|
322
|
+
function compactArtifactPath(value) {
|
|
323
|
+
if (typeof value === "string")
|
|
324
|
+
return value;
|
|
325
|
+
const record = asRecord(value);
|
|
326
|
+
return String(record.path ?? "<missing>");
|
|
261
327
|
}
|
|
262
328
|
function compactActorFiles(status) {
|
|
263
329
|
const run = String(status.run ?? "<unknown>");
|
|
@@ -267,12 +333,14 @@ function compactActorFiles(status) {
|
|
|
267
333
|
status.stderrLog,
|
|
268
334
|
status.eventsFile,
|
|
269
335
|
status.outboxFile,
|
|
270
|
-
status.state_dir
|
|
336
|
+
status.state_dir
|
|
337
|
+
? `${String(status.state_dir)}/communication.json`
|
|
338
|
+
: undefined,
|
|
271
339
|
status.state_dir ? `${String(status.state_dir)}/result.json` : undefined,
|
|
272
340
|
].filter((file) => typeof file === "string");
|
|
273
341
|
const artifactText = Object.keys(artifacts).length
|
|
274
342
|
? ` artifacts=${Object.entries(artifacts)
|
|
275
|
-
.map(([key, value]) => `${key}:${
|
|
343
|
+
.map(([key, value]) => `${key}:${compactArtifactPath(value)}`)
|
|
276
344
|
.join(",")}`
|
|
277
345
|
: "";
|
|
278
346
|
return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}`;
|
|
@@ -302,11 +370,49 @@ function compactToolActor(name, tool) {
|
|
|
302
370
|
const properties = asRecord(parameters.properties);
|
|
303
371
|
return `\ntool=${name} description=${String(tool.description ?? "").replaceAll(/\s+/g, "_")} args=${Object.keys(properties).join(",")} required=${required}`;
|
|
304
372
|
}
|
|
373
|
+
function compactRecipeImports(summary) {
|
|
374
|
+
const active = Array.isArray(summary.active)
|
|
375
|
+
? summary.active
|
|
376
|
+
: [];
|
|
377
|
+
const lines = active.flatMap((entry) => {
|
|
378
|
+
const imports = asRecord(entry.imports);
|
|
379
|
+
return Object.entries(imports).map(([alias, value]) => {
|
|
380
|
+
const binding = typeof value === "string" ? { from: value } : asRecord(value);
|
|
381
|
+
return `recipe=${String(entry.id ?? "<unknown>")} alias=${alias} from=${String(binding.from ?? value)}`;
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
return lines.length ? `\n${lines.join("\n")}` : "\n(no recipe imports)";
|
|
385
|
+
}
|
|
386
|
+
function compactRecipeDoctor(summary) {
|
|
387
|
+
const details = Array.isArray(summary.diagnostic_details)
|
|
388
|
+
? summary.diagnostic_details
|
|
389
|
+
: [];
|
|
390
|
+
const recommendations = Array.isArray(summary.recommendations)
|
|
391
|
+
? summary.recommendations
|
|
392
|
+
: [];
|
|
393
|
+
const counts = { error: 0, info: 0, warning: 0 };
|
|
394
|
+
for (const detail of details) {
|
|
395
|
+
const severity = String(detail.severity ?? "info");
|
|
396
|
+
if (severity === "error" || severity === "warning" || severity === "info")
|
|
397
|
+
counts[severity] += 1;
|
|
398
|
+
}
|
|
399
|
+
const lines = [
|
|
400
|
+
`recipes doctor errors=${counts.error} warnings=${counts.warning} info=${counts.info} recommendations=${recommendations.length}`,
|
|
401
|
+
];
|
|
402
|
+
for (const detail of details.slice(0, 8)) {
|
|
403
|
+
lines.push(`${String(detail.severity ?? "info")} id=${String(detail.id ?? "root")} action=${compactPreview(detail.action, Limits.DOCTOR_ACTION_PREVIEW_CHARS) ?? "inspect"}`);
|
|
404
|
+
}
|
|
405
|
+
return `\n${lines.join("\n")}`;
|
|
406
|
+
}
|
|
305
407
|
function compactRecipeRegistry(summary) {
|
|
306
408
|
const active = Array.isArray(summary.active) ? summary.active.length : 0;
|
|
307
|
-
const shadowed = Array.isArray(summary.shadowed)
|
|
409
|
+
const shadowed = Array.isArray(summary.shadowed)
|
|
410
|
+
? summary.shadowed.length
|
|
411
|
+
: 0;
|
|
308
412
|
const invalid = Array.isArray(summary.invalid) ? summary.invalid.length : 0;
|
|
309
|
-
const disabled = Array.isArray(summary.disabled)
|
|
413
|
+
const disabled = Array.isArray(summary.disabled)
|
|
414
|
+
? summary.disabled.length
|
|
415
|
+
: 0;
|
|
310
416
|
const diagnostics = Array.isArray(summary.diagnostics)
|
|
311
417
|
? summary.diagnostics.length
|
|
312
418
|
: 0;
|
|
@@ -420,6 +526,11 @@ function assertMessageSenderBelongsToRun(message, run, routeLabel) {
|
|
|
420
526
|
throw new Error(`message to ${routeLabel} requires from=run:${run} or branch:${run}/<branch>; got ${message.from}.`);
|
|
421
527
|
}
|
|
422
528
|
}
|
|
529
|
+
async function routeBranchEnvelope(stateDir, runId, recipient, message, _options) {
|
|
530
|
+
const branchMessage = { ...message, to: recipient };
|
|
531
|
+
ActorRooms.appendBranchInboxMessage(stateDir, runId, recipient, branchMessage);
|
|
532
|
+
return AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
533
|
+
}
|
|
423
534
|
function getRoomMulticastRecipients(message, run) {
|
|
424
535
|
const raw = message.metadata?.recipients;
|
|
425
536
|
if (raw === undefined)
|
|
@@ -471,13 +582,17 @@ export function createSpawnToolDefinition() {
|
|
|
471
582
|
run_id: runId,
|
|
472
583
|
state_dir: typeof input.state_dir === "string" ? input.state_dir : undefined,
|
|
473
584
|
...(input.template !== undefined
|
|
474
|
-
? {
|
|
585
|
+
? {
|
|
586
|
+
template: input.template,
|
|
587
|
+
}
|
|
475
588
|
: {}),
|
|
476
589
|
values: asRecord(input.values),
|
|
477
590
|
...(input.artifacts &&
|
|
478
591
|
typeof input.artifacts === "object" &&
|
|
479
592
|
!Array.isArray(input.artifacts)
|
|
480
|
-
? {
|
|
593
|
+
? {
|
|
594
|
+
artifacts: input.artifacts,
|
|
595
|
+
}
|
|
481
596
|
: {}),
|
|
482
597
|
}, ctx.cwd);
|
|
483
598
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
@@ -532,11 +647,15 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
532
647
|
const target = String(input.target ?? "");
|
|
533
648
|
const view = String(input.view ?? "");
|
|
534
649
|
if (target === "recipes" || target === "recipe-registry") {
|
|
535
|
-
if (view !== "status" && view !== "summary") {
|
|
536
|
-
throw new Error("inspect recipes supports view=status or view=
|
|
650
|
+
if (view !== "status" && view !== "summary" && view !== "doctor" && view !== "imports") {
|
|
651
|
+
throw new Error("inspect recipes supports view=status, view=summary, view=doctor, or view=imports.");
|
|
537
652
|
}
|
|
538
653
|
const discovered = RecipeDiscovery.discoverRecipeSources([
|
|
539
|
-
{
|
|
654
|
+
{
|
|
655
|
+
root: deps.recipeRoot ?? Paths.getRecipeRoot(),
|
|
656
|
+
defaultTool: true,
|
|
657
|
+
mutableUsage: true,
|
|
658
|
+
},
|
|
540
659
|
{ root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
|
|
541
660
|
]);
|
|
542
661
|
const summary = RecipeDiscovery.summarizeDiscovery(discovered);
|
|
@@ -544,7 +663,11 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
544
663
|
content: [
|
|
545
664
|
{
|
|
546
665
|
type: "text",
|
|
547
|
-
text: maybeJsonText(summary, input.verbose === true,
|
|
666
|
+
text: maybeJsonText(summary, input.verbose === true, view === "doctor"
|
|
667
|
+
? compactRecipeDoctor(summary)
|
|
668
|
+
: view === "imports"
|
|
669
|
+
? compactRecipeImports(summary)
|
|
670
|
+
: compactRecipeRegistry(summary)),
|
|
548
671
|
},
|
|
549
672
|
],
|
|
550
673
|
details: summary,
|
|
@@ -627,7 +750,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
627
750
|
};
|
|
628
751
|
}
|
|
629
752
|
if (view === "previews") {
|
|
630
|
-
const previews = ActorRooms.readRoomMessagePreviews(stateDir, address.room, Number(input.lines ||
|
|
753
|
+
const previews = ActorRooms.readRoomMessagePreviews(stateDir, address.room, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
|
|
631
754
|
return {
|
|
632
755
|
content: [
|
|
633
756
|
{
|
|
@@ -639,7 +762,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
639
762
|
};
|
|
640
763
|
}
|
|
641
764
|
if (view === "messages") {
|
|
642
|
-
const messages = ActorRooms.readRoomMessages(stateDir, address.room, Number(input.lines ||
|
|
765
|
+
const messages = ActorRooms.readRoomMessages(stateDir, address.room, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
|
|
643
766
|
return {
|
|
644
767
|
content: [
|
|
645
768
|
{
|
|
@@ -676,14 +799,16 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
676
799
|
}
|
|
677
800
|
throw new Error("inspect room:<run> supports view=status, view=messages, view=previews, view=roster, or view=contacts.");
|
|
678
801
|
}
|
|
679
|
-
const runId = address.kind === "run" || address.kind === "branch"
|
|
802
|
+
const runId = address.kind === "run" || address.kind === "branch"
|
|
803
|
+
? address.value
|
|
804
|
+
: undefined;
|
|
680
805
|
if (!runId)
|
|
681
806
|
throw new Error("inspect target must be run:<id>, branch:<run>/<branch>, coordinator, session:<id>, or tool:<name>.");
|
|
682
807
|
if (address.kind === "branch") {
|
|
683
808
|
if (view !== "mailbox")
|
|
684
809
|
throw new Error("inspect branch:<run>/<branch> supports view=mailbox.");
|
|
685
810
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
686
|
-
const branchInbox = ActorRooms.readBranchInboxDiagnostics(String(status.state_dir ?? ""), runId, target, Number(input.lines ||
|
|
811
|
+
const branchInbox = ActorRooms.readBranchInboxDiagnostics(String(status.state_dir ?? ""), runId, target, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
|
|
687
812
|
const messages = branchInbox.messages;
|
|
688
813
|
return {
|
|
689
814
|
content: [
|
|
@@ -710,7 +835,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
710
835
|
}
|
|
711
836
|
case "tail": {
|
|
712
837
|
assertRunAccessibleToContext(runId, ctx);
|
|
713
|
-
const text = AsyncRuns.tailRun(runId, Number(input.lines ||
|
|
838
|
+
const text = AsyncRuns.tailRun(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
|
|
714
839
|
return {
|
|
715
840
|
content: [{ type: "text", text: `\n${text}` }],
|
|
716
841
|
details: {},
|
|
@@ -718,7 +843,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
718
843
|
}
|
|
719
844
|
case "messages": {
|
|
720
845
|
assertRunAccessibleToContext(runId, ctx);
|
|
721
|
-
const messages = AsyncRuns.readRunEvents(runId, Number(input.lines ||
|
|
846
|
+
const messages = AsyncRuns.readRunEvents(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
|
|
722
847
|
return {
|
|
723
848
|
content: [
|
|
724
849
|
{
|
|
@@ -732,21 +857,30 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
732
857
|
case "artifacts":
|
|
733
858
|
case "files": {
|
|
734
859
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
860
|
+
const artifactManifest = AsyncRuns.resolveArtifactManifest(status.artifacts);
|
|
861
|
+
const details = artifactManifest
|
|
862
|
+
? { ...status, artifact_manifest: artifactManifest }
|
|
863
|
+
: status;
|
|
735
864
|
return {
|
|
736
865
|
content: [
|
|
737
866
|
{
|
|
738
867
|
type: "text",
|
|
739
|
-
text: maybeJsonText(
|
|
868
|
+
text: maybeJsonText(details, input.verbose === true, compactActorFiles(status)),
|
|
740
869
|
},
|
|
741
870
|
],
|
|
742
|
-
details
|
|
871
|
+
details,
|
|
743
872
|
};
|
|
744
873
|
}
|
|
745
874
|
case "mailbox": {
|
|
746
875
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
747
876
|
const mailbox = asRecord(status.mailbox);
|
|
748
|
-
const
|
|
749
|
-
const
|
|
877
|
+
const normalizedMailbox = normalizeMailboxContracts(mailbox);
|
|
878
|
+
const messages = AsyncRuns.readRunInboxMessages(runId, Number(input.lines || Limits.DEFAULT_INSPECT_LINES));
|
|
879
|
+
const details = {
|
|
880
|
+
mailbox,
|
|
881
|
+
normalized_mailbox: normalizedMailbox,
|
|
882
|
+
messages,
|
|
883
|
+
};
|
|
750
884
|
return {
|
|
751
885
|
content: [
|
|
752
886
|
{
|
|
@@ -802,7 +936,14 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
802
936
|
const address = ActorMessages.parseActorAddress(message.to);
|
|
803
937
|
let result;
|
|
804
938
|
if (address.kind === "run" && address.value) {
|
|
805
|
-
assertRunAccessibleToContext(address.value, ctx);
|
|
939
|
+
const status = assertRunAccessibleToContext(address.value, ctx);
|
|
940
|
+
const normalizedMailbox = normalizeMailboxContracts(asRecord(status.mailbox));
|
|
941
|
+
const acceptedTypes = new Set(mailboxTypes(normalizedMailbox.accepts));
|
|
942
|
+
const advisoryWarnings = acceptedTypes.size > 0 && !acceptedTypes.has(message.type)
|
|
943
|
+
? [
|
|
944
|
+
`Message type ${message.type} is not declared in mailbox.accepts for run:${address.value}.`,
|
|
945
|
+
]
|
|
946
|
+
: [];
|
|
806
947
|
if (message.type === "control.stop" ||
|
|
807
948
|
message.type === "control.cancel") {
|
|
808
949
|
result = AsyncRuns.cancelRun(address.value);
|
|
@@ -810,9 +951,21 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
810
951
|
else if (message.type === "control.kill") {
|
|
811
952
|
result = AsyncRuns.killRun(address.value);
|
|
812
953
|
}
|
|
954
|
+
else if (message.type === "control.archive") {
|
|
955
|
+
result = AsyncRuns.archiveRun(address.value);
|
|
956
|
+
}
|
|
957
|
+
else if (message.type === "control.prune") {
|
|
958
|
+
const body = asRecord(message.body);
|
|
959
|
+
result = AsyncRuns.pruneRun(address.value, {
|
|
960
|
+
preserveArtifacts: body.preserve_artifacts === true ||
|
|
961
|
+
body.preserveArtifacts === true,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
813
964
|
else {
|
|
814
965
|
result = await AsyncRuns.sendRunMessage(address.value, messageBodyToRunLine(message));
|
|
815
966
|
}
|
|
967
|
+
if (advisoryWarnings.length > 0)
|
|
968
|
+
result = { ...result, warnings: advisoryWarnings };
|
|
816
969
|
}
|
|
817
970
|
else if (address.kind === "branch" && address.value) {
|
|
818
971
|
const runId = address.value;
|
|
@@ -837,9 +990,10 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
837
990
|
}
|
|
838
991
|
}
|
|
839
992
|
ActorRooms.writeCommunicationSnapshot(stateDir, runId);
|
|
840
|
-
ActorRooms.appendBranchInboxMessage(stateDir, runId, message.to, message);
|
|
841
993
|
}
|
|
842
|
-
result = await
|
|
994
|
+
result = await routeBranchEnvelope(stateDir, runId, message.to, message, {
|
|
995
|
+
source: "direct",
|
|
996
|
+
});
|
|
843
997
|
}
|
|
844
998
|
else if (address.kind === "room" && address.value && address.room) {
|
|
845
999
|
const runId = address.value;
|
|
@@ -850,7 +1004,9 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
850
1004
|
throw new Error(`${message.to} has no run state directory.`);
|
|
851
1005
|
const recipients = getRoomMulticastRecipients(message, runId);
|
|
852
1006
|
const roomResult = ActorRooms.appendRoomMessage(stateDir, address.room, message);
|
|
853
|
-
await Promise.all(recipients.map((recipient) =>
|
|
1007
|
+
await Promise.all(recipients.map((recipient) => routeBranchEnvelope(stateDir, runId, recipient, message, {
|
|
1008
|
+
source: "room-multicast",
|
|
1009
|
+
})));
|
|
854
1010
|
result = {
|
|
855
1011
|
...roomResult,
|
|
856
1012
|
...(recipients.length > 0
|
|
@@ -898,7 +1054,10 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
898
1054
|
result = AsyncRuns.appendRunOutboxEvent(sender.value, {
|
|
899
1055
|
body: message.body,
|
|
900
1056
|
correlation_id: message.correlation_id,
|
|
901
|
-
delivery:
|
|
1057
|
+
delivery: message.metadata?.requires_response === true ||
|
|
1058
|
+
address.kind === "session"
|
|
1059
|
+
? "followup"
|
|
1060
|
+
: undefined,
|
|
902
1061
|
event: message.type,
|
|
903
1062
|
from: message.from,
|
|
904
1063
|
metadata: message.metadata,
|
|
@@ -965,7 +1124,7 @@ export function createRuntimeToolDefinition(cfg, exec) {
|
|
|
965
1124
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
966
1125
|
try {
|
|
967
1126
|
if (cfg.sourcePath)
|
|
968
|
-
RecipeUsage.recordRecipeLaunch(cfg.sourcePath);
|
|
1127
|
+
RecipeUsage.recordRecipeLaunch(cfg.sourcePath, new Date(), "tool");
|
|
969
1128
|
if (isAsyncRecipe) {
|
|
970
1129
|
const input = params;
|
|
971
1130
|
const { run_id, ...values } = input;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template recipe validator CLI logic.
|
|
3
|
+
* Zones: recipe validation, installed script entrypoint
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { readResolvedRecipeConfig } from "./recipe-references.js";
|
|
9
|
+
export function validateRecipeUsage() {
|
|
10
|
+
return `Usage:
|
|
11
|
+
validate-recipe.mjs <recipe-file-or-dir> [--all]
|
|
12
|
+
|
|
13
|
+
Validates one template recipe file, or all *.json/*.md files in a directory when --all is set.`;
|
|
14
|
+
}
|
|
15
|
+
function expandPath(value) {
|
|
16
|
+
return resolve(String(value).replace(/^~(?=\/|$)/, process.env.HOME ?? homedir()));
|
|
17
|
+
}
|
|
18
|
+
function templateKind(template) {
|
|
19
|
+
if (typeof template === "string")
|
|
20
|
+
return "leaf";
|
|
21
|
+
if (Array.isArray(template))
|
|
22
|
+
return "sequence";
|
|
23
|
+
if (template && typeof template === "object") {
|
|
24
|
+
const node = template;
|
|
25
|
+
if (typeof node.template === "string")
|
|
26
|
+
return "leaf";
|
|
27
|
+
if (Array.isArray(node.template))
|
|
28
|
+
return node.parallel === true ? "parallel" : "sequence";
|
|
29
|
+
if (node.parallel === true)
|
|
30
|
+
return "parallel";
|
|
31
|
+
return "object";
|
|
32
|
+
}
|
|
33
|
+
return "unknown";
|
|
34
|
+
}
|
|
35
|
+
function recipeFiles(target, all) {
|
|
36
|
+
if (!existsSync(target))
|
|
37
|
+
throw new Error(`Recipe path not found: ${target}`);
|
|
38
|
+
const stat = statSync(target);
|
|
39
|
+
if (stat.isFile())
|
|
40
|
+
return [target];
|
|
41
|
+
if (!stat.isDirectory())
|
|
42
|
+
throw new Error(`Recipe path is not a file or directory: ${target}`);
|
|
43
|
+
if (!all)
|
|
44
|
+
throw new Error("Directory validation requires --all.");
|
|
45
|
+
return readdirSync(target)
|
|
46
|
+
.filter((file) => file.endsWith(".json") || file.endsWith(".md"))
|
|
47
|
+
.sort((a, b) => a.localeCompare(b))
|
|
48
|
+
.map((file) => resolve(target, file));
|
|
49
|
+
}
|
|
50
|
+
function validateFile(file) {
|
|
51
|
+
try {
|
|
52
|
+
const config = readResolvedRecipeConfig(file);
|
|
53
|
+
if (!config?.template)
|
|
54
|
+
throw new Error("Recipe must define a non-empty template.");
|
|
55
|
+
return {
|
|
56
|
+
file,
|
|
57
|
+
ok: true,
|
|
58
|
+
name: config.name ?? "",
|
|
59
|
+
async: Boolean(config.async),
|
|
60
|
+
args: Array.isArray(config.args) ? config.args : [],
|
|
61
|
+
defaults: config.defaults && typeof config.defaults === "object"
|
|
62
|
+
? Object.keys(config.defaults).sort()
|
|
63
|
+
: [],
|
|
64
|
+
imports: config.imports && typeof config.imports === "object"
|
|
65
|
+
? Object.keys(config.imports).sort()
|
|
66
|
+
: [],
|
|
67
|
+
mailbox: config.mailbox && typeof config.mailbox === "object"
|
|
68
|
+
? {
|
|
69
|
+
accepts: Array.isArray(config.mailbox.accepts)
|
|
70
|
+
? config.mailbox.accepts
|
|
71
|
+
: [],
|
|
72
|
+
emits: Array.isArray(config.mailbox.emits)
|
|
73
|
+
? config.mailbox.emits
|
|
74
|
+
: [],
|
|
75
|
+
}
|
|
76
|
+
: undefined,
|
|
77
|
+
template: templateKind(config.template),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
file,
|
|
83
|
+
ok: false,
|
|
84
|
+
error: error instanceof Error ? error.message : String(error),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function validateRecipes(argv) {
|
|
89
|
+
const targetArg = argv.find((arg) => !arg.startsWith("-"));
|
|
90
|
+
const all = argv.includes("--all");
|
|
91
|
+
if (!targetArg || argv.includes("--help") || argv.includes("-h")) {
|
|
92
|
+
return { help: true, ok: Boolean(targetArg), usage: validateRecipeUsage() };
|
|
93
|
+
}
|
|
94
|
+
const files = recipeFiles(expandPath(targetArg), all);
|
|
95
|
+
const results = files.map(validateFile);
|
|
96
|
+
const failed = results.filter((result) => !result.ok).length;
|
|
97
|
+
return {
|
|
98
|
+
ok: failed === 0,
|
|
99
|
+
total: results.length,
|
|
100
|
+
passed: results.length - failed,
|
|
101
|
+
failed,
|
|
102
|
+
results,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"async": true,
|
|
3
|
+
"description": "Start a canonical mailbox-backed branch worker demo actor.",
|
|
4
|
+
"args": [
|
|
5
|
+
"repo:path",
|
|
6
|
+
"branch:string",
|
|
7
|
+
"poll_ms:int"
|
|
8
|
+
],
|
|
9
|
+
"defaults": {
|
|
10
|
+
"repo": "~/.pi/agent/extensions/pi-actors",
|
|
11
|
+
"branch": "worker",
|
|
12
|
+
"poll_ms": "1000"
|
|
13
|
+
},
|
|
14
|
+
"mailbox": {
|
|
15
|
+
"accepts": [
|
|
16
|
+
"task.assign",
|
|
17
|
+
"control.stop",
|
|
18
|
+
"control.cancel",
|
|
19
|
+
"control.kill"
|
|
20
|
+
],
|
|
21
|
+
"emits": [
|
|
22
|
+
"actor.join",
|
|
23
|
+
"task.claim",
|
|
24
|
+
"task.result",
|
|
25
|
+
"awaiting_assignment",
|
|
26
|
+
"actor.leave",
|
|
27
|
+
"run.done",
|
|
28
|
+
"run.failed"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"artifacts": {
|
|
32
|
+
"journal": "{state_dir}/worker-events.jsonl"
|
|
33
|
+
},
|
|
34
|
+
"template": "{repo}/scripts/actor-worker.mjs --state-dir {state_dir} --run {run_id} --branch {branch} --poll-ms {poll_ms}"
|
|
35
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"async": true,
|
|
3
|
+
"args": [
|
|
4
|
+
"repo:path",
|
|
5
|
+
"lease_ms:int"
|
|
6
|
+
],
|
|
7
|
+
"defaults": {
|
|
8
|
+
"repo": "~/.pi/agent/extensions/pi-actors",
|
|
9
|
+
"lease_ms": "600000"
|
|
10
|
+
},
|
|
11
|
+
"mailbox": {
|
|
12
|
+
"accepts": [
|
|
13
|
+
"coord.enqueue",
|
|
14
|
+
"coord.claim",
|
|
15
|
+
"coord.complete",
|
|
16
|
+
"coord.fail",
|
|
17
|
+
"lock.acquire",
|
|
18
|
+
"lock.renew",
|
|
19
|
+
"lock.release",
|
|
20
|
+
"control.stop",
|
|
21
|
+
"control.cancel",
|
|
22
|
+
"control.kill"
|
|
23
|
+
],
|
|
24
|
+
"emits": [
|
|
25
|
+
"coord.started",
|
|
26
|
+
"coord.enqueued",
|
|
27
|
+
"coord.assigned",
|
|
28
|
+
"coord.empty",
|
|
29
|
+
"coord.completed",
|
|
30
|
+
"coord.failed",
|
|
31
|
+
"lock.granted",
|
|
32
|
+
"lock.denied",
|
|
33
|
+
"lock.renewed",
|
|
34
|
+
"lock.released",
|
|
35
|
+
"run.done",
|
|
36
|
+
"run.failed"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"artifacts": {
|
|
40
|
+
"queue": "{state_dir}/queue.json",
|
|
41
|
+
"locks": "{state_dir}/locks.json",
|
|
42
|
+
"journal": "{state_dir}/journal.jsonl"
|
|
43
|
+
},
|
|
44
|
+
"template": "{repo}/scripts/locker.mjs serve --state-dir {state_dir} --lease-ms {lease_ms}"
|
|
45
|
+
}
|