@llblab/pi-actors 0.14.3 → 0.16.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 +5 -1
- package/BACKLOG.md +54 -32
- package/CHANGELOG.md +39 -0
- package/README.md +53 -61
- package/banner.jpg +0 -0
- package/docs/actor-messages.md +1 -1
- package/docs/async-runs.md +4 -4
- package/docs/command-templates.md +11 -11
- package/docs/recipe-library.md +7 -3
- package/docs/task-first-recipes.md +44 -43
- package/docs/template-recipes.md +45 -23
- package/docs/tool-registry.md +50 -42
- package/index.ts +34 -0
- package/lib/actor-messages.ts +20 -7
- package/lib/async-runs.ts +35 -12
- package/lib/command-templates.ts +6 -1
- package/lib/config.ts +3 -2
- package/lib/execution.ts +9 -5
- package/lib/observability.ts +20 -10
- package/lib/paths.ts +6 -1
- package/lib/prompts.ts +14 -21
- package/lib/recipe-discovery.ts +226 -0
- package/lib/recipe-migration.ts +123 -0
- package/lib/recipe-references.ts +45 -13
- package/lib/recipe-usage.ts +44 -0
- package/lib/registry.ts +48 -15
- package/lib/runtime.ts +59 -15
- package/lib/tools.ts +237 -65
- package/package.json +21 -11
- package/recipes/coordinator-locker.json +46 -0
- package/recipes/music-player.json +16 -2
- package/recipes/pipeline-architect-coordinator.json +11 -3
- package/recipes/pipeline-artifact-bundle.json +12 -3
- package/recipes/pipeline-artifact-report.json +9 -3
- package/recipes/pipeline-artifact-write.json +9 -3
- package/recipes/pipeline-async-run-ops.json +18 -9
- package/recipes/pipeline-checkpoint-continuation.json +14 -3
- package/recipes/pipeline-development-tasking.json +12 -3
- package/recipes/pipeline-docs-maintenance.json +12 -3
- package/recipes/pipeline-media-library.json +12 -3
- package/recipes/pipeline-quorum-review.json +12 -9
- package/recipes/pipeline-release-readiness.json +27 -9
- package/recipes/pipeline-release-summary.json +89 -0
- package/recipes/pipeline-repo-health.json +12 -3
- package/recipes/pipeline-research-synthesis.json +11 -3
- package/recipes/pipeline-review-readiness.json +12 -6
- package/recipes/subagent-artifact.json +9 -3
- package/recipes/subagent-checkpoint.json +10 -3
- package/recipes/subagent-conflict-report.json +11 -3
- package/recipes/subagent-contradiction-map.json +11 -3
- package/recipes/subagent-critic.json +11 -3
- package/recipes/subagent-evidence-map.json +11 -3
- package/recipes/subagent-followup.json +10 -3
- package/recipes/subagent-judge.json +11 -3
- package/recipes/subagent-merge.json +11 -3
- package/recipes/subagent-message.json +8 -3
- package/recipes/subagent-normalize.json +11 -3
- package/recipes/subagent-plan.json +11 -3
- package/recipes/subagent-prompt.json +10 -3
- package/recipes/subagent-quorum.json +10 -7
- package/recipes/subagent-review-coordinator.json +14 -6
- package/recipes/subagent-review.json +11 -3
- package/recipes/subagent-task-card.json +11 -3
- package/recipes/subagent-tools.json +10 -3
- package/recipes/subagent-verify.json +11 -3
- package/recipes/subagents-prompts.json +10 -3
- package/recipes/utility-coordinator-lock-snapshot.json +14 -0
- package/recipes/utility-run-ops-snapshot.json +3 -3
- package/recipes/utility-skill-summary.json +14 -0
- package/scripts/coordinator-locker.mjs +272 -0
- package/scripts/music-player.mjs +2 -1
- package/scripts/recipe-utils.mjs +239 -81
- package/scripts/validate-recipe.mjs +28 -10
- package/skills/actors/SKILL.md +301 -0
- package/skills/swarm/SKILL.md +451 -0
- package/skills/swarm/references/development-swarm.md +596 -0
package/lib/tools.ts
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
* Owns generated runtime tool schemas and the register_tool management tool schema
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { RegisteredTool } from "./config.ts";
|
|
8
7
|
import * as ActorMessages from "./actor-messages.ts";
|
|
9
8
|
import * as AsyncRuns from "./async-runs.ts";
|
|
10
9
|
import * as CommandTemplates from "./command-templates.ts";
|
|
10
|
+
import type { RegisteredTool } from "./config.ts";
|
|
11
11
|
import * as Execution from "./execution.ts";
|
|
12
|
+
import * as Paths from "./paths.ts";
|
|
12
13
|
import * as Prompts from "./prompts.ts";
|
|
14
|
+
import * as RecipeDiscovery from "./recipe-discovery.ts";
|
|
13
15
|
import * as RecipeReferences from "./recipe-references.ts";
|
|
16
|
+
import * as RecipeUsage from "./recipe-usage.ts";
|
|
14
17
|
import * as Registry from "./registry.ts";
|
|
15
18
|
import * as Schema from "./schema.ts";
|
|
16
19
|
|
|
@@ -86,8 +89,7 @@ function sampleValueForArg(
|
|
|
86
89
|
function shouldAddRuntimeToolUsageHint(error: unknown): boolean {
|
|
87
90
|
const message = error instanceof Error ? error.message : String(error);
|
|
88
91
|
return (
|
|
89
|
-
/^Argument \S+ must /.test(message) ||
|
|
90
|
-
/^Missing .* value: /.test(message)
|
|
92
|
+
/^Argument \S+ must /.test(message) || /^Missing .* value: /.test(message)
|
|
91
93
|
);
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -176,16 +178,15 @@ function compactAsyncRunStatus(value: unknown): string {
|
|
|
176
178
|
return `\n${tokens.join(" ")}`;
|
|
177
179
|
}
|
|
178
180
|
|
|
179
|
-
function
|
|
180
|
-
if (
|
|
181
|
-
return `\n${
|
|
182
|
-
.map((
|
|
181
|
+
function compactRunMessages(messages: AsyncRuns.RunOutboxEvent[]): string {
|
|
182
|
+
if (messages.length === 0) return "\n(no actor messages)";
|
|
183
|
+
return `\n${messages
|
|
184
|
+
.map((message) =>
|
|
183
185
|
[
|
|
184
|
-
`run=${
|
|
185
|
-
`
|
|
186
|
-
`level=${
|
|
187
|
-
`
|
|
188
|
-
`summary=${event.summary.replaceAll(/\s+/g, "_")}`,
|
|
186
|
+
`run=${message.run}`,
|
|
187
|
+
`type=${message.event}`,
|
|
188
|
+
`level=${message.level}`,
|
|
189
|
+
`summary=${message.summary.replaceAll(/\s+/g, "_")}`,
|
|
189
190
|
].join(" "),
|
|
190
191
|
)
|
|
191
192
|
.join("\n")}`;
|
|
@@ -209,10 +210,16 @@ function compactActorFiles(status: Record<string, unknown>): string {
|
|
|
209
210
|
return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}`;
|
|
210
211
|
}
|
|
211
212
|
|
|
212
|
-
function compactSessionRuns(
|
|
213
|
+
function compactSessionRuns(
|
|
214
|
+
session: string,
|
|
215
|
+
runs: Array<Record<string, unknown>>,
|
|
216
|
+
): string {
|
|
213
217
|
if (runs.length === 0) return `\nsession=${session} runs=0`;
|
|
214
218
|
return `\nsession=${session} runs=${runs.length}\n${runs
|
|
215
|
-
.map(
|
|
219
|
+
.map(
|
|
220
|
+
(run) =>
|
|
221
|
+
`run=${String(run.run ?? "")} status=${String(run.status ?? "")}${run.recipe ? ` recipe=${String(run.recipe)}` : ""}`,
|
|
222
|
+
)
|
|
216
223
|
.join("\n")}`;
|
|
217
224
|
}
|
|
218
225
|
|
|
@@ -225,6 +232,17 @@ function compactToolActor(name: string, tool: Record<string, unknown>): string {
|
|
|
225
232
|
return `\ntool=${name} description=${String(tool.description ?? "").replaceAll(/\s+/g, "_")} args=${Object.keys(properties).join(",")} required=${required}`;
|
|
226
233
|
}
|
|
227
234
|
|
|
235
|
+
function compactRecipeRegistry(summary: Record<string, unknown>): string {
|
|
236
|
+
const active = Array.isArray(summary.active) ? summary.active.length : 0;
|
|
237
|
+
const shadowed = Array.isArray(summary.shadowed) ? summary.shadowed.length : 0;
|
|
238
|
+
const invalid = Array.isArray(summary.invalid) ? summary.invalid.length : 0;
|
|
239
|
+
const disabled = Array.isArray(summary.disabled) ? summary.disabled.length : 0;
|
|
240
|
+
const diagnostics = Array.isArray(summary.diagnostics)
|
|
241
|
+
? summary.diagnostics.length
|
|
242
|
+
: 0;
|
|
243
|
+
return `\nrecipes active=${active} shadowed=${shadowed} invalid=${invalid} disabled=${disabled} diagnostics=${diagnostics}`;
|
|
244
|
+
}
|
|
245
|
+
|
|
228
246
|
function compactActorMessageResult(
|
|
229
247
|
message: ActorMessages.ActorMessage,
|
|
230
248
|
result: Record<string, unknown>,
|
|
@@ -236,7 +254,7 @@ function compactActorMessageResult(
|
|
|
236
254
|
];
|
|
237
255
|
if (result.bytes !== undefined) tokens.push(`bytes=${String(result.bytes)}`);
|
|
238
256
|
if (result.control) tokens.push(`control=${String(result.control)}`);
|
|
239
|
-
if (result.outbox) tokens.push(`
|
|
257
|
+
if (result.outbox) tokens.push(`messages=${String(result.outbox)}`);
|
|
240
258
|
if (result.tool) tokens.push(`tool=${String(result.tool)}`);
|
|
241
259
|
if (result.stopped === true) tokens.push("stopped=true");
|
|
242
260
|
if (result.signal) tokens.push(`signal=${String(result.signal)}`);
|
|
@@ -309,15 +327,23 @@ function messageBodyToRunLine(message: ActorMessages.ActorMessage): string {
|
|
|
309
327
|
return JSON.stringify(message.body);
|
|
310
328
|
}
|
|
311
329
|
|
|
312
|
-
function messageBodyToToolParams(
|
|
313
|
-
|
|
330
|
+
function messageBodyToToolParams(
|
|
331
|
+
message: ActorMessages.ActorMessage,
|
|
332
|
+
): Record<string, unknown> {
|
|
333
|
+
if (
|
|
334
|
+
message.body &&
|
|
335
|
+
typeof message.body === "object" &&
|
|
336
|
+
!Array.isArray(message.body)
|
|
337
|
+
) {
|
|
314
338
|
return message.body as Record<string, unknown>;
|
|
315
339
|
}
|
|
316
340
|
if (message.body === undefined) return {};
|
|
317
341
|
return { input: message.body };
|
|
318
342
|
}
|
|
319
343
|
|
|
320
|
-
function runIdFromActorAddress(
|
|
344
|
+
function runIdFromActorAddress(
|
|
345
|
+
address: string | undefined,
|
|
346
|
+
): string | undefined {
|
|
321
347
|
if (!address) return undefined;
|
|
322
348
|
const parsed = ActorMessages.parseActorAddress(address);
|
|
323
349
|
if (parsed.kind !== "run" || !parsed.value) {
|
|
@@ -336,10 +362,18 @@ export function createSpawnToolDefinition<
|
|
|
336
362
|
"Create an addressable actor from a recipe file or inline command template. Currently spawns run:<id> actors backed by async runs.",
|
|
337
363
|
parameters: objectSchema(
|
|
338
364
|
{
|
|
339
|
-
artifacts: looseObjectSchema(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
365
|
+
artifacts: looseObjectSchema(
|
|
366
|
+
"Optional named artifact paths for the spawned actor.",
|
|
367
|
+
),
|
|
368
|
+
as: stringSchema(
|
|
369
|
+
"Optional actor address for the spawned run, e.g. run:<id>.",
|
|
370
|
+
),
|
|
371
|
+
file: stringSchema(
|
|
372
|
+
"Optional template recipe JSON file. Bare names resolve under ~/.pi/agent/recipes.",
|
|
373
|
+
),
|
|
374
|
+
recipe: stringSchema(
|
|
375
|
+
"Alias for file; template recipe JSON file/name to spawn.",
|
|
376
|
+
),
|
|
343
377
|
state_dir: stringSchema("Optional explicit run state directory."),
|
|
344
378
|
template: unionSchema([
|
|
345
379
|
stringSchema("Inline command template string"),
|
|
@@ -348,7 +382,9 @@ export function createSpawnToolDefinition<
|
|
|
348
382
|
"Inline command-template object with flags such as parallel, repeat, retry, failure, and nested template.",
|
|
349
383
|
),
|
|
350
384
|
]),
|
|
351
|
-
values: looseObjectSchema(
|
|
385
|
+
values: looseObjectSchema(
|
|
386
|
+
"Runtime placeholder values passed to the actor.",
|
|
387
|
+
),
|
|
352
388
|
verbose: booleanSchema("Return full JSON instead of compact text."),
|
|
353
389
|
},
|
|
354
390
|
[],
|
|
@@ -366,13 +402,21 @@ export function createSpawnToolDefinition<
|
|
|
366
402
|
);
|
|
367
403
|
const meta = AsyncRuns.startRun(
|
|
368
404
|
{
|
|
369
|
-
file:
|
|
405
|
+
file:
|
|
406
|
+
typeof input.file === "string"
|
|
407
|
+
? input.file
|
|
408
|
+
: typeof input.recipe === "string"
|
|
409
|
+
? input.recipe
|
|
410
|
+
: undefined,
|
|
370
411
|
ownerId: getRunOwnerId(ctx),
|
|
371
412
|
run_id: runId,
|
|
372
|
-
state_dir:
|
|
413
|
+
state_dir:
|
|
414
|
+
typeof input.state_dir === "string" ? input.state_dir : undefined,
|
|
373
415
|
template: input.template as AsyncRuns.AsyncRunStartParams["template"],
|
|
374
416
|
values: asRecord(input.values),
|
|
375
|
-
...(input.artifacts &&
|
|
417
|
+
...(input.artifacts &&
|
|
418
|
+
typeof input.artifacts === "object" &&
|
|
419
|
+
!Array.isArray(input.artifacts)
|
|
376
420
|
? { artifacts: input.artifacts as Record<string, string> }
|
|
377
421
|
: {}),
|
|
378
422
|
},
|
|
@@ -382,7 +426,11 @@ export function createSpawnToolDefinition<
|
|
|
382
426
|
content: [
|
|
383
427
|
{
|
|
384
428
|
type: "text" as const,
|
|
385
|
-
text: maybeJsonText(
|
|
429
|
+
text: maybeJsonText(
|
|
430
|
+
meta,
|
|
431
|
+
input.verbose === true,
|
|
432
|
+
compactAsyncRunStatus(meta),
|
|
433
|
+
),
|
|
386
434
|
},
|
|
387
435
|
],
|
|
388
436
|
details: meta,
|
|
@@ -393,25 +441,36 @@ export function createSpawnToolDefinition<
|
|
|
393
441
|
|
|
394
442
|
export interface InspectToolDeps<TContext = unknown> {
|
|
395
443
|
getTool?: (name: string) => any | undefined;
|
|
444
|
+
packagedRecipeRoot?: string;
|
|
445
|
+
recipeRoot?: string;
|
|
396
446
|
}
|
|
397
447
|
|
|
398
448
|
function getContextSessionId(ctx: unknown): string | undefined {
|
|
399
|
-
return (
|
|
449
|
+
return (
|
|
450
|
+
ctx as AsyncRunToolContext | undefined
|
|
451
|
+
)?.sessionManager?.getSessionId?.();
|
|
400
452
|
}
|
|
401
453
|
|
|
402
454
|
function requireContextSessionId(ctx: unknown, actor: string): string {
|
|
403
455
|
const sessionId = getContextSessionId(ctx);
|
|
404
456
|
if (!sessionId) {
|
|
405
|
-
throw new Error(
|
|
457
|
+
throw new Error(
|
|
458
|
+
`${actor} requires a current coordinator session; use session:<id> or session:all for explicit session inventory.`,
|
|
459
|
+
);
|
|
406
460
|
}
|
|
407
461
|
return sessionId;
|
|
408
462
|
}
|
|
409
463
|
|
|
410
|
-
function assertRunAccessibleToContext(
|
|
464
|
+
function assertRunAccessibleToContext(
|
|
465
|
+
runId: string,
|
|
466
|
+
ctx: unknown,
|
|
467
|
+
): Record<string, unknown> {
|
|
411
468
|
const status = AsyncRuns.getRunStatus(runId);
|
|
412
469
|
const sessionId = getContextSessionId(ctx);
|
|
413
470
|
if (sessionId && status.ownerId && status.ownerId !== sessionId) {
|
|
414
|
-
throw new Error(
|
|
471
|
+
throw new Error(
|
|
472
|
+
`run:${runId} is owned by session:${status.ownerId}; current session is ${sessionId}.`,
|
|
473
|
+
);
|
|
415
474
|
}
|
|
416
475
|
return status;
|
|
417
476
|
}
|
|
@@ -427,10 +486,18 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
427
486
|
parameters: objectSchema(
|
|
428
487
|
{
|
|
429
488
|
lines: stringSchema("Line count for tail/messages views. Default 40."),
|
|
430
|
-
status: stringSchema(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
489
|
+
status: stringSchema(
|
|
490
|
+
"Optional session run filter: all, running, active, terminal, done, failed, cancelled, killed, or exited.",
|
|
491
|
+
),
|
|
492
|
+
target: stringSchema(
|
|
493
|
+
"Actor address to inspect, e.g. run:<id>, coordinator, session:<id>, session:all, or tool:<name>.",
|
|
494
|
+
),
|
|
495
|
+
verbose: booleanSchema(
|
|
496
|
+
"Return full JSON instead of compact text where available.",
|
|
497
|
+
),
|
|
498
|
+
view: stringSchema(
|
|
499
|
+
"Inspection view: status, tail, messages, artifacts, files, or mailbox.",
|
|
500
|
+
),
|
|
434
501
|
},
|
|
435
502
|
["target", "view"],
|
|
436
503
|
),
|
|
@@ -443,21 +510,53 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
443
510
|
) {
|
|
444
511
|
const input = asRecord(params);
|
|
445
512
|
const target = String(input.target ?? "");
|
|
446
|
-
const address = ActorMessages.parseActorAddress(target);
|
|
447
513
|
const view = String(input.view ?? "");
|
|
514
|
+
if (target === "recipes" || target === "recipe-registry") {
|
|
515
|
+
if (view !== "status" && view !== "summary") {
|
|
516
|
+
throw new Error("inspect recipes supports view=status or view=summary.");
|
|
517
|
+
}
|
|
518
|
+
const discovered = RecipeDiscovery.discoverRecipeSources([
|
|
519
|
+
{ root: deps.recipeRoot ?? Paths.getRecipeRoot(), defaultTool: true, mutableUsage: true },
|
|
520
|
+
{ root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
|
|
521
|
+
]);
|
|
522
|
+
const summary = RecipeDiscovery.summarizeDiscovery(discovered);
|
|
523
|
+
return {
|
|
524
|
+
content: [
|
|
525
|
+
{
|
|
526
|
+
type: "text" as const,
|
|
527
|
+
text: maybeJsonText(
|
|
528
|
+
summary,
|
|
529
|
+
input.verbose === true,
|
|
530
|
+
compactRecipeRegistry(summary),
|
|
531
|
+
),
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
details: summary,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
const address = ActorMessages.parseActorAddress(target);
|
|
448
538
|
if (address.kind === "coordinator") {
|
|
449
539
|
if (view !== "status" && view !== "runs") {
|
|
450
|
-
throw new Error(
|
|
540
|
+
throw new Error(
|
|
541
|
+
"inspect coordinator supports view=status or view=runs.",
|
|
542
|
+
);
|
|
451
543
|
}
|
|
452
544
|
const session = requireContextSessionId(ctx, "inspect coordinator");
|
|
453
|
-
const runs = AsyncRuns.listRuns(
|
|
545
|
+
const runs = AsyncRuns.listRuns(
|
|
546
|
+
undefined,
|
|
547
|
+
typeof input.status === "string" ? input.status : undefined,
|
|
548
|
+
)
|
|
454
549
|
.map((run) => AsyncRuns.getRunStatus(String(run.state_dir)))
|
|
455
550
|
.filter((run) => run.ownerId === session);
|
|
456
551
|
return {
|
|
457
552
|
content: [
|
|
458
553
|
{
|
|
459
554
|
type: "text" as const,
|
|
460
|
-
text: maybeJsonText(
|
|
555
|
+
text: maybeJsonText(
|
|
556
|
+
{ session, runs },
|
|
557
|
+
input.verbose === true,
|
|
558
|
+
compactSessionRuns(session, runs),
|
|
559
|
+
),
|
|
461
560
|
},
|
|
462
561
|
],
|
|
463
562
|
details: { session, runs },
|
|
@@ -465,16 +564,27 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
465
564
|
}
|
|
466
565
|
if (address.kind === "session") {
|
|
467
566
|
if (view !== "status" && view !== "runs") {
|
|
468
|
-
throw new Error(
|
|
567
|
+
throw new Error(
|
|
568
|
+
"inspect session:<id> supports view=status or view=runs.",
|
|
569
|
+
);
|
|
469
570
|
}
|
|
470
|
-
const runs = AsyncRuns.listRuns(
|
|
571
|
+
const runs = AsyncRuns.listRuns(
|
|
572
|
+
undefined,
|
|
573
|
+
typeof input.status === "string" ? input.status : undefined,
|
|
574
|
+
)
|
|
471
575
|
.map((run) => AsyncRuns.getRunStatus(String(run.state_dir)))
|
|
472
|
-
.filter(
|
|
576
|
+
.filter(
|
|
577
|
+
(run) => address.value === "all" || run.ownerId === address.value,
|
|
578
|
+
);
|
|
473
579
|
return {
|
|
474
580
|
content: [
|
|
475
581
|
{
|
|
476
582
|
type: "text" as const,
|
|
477
|
-
text: maybeJsonText(
|
|
583
|
+
text: maybeJsonText(
|
|
584
|
+
{ session: address.value, runs },
|
|
585
|
+
input.verbose === true,
|
|
586
|
+
compactSessionRuns(address.value || "", runs),
|
|
587
|
+
),
|
|
478
588
|
},
|
|
479
589
|
],
|
|
480
590
|
details: { session: address.value, runs },
|
|
@@ -482,7 +592,9 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
482
592
|
}
|
|
483
593
|
if (address.kind === "tool" && address.value) {
|
|
484
594
|
if (view !== "status" && view !== "schema") {
|
|
485
|
-
throw new Error(
|
|
595
|
+
throw new Error(
|
|
596
|
+
"inspect tool:<name> supports view=status or view=schema.",
|
|
597
|
+
);
|
|
486
598
|
}
|
|
487
599
|
const tool = deps.getTool?.(address.value);
|
|
488
600
|
if (!tool) throw new Error(`tool actor not found: ${address.value}`);
|
|
@@ -496,14 +608,21 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
496
608
|
content: [
|
|
497
609
|
{
|
|
498
610
|
type: "text" as const,
|
|
499
|
-
text: maybeJsonText(
|
|
611
|
+
text: maybeJsonText(
|
|
612
|
+
details,
|
|
613
|
+
input.verbose === true || view === "schema",
|
|
614
|
+
compactToolActor(address.value, details),
|
|
615
|
+
),
|
|
500
616
|
},
|
|
501
617
|
],
|
|
502
618
|
details,
|
|
503
619
|
};
|
|
504
620
|
}
|
|
505
621
|
const runId = address.kind === "run" ? address.value : undefined;
|
|
506
|
-
if (!runId)
|
|
622
|
+
if (!runId)
|
|
623
|
+
throw new Error(
|
|
624
|
+
"inspect target must be run:<id>, coordinator, session:<id>, or tool:<name>.",
|
|
625
|
+
);
|
|
507
626
|
switch (view) {
|
|
508
627
|
case "status": {
|
|
509
628
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
@@ -511,7 +630,11 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
511
630
|
content: [
|
|
512
631
|
{
|
|
513
632
|
type: "text" as const,
|
|
514
|
-
text: maybeJsonText(
|
|
633
|
+
text: maybeJsonText(
|
|
634
|
+
status,
|
|
635
|
+
input.verbose === true,
|
|
636
|
+
compactAsyncRunStatus(status),
|
|
637
|
+
),
|
|
515
638
|
},
|
|
516
639
|
],
|
|
517
640
|
details: status,
|
|
@@ -520,16 +643,26 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
520
643
|
case "tail": {
|
|
521
644
|
assertRunAccessibleToContext(runId, ctx);
|
|
522
645
|
const text = AsyncRuns.tailRun(runId, Number(input.lines || 40));
|
|
523
|
-
return {
|
|
646
|
+
return {
|
|
647
|
+
content: [{ type: "text" as const, text: `\n${text}` }],
|
|
648
|
+
details: {},
|
|
649
|
+
};
|
|
524
650
|
}
|
|
525
651
|
case "messages": {
|
|
526
652
|
assertRunAccessibleToContext(runId, ctx);
|
|
527
|
-
const messages = AsyncRuns.readRunEvents(
|
|
653
|
+
const messages = AsyncRuns.readRunEvents(
|
|
654
|
+
runId,
|
|
655
|
+
Number(input.lines || 40),
|
|
656
|
+
);
|
|
528
657
|
return {
|
|
529
658
|
content: [
|
|
530
659
|
{
|
|
531
660
|
type: "text" as const,
|
|
532
|
-
text: maybeJsonText(
|
|
661
|
+
text: maybeJsonText(
|
|
662
|
+
messages,
|
|
663
|
+
input.verbose === true,
|
|
664
|
+
compactRunMessages(messages),
|
|
665
|
+
),
|
|
533
666
|
},
|
|
534
667
|
],
|
|
535
668
|
details: { messages },
|
|
@@ -542,7 +675,11 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
542
675
|
content: [
|
|
543
676
|
{
|
|
544
677
|
type: "text" as const,
|
|
545
|
-
text: maybeJsonText(
|
|
678
|
+
text: maybeJsonText(
|
|
679
|
+
status,
|
|
680
|
+
input.verbose === true,
|
|
681
|
+
compactActorFiles(status),
|
|
682
|
+
),
|
|
546
683
|
},
|
|
547
684
|
],
|
|
548
685
|
details: status,
|
|
@@ -555,14 +692,20 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
555
692
|
content: [
|
|
556
693
|
{
|
|
557
694
|
type: "text" as const,
|
|
558
|
-
text: maybeJsonText(
|
|
695
|
+
text: maybeJsonText(
|
|
696
|
+
mailbox,
|
|
697
|
+
input.verbose === true,
|
|
698
|
+
`\nrun=${String(status.run ?? runId)} accepts=${Array.isArray(mailbox.accepts) ? mailbox.accepts.join(",") : ""} emits=${Array.isArray(mailbox.emits) ? mailbox.emits.join(",") : ""}`,
|
|
699
|
+
),
|
|
559
700
|
},
|
|
560
701
|
],
|
|
561
702
|
details: { mailbox },
|
|
562
703
|
};
|
|
563
704
|
}
|
|
564
705
|
default:
|
|
565
|
-
throw new Error(
|
|
706
|
+
throw new Error(
|
|
707
|
+
"inspect view must be one of: status, tail, messages, artifacts, files, mailbox.",
|
|
708
|
+
);
|
|
566
709
|
}
|
|
567
710
|
},
|
|
568
711
|
};
|
|
@@ -583,17 +726,29 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
583
726
|
parameters: objectSchema(
|
|
584
727
|
{
|
|
585
728
|
body: unionSchema([
|
|
586
|
-
stringSchema(
|
|
729
|
+
stringSchema(
|
|
730
|
+
"Message body. For run:<id>, this is the run-local command line.",
|
|
731
|
+
),
|
|
587
732
|
looseObjectSchema("Structured JSON message body."),
|
|
588
733
|
arraySchema("Structured JSON message body array."),
|
|
589
734
|
]),
|
|
590
|
-
correlation_id: stringSchema(
|
|
591
|
-
|
|
592
|
-
|
|
735
|
+
correlation_id: stringSchema(
|
|
736
|
+
"Optional correlation id for workflow/task linkage.",
|
|
737
|
+
),
|
|
738
|
+
from: stringSchema(
|
|
739
|
+
"Optional sender address, such as coordinator or run:<id>.",
|
|
740
|
+
),
|
|
741
|
+
metadata: looseObjectSchema(
|
|
742
|
+
"Optional structured metadata for routing or domain hints.",
|
|
743
|
+
),
|
|
593
744
|
reply_to: stringSchema("Optional message id this message replies to."),
|
|
594
745
|
summary: stringSchema("Optional short human-facing summary."),
|
|
595
|
-
to: stringSchema(
|
|
596
|
-
|
|
746
|
+
to: stringSchema(
|
|
747
|
+
"Destination actor address, e.g. run:<id>, branch:<run>/<branch>, coordinator, session:<id>, or tool:<name>.",
|
|
748
|
+
),
|
|
749
|
+
type: stringSchema(
|
|
750
|
+
"Semantic message type, e.g. control.approve or checkpoint.needs_scope.",
|
|
751
|
+
),
|
|
597
752
|
verbose: booleanSchema("Return full JSON instead of compact text."),
|
|
598
753
|
},
|
|
599
754
|
["to", "type"],
|
|
@@ -611,7 +766,10 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
611
766
|
let result: Record<string, unknown>;
|
|
612
767
|
if (address.kind === "run" && address.value) {
|
|
613
768
|
assertRunAccessibleToContext(address.value, ctx);
|
|
614
|
-
if (
|
|
769
|
+
if (
|
|
770
|
+
message.type === "control.stop" ||
|
|
771
|
+
message.type === "control.cancel"
|
|
772
|
+
) {
|
|
615
773
|
result = AsyncRuns.cancelRun(address.value);
|
|
616
774
|
} else if (message.type === "control.kill") {
|
|
617
775
|
result = AsyncRuns.killRun(address.value);
|
|
@@ -630,7 +788,9 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
630
788
|
} else if (address.kind === "tool" && address.value) {
|
|
631
789
|
const tool = deps.getTool?.(address.value);
|
|
632
790
|
if (!tool || typeof tool.execute !== "function") {
|
|
633
|
-
throw new Error(
|
|
791
|
+
throw new Error(
|
|
792
|
+
`tool actor not found or not executable: ${address.value}`,
|
|
793
|
+
);
|
|
634
794
|
}
|
|
635
795
|
const toolResult = await tool.execute(
|
|
636
796
|
`message:${message.type}`,
|
|
@@ -651,15 +811,21 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
651
811
|
}
|
|
652
812
|
const sender = ActorMessages.parseActorAddress(message.from);
|
|
653
813
|
if (sender.kind !== "run" || !sender.value) {
|
|
654
|
-
throw new Error(
|
|
814
|
+
throw new Error(
|
|
815
|
+
`message to ${address.kind} currently requires from=run:<id>.`,
|
|
816
|
+
);
|
|
655
817
|
}
|
|
656
818
|
const senderStatus = assertRunAccessibleToContext(sender.value, ctx);
|
|
657
819
|
if (address.kind === "session") {
|
|
658
820
|
if (!senderStatus.ownerId) {
|
|
659
|
-
throw new Error(
|
|
821
|
+
throw new Error(
|
|
822
|
+
`message to session:${address.value} requires sender run owner ${address.value}; got no owner.`,
|
|
823
|
+
);
|
|
660
824
|
}
|
|
661
825
|
if (senderStatus.ownerId !== address.value) {
|
|
662
|
-
throw new Error(
|
|
826
|
+
throw new Error(
|
|
827
|
+
`message to session:${address.value} requires sender run owner ${address.value}; got ${senderStatus.ownerId}.`,
|
|
828
|
+
);
|
|
663
829
|
}
|
|
664
830
|
}
|
|
665
831
|
result = AsyncRuns.appendRunOutboxEvent(sender.value, {
|
|
@@ -754,6 +920,7 @@ export function createRuntimeToolDefinition(
|
|
|
754
920
|
ctx: AsyncRunToolContext,
|
|
755
921
|
) {
|
|
756
922
|
try {
|
|
923
|
+
if (cfg.sourcePath) RecipeUsage.recordRecipeLaunch(cfg.sourcePath);
|
|
757
924
|
if (isAsyncRecipe) {
|
|
758
925
|
const input = params as Record<string, unknown>;
|
|
759
926
|
const { run_id, ...values } = input;
|
|
@@ -807,7 +974,12 @@ export function createRuntimeToolDefinition(
|
|
|
807
974
|
signal,
|
|
808
975
|
);
|
|
809
976
|
} catch (error) {
|
|
810
|
-
throw formatRuntimeToolArgumentError(
|
|
977
|
+
throw formatRuntimeToolArgumentError(
|
|
978
|
+
cfg,
|
|
979
|
+
error,
|
|
980
|
+
required,
|
|
981
|
+
isAsyncRecipe,
|
|
982
|
+
);
|
|
811
983
|
}
|
|
812
984
|
},
|
|
813
985
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-actors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Actor runtime and orchestrator for agent-managed local processes",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"actors",
|
|
10
|
+
"orchestration",
|
|
11
|
+
"automation",
|
|
12
|
+
"tools"
|
|
13
|
+
],
|
|
6
14
|
"type": "module",
|
|
7
15
|
"license": "MIT",
|
|
8
16
|
"repository": {
|
|
@@ -13,14 +21,9 @@
|
|
|
13
21
|
"bugs": {
|
|
14
22
|
"url": "https://github.com/llblab/pi-actors/issues"
|
|
15
23
|
},
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"actors",
|
|
20
|
-
"orchestration",
|
|
21
|
-
"automation",
|
|
22
|
-
"tools"
|
|
23
|
-
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22.19.0"
|
|
26
|
+
},
|
|
24
27
|
"scripts": {
|
|
25
28
|
"check": "node --experimental-strip-types -e \"await import('./index.ts'); console.log('pi-actors: extension import ok')\"",
|
|
26
29
|
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
@@ -32,16 +35,23 @@
|
|
|
32
35
|
"lib",
|
|
33
36
|
"scripts",
|
|
34
37
|
"recipes",
|
|
38
|
+
"skills",
|
|
35
39
|
"README.md",
|
|
36
40
|
"AGENTS.md",
|
|
37
41
|
"BACKLOG.md",
|
|
38
42
|
"CHANGELOG.md",
|
|
39
|
-
"docs"
|
|
43
|
+
"docs",
|
|
44
|
+
"banner.jpg"
|
|
40
45
|
],
|
|
41
46
|
"pi": {
|
|
42
47
|
"extensions": [
|
|
43
48
|
"./index.ts"
|
|
44
|
-
]
|
|
49
|
+
],
|
|
50
|
+
"skills": [
|
|
51
|
+
"./skills/actors/SKILL.md",
|
|
52
|
+
"./skills/swarm/SKILL.md"
|
|
53
|
+
],
|
|
54
|
+
"image": "https://github.com/llblab/pi-actors/raw/main/banner.jpg"
|
|
45
55
|
},
|
|
46
56
|
"peerDependencies": {
|
|
47
57
|
"@earendil-works/pi-coding-agent": "*"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coordinator-locker",
|
|
3
|
+
"async": true,
|
|
4
|
+
"args": [
|
|
5
|
+
"repo:path",
|
|
6
|
+
"lease_ms:int"
|
|
7
|
+
],
|
|
8
|
+
"defaults": {
|
|
9
|
+
"repo": "~/.pi/agent/extensions/pi-actors",
|
|
10
|
+
"lease_ms": "600000"
|
|
11
|
+
},
|
|
12
|
+
"mailbox": {
|
|
13
|
+
"accepts": [
|
|
14
|
+
"coord.enqueue",
|
|
15
|
+
"coord.claim",
|
|
16
|
+
"coord.complete",
|
|
17
|
+
"coord.fail",
|
|
18
|
+
"lock.acquire",
|
|
19
|
+
"lock.renew",
|
|
20
|
+
"lock.release",
|
|
21
|
+
"control.stop",
|
|
22
|
+
"control.cancel",
|
|
23
|
+
"control.kill"
|
|
24
|
+
],
|
|
25
|
+
"emits": [
|
|
26
|
+
"coord.started",
|
|
27
|
+
"coord.enqueued",
|
|
28
|
+
"coord.assigned",
|
|
29
|
+
"coord.empty",
|
|
30
|
+
"coord.completed",
|
|
31
|
+
"coord.failed",
|
|
32
|
+
"lock.granted",
|
|
33
|
+
"lock.denied",
|
|
34
|
+
"lock.renewed",
|
|
35
|
+
"lock.released",
|
|
36
|
+
"run.done",
|
|
37
|
+
"run.failed"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"artifacts": {
|
|
41
|
+
"queue": "{state_dir}/queue.json",
|
|
42
|
+
"locks": "{state_dir}/locks.json",
|
|
43
|
+
"journal": "{state_dir}/journal.jsonl"
|
|
44
|
+
},
|
|
45
|
+
"template": "{repo}/scripts/coordinator-locker.mjs serve --state-dir {state_dir} --lease-ms {lease_ms}"
|
|
46
|
+
}
|