@llblab/pi-actors 0.19.11 → 0.20.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 -1
- package/CHANGELOG.md +8 -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 +28 -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/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,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-tools schema helpers
|
|
3
|
+
* Zones: tool schema, registry args, command-template placeholders
|
|
4
|
+
* Owns tool argument declarations, placeholder-derived tool schemas, and persisted registry normalization
|
|
5
|
+
*/
|
|
6
|
+
import * as CommandTemplates from "./command-templates.js";
|
|
7
|
+
function mergeUnique(items) {
|
|
8
|
+
return [...new Set(items.filter(Boolean))];
|
|
9
|
+
}
|
|
10
|
+
function parseArgType(value) {
|
|
11
|
+
const source = value?.trim();
|
|
12
|
+
if (!source)
|
|
13
|
+
return { kind: "string" };
|
|
14
|
+
if (source === "string")
|
|
15
|
+
return { kind: "string" };
|
|
16
|
+
if (source === "path")
|
|
17
|
+
return { kind: "path" };
|
|
18
|
+
if (source === "int")
|
|
19
|
+
return { kind: "int" };
|
|
20
|
+
if (source === "number")
|
|
21
|
+
return { kind: "number" };
|
|
22
|
+
if (source === "bool")
|
|
23
|
+
return { kind: "bool" };
|
|
24
|
+
if (source === "array")
|
|
25
|
+
return { kind: "array" };
|
|
26
|
+
const enumMatch = source.match(/^enum\(([^)]*)\)$/);
|
|
27
|
+
if (enumMatch) {
|
|
28
|
+
const values = enumMatch[1]
|
|
29
|
+
.split(",")
|
|
30
|
+
.map((item) => item.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
return values.length > 0 ? { kind: "enum", values } : undefined;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function splitArgDeclarations(value) {
|
|
37
|
+
const items = [];
|
|
38
|
+
let depth = 0;
|
|
39
|
+
let current = "";
|
|
40
|
+
for (const char of value) {
|
|
41
|
+
if (char === "(")
|
|
42
|
+
depth += 1;
|
|
43
|
+
if (char === ")" && depth > 0)
|
|
44
|
+
depth -= 1;
|
|
45
|
+
if (char === "," && depth === 0) {
|
|
46
|
+
if (current.trim())
|
|
47
|
+
items.push(current.trim());
|
|
48
|
+
current = "";
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
current += char;
|
|
52
|
+
}
|
|
53
|
+
if (current.trim())
|
|
54
|
+
items.push(current.trim());
|
|
55
|
+
return items;
|
|
56
|
+
}
|
|
57
|
+
function canonicalArgDeclaration(arg, type) {
|
|
58
|
+
switch (type.kind) {
|
|
59
|
+
case "string":
|
|
60
|
+
return arg;
|
|
61
|
+
case "path":
|
|
62
|
+
case "int":
|
|
63
|
+
case "number":
|
|
64
|
+
case "bool":
|
|
65
|
+
case "array":
|
|
66
|
+
return `${arg}:${type.kind}`;
|
|
67
|
+
case "enum":
|
|
68
|
+
return `${arg}:enum(${type.values.join(",")})`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function parseToolArgToken(value) {
|
|
72
|
+
const separatorIndex = value.indexOf("=");
|
|
73
|
+
const rawName = separatorIndex === -1 ? value : value.slice(0, separatorIndex);
|
|
74
|
+
const defaultValue = separatorIndex === -1 ? undefined : value.slice(separatorIndex + 1).trim();
|
|
75
|
+
const typedMatch = rawName.trim().match(/^([^:\s]+)(?::(.+))?$/);
|
|
76
|
+
if (!typedMatch) {
|
|
77
|
+
return {
|
|
78
|
+
arg: rawName.trim(),
|
|
79
|
+
defaultValue,
|
|
80
|
+
declaration: rawName.trim(),
|
|
81
|
+
type: { kind: "string" },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const arg = typedMatch[1].trim();
|
|
85
|
+
const type = parseArgType(typedMatch[2]) ?? { kind: "string" };
|
|
86
|
+
return {
|
|
87
|
+
arg,
|
|
88
|
+
defaultValue,
|
|
89
|
+
declaration: canonicalArgDeclaration(arg, type),
|
|
90
|
+
type,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function isValidDefault(type, value) {
|
|
94
|
+
switch (type.kind) {
|
|
95
|
+
case "int":
|
|
96
|
+
return /^-?\d+$/.test(value);
|
|
97
|
+
case "number":
|
|
98
|
+
return /^-?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value);
|
|
99
|
+
case "bool":
|
|
100
|
+
return /^(?:true|false|1|0|yes|no)$/i.test(value);
|
|
101
|
+
case "enum":
|
|
102
|
+
return type.values.includes(value);
|
|
103
|
+
case "path":
|
|
104
|
+
case "array":
|
|
105
|
+
case "string":
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function parseToolArgDeclarations(value) {
|
|
110
|
+
const source = splitArgDeclarations(value);
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const duplicates = new Set();
|
|
113
|
+
const args = [];
|
|
114
|
+
const argTypes = {};
|
|
115
|
+
const declarations = [];
|
|
116
|
+
const defaults = {};
|
|
117
|
+
for (const item of source) {
|
|
118
|
+
const parsed = parseToolArgToken(item);
|
|
119
|
+
if (!parsed.arg)
|
|
120
|
+
continue;
|
|
121
|
+
if (seen.has(parsed.arg))
|
|
122
|
+
duplicates.add(parsed.arg);
|
|
123
|
+
seen.add(parsed.arg);
|
|
124
|
+
args.push(parsed.arg);
|
|
125
|
+
if (parsed.type.kind !== "string")
|
|
126
|
+
argTypes[parsed.arg] = parsed.type;
|
|
127
|
+
declarations.push(parsed.declaration);
|
|
128
|
+
if (parsed.defaultValue !== undefined) {
|
|
129
|
+
if (!isValidDefault(parsed.type, parsed.defaultValue)) {
|
|
130
|
+
return {
|
|
131
|
+
args: [],
|
|
132
|
+
argTypes: {},
|
|
133
|
+
declarations: [],
|
|
134
|
+
defaults: {},
|
|
135
|
+
error: `Invalid default for ${parsed.arg}:${parsed.type.kind}`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
defaults[parsed.arg] = parsed.defaultValue;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (duplicates.size > 0) {
|
|
142
|
+
return {
|
|
143
|
+
args: [],
|
|
144
|
+
argTypes: {},
|
|
145
|
+
declarations: [],
|
|
146
|
+
defaults: {},
|
|
147
|
+
error: `Duplicate argument name(s): ${[...duplicates].join(", ")}`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { args, argTypes, declarations, defaults };
|
|
151
|
+
}
|
|
152
|
+
export function normalizeStoredToolArgDeclarations(argsValue, defaultsValue) {
|
|
153
|
+
const provided = argsValue !== undefined || defaultsValue !== undefined;
|
|
154
|
+
const source = Array.isArray(argsValue)
|
|
155
|
+
? argsValue
|
|
156
|
+
: typeof argsValue === "string"
|
|
157
|
+
? splitArgDeclarations(argsValue)
|
|
158
|
+
: [];
|
|
159
|
+
const rawDefaults = defaultsValue && typeof defaultsValue === "object"
|
|
160
|
+
? defaultsValue
|
|
161
|
+
: {};
|
|
162
|
+
const seen = new Set();
|
|
163
|
+
const args = [];
|
|
164
|
+
const argTypes = {};
|
|
165
|
+
const declarations = [];
|
|
166
|
+
const defaults = {};
|
|
167
|
+
for (const item of source) {
|
|
168
|
+
const parsed = parseToolArgToken(String(item).trim());
|
|
169
|
+
if (!parsed.arg || seen.has(parsed.arg))
|
|
170
|
+
continue;
|
|
171
|
+
seen.add(parsed.arg);
|
|
172
|
+
args.push(parsed.arg);
|
|
173
|
+
if (parsed.type.kind !== "string")
|
|
174
|
+
argTypes[parsed.arg] = parsed.type;
|
|
175
|
+
declarations.push(parsed.declaration);
|
|
176
|
+
const storedDefault = rawDefaults[parsed.arg];
|
|
177
|
+
if (typeof storedDefault === "string")
|
|
178
|
+
defaults[parsed.arg] = storedDefault;
|
|
179
|
+
else if (parsed.defaultValue !== undefined)
|
|
180
|
+
defaults[parsed.arg] = parsed.defaultValue;
|
|
181
|
+
}
|
|
182
|
+
for (const [key, value] of Object.entries(rawDefaults)) {
|
|
183
|
+
const arg = key.trim();
|
|
184
|
+
if (!arg || Object.hasOwn(defaults, arg))
|
|
185
|
+
continue;
|
|
186
|
+
defaults[arg] = value === undefined || value === null ? "" : String(value);
|
|
187
|
+
}
|
|
188
|
+
const canonicalArgs = argsValue === undefined ? undefined : declarations;
|
|
189
|
+
const canonicalDefaults = Object.keys(defaults).length > 0 ? defaults : {};
|
|
190
|
+
const changed = provided &&
|
|
191
|
+
(JSON.stringify(canonicalArgs ?? []) !== JSON.stringify(argsValue ?? []) ||
|
|
192
|
+
JSON.stringify(canonicalDefaults) !==
|
|
193
|
+
JSON.stringify(defaultsValue ?? {}));
|
|
194
|
+
return { args, argTypes, changed, declarations, defaults, provided };
|
|
195
|
+
}
|
|
196
|
+
export function formatToolArgs(args) {
|
|
197
|
+
return args.length > 0 ? args.join(", ") : "none";
|
|
198
|
+
}
|
|
199
|
+
function parseTemplatePlaceholderDeclaration(content) {
|
|
200
|
+
if (content.startsWith("_("))
|
|
201
|
+
return undefined;
|
|
202
|
+
const nullish = content.match(/^([A-Za-z_][A-Za-z0-9_-]*)\?\?.*$/);
|
|
203
|
+
if (nullish)
|
|
204
|
+
return parseToolArgToken(nullish[1]);
|
|
205
|
+
const ternary = content.match(/^([A-Za-z_][A-Za-z0-9_-]*)\?[^:]*:.*$/);
|
|
206
|
+
if (ternary)
|
|
207
|
+
return parseToolArgToken(`${ternary[1]}:bool=false`);
|
|
208
|
+
const typedMatch = content.match(/^([A-Za-z_][A-Za-z0-9_-]*)(?::(?:string|path|int|number|bool|array|enum\([^)]*\)))?(?:=([^}]*))?$/);
|
|
209
|
+
if (!typedMatch)
|
|
210
|
+
return undefined;
|
|
211
|
+
const parsed = parseToolArgToken(content);
|
|
212
|
+
if (!parsed.arg ||
|
|
213
|
+
CommandTemplates.isCommandTemplateRepeatPlaceholder(parsed.arg))
|
|
214
|
+
return undefined;
|
|
215
|
+
return parsed;
|
|
216
|
+
}
|
|
217
|
+
function collectTemplatePlaceholderDeclarations(source, declarations) {
|
|
218
|
+
for (const match of source.matchAll(/\{([^{}]+)\}/g)) {
|
|
219
|
+
const parsed = parseTemplatePlaceholderDeclaration(match[1]);
|
|
220
|
+
if (parsed)
|
|
221
|
+
declarations.push(parsed);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function collectWhenDeclarations(source, declarations) {
|
|
225
|
+
collectTemplatePlaceholderDeclarations(source, declarations);
|
|
226
|
+
const bareCondition = source.match(/^!?([A-Za-z_][A-Za-z0-9_-]*)$/);
|
|
227
|
+
if (bareCondition)
|
|
228
|
+
declarations.push(parseToolArgToken(`${bareCondition[1]}:bool=false`));
|
|
229
|
+
}
|
|
230
|
+
function collectCommandTemplateConfigDeclarations(config, declarations) {
|
|
231
|
+
if (typeof config === "string") {
|
|
232
|
+
collectTemplatePlaceholderDeclarations(config, declarations);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (Array.isArray(config)) {
|
|
236
|
+
for (const step of config)
|
|
237
|
+
collectCommandTemplateConfigDeclarations(step, declarations);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (config.template !== undefined)
|
|
241
|
+
collectCommandTemplateConfigDeclarations(config.template, declarations);
|
|
242
|
+
if (config.recover !== undefined)
|
|
243
|
+
collectCommandTemplateConfigDeclarations(config.recover, declarations);
|
|
244
|
+
for (const field of [config.timeout, config.delay, config.retry, config.repeat]) {
|
|
245
|
+
if (typeof field === "string")
|
|
246
|
+
collectTemplatePlaceholderDeclarations(field, declarations);
|
|
247
|
+
}
|
|
248
|
+
if (typeof config.when === "string")
|
|
249
|
+
collectWhenDeclarations(config.when, declarations);
|
|
250
|
+
}
|
|
251
|
+
function getTemplatePlaceholderDeclarations(config) {
|
|
252
|
+
const declarations = [];
|
|
253
|
+
collectCommandTemplateConfigDeclarations(config, declarations);
|
|
254
|
+
return declarations;
|
|
255
|
+
}
|
|
256
|
+
export function getTemplatePlaceholderNames(config) {
|
|
257
|
+
return mergeUnique(getTemplatePlaceholderDeclarations(config).map((item) => item.arg));
|
|
258
|
+
}
|
|
259
|
+
export function getTemplateArgTypes(config) {
|
|
260
|
+
const argTypes = {};
|
|
261
|
+
for (const declaration of getTemplatePlaceholderDeclarations(config)) {
|
|
262
|
+
if (declaration.type.kind !== "string")
|
|
263
|
+
argTypes[declaration.arg] = declaration.type;
|
|
264
|
+
}
|
|
265
|
+
return argTypes;
|
|
266
|
+
}
|
|
267
|
+
export function getExplicitToolArgNames(args) {
|
|
268
|
+
return mergeUnique((args ?? []).map((item) => parseToolArgToken(String(item)).arg));
|
|
269
|
+
}
|
|
270
|
+
export function getToolArgNames(config) {
|
|
271
|
+
const normalizedConfig = CommandTemplates.normalizeCommandTemplateConfig(config);
|
|
272
|
+
const declaredArgs = Array.isArray(normalizedConfig.args)
|
|
273
|
+
? normalizedConfig.args.map((item) => parseToolArgToken(String(item)).arg)
|
|
274
|
+
: [];
|
|
275
|
+
return mergeUnique([...declaredArgs, ...getTemplatePlaceholderNames(config)]);
|
|
276
|
+
}
|
|
277
|
+
export function getRequiredToolArgNames(config) {
|
|
278
|
+
const required = new Set();
|
|
279
|
+
for (const step of CommandTemplates.expandCommandTemplateConfigs(config)) {
|
|
280
|
+
const defaults = CommandTemplates.getCommandTemplateDefaults(step);
|
|
281
|
+
for (const declaration of getTemplatePlaceholderDeclarations(step)) {
|
|
282
|
+
if (declaration.defaultValue === undefined &&
|
|
283
|
+
!Object.hasOwn(defaults, declaration.arg))
|
|
284
|
+
required.add(declaration.arg);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return required;
|
|
288
|
+
}
|
|
289
|
+
function normalizeTypedArgValue(name, type, value) {
|
|
290
|
+
if (value === undefined || value === null)
|
|
291
|
+
return "";
|
|
292
|
+
switch (type.kind) {
|
|
293
|
+
case "int": {
|
|
294
|
+
if (typeof value === "number" && Number.isInteger(value))
|
|
295
|
+
return String(value);
|
|
296
|
+
if (typeof value === "string" && /^-?\d+$/.test(value.trim()))
|
|
297
|
+
return value.trim();
|
|
298
|
+
throw new Error(`Argument ${name} must be an integer.`);
|
|
299
|
+
}
|
|
300
|
+
case "number": {
|
|
301
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
302
|
+
return String(value);
|
|
303
|
+
if (typeof value === "string" &&
|
|
304
|
+
/^-?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value.trim()))
|
|
305
|
+
return value.trim();
|
|
306
|
+
throw new Error(`Argument ${name} must be a number.`);
|
|
307
|
+
}
|
|
308
|
+
case "bool": {
|
|
309
|
+
if (typeof value === "boolean")
|
|
310
|
+
return value ? "true" : "false";
|
|
311
|
+
if (typeof value === "string") {
|
|
312
|
+
const normalized = value.trim().toLowerCase();
|
|
313
|
+
if (["true", "1", "yes"].includes(normalized))
|
|
314
|
+
return "true";
|
|
315
|
+
if (["false", "0", "no"].includes(normalized))
|
|
316
|
+
return "false";
|
|
317
|
+
}
|
|
318
|
+
throw new Error(`Argument ${name} must be a boolean.`);
|
|
319
|
+
}
|
|
320
|
+
case "enum": {
|
|
321
|
+
const normalized = String(value);
|
|
322
|
+
if (type.values.includes(normalized))
|
|
323
|
+
return normalized;
|
|
324
|
+
throw new Error(`Argument ${name} must be one of: ${type.values.join(", ")}.`);
|
|
325
|
+
}
|
|
326
|
+
case "array": {
|
|
327
|
+
if (Array.isArray(value))
|
|
328
|
+
return value;
|
|
329
|
+
if (typeof value === "string") {
|
|
330
|
+
try {
|
|
331
|
+
const parsed = JSON.parse(value);
|
|
332
|
+
if (Array.isArray(parsed))
|
|
333
|
+
return parsed;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// Fall through to error.
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
throw new Error(`Argument ${name} must be an array.`);
|
|
340
|
+
}
|
|
341
|
+
case "path":
|
|
342
|
+
case "string":
|
|
343
|
+
return String(value);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
export function normalizeRuntimeValues(values, argTypes) {
|
|
347
|
+
if (!argTypes || Object.keys(argTypes).length === 0)
|
|
348
|
+
return values;
|
|
349
|
+
const normalized = { ...values };
|
|
350
|
+
for (const [name, type] of Object.entries(argTypes)) {
|
|
351
|
+
if (Object.hasOwn(normalized, name))
|
|
352
|
+
normalized[name] = normalizeTypedArgValue(name, type, normalized[name]);
|
|
353
|
+
}
|
|
354
|
+
return normalized;
|
|
355
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension temp-directory helpers
|
|
3
|
+
* Zones: temp directory, cleanup, runtime files
|
|
4
|
+
* Owns pi-agent tmp directory preparation and stale-entry cleanup
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_TEMP_MAX_AGE_MS: number;
|
|
7
|
+
export declare const DEFAULT_RUN_MAX_AGE_MS: number;
|
|
8
|
+
export declare function cleanupStaleTempEntries(tempDir: string, maxAgeMs?: number, now?: number, preservedEntries?: Set<string>): Promise<number>;
|
|
9
|
+
export declare function cleanupStaleRunEntries(runsDir: string, maxAgeMs?: number, now?: number): Promise<number>;
|
|
10
|
+
export declare function prepareExtensionTempDir(tempDir: string, maxAgeMs?: number, runMaxAgeMs?: number): Promise<number>;
|
package/dist/lib/temp.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension temp-directory helpers
|
|
3
|
+
* Zones: temp directory, cleanup, runtime files
|
|
4
|
+
* Owns pi-agent tmp directory preparation and stale-entry cleanup
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir, readdir, readFile, rm, stat } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
export const DEFAULT_TEMP_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
export const DEFAULT_RUN_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
10
|
+
export async function cleanupStaleTempEntries(tempDir, maxAgeMs = DEFAULT_TEMP_MAX_AGE_MS, now = Date.now(), preservedEntries = new Set(["runs"])) {
|
|
11
|
+
let entries;
|
|
12
|
+
let removed = 0;
|
|
13
|
+
try {
|
|
14
|
+
entries = await readdir(tempDir, { withFileTypes: true });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
if (preservedEntries.has(entry.name))
|
|
21
|
+
continue;
|
|
22
|
+
const path = join(tempDir, entry.name);
|
|
23
|
+
try {
|
|
24
|
+
const info = await stat(path);
|
|
25
|
+
if (now - info.mtimeMs <= maxAgeMs)
|
|
26
|
+
continue;
|
|
27
|
+
await rm(path, { force: true, recursive: true });
|
|
28
|
+
removed += 1;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Ignore temp cleanup races.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return removed;
|
|
35
|
+
}
|
|
36
|
+
function isPidAlive(pid) {
|
|
37
|
+
try {
|
|
38
|
+
process.kill(pid, 0);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function isRunEntryAlive(path) {
|
|
46
|
+
try {
|
|
47
|
+
const raw = await readFile(join(path, "run.json"), "utf8");
|
|
48
|
+
const meta = JSON.parse(raw);
|
|
49
|
+
const pid = Number(meta.pid || 0);
|
|
50
|
+
return pid > 0 && isPidAlive(pid);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export async function cleanupStaleRunEntries(runsDir, maxAgeMs = DEFAULT_RUN_MAX_AGE_MS, now = Date.now()) {
|
|
57
|
+
let entries;
|
|
58
|
+
let removed = 0;
|
|
59
|
+
try {
|
|
60
|
+
entries = await readdir(runsDir, { withFileTypes: true });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
if (!entry.isDirectory())
|
|
67
|
+
continue;
|
|
68
|
+
const path = join(runsDir, entry.name);
|
|
69
|
+
try {
|
|
70
|
+
const info = await stat(path);
|
|
71
|
+
const timestamp = Math.min(info.birthtimeMs || info.mtimeMs, info.mtimeMs);
|
|
72
|
+
if (now - timestamp <= maxAgeMs)
|
|
73
|
+
continue;
|
|
74
|
+
if (await isRunEntryAlive(path))
|
|
75
|
+
continue;
|
|
76
|
+
await rm(path, { force: true, recursive: true });
|
|
77
|
+
removed += 1;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Ignore temp cleanup races.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return removed;
|
|
84
|
+
}
|
|
85
|
+
export async function prepareExtensionTempDir(tempDir, maxAgeMs = DEFAULT_TEMP_MAX_AGE_MS, runMaxAgeMs = DEFAULT_RUN_MAX_AGE_MS) {
|
|
86
|
+
await mkdir(tempDir, { recursive: true });
|
|
87
|
+
const removedTemp = await cleanupStaleTempEntries(tempDir, maxAgeMs);
|
|
88
|
+
const removedRuns = await cleanupStaleRunEntries(join(tempDir, "runs"), runMaxAgeMs);
|
|
89
|
+
return removedTemp + removedRuns;
|
|
90
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi-facing tool definition helpers
|
|
3
|
+
* Zones: pi tools, registry tools, async run launchers
|
|
4
|
+
* Owns generated runtime tool schemas and the register_tool management tool schema
|
|
5
|
+
*/
|
|
6
|
+
import type { RegisteredTool } from "./config.ts";
|
|
7
|
+
import * as Execution from "./execution.ts";
|
|
8
|
+
import * as Registry from "./registry.ts";
|
|
9
|
+
export type RegisterToolInput = Registry.RegisterToolInput;
|
|
10
|
+
export type RegisterToolRuntimeDeps<TContext> = Registry.RegisterToolRuntimeDeps<TContext>;
|
|
11
|
+
type JsonSchema = Record<string, unknown>;
|
|
12
|
+
export declare function createRegisterToolDefinition<TContext>(deps: RegisterToolRuntimeDeps<TContext>): {
|
|
13
|
+
name: string;
|
|
14
|
+
label: string;
|
|
15
|
+
description: string;
|
|
16
|
+
promptSnippet: string;
|
|
17
|
+
promptGuidelines: string[];
|
|
18
|
+
parameters: JsonSchema;
|
|
19
|
+
execute: (_toolCallId: string, params: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: TContext) => Promise<Registry.RegisterToolResult>;
|
|
20
|
+
};
|
|
21
|
+
export interface AsyncRunToolContext {
|
|
22
|
+
cwd: string;
|
|
23
|
+
sessionManager?: {
|
|
24
|
+
getSessionId?: () => string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare function createSpawnToolDefinition<TContext extends AsyncRunToolContext>(): any;
|
|
28
|
+
export interface InspectToolDeps<TContext = unknown> {
|
|
29
|
+
getTool?: (name: string) => any | undefined;
|
|
30
|
+
packagedRecipeRoot?: string;
|
|
31
|
+
recipeRoot?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function createInspectToolDefinition<TContext = unknown>(deps?: InspectToolDeps<TContext>): any;
|
|
34
|
+
export interface ActorMessageToolDeps<TContext = unknown> {
|
|
35
|
+
getTool?: (name: string) => any | undefined;
|
|
36
|
+
}
|
|
37
|
+
export declare function createActorMessageToolDefinition<TContext = unknown>(deps?: ActorMessageToolDeps<TContext>): any;
|
|
38
|
+
export declare function createRuntimeToolDefinition(cfg: RegisteredTool, exec: Execution.RegisteredToolExec): any;
|
|
39
|
+
export {};
|