@llblab/pi-actors 0.29.3 → 0.30.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/lib/tools.ts CHANGED
@@ -27,6 +27,34 @@ export type RegisterToolInput = Registry.RegisterToolInput;
27
27
  export type RegisterToolRuntimeDeps<TContext> =
28
28
  Registry.RegisterToolRuntimeDeps<TContext>;
29
29
 
30
+ export interface CoreActorToolDefinitionDeps<TContext extends AsyncRunToolContext> {
31
+ configPath: string;
32
+ getActiveTools: () => string[];
33
+ getRuntimeTool: (name: string) => unknown;
34
+ registryRuntime: Pick<
35
+ RegisterToolRuntimeDeps<TContext>,
36
+ | "getExternalToolConflict"
37
+ | "getTools"
38
+ | "notify"
39
+ | "registerRuntimeTool"
40
+ >;
41
+ setActiveTools: (toolNames: string[]) => void;
42
+ }
43
+
44
+ export const RESERVED_TOOL_NAMES = new Set([
45
+ "read",
46
+ "write",
47
+ "edit",
48
+ "bash",
49
+ "find",
50
+ "grep",
51
+ "ls",
52
+ "register_tool",
53
+ "message",
54
+ "spawn",
55
+ "inspect",
56
+ ]);
57
+
30
58
  type JsonSchema = Record<string, unknown>;
31
59
 
32
60
  function stringSchema(description: string): JsonSchema {
@@ -183,7 +211,8 @@ function compactAsyncRunStatus(value: unknown): string {
183
211
  tokens.push(`failures=${failures}`);
184
212
  if (result.code !== undefined) tokens.push(`code=${String(result.code)}`);
185
213
  if (result.killed === true) tokens.push("killed=true");
186
- if (status.candidate_recipe) tokens.push(`candidate_recipe=${String(status.candidate_recipe)}`);
214
+ if (status.candidate_recipe)
215
+ tokens.push(`candidate_recipe=${String(status.candidate_recipe)}`);
187
216
  return `\n${tokens.join(" ")}`;
188
217
  }
189
218
 
@@ -448,10 +477,17 @@ function compactSessionRuns(
448
477
  summary: Record<string, unknown> = {},
449
478
  ): string {
450
479
  const suffix = [
451
- summary.other_sessions !== undefined ? `other_sessions=${String(summary.other_sessions)}` : "",
452
- summary.other_runs !== undefined ? `other_runs=${String(summary.other_runs)}` : "",
453
- ].filter(Boolean).join(" ");
454
- if (runs.length === 0) return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
480
+ summary.other_sessions !== undefined
481
+ ? `other_sessions=${String(summary.other_sessions)}`
482
+ : "",
483
+ summary.other_runs !== undefined
484
+ ? `other_runs=${String(summary.other_runs)}`
485
+ : "",
486
+ ]
487
+ .filter(Boolean)
488
+ .join(" ");
489
+ if (runs.length === 0)
490
+ return `\nsession=${session} runs=0${suffix ? ` ${suffix}` : ""}`;
455
491
  return `\nsession=${session} runs=${runs.length}${suffix ? ` ${suffix}` : ""}\n${runs
456
492
  .map((run) => {
457
493
  const tokens = [
@@ -471,14 +507,21 @@ function getPiActorsRuntimeStatus(): Record<string, unknown> {
471
507
  const packageRoot = dirname(packagedRecipeRoot);
472
508
  const packageJsonPath = join(packageRoot, "package.json");
473
509
  const packageJson = existsSync(packageJsonPath)
474
- ? JSON.parse(readFileSync(packageJsonPath, "utf8")) as Record<string, unknown>
510
+ ? (JSON.parse(readFileSync(packageJsonPath, "utf8")) as Record<
511
+ string,
512
+ unknown
513
+ >)
475
514
  : {};
476
515
  let git_commit: string | undefined;
477
516
  try {
478
- git_commit = execFileSync("git", ["-C", packageRoot, "rev-parse", "--short", "HEAD"], {
479
- encoding: "utf8",
480
- stdio: ["ignore", "pipe", "ignore"],
481
- }).trim();
517
+ git_commit = execFileSync(
518
+ "git",
519
+ ["-C", packageRoot, "rev-parse", "--short", "HEAD"],
520
+ {
521
+ encoding: "utf8",
522
+ stdio: ["ignore", "pipe", "ignore"],
523
+ },
524
+ ).trim();
482
525
  } catch {
483
526
  git_commit = undefined;
484
527
  }
@@ -510,12 +553,13 @@ function compactToolActor(name: string, tool: Record<string, unknown>): string {
510
553
 
511
554
  function compactRecipeImports(summary: Record<string, unknown>): string {
512
555
  const active = Array.isArray(summary.active)
513
- ? summary.active as Array<Record<string, unknown>>
556
+ ? (summary.active as Array<Record<string, unknown>>)
514
557
  : [];
515
558
  const lines = active.flatMap((entry) => {
516
559
  const imports = asRecord(entry.imports);
517
560
  return Object.entries(imports).map(([alias, value]) => {
518
- const binding = typeof value === "string" ? { from: value } : asRecord(value);
561
+ const binding =
562
+ typeof value === "string" ? { from: value } : asRecord(value);
519
563
  return `recipe=${String(entry.id ?? "<unknown>")} alias=${alias} from=${String(binding.from ?? value)}`;
520
564
  });
521
565
  });
@@ -552,7 +596,10 @@ function compactRecipeDoctor(summary: Record<string, unknown>): string {
552
596
  );
553
597
  }
554
598
  for (const item of remediations.slice(0, 8)) {
555
- const action = compactPreview(item.action, Limits.DOCTOR_ACTION_PREVIEW_CHARS);
599
+ const action = compactPreview(
600
+ item.action,
601
+ Limits.DOCTOR_ACTION_PREVIEW_CHARS,
602
+ );
556
603
  const blocked = item.blocked_candidate
557
604
  ? ` blocked=${compactPreview(item.blocked_candidate, Limits.DOCTOR_ACTION_PREVIEW_CHARS)}`
558
605
  : "";
@@ -717,7 +764,11 @@ function formatToolActorFailure(
717
764
  function shadowedRecipeLaunchDiagnostic(
718
765
  recipe: unknown,
719
766
  ): Record<string, unknown> | undefined {
720
- if (typeof recipe !== "string" || recipe.includes("/") || recipe.includes("~"))
767
+ if (
768
+ typeof recipe !== "string" ||
769
+ recipe.includes("/") ||
770
+ recipe.includes("~")
771
+ )
721
772
  return undefined;
722
773
  const discovery = RecipeDiscovery.discoverRecipeSources([
723
774
  { root: Paths.getRecipeRoot(), defaultTool: true, mutableUsage: true },
@@ -730,7 +781,9 @@ function candidateRecipeName(run: string): string {
730
781
  return `${run.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "spawn"}.json`;
731
782
  }
732
783
 
733
- function candidateRecipeDefaults(values: Record<string, unknown>): Record<string, unknown> | undefined {
784
+ function candidateRecipeDefaults(
785
+ values: Record<string, unknown>,
786
+ ): Record<string, unknown> | undefined {
734
787
  const ignored = new Set([
735
788
  "actor_address",
736
789
  "communication_file",
@@ -753,7 +806,11 @@ function writeSpawnCandidateRecipe(
753
806
  process.env.PI_ACTORS_ENABLE_SPAWN_CANDIDATES_IN_TEST !== "1"
754
807
  )
755
808
  return undefined;
756
- if (input.template === undefined || input.file !== undefined || input.recipe !== undefined)
809
+ if (
810
+ input.template === undefined ||
811
+ input.file !== undefined ||
812
+ input.recipe !== undefined
813
+ )
757
814
  return undefined;
758
815
  const root = Paths.getRecipeCandidateRoot();
759
816
  mkdirSync(root, { recursive: true });
@@ -772,7 +829,8 @@ function writeSpawnCandidateRecipe(
772
829
 
773
830
  function enhanceSpawnRecipeError(error: unknown, recipe: unknown): Error {
774
831
  const diagnostic = shadowedRecipeLaunchDiagnostic(recipe);
775
- if (!diagnostic) return error instanceof Error ? error : new Error(String(error));
832
+ if (!diagnostic)
833
+ return error instanceof Error ? error : new Error(String(error));
776
834
  const original = error instanceof Error ? error.message : String(error);
777
835
  return Object.assign(
778
836
  new Error(
@@ -832,12 +890,17 @@ async function routeBranchEnvelope(
832
890
  try {
833
891
  return await AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
834
892
  } catch (error) {
835
- const record = error && typeof error === "object" ? (error as Record<string, unknown>) : {};
893
+ const record =
894
+ error && typeof error === "object"
895
+ ? (error as Record<string, unknown>)
896
+ : {};
836
897
  if (record.queued === true) {
837
898
  return {
838
899
  control_path: record.control_path,
839
900
  control_type: record.control_type,
840
- delivery_error: record.delivery_error ?? (error instanceof Error ? error.message : String(error)),
901
+ delivery_error:
902
+ record.delivery_error ??
903
+ (error instanceof Error ? error.message : String(error)),
841
904
  inbox_id: record.inbox_id,
842
905
  queued: true,
843
906
  run: runId,
@@ -1070,7 +1133,12 @@ export function createInspectToolDefinition<TContext = unknown>(
1070
1133
  const target = String(input.target ?? "");
1071
1134
  const view = String(input.view ?? "");
1072
1135
  if (target === "recipes" || target === "recipe-registry") {
1073
- if (view !== "status" && view !== "summary" && view !== "doctor" && view !== "imports") {
1136
+ if (
1137
+ view !== "status" &&
1138
+ view !== "summary" &&
1139
+ view !== "doctor" &&
1140
+ view !== "imports"
1141
+ ) {
1074
1142
  throw new Error(
1075
1143
  "inspect recipes supports view=status, view=summary, view=doctor, or view=imports.",
1076
1144
  );
@@ -1086,7 +1154,9 @@ export function createInspectToolDefinition<TContext = unknown>(
1086
1154
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
1087
1155
  const summary = {
1088
1156
  ...RecipeDiscovery.summarizeDiscovery(discovered),
1089
- candidates: RecipeDiscovery.listCandidateRecipes(join(recipeRoot, "candidates")),
1157
+ candidates: RecipeDiscovery.listCandidateRecipes(
1158
+ join(recipeRoot, "candidates"),
1159
+ ),
1090
1160
  };
1091
1161
  return {
1092
1162
  content: [
@@ -1147,9 +1217,10 @@ export function createInspectToolDefinition<TContext = unknown>(
1147
1217
  const runs = allRuns.filter(
1148
1218
  (run) => address.value === "all" || run.ownerId === address.value,
1149
1219
  );
1150
- const sessionSummary = address.value === "all"
1151
- ? {}
1152
- : summarizeOtherSessions(address.value || "", allRuns);
1220
+ const sessionSummary =
1221
+ address.value === "all"
1222
+ ? {}
1223
+ : summarizeOtherSessions(address.value || "", allRuns);
1153
1224
  return {
1154
1225
  content: [
1155
1226
  {
@@ -1725,6 +1796,30 @@ export function createActorMessageToolDefinition<TContext = unknown>(
1725
1796
  };
1726
1797
  }
1727
1798
 
1799
+ export function createCoreActorToolDefinitions<TContext extends AsyncRunToolContext>(
1800
+ deps: CoreActorToolDefinitionDeps<TContext>,
1801
+ ): any[] {
1802
+ return [
1803
+ createRegisterToolDefinition<TContext>({
1804
+ configPath: deps.configPath,
1805
+ getActiveTools: deps.getActiveTools,
1806
+ getExternalToolConflict: deps.registryRuntime.getExternalToolConflict,
1807
+ getTools: deps.registryRuntime.getTools,
1808
+ notify: deps.registryRuntime.notify,
1809
+ registerRuntimeTool: deps.registryRuntime.registerRuntimeTool,
1810
+ reservedToolNames: RESERVED_TOOL_NAMES,
1811
+ setActiveTools: deps.setActiveTools,
1812
+ }),
1813
+ createSpawnToolDefinition<TContext>(),
1814
+ createActorMessageToolDefinition<TContext>({
1815
+ getTool: (name) => deps.getRuntimeTool(name),
1816
+ }),
1817
+ createInspectToolDefinition<TContext>({
1818
+ getTool: (name) => deps.getRuntimeTool(name),
1819
+ }),
1820
+ ];
1821
+ }
1822
+
1728
1823
  export function createRuntimeToolDefinition(
1729
1824
  cfg: RegisteredTool,
1730
1825
  exec: Execution.RegisteredToolExec,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.29.3",
3
+ "version": "0.30.0",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -2,7 +2,7 @@
2
2
  name: actors
3
3
  description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
4
4
  metadata:
5
- version: 0.29.3
5
+ version: 0.30.0
6
6
  ---
7
7
 
8
8
  # Actors (pi-actors)
@@ -2,7 +2,7 @@
2
2
  name: swarm
3
3
  description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
4
4
  metadata:
5
- version: 0.29.3
5
+ version: 0.30.0
6
6
  ---
7
7
 
8
8
  # Swarm