@llblab/pi-actors 0.19.11 → 0.20.1

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.
Files changed (55) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/lib/actor-inspector-tui.d.ts +55 -0
  4. package/dist/lib/actor-inspector-tui.js +559 -0
  5. package/dist/lib/actor-messages.d.ts +25 -0
  6. package/dist/lib/actor-messages.js +122 -0
  7. package/dist/lib/actor-recipe-context.d.ts +14 -0
  8. package/dist/lib/actor-recipe-context.js +79 -0
  9. package/dist/lib/actor-rooms.d.ts +81 -0
  10. package/dist/lib/actor-rooms.js +468 -0
  11. package/dist/lib/async-runs.d.ts +101 -0
  12. package/dist/lib/async-runs.js +612 -0
  13. package/dist/lib/command-templates.d.ts +70 -0
  14. package/dist/lib/command-templates.js +592 -0
  15. package/dist/lib/config.d.ts +34 -0
  16. package/dist/lib/config.js +226 -0
  17. package/dist/lib/execution.d.ts +63 -0
  18. package/dist/lib/execution.js +450 -0
  19. package/dist/lib/file-state.d.ts +6 -0
  20. package/dist/lib/file-state.js +25 -0
  21. package/dist/lib/identity.d.ts +9 -0
  22. package/dist/lib/identity.js +27 -0
  23. package/dist/lib/observability.d.ts +86 -0
  24. package/dist/lib/observability.js +534 -0
  25. package/dist/lib/output.d.ts +25 -0
  26. package/dist/lib/output.js +89 -0
  27. package/dist/lib/paths.d.ts +11 -0
  28. package/dist/lib/paths.js +33 -0
  29. package/dist/lib/prompts.d.ts +23 -0
  30. package/dist/lib/prompts.js +50 -0
  31. package/dist/lib/recipe-discovery.d.ts +50 -0
  32. package/dist/lib/recipe-discovery.js +317 -0
  33. package/dist/lib/recipe-migration.d.ts +21 -0
  34. package/dist/lib/recipe-migration.js +90 -0
  35. package/dist/lib/recipe-references.d.ts +67 -0
  36. package/dist/lib/recipe-references.js +542 -0
  37. package/dist/lib/recipe-usage.d.ts +6 -0
  38. package/dist/lib/recipe-usage.js +57 -0
  39. package/dist/lib/registry.d.ts +47 -0
  40. package/dist/lib/registry.js +222 -0
  41. package/dist/lib/runtime.d.ts +36 -0
  42. package/dist/lib/runtime.js +126 -0
  43. package/dist/lib/schema.d.ts +48 -0
  44. package/dist/lib/schema.js +355 -0
  45. package/dist/lib/temp.d.ts +10 -0
  46. package/dist/lib/temp.js +90 -0
  47. package/dist/lib/tools.d.ts +39 -0
  48. package/dist/lib/tools.js +982 -0
  49. package/lib/async-runs.ts +20 -4
  50. package/lib/paths.ts +5 -1
  51. package/package.json +5 -2
  52. package/scripts/async-runner.mjs +8 -12
  53. package/scripts/validate-recipe.mjs +9 -13
  54. package/skills/actors/SKILL.md +1 -1
  55. package/skills/swarm/SKILL.md +1 -1
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Persistent tool registry config helpers
3
+ * Zones: registry config, persistence, migration boundary
4
+ * Owns registered-tool config loading, normalization, unsupported-shape rejection, and serialization
5
+ */
6
+ import { existsSync, readFileSync } from "node:fs";
7
+ import * as CommandTemplates from "./command-templates.js";
8
+ import { writeJsonAtomic } from "./file-state.js";
9
+ import { normalizeToolName } from "./identity.js";
10
+ import * as RecipeReferences from "./recipe-references.js";
11
+ import * as Schema from "./schema.js";
12
+ export function serializeTools(source) {
13
+ const entries = [...source.entries()].sort(([a], [b]) => a.localeCompare(b));
14
+ const result = {};
15
+ for (const [name, cfg] of entries) {
16
+ const entry = {
17
+ description: cfg.description,
18
+ };
19
+ if (cfg.storedArgs && cfg.storedArgs.length > 0)
20
+ entry.args = cfg.storedArgs;
21
+ if (cfg.storedDefaults && Object.keys(cfg.storedDefaults).length > 0)
22
+ entry.defaults = cfg.storedDefaults;
23
+ if (cfg.recipe?.name)
24
+ entry.name = cfg.recipe.name;
25
+ if (cfg.recipe?.async !== undefined)
26
+ entry.async = cfg.recipe.async;
27
+ if (cfg.recipe?.state_dir)
28
+ entry.state_dir = cfg.recipe.state_dir;
29
+ if (cfg.recipe?.values)
30
+ entry.values = cfg.recipe.values;
31
+ if (cfg.template)
32
+ entry.template = cfg.template;
33
+ result[name] = entry;
34
+ }
35
+ return result;
36
+ }
37
+ export function saveTools(path, source) {
38
+ try {
39
+ writeJsonAtomic(path, serializeTools(source));
40
+ return undefined;
41
+ }
42
+ catch (error) {
43
+ return getErrorMessage(error);
44
+ }
45
+ }
46
+ export function getStoredEntries(raw) {
47
+ if (Array.isArray(raw))
48
+ return raw.map((value) => [undefined, value]);
49
+ if (raw && typeof raw === "object") {
50
+ return Object.entries(raw);
51
+ }
52
+ return [];
53
+ }
54
+ function getStoredTemplate(value) {
55
+ if (typeof value === "string")
56
+ return value.trim() || undefined;
57
+ if (!Array.isArray(value))
58
+ return undefined;
59
+ const template = value;
60
+ return CommandTemplates.expandCommandTemplateConfigs({ template }).length > 0
61
+ ? template
62
+ : undefined;
63
+ }
64
+ function formatTemplateForDescription(template) {
65
+ return typeof template === "string" ? template : JSON.stringify(template);
66
+ }
67
+ export function normalizeStoredTool(key, value, reservedToolNames) {
68
+ if (!value || typeof value !== "object") {
69
+ return {
70
+ changed: true,
71
+ warning: `Invalid tool entry: ${key ?? "<array item>"}`,
72
+ };
73
+ }
74
+ const record = value;
75
+ const rawName = key ?? (typeof record.name === "string" ? record.name : "");
76
+ const name = normalizeToolName(rawName);
77
+ if (!name) {
78
+ return {
79
+ changed: true,
80
+ warning: `Invalid tool name: ${rawName || key || "<empty>"}`,
81
+ };
82
+ }
83
+ if (reservedToolNames.has(name)) {
84
+ return { changed: true, warning: `Reserved tool name skipped: ${name}` };
85
+ }
86
+ const template = getStoredTemplate(record.template);
87
+ if (!template && typeof record.script === "string") {
88
+ return {
89
+ changed: false,
90
+ warning: `Tool "${name}" uses unsupported script config. Use template because pi-actors cannot load script entries.`,
91
+ };
92
+ }
93
+ if (Object.hasOwn(record, "tool")) {
94
+ return {
95
+ changed: false,
96
+ warning: `Tool "${name}" cannot define tool; use template directly.`,
97
+ };
98
+ }
99
+ if (record.job !== undefined || record.recipe !== undefined) {
100
+ return {
101
+ changed: false,
102
+ warning: `Tool "${name}" uses unsupported job/recipe config. Use template with optional name and async fields.`,
103
+ };
104
+ }
105
+ const keyedRecipeName = key !== undefined && typeof record.name === "string" && record.name.trim()
106
+ ? record.name.trim()
107
+ : undefined;
108
+ if ((keyedRecipeName || typeof record.async === "boolean") && !template) {
109
+ return {
110
+ changed: false,
111
+ warning: `Tool "${name}" uses recipe config without template. Add template to make it a co-located template recipe.`,
112
+ };
113
+ }
114
+ if (!template) {
115
+ return { changed: true, warning: `Tool "${name}" has no template` };
116
+ }
117
+ const recipeName = keyedRecipeName ?? (typeof record.async === "boolean" ? name : undefined);
118
+ const recipe = recipeName
119
+ ? {
120
+ name: recipeName,
121
+ ...(typeof record.async === "boolean" ? { async: record.async } : {}),
122
+ ...(typeof record.state_dir === "string" && record.state_dir.trim()
123
+ ? { state_dir: record.state_dir.trim() }
124
+ : {}),
125
+ template,
126
+ ...(record.values &&
127
+ typeof record.values === "object" &&
128
+ !Array.isArray(record.values)
129
+ ? { values: record.values }
130
+ : {}),
131
+ }
132
+ : undefined;
133
+ const isRecipe = RecipeReferences.isRecipeTool(template, recipe);
134
+ const recipeTemplate = RecipeReferences.getRecipeTemplate(template);
135
+ const argTemplate = recipeTemplate ?? template;
136
+ const description = typeof record.description === "string" && record.description.trim()
137
+ ? record.description.trim()
138
+ : isRecipe
139
+ ? `${recipe?.async === true || RecipeReferences.isAsyncRecipeReference(template) ? "Start async" : "Execute"} template recipe: ${recipe?.name ?? formatTemplateForDescription(template)}`
140
+ : `Execute command template: ${formatTemplateForDescription(template)}`;
141
+ const declarations = Schema.normalizeStoredToolArgDeclarations(record.args, record.defaults);
142
+ const storedArgs = declarations.provided
143
+ ? declarations.declarations
144
+ : undefined;
145
+ const storedDefaults = declarations.provided && Object.keys(declarations.defaults).length > 0
146
+ ? declarations.defaults
147
+ : undefined;
148
+ const argTemplateConfig = typeof argTemplate === "object" && !Array.isArray(argTemplate)
149
+ ? {
150
+ ...argTemplate,
151
+ ...(storedArgs !== undefined ? { args: storedArgs } : {}),
152
+ defaults: {
153
+ ...(argTemplate.defaults ?? {}),
154
+ ...declarations.defaults,
155
+ },
156
+ }
157
+ : {
158
+ args: storedArgs,
159
+ defaults: declarations.defaults,
160
+ template: argTemplate,
161
+ };
162
+ const inferredArgTypes = Schema.getTemplateArgTypes(argTemplateConfig);
163
+ const argTypes = { ...inferredArgTypes, ...declarations.argTypes };
164
+ const cfg = {
165
+ name,
166
+ description,
167
+ args: isRecipe && storedArgs !== undefined
168
+ ? Schema.getExplicitToolArgNames(storedArgs)
169
+ : RecipeReferences.isRecipeReference(template) && !recipeTemplate
170
+ ? Schema.getExplicitToolArgNames(storedArgs)
171
+ : Schema.getToolArgNames(argTemplateConfig),
172
+ defaults: declarations.defaults,
173
+ ...(Object.keys(argTypes).length > 0 ? { argTypes } : {}),
174
+ ...(recipe ? { recipe } : {}),
175
+ template,
176
+ ...(storedArgs !== undefined ? { storedArgs } : {}),
177
+ ...(storedDefaults !== undefined ? { storedDefaults } : {}),
178
+ };
179
+ const changed = (key === undefined && record.name !== undefined) ||
180
+ record.label !== undefined ||
181
+ JSON.stringify(record.template) !== JSON.stringify(template) ||
182
+ description !== record.description ||
183
+ declarations.changed;
184
+ return { cfg, changed };
185
+ }
186
+ export function loadToolConfig(path, reservedToolNames) {
187
+ const warnings = [];
188
+ const tools = new Map();
189
+ let changed = false;
190
+ if (!existsSync(path))
191
+ return { tools, warnings, changed };
192
+ try {
193
+ const raw = JSON.parse(readFileSync(path, "utf8"));
194
+ const entries = getStoredEntries(raw);
195
+ for (const [key, value] of entries) {
196
+ const result = normalizeStoredTool(key, value, reservedToolNames);
197
+ changed = changed || result.changed;
198
+ if (result.warning)
199
+ warnings.push(result.warning);
200
+ if (!result.cfg)
201
+ continue;
202
+ if (tools.has(result.cfg.name)) {
203
+ warnings.push(`Duplicate tool kept from last entry: ${result.cfg.name}`);
204
+ }
205
+ if (tools.has(result.cfg.name))
206
+ changed = true;
207
+ tools.set(result.cfg.name, result.cfg);
208
+ }
209
+ if (entries.length === 0 && raw && typeof raw !== "object") {
210
+ warnings.push(`Invalid ${path} format`);
211
+ }
212
+ if (entries.length === 0 && raw && typeof raw !== "object")
213
+ changed = true;
214
+ return { tools, warnings, changed };
215
+ }
216
+ catch (error) {
217
+ return {
218
+ tools,
219
+ warnings: [`Failed to load ${path}: ${getErrorMessage(error)}`],
220
+ changed: false,
221
+ };
222
+ }
223
+ }
224
+ function getErrorMessage(error) {
225
+ return error instanceof Error ? error.message : String(error);
226
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Registered tool execution runtime
3
+ * Zones: tool execution, command templates, output formatting
4
+ * Owns command-template invocation execution and pi tool-result payload formatting
5
+ */
6
+ import * as CommandTemplates from "./command-templates.ts";
7
+ import type { RegisteredTool } from "./config.ts";
8
+ export interface ToolExecOptions {
9
+ actorRecipeContext?: CommandTemplates.CommandTemplateActorRecipeContext;
10
+ cwd?: string;
11
+ signal?: AbortSignal;
12
+ stdin?: string;
13
+ timeout?: number;
14
+ retry?: number;
15
+ }
16
+ export interface ToolExecResult {
17
+ stdout: string;
18
+ stderr: string;
19
+ code: number;
20
+ killed: boolean;
21
+ }
22
+ export interface BranchReport {
23
+ code: number;
24
+ command: string;
25
+ killed: boolean;
26
+ label: string;
27
+ status: "done" | "failed" | "timeout";
28
+ stderr?: string;
29
+ stdoutBytes: number;
30
+ }
31
+ export interface SoftQuorumReport {
32
+ coverage: number;
33
+ degraded: boolean;
34
+ done: number;
35
+ expected: number;
36
+ failed: number;
37
+ usable: boolean;
38
+ }
39
+ export interface RegisteredToolExecutionResult {
40
+ content: Array<{
41
+ type: "text";
42
+ text: string;
43
+ }>;
44
+ details: {
45
+ branches?: BranchReport[];
46
+ code: number;
47
+ command: string;
48
+ fullOutputPath?: string;
49
+ killed: boolean;
50
+ nonCriticalFailures?: Array<{
51
+ code: number;
52
+ command: string;
53
+ killed: boolean;
54
+ }>;
55
+ softQuorum?: SoftQuorumReport;
56
+ template: CommandTemplates.CommandTemplateValue;
57
+ templateWarnings?: string[];
58
+ tool: string;
59
+ truncated: boolean;
60
+ };
61
+ }
62
+ export type RegisteredToolExec = (command: string, args: string[], options?: ToolExecOptions) => Promise<ToolExecResult>;
63
+ export declare function executeRegisteredTool(cfg: RegisteredTool, params: Record<string, unknown>, exec: RegisteredToolExec, cwd: string, signal?: AbortSignal): Promise<RegisteredToolExecutionResult>;