@llblab/pi-actors 0.31.0 → 0.33.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 +7 -3
- package/BACKLOG.md +14 -55
- package/CHANGELOG.md +16 -0
- package/README.md +16 -11
- package/dist/lib/observability.js +17 -6
- package/dist/lib/paths.d.ts +1 -1
- package/dist/lib/paths.js +3 -3
- package/dist/lib/recipe-discovery.d.ts +1 -1
- package/dist/lib/recipe-discovery.js +11 -11
- package/dist/lib/tools.d.ts +6 -1
- package/dist/lib/tools.js +150 -37
- package/dist/scripts/conformance.mjs +34 -18
- package/dist/skills/actors/SKILL.md +14 -19
- package/dist/skills/swarm/SKILL.md +3 -3
- package/docs/actor-messages.md +1 -1
- package/docs/tool-registry.md +3 -3
- package/index.ts +1 -1
- package/lib/observability.ts +18 -6
- package/lib/paths.ts +3 -3
- package/lib/recipe-discovery.ts +11 -11
- package/lib/tools.ts +175 -52
- package/package.json +1 -1
- package/scripts/conformance.mjs +34 -18
- package/skills/actors/SKILL.md +14 -19
- package/skills/swarm/SKILL.md +3 -3
- package/dist/lib/conformance.d.ts +0 -12
- package/dist/lib/conformance.js +0 -28
- package/lib/conformance.ts +0 -46
package/lib/paths.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface ExtensionRuntimePaths {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function getConfigPath(agentDir = getAgentDir()): string {
|
|
27
|
-
return join(agentDir, "
|
|
27
|
+
return join(agentDir, "tool-registry.json");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function getExtensionTmpDir(
|
|
@@ -63,8 +63,8 @@ export function getRecipeRoot(agentDir = getAgentDir()): string {
|
|
|
63
63
|
return join(agentDir, "recipes");
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function
|
|
67
|
-
return join(getRecipeRoot(agentDir), "
|
|
66
|
+
export function getRecipeDraftRoot(agentDir = getAgentDir()): string {
|
|
67
|
+
return join(getRecipeRoot(agentDir), "drafts");
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export function getPackagedRecipeRoot(): string {
|
package/lib/recipe-discovery.ts
CHANGED
|
@@ -461,16 +461,16 @@ function remediationForEntry(
|
|
|
461
461
|
const riskyDiagnostics = entry.diagnostics.filter(
|
|
462
462
|
(message) => diagnosticSeverity(message) === "warning",
|
|
463
463
|
);
|
|
464
|
-
const
|
|
464
|
+
const blockedFallback = entry.shadows[0];
|
|
465
465
|
if (entry.invalid) {
|
|
466
466
|
return {
|
|
467
467
|
id: entry.id,
|
|
468
|
-
kind:
|
|
468
|
+
kind: blockedFallback ? "blocking_invalid" : "invalid",
|
|
469
469
|
severity: "error",
|
|
470
470
|
path: entry.path,
|
|
471
|
-
...(
|
|
472
|
-
reason:
|
|
473
|
-
? "invalid higher-priority recipe blocks a lower-priority
|
|
471
|
+
...(blockedFallback ? { blocked_fallback: blockedFallback } : {}),
|
|
472
|
+
reason: blockedFallback
|
|
473
|
+
? "invalid higher-priority recipe blocks a lower-priority fallback"
|
|
474
474
|
: "recipe is invalid and cannot be exposed as a tool",
|
|
475
475
|
action:
|
|
476
476
|
"fix recipe syntax/config, or disable/delete/archive it to restore fallback",
|
|
@@ -479,12 +479,12 @@ function remediationForEntry(
|
|
|
479
479
|
if (entry.disabled && entry.active) {
|
|
480
480
|
return {
|
|
481
481
|
id: entry.id,
|
|
482
|
-
kind:
|
|
482
|
+
kind: blockedFallback ? "blocking_disabled" : "disabled",
|
|
483
483
|
severity: "warning",
|
|
484
484
|
path: entry.path,
|
|
485
|
-
...(
|
|
486
|
-
reason:
|
|
487
|
-
? "disabled higher-priority recipe intentionally blocks a lower-priority
|
|
485
|
+
...(blockedFallback ? { blocked_fallback: blockedFallback } : {}),
|
|
486
|
+
reason: blockedFallback
|
|
487
|
+
? "disabled higher-priority recipe intentionally blocks a lower-priority fallback"
|
|
488
488
|
: "recipe is disabled and not exposed as a tool",
|
|
489
489
|
action:
|
|
490
490
|
"keep disabled intentionally, re-enable, or delete/archive the file",
|
|
@@ -568,13 +568,13 @@ export function getShadowedLaunchDiagnostic(
|
|
|
568
568
|
if (!active.invalid && !active.disabled) return undefined;
|
|
569
569
|
return {
|
|
570
570
|
active_path: active.path,
|
|
571
|
-
|
|
571
|
+
blocked_fallback: active.shadows[0],
|
|
572
572
|
hint: "inspect_recipes_doctor",
|
|
573
573
|
reason: active.invalid ? "shadowed_invalid" : "shadowed_disabled",
|
|
574
574
|
};
|
|
575
575
|
}
|
|
576
576
|
|
|
577
|
-
export function
|
|
577
|
+
export function listDraftRecipes(root: string): Array<Record<string, unknown>> {
|
|
578
578
|
return listRecipeFiles(root).map((path) => {
|
|
579
579
|
const id = RecipeReferences.getRecipeIdFromPath(path);
|
|
580
580
|
const config = RecipeReferences.readRawRecipeConfig(path);
|
package/lib/tools.ts
CHANGED
|
@@ -27,6 +27,12 @@ export type RegisterToolInput = Registry.RegisterToolInput;
|
|
|
27
27
|
export type RegisterToolRuntimeDeps<TContext> =
|
|
28
28
|
Registry.RegisterToolRuntimeDeps<TContext>;
|
|
29
29
|
|
|
30
|
+
export interface ActorToolDefinition {
|
|
31
|
+
name: string;
|
|
32
|
+
execute?: (...args: never[]) => unknown;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
export interface CoreActorToolDefinitionDeps<TContext extends AsyncRunToolContext> {
|
|
31
37
|
configPath: string;
|
|
32
38
|
getActiveTools: () => string[];
|
|
@@ -222,8 +228,8 @@ function compactAsyncRunStatus(value: unknown): string {
|
|
|
222
228
|
tokens.push(`failures=${failures}`);
|
|
223
229
|
if (result.code !== undefined) tokens.push(`code=${String(result.code)}`);
|
|
224
230
|
if (result.killed === true) tokens.push("killed=true");
|
|
225
|
-
|
|
226
|
-
|
|
231
|
+
const draftRecipe = status.draft_recipe;
|
|
232
|
+
if (draftRecipe) tokens.push(`draft_recipe=${String(draftRecipe)}`);
|
|
227
233
|
const nextActions = actorRunNextActions(run);
|
|
228
234
|
if (nextActions.length > 0)
|
|
229
235
|
tokens.push(`next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`);
|
|
@@ -451,6 +457,15 @@ function compactArtifactPath(value: unknown): string {
|
|
|
451
457
|
return String(record.path ?? "<missing>");
|
|
452
458
|
}
|
|
453
459
|
|
|
460
|
+
function artifactNextActions(run: unknown, artifacts: Record<string, unknown>): string[] {
|
|
461
|
+
const id = String(run ?? "").trim();
|
|
462
|
+
if (!id || Object.keys(artifacts).length === 0) return [];
|
|
463
|
+
return [
|
|
464
|
+
`inspect target=run:${id} view=artifacts verbose=true`,
|
|
465
|
+
`inspect target=run:${id} view=messages`,
|
|
466
|
+
];
|
|
467
|
+
}
|
|
468
|
+
|
|
454
469
|
function compactActorFiles(status: Record<string, unknown>): string {
|
|
455
470
|
const run = String(status.run ?? "<unknown>");
|
|
456
471
|
const artifacts = asRecord(status.artifacts);
|
|
@@ -469,7 +484,11 @@ function compactActorFiles(status: Record<string, unknown>): string {
|
|
|
469
484
|
.map(([key, value]) => `${key}:${compactArtifactPath(value)}`)
|
|
470
485
|
.join(",")}`
|
|
471
486
|
: "";
|
|
472
|
-
|
|
487
|
+
const nextActions = artifactNextActions(run, artifacts);
|
|
488
|
+
const nextText = nextActions.length
|
|
489
|
+
? ` next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`
|
|
490
|
+
: "";
|
|
491
|
+
return `\nrun=${run}${artifactText}${files.length ? ` files=${files.join(",")}` : ""}${nextText}`;
|
|
473
492
|
}
|
|
474
493
|
|
|
475
494
|
function summarizeOtherSessions(
|
|
@@ -614,16 +633,50 @@ function compactRecipeDoctor(summary: Record<string, unknown>): string {
|
|
|
614
633
|
item.action,
|
|
615
634
|
Limits.DOCTOR_ACTION_PREVIEW_CHARS,
|
|
616
635
|
);
|
|
617
|
-
const blocked = item.
|
|
618
|
-
? ` blocked=${compactPreview(item.
|
|
636
|
+
const blocked = item.blocked_fallback
|
|
637
|
+
? ` blocked=${compactPreview(item.blocked_fallback, Limits.DOCTOR_ACTION_PREVIEW_CHARS)}`
|
|
619
638
|
: "";
|
|
620
639
|
lines.push(
|
|
621
640
|
`${String(item.severity ?? "info")} kind=${String(item.kind ?? "inspect")} id=${String(item.id ?? "root")}${blocked} action=${action ?? "inspect"}`,
|
|
622
641
|
);
|
|
623
642
|
}
|
|
643
|
+
const nextActions = Array.isArray(summary.next_actions)
|
|
644
|
+
? (summary.next_actions as string[])
|
|
645
|
+
: [];
|
|
646
|
+
if (nextActions.length > 0) lines[0] = `${lines[0]}${compactNextActions(nextActions)}`;
|
|
624
647
|
return `\n${lines.join("\n")}`;
|
|
625
648
|
}
|
|
626
649
|
|
|
650
|
+
function recipeRegistryNextActions(summary: Record<string, unknown>, view: string): string[] {
|
|
651
|
+
const actions: string[] = [];
|
|
652
|
+
const drafts = Array.isArray(summary.drafts)
|
|
653
|
+
? (summary.drafts as Array<Record<string, unknown>>)
|
|
654
|
+
: [];
|
|
655
|
+
const invalid = Array.isArray(summary.invalid) ? summary.invalid.length : 0;
|
|
656
|
+
const diagnostics = Array.isArray(summary.diagnostics)
|
|
657
|
+
? summary.diagnostics.length
|
|
658
|
+
: 0;
|
|
659
|
+
const topAction = asRecord(summary.top_action);
|
|
660
|
+
if (view !== "doctor" && (invalid > 0 || diagnostics > 0)) {
|
|
661
|
+
actions.push("inspect target=recipes view=doctor");
|
|
662
|
+
}
|
|
663
|
+
if (view === "doctor" && typeof topAction.action === "string") {
|
|
664
|
+
actions.push(String(topAction.action));
|
|
665
|
+
}
|
|
666
|
+
if (drafts.length > 0) {
|
|
667
|
+
actions.push("inspect target=recipes view=summary verbose=true");
|
|
668
|
+
const firstPath = typeof drafts[0]?.path === "string" ? drafts[0].path : undefined;
|
|
669
|
+
if (firstPath) actions.push(`spawn file=${firstPath}`);
|
|
670
|
+
}
|
|
671
|
+
return [...new Set(actions)].slice(0, 4);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function compactNextActions(actions: string[]): string {
|
|
675
|
+
return actions.length
|
|
676
|
+
? ` next=${actions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`
|
|
677
|
+
: "";
|
|
678
|
+
}
|
|
679
|
+
|
|
627
680
|
function compactRecipeRegistry(summary: Record<string, unknown>): string {
|
|
628
681
|
const active = Array.isArray(summary.active) ? summary.active.length : 0;
|
|
629
682
|
const shadowed = Array.isArray(summary.shadowed)
|
|
@@ -636,13 +689,39 @@ function compactRecipeRegistry(summary: Record<string, unknown>): string {
|
|
|
636
689
|
const diagnostics = Array.isArray(summary.diagnostics)
|
|
637
690
|
? summary.diagnostics.length
|
|
638
691
|
: 0;
|
|
639
|
-
const
|
|
640
|
-
? summary.candidates.length
|
|
641
|
-
: 0;
|
|
692
|
+
const drafts = Array.isArray(summary.drafts) ? summary.drafts.length : 0;
|
|
642
693
|
const recommendations = Array.isArray(summary.recommendations)
|
|
643
694
|
? summary.recommendations.length
|
|
644
695
|
: 0;
|
|
645
|
-
|
|
696
|
+
const nextActions = Array.isArray(summary.next_actions)
|
|
697
|
+
? (summary.next_actions as string[])
|
|
698
|
+
: [];
|
|
699
|
+
return `\nrecipes active=${active} drafts=${drafts} shadowed=${shadowed} invalid=${invalid} disabled=${disabled} recommendations=${recommendations} diagnostics=${diagnostics}${compactNextActions(nextActions)}`;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function actorMessageNextActions(
|
|
703
|
+
message: ActorMessages.ActorMessage,
|
|
704
|
+
result: Record<string, unknown>,
|
|
705
|
+
): string[] {
|
|
706
|
+
const actions: string[] = [];
|
|
707
|
+
const address = ActorMessages.parseActorAddress(message.to);
|
|
708
|
+
if (result.delivery_error || result.sent === false) {
|
|
709
|
+
if (address.kind === "run" && address.value) {
|
|
710
|
+
actions.push(`inspect target=run:${address.value} view=status`);
|
|
711
|
+
actions.push(`inspect target=run:${address.value} view=mailbox`);
|
|
712
|
+
} else if (address.kind === "branch" && address.value) {
|
|
713
|
+
actions.push(`inspect target=branch:${address.value}/${address.branch ?? "main"} view=mailbox`);
|
|
714
|
+
actions.push(`inspect target=run:${address.value} view=status`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (result.queued === true) {
|
|
718
|
+
if (address.kind === "branch" && address.value) {
|
|
719
|
+
actions.push(`inspect target=branch:${address.value}/${address.branch ?? "main"} view=mailbox`);
|
|
720
|
+
} else if (address.kind === "run" && address.value) {
|
|
721
|
+
actions.push(`inspect target=run:${address.value} view=mailbox`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return [...new Set(actions)].slice(0, 3);
|
|
646
725
|
}
|
|
647
726
|
|
|
648
727
|
function compactActorMessageResult(
|
|
@@ -670,6 +749,11 @@ function compactActorMessageResult(
|
|
|
670
749
|
if (result.delivery_error) {
|
|
671
750
|
tokens.push(`delivery_error=${compactPreview(result.delivery_error, 96)}`);
|
|
672
751
|
}
|
|
752
|
+
const nextActions = Array.isArray(result.next_actions)
|
|
753
|
+
? (result.next_actions as string[])
|
|
754
|
+
: actorMessageNextActions(message, result);
|
|
755
|
+
if (nextActions.length > 0)
|
|
756
|
+
tokens.push(`next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`);
|
|
673
757
|
return `\n${tokens.join(" ")}`;
|
|
674
758
|
}
|
|
675
759
|
|
|
@@ -791,11 +875,11 @@ function shadowedRecipeLaunchDiagnostic(
|
|
|
791
875
|
return RecipeDiscovery.getShadowedLaunchDiagnostic(discovery, recipe);
|
|
792
876
|
}
|
|
793
877
|
|
|
794
|
-
function
|
|
878
|
+
function draftRecipeName(run: string): string {
|
|
795
879
|
return `${run.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "spawn"}.json`;
|
|
796
880
|
}
|
|
797
881
|
|
|
798
|
-
function
|
|
882
|
+
function draftRecipeDefaults(
|
|
799
883
|
values: Record<string, unknown>,
|
|
800
884
|
): Record<string, unknown> | undefined {
|
|
801
885
|
const ignored = new Set([
|
|
@@ -811,13 +895,13 @@ function candidateRecipeDefaults(
|
|
|
811
895
|
return Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
812
896
|
}
|
|
813
897
|
|
|
814
|
-
function
|
|
898
|
+
function writeSpawnDraftRecipe(
|
|
815
899
|
input: Record<string, unknown>,
|
|
816
900
|
meta: AsyncRuns.AsyncRunMeta,
|
|
817
901
|
): string | undefined {
|
|
818
902
|
if (
|
|
819
903
|
process.env.NODE_TEST_CONTEXT &&
|
|
820
|
-
process.env.
|
|
904
|
+
process.env.PI_ACTORS_ENABLE_SPAWN_DRAFTS_IN_TEST !== "1"
|
|
821
905
|
)
|
|
822
906
|
return undefined;
|
|
823
907
|
if (
|
|
@@ -826,13 +910,13 @@ function writeSpawnCandidateRecipe(
|
|
|
826
910
|
input.recipe !== undefined
|
|
827
911
|
)
|
|
828
912
|
return undefined;
|
|
829
|
-
const root = Paths.
|
|
913
|
+
const root = Paths.getRecipeDraftRoot();
|
|
830
914
|
mkdirSync(root, { recursive: true });
|
|
831
|
-
const path = join(root,
|
|
832
|
-
const defaults =
|
|
915
|
+
const path = join(root, draftRecipeName(String(meta.run)));
|
|
916
|
+
const defaults = draftRecipeDefaults(meta.values);
|
|
833
917
|
const recipe = {
|
|
834
918
|
async: true,
|
|
835
|
-
description: `
|
|
919
|
+
description: `Draft recipe captured from spawn run ${String(meta.run)}`,
|
|
836
920
|
...(meta.artifacts ? { artifacts: meta.artifacts } : {}),
|
|
837
921
|
...(defaults ? { defaults } : {}),
|
|
838
922
|
template: input.template,
|
|
@@ -848,7 +932,7 @@ function enhanceSpawnRecipeError(error: unknown, recipe: unknown): Error {
|
|
|
848
932
|
const original = error instanceof Error ? error.message : String(error);
|
|
849
933
|
return Object.assign(
|
|
850
934
|
new Error(
|
|
851
|
-
`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path}
|
|
935
|
+
`${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path} blocked_fallback=${diagnostic.blocked_fallback} hint=${diagnostic.hint}`,
|
|
852
936
|
),
|
|
853
937
|
{
|
|
854
938
|
...diagnostic,
|
|
@@ -1036,11 +1120,11 @@ export function createSpawnToolDefinition<
|
|
|
1036
1120
|
} catch (error) {
|
|
1037
1121
|
throw enhanceSpawnRecipeError(error, recipe);
|
|
1038
1122
|
}
|
|
1039
|
-
const
|
|
1123
|
+
const draftRecipe = writeSpawnDraftRecipe(input, meta);
|
|
1040
1124
|
const nextActions = actorRunNextActions(meta.run);
|
|
1041
1125
|
const details = {
|
|
1042
1126
|
...meta,
|
|
1043
|
-
...(
|
|
1127
|
+
...(draftRecipe ? { draft_recipe: draftRecipe } : {}),
|
|
1044
1128
|
next_actions: nextActions,
|
|
1045
1129
|
};
|
|
1046
1130
|
ActorRooms.ensureDefaultRoom(meta.state_dir, String(meta.run));
|
|
@@ -1084,6 +1168,33 @@ function requireContextSessionId(ctx: unknown, actor: string): string {
|
|
|
1084
1168
|
return sessionId;
|
|
1085
1169
|
}
|
|
1086
1170
|
|
|
1171
|
+
function sessionMismatchError(input: {
|
|
1172
|
+
currentSession?: string;
|
|
1173
|
+
expectedSession?: string;
|
|
1174
|
+
run?: string;
|
|
1175
|
+
target?: string;
|
|
1176
|
+
}): Error {
|
|
1177
|
+
const ownerSession = input.expectedSession ?? "none";
|
|
1178
|
+
const currentSession = input.currentSession ?? "none";
|
|
1179
|
+
const actor = input.run ? `run:${input.run}` : (input.target ?? "session");
|
|
1180
|
+
const hintTarget = input.expectedSession
|
|
1181
|
+
? `session:${input.expectedSession}`
|
|
1182
|
+
: "session:all";
|
|
1183
|
+
return Object.assign(
|
|
1184
|
+
new Error(
|
|
1185
|
+
`${actor} reason=session_mismatch owner_session=${ownerSession} current_session=${currentSession} hint=inspect_session:${input.expectedSession ?? "all"}`,
|
|
1186
|
+
),
|
|
1187
|
+
{
|
|
1188
|
+
current_session: input.currentSession,
|
|
1189
|
+
hint: `inspect target=${hintTarget} view=status`,
|
|
1190
|
+
owner_session: input.expectedSession,
|
|
1191
|
+
reason: "session_mismatch",
|
|
1192
|
+
run: input.run,
|
|
1193
|
+
target: input.target,
|
|
1194
|
+
},
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1087
1198
|
function assertRunAccessibleToContext(
|
|
1088
1199
|
runId: string,
|
|
1089
1200
|
ctx: unknown,
|
|
@@ -1091,18 +1202,11 @@ function assertRunAccessibleToContext(
|
|
|
1091
1202
|
const status = AsyncRuns.getRunStatus(runId);
|
|
1092
1203
|
const sessionId = getContextSessionId(ctx);
|
|
1093
1204
|
if (sessionId && status.ownerId && status.ownerId !== sessionId) {
|
|
1094
|
-
throw
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
current_session: sessionId,
|
|
1100
|
-
hint: `inspect target=session:${status.ownerId} view=status`,
|
|
1101
|
-
owner_session: status.ownerId,
|
|
1102
|
-
reason: "session_mismatch",
|
|
1103
|
-
run: runId,
|
|
1104
|
-
},
|
|
1105
|
-
);
|
|
1205
|
+
throw sessionMismatchError({
|
|
1206
|
+
currentSession: sessionId,
|
|
1207
|
+
expectedSession: String(status.ownerId),
|
|
1208
|
+
run: runId,
|
|
1209
|
+
});
|
|
1106
1210
|
}
|
|
1107
1211
|
return status;
|
|
1108
1212
|
}
|
|
@@ -1120,7 +1224,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1120
1224
|
name: "inspect",
|
|
1121
1225
|
label: "Inspect",
|
|
1122
1226
|
description:
|
|
1123
|
-
"Intentionally inspect
|
|
1227
|
+
"Intentionally inspect actors at decision points, after follow-ups, or during diagnosis instead of polling. Core targets are run:<id> and tool:<name>; advanced targets include branch:<run>/<branch>, room:<run>, coordinator, session:<id>, and session:all.",
|
|
1124
1228
|
parameters: objectSchema(
|
|
1125
1229
|
{
|
|
1126
1230
|
lines: stringSchema("Line count for tail/messages views. Default 40."),
|
|
@@ -1128,13 +1232,13 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1128
1232
|
"Optional session run filter: all, running, active, terminal, done, failed, cancelled, killed, or exited.",
|
|
1129
1233
|
),
|
|
1130
1234
|
target: stringSchema(
|
|
1131
|
-
"Actor address to inspect, e.g. run:<id>, room:<run>, coordinator, session:<id>, session:all
|
|
1235
|
+
"Actor address to inspect, e.g. run:<id> or tool:<name>; advanced: branch:<run>/<branch>, room:<run>, coordinator, session:<id>, session:all.",
|
|
1132
1236
|
),
|
|
1133
1237
|
verbose: booleanSchema(
|
|
1134
1238
|
"Return full JSON instead of compact text where available.",
|
|
1135
1239
|
),
|
|
1136
1240
|
view: stringSchema(
|
|
1137
|
-
"Inspection view: status, tail, messages, artifacts, files, mailbox
|
|
1241
|
+
"Inspection view. Core run views: status, tail, messages, artifacts, files, mailbox. Advanced views include communication, roster, and contacts.",
|
|
1138
1242
|
),
|
|
1139
1243
|
},
|
|
1140
1244
|
["target", "view"],
|
|
@@ -1169,11 +1273,13 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1169
1273
|
{ root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
|
|
1170
1274
|
]);
|
|
1171
1275
|
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
1172
|
-
const
|
|
1276
|
+
const summaryBase = {
|
|
1173
1277
|
...RecipeDiscovery.summarizeDiscovery(discovered),
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1278
|
+
drafts: RecipeDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
|
|
1279
|
+
};
|
|
1280
|
+
const summary = {
|
|
1281
|
+
...summaryBase,
|
|
1282
|
+
next_actions: recipeRegistryNextActions(summaryBase, view),
|
|
1177
1283
|
};
|
|
1178
1284
|
return {
|
|
1179
1285
|
content: [
|
|
@@ -1488,7 +1594,14 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
1488
1594
|
| undefined,
|
|
1489
1595
|
);
|
|
1490
1596
|
const details = artifactManifest
|
|
1491
|
-
? {
|
|
1597
|
+
? {
|
|
1598
|
+
...status,
|
|
1599
|
+
artifact_manifest: artifactManifest,
|
|
1600
|
+
next_actions: artifactNextActions(
|
|
1601
|
+
status.run ?? runId,
|
|
1602
|
+
asRecord(status.artifacts),
|
|
1603
|
+
),
|
|
1604
|
+
}
|
|
1492
1605
|
: status;
|
|
1493
1606
|
return {
|
|
1494
1607
|
content: [
|
|
@@ -1574,7 +1687,7 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1574
1687
|
name: "message",
|
|
1575
1688
|
label: "Message",
|
|
1576
1689
|
description:
|
|
1577
|
-
"Send one typed addressed message to steer an existing actor instead of restarting it.
|
|
1690
|
+
"Send one typed addressed message to steer an existing actor instead of restarting it. Core routes are run:<id> and tool:<name>; advanced routes include branch:<run>/<branch>, room:<run> group timelines, coordinator, and session:<id>.",
|
|
1578
1691
|
parameters: objectSchema(
|
|
1579
1692
|
{
|
|
1580
1693
|
body: unionSchema([
|
|
@@ -1596,7 +1709,7 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1596
1709
|
reply_to: stringSchema("Optional message id this message replies to."),
|
|
1597
1710
|
summary: stringSchema("Optional short human-facing summary."),
|
|
1598
1711
|
to: stringSchema(
|
|
1599
|
-
"Destination actor address, e.g. run:<id
|
|
1712
|
+
"Destination actor address, e.g. run:<id> or tool:<name>; advanced: branch:<run>/<branch>, room:<run>, coordinator, session:<id>.",
|
|
1600
1713
|
),
|
|
1601
1714
|
type: stringSchema(
|
|
1602
1715
|
"Semantic message type, e.g. control.approve or checkpoint.needs_scope.",
|
|
@@ -1765,14 +1878,20 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1765
1878
|
const senderStatus = assertRunAccessibleToContext(sender.value, ctx);
|
|
1766
1879
|
if (address.kind === "session") {
|
|
1767
1880
|
if (!senderStatus.ownerId) {
|
|
1768
|
-
throw
|
|
1769
|
-
|
|
1770
|
-
|
|
1881
|
+
throw sessionMismatchError({
|
|
1882
|
+
currentSession: undefined,
|
|
1883
|
+
expectedSession: address.value,
|
|
1884
|
+
run: sender.value,
|
|
1885
|
+
target: `session:${address.value}`,
|
|
1886
|
+
});
|
|
1771
1887
|
}
|
|
1772
1888
|
if (senderStatus.ownerId !== address.value) {
|
|
1773
|
-
throw
|
|
1774
|
-
|
|
1775
|
-
|
|
1889
|
+
throw sessionMismatchError({
|
|
1890
|
+
currentSession: String(senderStatus.ownerId),
|
|
1891
|
+
expectedSession: address.value,
|
|
1892
|
+
run: sender.value,
|
|
1893
|
+
target: `session:${address.value}`,
|
|
1894
|
+
});
|
|
1776
1895
|
}
|
|
1777
1896
|
}
|
|
1778
1897
|
result = AsyncRuns.appendRunOutboxEvent(sender.value, {
|
|
@@ -1796,18 +1915,22 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1796
1915
|
`message currently supports run:<id>, branch:<run>/<branch>, room:<run>, tool:<name>, coordinator, and session:<id> destinations; unsupported destination: ${message.to}`,
|
|
1797
1916
|
);
|
|
1798
1917
|
}
|
|
1918
|
+
const nextActions = actorMessageNextActions(message, result);
|
|
1919
|
+
const resultWithNext = nextActions.length
|
|
1920
|
+
? { ...result, next_actions: nextActions }
|
|
1921
|
+
: result;
|
|
1799
1922
|
return {
|
|
1800
1923
|
content: [
|
|
1801
1924
|
{
|
|
1802
1925
|
type: "text" as const,
|
|
1803
1926
|
text: maybeJsonText(
|
|
1804
|
-
{ message, result },
|
|
1927
|
+
{ message, result: resultWithNext },
|
|
1805
1928
|
input.verbose === true,
|
|
1806
|
-
compactActorMessageResult(message,
|
|
1929
|
+
compactActorMessageResult(message, resultWithNext),
|
|
1807
1930
|
),
|
|
1808
1931
|
},
|
|
1809
1932
|
],
|
|
1810
|
-
details: { message, result },
|
|
1933
|
+
details: { message, result: resultWithNext },
|
|
1811
1934
|
};
|
|
1812
1935
|
},
|
|
1813
1936
|
};
|
|
@@ -1815,7 +1938,7 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
1815
1938
|
|
|
1816
1939
|
export function createCoreActorToolDefinitions<TContext extends AsyncRunToolContext>(
|
|
1817
1940
|
deps: CoreActorToolDefinitionDeps<TContext>,
|
|
1818
|
-
):
|
|
1941
|
+
): ActorToolDefinition[] {
|
|
1819
1942
|
return [
|
|
1820
1943
|
createRegisterToolDefinition<TContext>({
|
|
1821
1944
|
configPath: deps.configPath,
|
package/package.json
CHANGED
package/scripts/conformance.mjs
CHANGED
|
@@ -1,33 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Internal conformance runner
|
|
4
|
+
* Internal conformance runner.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* This script is intentionally standalone package/release glue rather than a
|
|
7
|
+
* lib domain: it only selects regression suites and formats their summary.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { dirname
|
|
12
|
-
import { fileURLToPath
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const conformanceSuites = [
|
|
15
|
+
"tests/protocol-examples.test.ts",
|
|
16
|
+
"tests/recipe-discovery.test.ts",
|
|
17
|
+
"tests/registry.test.ts",
|
|
18
|
+
"tests/runtime-registry.test.ts",
|
|
19
|
+
"tests/async-runs.test.ts",
|
|
20
|
+
"tests/actor-rooms.test.ts",
|
|
21
|
+
"tests/tools.test.ts",
|
|
22
|
+
];
|
|
13
23
|
|
|
14
24
|
function packageRoot() {
|
|
15
25
|
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const result = spawnSync(
|
|
29
|
+
process.execPath,
|
|
30
|
+
["--experimental-strip-types", "--test", ...conformanceSuites],
|
|
31
|
+
{ cwd: packageRoot(), encoding: "utf8", stdio: "pipe" },
|
|
32
|
+
);
|
|
23
33
|
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
34
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
35
|
+
const summary = output
|
|
36
|
+
.split("\n")
|
|
37
|
+
.filter((line) =>
|
|
38
|
+
/^ℹ (tests|pass|fail|cancelled|skipped|todo|duration_ms) /.test(line),
|
|
39
|
+
)
|
|
40
|
+
.join("\n");
|
|
26
41
|
|
|
27
42
|
console.log("pi-actors conformance");
|
|
28
|
-
console.log(`suites ${
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
console.log(`suites ${conformanceSuites.length}`);
|
|
44
|
+
|
|
45
|
+
if (summary) console.log(summary);
|
|
46
|
+
if ((result.status ?? 1) !== 0) {
|
|
47
|
+
console.error(output.trimEnd());
|
|
48
|
+
process.exit(result.status ?? 1);
|
|
33
49
|
}
|