@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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/lib/actor-inspector-tui.d.ts +55 -0
- package/dist/lib/actor-inspector-tui.js +559 -0
- package/dist/lib/actor-messages.d.ts +25 -0
- package/dist/lib/actor-messages.js +122 -0
- package/dist/lib/actor-recipe-context.d.ts +14 -0
- package/dist/lib/actor-recipe-context.js +79 -0
- package/dist/lib/actor-rooms.d.ts +81 -0
- package/dist/lib/actor-rooms.js +468 -0
- package/dist/lib/async-runs.d.ts +101 -0
- package/dist/lib/async-runs.js +612 -0
- package/dist/lib/command-templates.d.ts +70 -0
- package/dist/lib/command-templates.js +592 -0
- package/dist/lib/config.d.ts +34 -0
- package/dist/lib/config.js +226 -0
- package/dist/lib/execution.d.ts +63 -0
- package/dist/lib/execution.js +450 -0
- package/dist/lib/file-state.d.ts +6 -0
- package/dist/lib/file-state.js +25 -0
- package/dist/lib/identity.d.ts +9 -0
- package/dist/lib/identity.js +27 -0
- package/dist/lib/observability.d.ts +86 -0
- package/dist/lib/observability.js +534 -0
- package/dist/lib/output.d.ts +25 -0
- package/dist/lib/output.js +89 -0
- package/dist/lib/paths.d.ts +11 -0
- package/dist/lib/paths.js +33 -0
- package/dist/lib/prompts.d.ts +23 -0
- package/dist/lib/prompts.js +50 -0
- package/dist/lib/recipe-discovery.d.ts +50 -0
- package/dist/lib/recipe-discovery.js +317 -0
- package/dist/lib/recipe-migration.d.ts +21 -0
- package/dist/lib/recipe-migration.js +90 -0
- package/dist/lib/recipe-references.d.ts +67 -0
- package/dist/lib/recipe-references.js +542 -0
- package/dist/lib/recipe-usage.d.ts +6 -0
- package/dist/lib/recipe-usage.js +57 -0
- package/dist/lib/registry.d.ts +47 -0
- package/dist/lib/registry.js +222 -0
- package/dist/lib/runtime.d.ts +36 -0
- package/dist/lib/runtime.js +126 -0
- package/dist/lib/schema.d.ts +48 -0
- package/dist/lib/schema.js +355 -0
- package/dist/lib/temp.d.ts +10 -0
- package/dist/lib/temp.js +90 -0
- package/dist/lib/tools.d.ts +39 -0
- package/dist/lib/tools.js +982 -0
- package/lib/async-runs.ts +20 -4
- package/lib/paths.ts +5 -1
- package/package.json +5 -2
- package/scripts/async-runner.mjs +8 -12
- package/scripts/validate-recipe.mjs +9 -13
- package/skills/actors/SKILL.md +1 -1
- 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>;
|