@llblab/pi-actors 0.22.5 → 0.23.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 +1 -0
- package/BACKLOG.md +0 -220
- package/CHANGELOG.md +18 -1
- package/README.md +1 -1
- package/dist/index.js +15 -0
- package/dist/lib/actor-inspector-tui.d.ts +4 -0
- package/dist/lib/actor-inspector-tui.js +53 -12
- package/dist/lib/actor-rooms.d.ts +8 -0
- package/dist/lib/actor-rooms.js +61 -10
- package/dist/lib/async-runs.d.ts +34 -2
- package/dist/lib/async-runs.js +215 -22
- package/dist/lib/command-templates.js +5 -5
- package/dist/lib/limits.d.ts +11 -0
- package/dist/lib/limits.js +11 -0
- package/dist/lib/observability.js +6 -4
- package/dist/lib/output.js +4 -5
- package/dist/lib/recipe-discovery.js +1 -0
- package/dist/lib/recipe-references.d.ts +11 -2
- package/dist/lib/recipe-references.js +4 -2
- package/dist/lib/recipe-usage.d.ts +2 -1
- package/dist/lib/recipe-usage.js +15 -3
- package/dist/lib/runtime.js +12 -2
- package/dist/lib/tools.js +197 -38
- package/docs/async-runs.md +2 -1
- package/docs/template-recipes.md +1 -1
- package/docs/tool-registry.md +0 -1
- package/index.ts +18 -0
- package/lib/actor-inspector-tui.ts +131 -29
- package/lib/actor-rooms.ts +155 -44
- package/lib/async-runs.ts +290 -31
- package/lib/command-templates.ts +5 -5
- package/lib/limits.ts +12 -0
- package/lib/observability.ts +10 -4
- package/lib/output.ts +4 -6
- package/lib/recipe-discovery.ts +1 -0
- package/lib/recipe-references.ts +20 -4
- package/lib/recipe-usage.ts +31 -4
- package/lib/runtime.ts +31 -7
- package/lib/tools.ts +296 -57
- package/package.json +2 -1
- package/scripts/async-runner.mjs +6 -0
- package/scripts/conformance.mjs +47 -0
- package/scripts/coordinator.mjs +13 -0
- package/scripts/locker.mjs +13 -0
- package/scripts/music-player.mjs +21 -2
- package/scripts/recipe-utils.mjs +13 -0
- package/scripts/validate-recipe.mjs +13 -0
- package/skills/actors/SKILL.md +3 -3
- package/skills/swarm/SKILL.md +1 -1
package/lib/tools.ts
CHANGED
|
@@ -10,6 +10,7 @@ import * as AsyncRuns from "./async-runs.ts";
|
|
|
10
10
|
import * as CommandTemplates from "./command-templates.ts";
|
|
11
11
|
import type { RegisteredTool } from "./config.ts";
|
|
12
12
|
import * as Execution from "./execution.ts";
|
|
13
|
+
import * as Limits from "./limits.ts";
|
|
13
14
|
import * as Paths from "./paths.ts";
|
|
14
15
|
import * as Prompts from "./prompts.ts";
|
|
15
16
|
import * as RecipeDiscovery from "./recipe-discovery.ts";
|
|
@@ -164,7 +165,8 @@ function compactAsyncRunStatus(value: unknown): string {
|
|
|
164
165
|
];
|
|
165
166
|
if (status.tool) tokens.push(`tool=${String(status.tool)}`);
|
|
166
167
|
if (status.recipe) tokens.push(`recipe=${String(status.recipe)}`);
|
|
167
|
-
if (status.retire_when)
|
|
168
|
+
if (status.retire_when)
|
|
169
|
+
tokens.push(`retire_when=${String(status.retire_when)}`);
|
|
168
170
|
if (Number(status.pid) > 0) tokens.push(`pid=${Number(status.pid)}`);
|
|
169
171
|
if (progress.phase && progress.phase !== status.status)
|
|
170
172
|
tokens.push(`phase=${String(progress.phase)}`);
|
|
@@ -194,7 +196,10 @@ function compactRunMessages(messages: AsyncRuns.RunOutboxEvent[]): string {
|
|
|
194
196
|
.join("\n")}`;
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
function compactPreview(
|
|
199
|
+
function compactPreview(
|
|
200
|
+
value: unknown,
|
|
201
|
+
maxLength = Limits.COMPACT_PREVIEW_CHARS,
|
|
202
|
+
): string | undefined {
|
|
198
203
|
if (value === undefined) return undefined;
|
|
199
204
|
const text =
|
|
200
205
|
typeof value === "string" ? value : JSON.stringify(value, undefined, 0);
|
|
@@ -204,7 +209,9 @@ function compactPreview(value: unknown, maxLength = 80): string | undefined {
|
|
|
204
209
|
: compact;
|
|
205
210
|
}
|
|
206
211
|
|
|
207
|
-
function compactRoomPreviews(
|
|
212
|
+
function compactRoomPreviews(
|
|
213
|
+
previews: ActorRooms.RoomMessagePreview[],
|
|
214
|
+
): string {
|
|
208
215
|
if (previews.length === 0) return "\n(no room message previews)";
|
|
209
216
|
return `\n${previews
|
|
210
217
|
.map((preview) =>
|
|
@@ -214,7 +221,9 @@ function compactRoomPreviews(previews: ActorRooms.RoomMessagePreview[]): string
|
|
|
214
221
|
`to=${preview.to}`,
|
|
215
222
|
`type=${preview.type}`,
|
|
216
223
|
preview.summary ? `summary=${compactPreview(preview.summary)}` : "",
|
|
217
|
-
preview.body_preview
|
|
224
|
+
preview.body_preview
|
|
225
|
+
? `body=${compactPreview(preview.body_preview)}`
|
|
226
|
+
: "",
|
|
218
227
|
]
|
|
219
228
|
.filter(Boolean)
|
|
220
229
|
.join(" "),
|
|
@@ -232,7 +241,9 @@ function compactRoomMessages(messages: ActorRooms.RoomTimelineEntry[]): string {
|
|
|
232
241
|
`to=${message.to}`,
|
|
233
242
|
`type=${message.type}`,
|
|
234
243
|
`summary=${String(message.summary ?? "").replaceAll(/\s+/g, "_")}`,
|
|
235
|
-
compactPreview(message.body)
|
|
244
|
+
compactPreview(message.body)
|
|
245
|
+
? `body=${compactPreview(message.body)}`
|
|
246
|
+
: "",
|
|
236
247
|
]
|
|
237
248
|
.filter(Boolean)
|
|
238
249
|
.join(" "),
|
|
@@ -248,8 +259,12 @@ function compactRoomContacts(contacts: ActorRooms.RoomContact[]): string {
|
|
|
248
259
|
`address=${contact.address}`,
|
|
249
260
|
contact.role !== undefined ? `role=${String(contact.role)}` : "",
|
|
250
261
|
contact.parent !== undefined ? `parent=${String(contact.parent)}` : "",
|
|
251
|
-
contact.caps !== undefined
|
|
252
|
-
|
|
262
|
+
contact.caps !== undefined
|
|
263
|
+
? `caps=${Array.isArray(contact.caps) ? contact.caps.join(",") : String(contact.caps)}`
|
|
264
|
+
: "",
|
|
265
|
+
contact.claim !== undefined
|
|
266
|
+
? `claim=${String(contact.claim).replaceAll(/\s+/g, "_")}`
|
|
267
|
+
: "",
|
|
253
268
|
contact.status !== undefined ? `status=${String(contact.status)}` : "",
|
|
254
269
|
]
|
|
255
270
|
.filter(Boolean)
|
|
@@ -258,7 +273,9 @@ function compactRoomContacts(contacts: ActorRooms.RoomContact[]): string {
|
|
|
258
273
|
.join("\n")}`;
|
|
259
274
|
}
|
|
260
275
|
|
|
261
|
-
function compactRoomRoster(
|
|
276
|
+
function compactRoomRoster(
|
|
277
|
+
roster: Record<string, ActorRooms.RoomMember>,
|
|
278
|
+
): string {
|
|
262
279
|
const members = Object.values(roster);
|
|
263
280
|
if (members.length === 0) return "\n(no room members)";
|
|
264
281
|
return `\n${members
|
|
@@ -267,8 +284,12 @@ function compactRoomRoster(roster: Record<string, ActorRooms.RoomMember>): strin
|
|
|
267
284
|
`address=${member.address}`,
|
|
268
285
|
`role=${String(member.role ?? "")}`,
|
|
269
286
|
member.parent !== undefined ? `parent=${String(member.parent)}` : "",
|
|
270
|
-
member.caps !== undefined
|
|
271
|
-
|
|
287
|
+
member.caps !== undefined
|
|
288
|
+
? `caps=${Array.isArray(member.caps) ? member.caps.join(",") : String(member.caps)}`
|
|
289
|
+
: "",
|
|
290
|
+
member.claim !== undefined
|
|
291
|
+
? `claim=${String(member.claim).replaceAll(/\s+/g, "_")}`
|
|
292
|
+
: "",
|
|
272
293
|
`status=${String(member.status ?? "")}`,
|
|
273
294
|
`last_seen=${member.last_seen}`,
|
|
274
295
|
].join(" "),
|
|
@@ -300,11 +321,19 @@ function compactInboxMessages(
|
|
|
300
321
|
`type=${String(message.type ?? "")}`,
|
|
301
322
|
`from=${String(message.from ?? "")}`,
|
|
302
323
|
`to=${String(message.to ?? "")}`,
|
|
303
|
-
...(message.queued_at
|
|
324
|
+
...(message.queued_at
|
|
325
|
+
? [`queued_at=${String(message.queued_at)}`]
|
|
326
|
+
: []),
|
|
304
327
|
...(message.sent_at ? [`sent_at=${String(message.sent_at)}`] : []),
|
|
305
|
-
...(message.claimed_at
|
|
306
|
-
|
|
307
|
-
|
|
328
|
+
...(message.claimed_at
|
|
329
|
+
? [`claimed_at=${String(message.claimed_at)}`]
|
|
330
|
+
: []),
|
|
331
|
+
...(message.handled_at
|
|
332
|
+
? [`handled_at=${String(message.handled_at)}`]
|
|
333
|
+
: []),
|
|
334
|
+
...(message.failed_at
|
|
335
|
+
? [`failed_at=${String(message.failed_at)}`]
|
|
336
|
+
: []),
|
|
308
337
|
].join(" "),
|
|
309
338
|
)
|
|
310
339
|
.join("\n")}`;
|
|
@@ -314,12 +343,64 @@ function compactBranchInbox(messages: Array<Record<string, unknown>>): string {
|
|
|
314
343
|
return compactInboxMessages(messages, "branch inbox");
|
|
315
344
|
}
|
|
316
345
|
|
|
346
|
+
function normalizeMailboxEntry(
|
|
347
|
+
value: unknown,
|
|
348
|
+
): Record<string, unknown> | undefined {
|
|
349
|
+
if (typeof value === "string") return { type: value };
|
|
350
|
+
const record = asRecord(value);
|
|
351
|
+
if (typeof record.type !== "string" || !record.type.trim()) return undefined;
|
|
352
|
+
return {
|
|
353
|
+
type: record.type.trim(),
|
|
354
|
+
...(record.body_schema !== undefined
|
|
355
|
+
? { body_schema: record.body_schema }
|
|
356
|
+
: {}),
|
|
357
|
+
...(record.ack !== undefined ? { ack: record.ack } : {}),
|
|
358
|
+
...(typeof record.idempotency === "string"
|
|
359
|
+
? { idempotency: record.idempotency }
|
|
360
|
+
: {}),
|
|
361
|
+
...(typeof record.level === "string" ? { level: record.level } : {}),
|
|
362
|
+
...(record.requires_response === true ? { requires_response: true } : {}),
|
|
363
|
+
...(typeof record.summary === "string" ? { summary: record.summary } : {}),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function normalizeMailboxContracts(
|
|
368
|
+
mailbox: Record<string, unknown>,
|
|
369
|
+
): Record<string, unknown[]> {
|
|
370
|
+
const accepts = Array.isArray(mailbox.accepts)
|
|
371
|
+
? mailbox.accepts
|
|
372
|
+
.map(normalizeMailboxEntry)
|
|
373
|
+
.filter((entry): entry is Record<string, unknown> => Boolean(entry))
|
|
374
|
+
: [];
|
|
375
|
+
const emits = Array.isArray(mailbox.emits)
|
|
376
|
+
? mailbox.emits
|
|
377
|
+
.map(normalizeMailboxEntry)
|
|
378
|
+
.filter((entry): entry is Record<string, unknown> => Boolean(entry))
|
|
379
|
+
: [];
|
|
380
|
+
return { accepts, emits };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function mailboxTypes(entries: unknown[]): string[] {
|
|
384
|
+
return entries.flatMap((entry) =>
|
|
385
|
+
typeof asRecord(entry).type === "string"
|
|
386
|
+
? [String(asRecord(entry).type)]
|
|
387
|
+
: [],
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
317
391
|
function compactRunMailbox(
|
|
318
392
|
run: string,
|
|
319
393
|
mailbox: Record<string, unknown>,
|
|
320
394
|
messages: Array<Record<string, unknown>>,
|
|
321
395
|
): string {
|
|
322
|
-
|
|
396
|
+
const normalized = normalizeMailboxContracts(mailbox);
|
|
397
|
+
return `\nrun=${run} accepts=${mailboxTypes(normalized.accepts).join(",")} emits=${mailboxTypes(normalized.emits).join(",")}${compactInboxMessages(messages, "run inbox")}`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function compactArtifactPath(value: unknown): string {
|
|
401
|
+
if (typeof value === "string") return value;
|
|
402
|
+
const record = asRecord(value);
|
|
403
|
+
return String(record.path ?? "<missing>");
|
|
323
404
|
}
|
|
324
405
|
|
|
325
406
|
function compactActorFiles(status: Record<string, unknown>): string {
|
|
@@ -330,12 +411,14 @@ function compactActorFiles(status: Record<string, unknown>): string {
|
|
|
330
411
|
status.stderrLog,
|
|
331
412
|
status.eventsFile,
|
|
332
413
|
status.outboxFile,
|
|
333
|
-
status.state_dir
|
|
414
|
+
status.state_dir
|
|
415
|
+
? `${String(status.state_dir)}/communication.json`
|
|
416
|
+
: undefined,
|
|
334
417
|
status.state_dir ? `${String(status.state_dir)}/result.json` : undefined,
|
|
335
418
|
].filter((file): file is string => typeof file === "string");
|
|
336
419
|
const artifactText = Object.keys(artifacts).length
|
|
337
420
|
? ` artifacts=${Object.entries(artifacts)
|
|
338
|
-
.map(([key, value]) => `${key}:${
|
|
421
|
+
.map(([key, value]) => `${key}:${compactArtifactPath(value)}`)
|
|
339
422
|
.join(",")}`
|
|
340
423
|
: "";
|
|
341
424
|
return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}`;
|
|
@@ -353,7 +436,8 @@ function compactSessionRuns(
|
|
|
353
436
|
`status=${String(run.status ?? "")}`,
|
|
354
437
|
];
|
|
355
438
|
if (run.recipe) tokens.push(`recipe=${String(run.recipe)}`);
|
|
356
|
-
if (run.retire_when)
|
|
439
|
+
if (run.retire_when)
|
|
440
|
+
tokens.push(`retire_when=${String(run.retire_when)}`);
|
|
357
441
|
return tokens.join(" ");
|
|
358
442
|
})
|
|
359
443
|
.join("\n")}`;
|
|
@@ -368,11 +452,53 @@ function compactToolActor(name: string, tool: Record<string, unknown>): string {
|
|
|
368
452
|
return `\ntool=${name} description=${String(tool.description ?? "").replaceAll(/\s+/g, "_")} args=${Object.keys(properties).join(",")} required=${required}`;
|
|
369
453
|
}
|
|
370
454
|
|
|
455
|
+
function compactRecipeImports(summary: Record<string, unknown>): string {
|
|
456
|
+
const active = Array.isArray(summary.active)
|
|
457
|
+
? summary.active as Array<Record<string, unknown>>
|
|
458
|
+
: [];
|
|
459
|
+
const lines = active.flatMap((entry) => {
|
|
460
|
+
const imports = asRecord(entry.imports);
|
|
461
|
+
return Object.entries(imports).map(([alias, value]) => {
|
|
462
|
+
const binding = typeof value === "string" ? { from: value } : asRecord(value);
|
|
463
|
+
return `recipe=${String(entry.id ?? "<unknown>")} alias=${alias} from=${String(binding.from ?? value)}`;
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
return lines.length ? `\n${lines.join("\n")}` : "\n(no recipe imports)";
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function compactRecipeDoctor(summary: Record<string, unknown>): string {
|
|
470
|
+
const details = Array.isArray(summary.diagnostic_details)
|
|
471
|
+
? (summary.diagnostic_details as Array<Record<string, unknown>>)
|
|
472
|
+
: [];
|
|
473
|
+
const recommendations = Array.isArray(summary.recommendations)
|
|
474
|
+
? (summary.recommendations as Array<Record<string, unknown>>)
|
|
475
|
+
: [];
|
|
476
|
+
const counts = { error: 0, info: 0, warning: 0 };
|
|
477
|
+
for (const detail of details) {
|
|
478
|
+
const severity = String(detail.severity ?? "info");
|
|
479
|
+
if (severity === "error" || severity === "warning" || severity === "info")
|
|
480
|
+
counts[severity] += 1;
|
|
481
|
+
}
|
|
482
|
+
const lines = [
|
|
483
|
+
`recipes doctor errors=${counts.error} warnings=${counts.warning} info=${counts.info} recommendations=${recommendations.length}`,
|
|
484
|
+
];
|
|
485
|
+
for (const detail of details.slice(0, 8)) {
|
|
486
|
+
lines.push(
|
|
487
|
+
`${String(detail.severity ?? "info")} id=${String(detail.id ?? "root")} action=${compactPreview(detail.action, Limits.DOCTOR_ACTION_PREVIEW_CHARS) ?? "inspect"}`,
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
return `\n${lines.join("\n")}`;
|
|
491
|
+
}
|
|
492
|
+
|
|
371
493
|
function compactRecipeRegistry(summary: Record<string, unknown>): string {
|
|
372
494
|
const active = Array.isArray(summary.active) ? summary.active.length : 0;
|
|
373
|
-
const shadowed = Array.isArray(summary.shadowed)
|
|
495
|
+
const shadowed = Array.isArray(summary.shadowed)
|
|
496
|
+
? summary.shadowed.length
|
|
497
|
+
: 0;
|
|
374
498
|
const invalid = Array.isArray(summary.invalid) ? summary.invalid.length : 0;
|
|
375
|
-
const disabled = Array.isArray(summary.disabled)
|
|
499
|
+
const disabled = Array.isArray(summary.disabled)
|
|
500
|
+
? summary.disabled.length
|
|
501
|
+
: 0;
|
|
376
502
|
const diagnostics = Array.isArray(summary.diagnostics)
|
|
377
503
|
? summary.diagnostics.length
|
|
378
504
|
: 0;
|
|
@@ -537,6 +663,23 @@ function assertMessageSenderBelongsToRun(
|
|
|
537
663
|
}
|
|
538
664
|
}
|
|
539
665
|
|
|
666
|
+
async function routeBranchEnvelope(
|
|
667
|
+
stateDir: string,
|
|
668
|
+
runId: string,
|
|
669
|
+
recipient: string,
|
|
670
|
+
message: ActorMessages.ActorMessage,
|
|
671
|
+
_options: { source: "direct" | "room-multicast" },
|
|
672
|
+
): Promise<Record<string, unknown>> {
|
|
673
|
+
const branchMessage = { ...message, to: recipient };
|
|
674
|
+
ActorRooms.appendBranchInboxMessage(
|
|
675
|
+
stateDir,
|
|
676
|
+
runId,
|
|
677
|
+
recipient,
|
|
678
|
+
branchMessage,
|
|
679
|
+
);
|
|
680
|
+
return AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
681
|
+
}
|
|
682
|
+
|
|
540
683
|
function getRoomMulticastRecipients(
|
|
541
684
|
message: ActorMessages.ActorMessage,
|
|
542
685
|
run: string,
|
|
@@ -622,13 +765,21 @@ export function createSpawnToolDefinition<
|
|
|
622
765
|
state_dir:
|
|
623
766
|
typeof input.state_dir === "string" ? input.state_dir : undefined,
|
|
624
767
|
...(input.template !== undefined
|
|
625
|
-
? {
|
|
768
|
+
? {
|
|
769
|
+
template:
|
|
770
|
+
input.template as AsyncRuns.AsyncRunStartParams["template"],
|
|
771
|
+
}
|
|
626
772
|
: {}),
|
|
627
773
|
values: asRecord(input.values),
|
|
628
774
|
...(input.artifacts &&
|
|
629
775
|
typeof input.artifacts === "object" &&
|
|
630
776
|
!Array.isArray(input.artifacts)
|
|
631
|
-
? {
|
|
777
|
+
? {
|
|
778
|
+
artifacts: input.artifacts as Record<
|
|
779
|
+
string,
|
|
780
|
+
AsyncRuns.RunArtifactDeclaration
|
|
781
|
+
>,
|
|
782
|
+
}
|
|
632
783
|
: {}),
|
|
633
784
|
},
|
|
634
785
|
ctx.cwd,
|
|
@@ -688,7 +839,9 @@ function assertRunAccessibleToContext(
|
|
|
688
839
|
return status;
|
|
689
840
|
}
|
|
690
841
|
|
|
691
|
-
function assertRunExistsForActorMessage(
|
|
842
|
+
function assertRunExistsForActorMessage(
|
|
843
|
+
runId: string,
|
|
844
|
+
): Record<string, unknown> {
|
|
692
845
|
return AsyncRuns.getRunStatus(runId);
|
|
693
846
|
}
|
|
694
847
|
|
|
@@ -729,11 +882,17 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
729
882
|
const target = String(input.target ?? "");
|
|
730
883
|
const view = String(input.view ?? "");
|
|
731
884
|
if (target === "recipes" || target === "recipe-registry") {
|
|
732
|
-
if (view !== "status" && view !== "summary") {
|
|
733
|
-
throw new Error(
|
|
885
|
+
if (view !== "status" && view !== "summary" && view !== "doctor" && view !== "imports") {
|
|
886
|
+
throw new Error(
|
|
887
|
+
"inspect recipes supports view=status, view=summary, view=doctor, or view=imports.",
|
|
888
|
+
);
|
|
734
889
|
}
|
|
735
890
|
const discovered = RecipeDiscovery.discoverRecipeSources([
|
|
736
|
-
{
|
|
891
|
+
{
|
|
892
|
+
root: deps.recipeRoot ?? Paths.getRecipeRoot(),
|
|
893
|
+
defaultTool: true,
|
|
894
|
+
mutableUsage: true,
|
|
895
|
+
},
|
|
737
896
|
{ root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
|
|
738
897
|
]);
|
|
739
898
|
const summary = RecipeDiscovery.summarizeDiscovery(discovered);
|
|
@@ -744,7 +903,11 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
744
903
|
text: maybeJsonText(
|
|
745
904
|
summary,
|
|
746
905
|
input.verbose === true,
|
|
747
|
-
|
|
906
|
+
view === "doctor"
|
|
907
|
+
? compactRecipeDoctor(summary)
|
|
908
|
+
: view === "imports"
|
|
909
|
+
? compactRecipeImports(summary)
|
|
910
|
+
: compactRecipeRegistry(summary),
|
|
748
911
|
),
|
|
749
912
|
},
|
|
750
913
|
],
|
|
@@ -838,7 +1001,8 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
838
1001
|
if (address.kind === "room" && address.value && address.room) {
|
|
839
1002
|
const status = assertRunAccessibleToContext(address.value, ctx);
|
|
840
1003
|
const stateDir = String(status.state_dir ?? "");
|
|
841
|
-
if (!stateDir)
|
|
1004
|
+
if (!stateDir)
|
|
1005
|
+
throw new Error(`room:${address.value} has no run state directory.`);
|
|
842
1006
|
if (view === "status") {
|
|
843
1007
|
const status = ActorRooms.getRoomStatus(stateDir, address.room);
|
|
844
1008
|
return {
|
|
@@ -859,7 +1023,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
859
1023
|
const previews = ActorRooms.readRoomMessagePreviews(
|
|
860
1024
|
stateDir,
|
|
861
1025
|
address.room,
|
|
862
|
-
Number(input.lines ||
|
|
1026
|
+
Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
|
|
863
1027
|
);
|
|
864
1028
|
return {
|
|
865
1029
|
content: [
|
|
@@ -879,7 +1043,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
879
1043
|
const messages = ActorRooms.readRoomMessages(
|
|
880
1044
|
stateDir,
|
|
881
1045
|
address.room,
|
|
882
|
-
Number(input.lines ||
|
|
1046
|
+
Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
|
|
883
1047
|
);
|
|
884
1048
|
return {
|
|
885
1049
|
content: [
|
|
@@ -927,21 +1091,29 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
927
1091
|
details: { roster },
|
|
928
1092
|
};
|
|
929
1093
|
}
|
|
930
|
-
throw new Error(
|
|
1094
|
+
throw new Error(
|
|
1095
|
+
"inspect room:<run> supports view=status, view=messages, view=previews, view=roster, or view=contacts.",
|
|
1096
|
+
);
|
|
931
1097
|
}
|
|
932
|
-
const runId =
|
|
1098
|
+
const runId =
|
|
1099
|
+
address.kind === "run" || address.kind === "branch"
|
|
1100
|
+
? address.value
|
|
1101
|
+
: undefined;
|
|
933
1102
|
if (!runId)
|
|
934
1103
|
throw new Error(
|
|
935
1104
|
"inspect target must be run:<id>, branch:<run>/<branch>, coordinator, session:<id>, or tool:<name>.",
|
|
936
1105
|
);
|
|
937
1106
|
if (address.kind === "branch") {
|
|
938
|
-
if (view !== "mailbox")
|
|
1107
|
+
if (view !== "mailbox")
|
|
1108
|
+
throw new Error(
|
|
1109
|
+
"inspect branch:<run>/<branch> supports view=mailbox.",
|
|
1110
|
+
);
|
|
939
1111
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
940
1112
|
const branchInbox = ActorRooms.readBranchInboxDiagnostics(
|
|
941
1113
|
String(status.state_dir ?? ""),
|
|
942
1114
|
runId,
|
|
943
1115
|
target,
|
|
944
|
-
Number(input.lines ||
|
|
1116
|
+
Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
|
|
945
1117
|
);
|
|
946
1118
|
const messages = branchInbox.messages;
|
|
947
1119
|
return {
|
|
@@ -977,7 +1149,10 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
977
1149
|
}
|
|
978
1150
|
case "tail": {
|
|
979
1151
|
assertRunAccessibleToContext(runId, ctx);
|
|
980
|
-
const text = AsyncRuns.tailRun(
|
|
1152
|
+
const text = AsyncRuns.tailRun(
|
|
1153
|
+
runId,
|
|
1154
|
+
Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
|
|
1155
|
+
);
|
|
981
1156
|
return {
|
|
982
1157
|
content: [{ type: "text" as const, text: `\n${text}` }],
|
|
983
1158
|
details: {},
|
|
@@ -987,7 +1162,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
987
1162
|
assertRunAccessibleToContext(runId, ctx);
|
|
988
1163
|
const messages = AsyncRuns.readRunEvents(
|
|
989
1164
|
runId,
|
|
990
|
-
Number(input.lines ||
|
|
1165
|
+
Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
|
|
991
1166
|
);
|
|
992
1167
|
return {
|
|
993
1168
|
content: [
|
|
@@ -1006,28 +1181,41 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1006
1181
|
case "artifacts":
|
|
1007
1182
|
case "files": {
|
|
1008
1183
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
1184
|
+
const artifactManifest = AsyncRuns.resolveArtifactManifest(
|
|
1185
|
+
status.artifacts as
|
|
1186
|
+
| Record<string, AsyncRuns.RunArtifactDeclaration>
|
|
1187
|
+
| undefined,
|
|
1188
|
+
);
|
|
1189
|
+
const details = artifactManifest
|
|
1190
|
+
? { ...status, artifact_manifest: artifactManifest }
|
|
1191
|
+
: status;
|
|
1009
1192
|
return {
|
|
1010
1193
|
content: [
|
|
1011
1194
|
{
|
|
1012
1195
|
type: "text" as const,
|
|
1013
1196
|
text: maybeJsonText(
|
|
1014
|
-
|
|
1197
|
+
details,
|
|
1015
1198
|
input.verbose === true,
|
|
1016
1199
|
compactActorFiles(status),
|
|
1017
1200
|
),
|
|
1018
1201
|
},
|
|
1019
1202
|
],
|
|
1020
|
-
details
|
|
1203
|
+
details,
|
|
1021
1204
|
};
|
|
1022
1205
|
}
|
|
1023
1206
|
case "mailbox": {
|
|
1024
1207
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
1025
1208
|
const mailbox = asRecord(status.mailbox);
|
|
1209
|
+
const normalizedMailbox = normalizeMailboxContracts(mailbox);
|
|
1026
1210
|
const messages = AsyncRuns.readRunInboxMessages(
|
|
1027
1211
|
runId,
|
|
1028
|
-
Number(input.lines ||
|
|
1212
|
+
Number(input.lines || Limits.DEFAULT_INSPECT_LINES),
|
|
1029
1213
|
);
|
|
1030
|
-
const details = {
|
|
1214
|
+
const details = {
|
|
1215
|
+
mailbox,
|
|
1216
|
+
normalized_mailbox: normalizedMailbox,
|
|
1217
|
+
messages,
|
|
1218
|
+
};
|
|
1031
1219
|
return {
|
|
1032
1220
|
content: [
|
|
1033
1221
|
{
|
|
@@ -1035,7 +1223,11 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1035
1223
|
text: maybeJsonText(
|
|
1036
1224
|
details,
|
|
1037
1225
|
input.verbose === true,
|
|
1038
|
-
compactRunMailbox(
|
|
1226
|
+
compactRunMailbox(
|
|
1227
|
+
String(status.run ?? runId),
|
|
1228
|
+
mailbox,
|
|
1229
|
+
messages,
|
|
1230
|
+
),
|
|
1039
1231
|
),
|
|
1040
1232
|
},
|
|
1041
1233
|
],
|
|
@@ -1124,7 +1316,17 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1124
1316
|
const address = ActorMessages.parseActorAddress(message.to);
|
|
1125
1317
|
let result: Record<string, unknown>;
|
|
1126
1318
|
if (address.kind === "run" && address.value) {
|
|
1127
|
-
assertRunAccessibleToContext(address.value, ctx);
|
|
1319
|
+
const status = assertRunAccessibleToContext(address.value, ctx);
|
|
1320
|
+
const normalizedMailbox = normalizeMailboxContracts(
|
|
1321
|
+
asRecord(status.mailbox),
|
|
1322
|
+
);
|
|
1323
|
+
const acceptedTypes = new Set(mailboxTypes(normalizedMailbox.accepts));
|
|
1324
|
+
const advisoryWarnings =
|
|
1325
|
+
acceptedTypes.size > 0 && !acceptedTypes.has(message.type)
|
|
1326
|
+
? [
|
|
1327
|
+
`Message type ${message.type} is not declared in mailbox.accepts for run:${address.value}.`,
|
|
1328
|
+
]
|
|
1329
|
+
: [];
|
|
1128
1330
|
if (
|
|
1129
1331
|
message.type === "control.stop" ||
|
|
1130
1332
|
message.type === "control.cancel"
|
|
@@ -1132,15 +1334,31 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1132
1334
|
result = AsyncRuns.cancelRun(address.value);
|
|
1133
1335
|
} else if (message.type === "control.kill") {
|
|
1134
1336
|
result = AsyncRuns.killRun(address.value);
|
|
1337
|
+
} else if (message.type === "control.archive") {
|
|
1338
|
+
result = AsyncRuns.archiveRun(address.value);
|
|
1339
|
+
} else if (message.type === "control.prune") {
|
|
1340
|
+
const body = asRecord(message.body);
|
|
1341
|
+
result = AsyncRuns.pruneRun(address.value, {
|
|
1342
|
+
preserveArtifacts:
|
|
1343
|
+
body.preserve_artifacts === true ||
|
|
1344
|
+
body.preserveArtifacts === true,
|
|
1345
|
+
});
|
|
1135
1346
|
} else {
|
|
1136
1347
|
result = await AsyncRuns.sendRunMessage(
|
|
1137
1348
|
address.value,
|
|
1138
1349
|
messageBodyToRunLine(message),
|
|
1139
1350
|
);
|
|
1140
1351
|
}
|
|
1352
|
+
if (advisoryWarnings.length > 0)
|
|
1353
|
+
result = { ...result, warnings: advisoryWarnings };
|
|
1141
1354
|
} else if (address.kind === "branch" && address.value) {
|
|
1142
1355
|
const runId = address.value;
|
|
1143
|
-
if (message.from)
|
|
1356
|
+
if (message.from)
|
|
1357
|
+
assertMessageSenderBelongsToRun(
|
|
1358
|
+
message,
|
|
1359
|
+
runId,
|
|
1360
|
+
`branch:${runId}/<branch>`,
|
|
1361
|
+
);
|
|
1144
1362
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
1145
1363
|
const stateDir = String(status.state_dir ?? "");
|
|
1146
1364
|
if (stateDir && address.branch) {
|
|
@@ -1171,26 +1389,34 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1171
1389
|
}
|
|
1172
1390
|
}
|
|
1173
1391
|
ActorRooms.writeCommunicationSnapshot(stateDir, runId);
|
|
1174
|
-
ActorRooms.appendBranchInboxMessage(stateDir, runId, message.to, message);
|
|
1175
1392
|
}
|
|
1176
|
-
result = await
|
|
1177
|
-
|
|
1178
|
-
|
|
1393
|
+
result = await routeBranchEnvelope(
|
|
1394
|
+
stateDir,
|
|
1395
|
+
runId,
|
|
1396
|
+
message.to,
|
|
1397
|
+
message,
|
|
1398
|
+
{
|
|
1399
|
+
source: "direct",
|
|
1400
|
+
},
|
|
1179
1401
|
);
|
|
1180
1402
|
} else if (address.kind === "room" && address.value && address.room) {
|
|
1181
1403
|
const runId = address.value;
|
|
1182
1404
|
assertMessageSenderBelongsToRun(message, runId, `room:${runId}`);
|
|
1183
1405
|
const status = assertRunAccessibleToContext(runId, ctx);
|
|
1184
1406
|
const stateDir = String(status.state_dir ?? "");
|
|
1185
|
-
if (!stateDir)
|
|
1407
|
+
if (!stateDir)
|
|
1408
|
+
throw new Error(`${message.to} has no run state directory.`);
|
|
1186
1409
|
const recipients = getRoomMulticastRecipients(message, runId);
|
|
1187
|
-
const roomResult = ActorRooms.appendRoomMessage(
|
|
1410
|
+
const roomResult = ActorRooms.appendRoomMessage(
|
|
1411
|
+
stateDir,
|
|
1412
|
+
address.room,
|
|
1413
|
+
message,
|
|
1414
|
+
);
|
|
1188
1415
|
await Promise.all(
|
|
1189
1416
|
recipients.map((recipient) =>
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
),
|
|
1417
|
+
routeBranchEnvelope(stateDir, runId, recipient, message, {
|
|
1418
|
+
source: "room-multicast",
|
|
1419
|
+
}),
|
|
1194
1420
|
),
|
|
1195
1421
|
);
|
|
1196
1422
|
result = {
|
|
@@ -1217,7 +1443,12 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1217
1443
|
ctx,
|
|
1218
1444
|
);
|
|
1219
1445
|
} catch (error) {
|
|
1220
|
-
throw formatToolActorFailure(
|
|
1446
|
+
throw formatToolActorFailure(
|
|
1447
|
+
address.value,
|
|
1448
|
+
message,
|
|
1449
|
+
toolParams,
|
|
1450
|
+
error,
|
|
1451
|
+
);
|
|
1221
1452
|
}
|
|
1222
1453
|
result = {
|
|
1223
1454
|
invoked: true,
|
|
@@ -1251,7 +1482,11 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1251
1482
|
result = AsyncRuns.appendRunOutboxEvent(sender.value, {
|
|
1252
1483
|
body: message.body,
|
|
1253
1484
|
correlation_id: message.correlation_id,
|
|
1254
|
-
delivery:
|
|
1485
|
+
delivery:
|
|
1486
|
+
message.metadata?.requires_response === true ||
|
|
1487
|
+
address.kind === "session"
|
|
1488
|
+
? "followup"
|
|
1489
|
+
: undefined,
|
|
1255
1490
|
event: message.type,
|
|
1256
1491
|
from: message.from,
|
|
1257
1492
|
metadata: message.metadata,
|
|
@@ -1340,7 +1575,8 @@ export function createRuntimeToolDefinition(
|
|
|
1340
1575
|
ctx: AsyncRunToolContext,
|
|
1341
1576
|
) {
|
|
1342
1577
|
try {
|
|
1343
|
-
if (cfg.sourcePath)
|
|
1578
|
+
if (cfg.sourcePath)
|
|
1579
|
+
RecipeUsage.recordRecipeLaunch(cfg.sourcePath, new Date(), "tool");
|
|
1344
1580
|
if (isAsyncRecipe) {
|
|
1345
1581
|
const input = params as Record<string, unknown>;
|
|
1346
1582
|
const { run_id, ...values } = input;
|
|
@@ -1364,7 +1600,10 @@ export function createRuntimeToolDefinition(
|
|
|
1364
1600
|
ctx.cwd,
|
|
1365
1601
|
);
|
|
1366
1602
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
1367
|
-
ActorRooms.writeCommunicationSnapshot(
|
|
1603
|
+
ActorRooms.writeCommunicationSnapshot(
|
|
1604
|
+
meta.state_dir,
|
|
1605
|
+
String(meta.run),
|
|
1606
|
+
);
|
|
1368
1607
|
return {
|
|
1369
1608
|
content: [
|
|
1370
1609
|
{ type: "text" as const, text: compactAsyncRunStatus(meta) },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-actors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local Actor Kernel for Pi",
|
|
6
6
|
"keywords": [
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"check": "node --experimental-strip-types -e \"await import('./index.ts'); console.log('pi-actors: extension import ok')\"",
|
|
29
|
+
"conformance": "node scripts/conformance.mjs",
|
|
29
30
|
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
30
31
|
"pack:dry": "npm pack --dry-run",
|
|
31
32
|
"validate": "npx tsc --noEmit && npm run build && npm run check && npm test && npm run pack:dry",
|