@llblab/pi-actors 0.46.1 → 0.48.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 -5
- package/BACKLOG.md +0 -582
- package/CHANGELOG.md +16 -0
- package/README.md +8 -1
- package/banner.jpg +0 -0
- package/dist/lib/prompts.d.ts +6 -5
- package/dist/lib/prompts.js +22 -23
- package/dist/lib/recipes-discovery.d.ts +4 -0
- package/dist/lib/recipes-discovery.js +13 -1
- package/dist/lib/recipes-references.js +10 -6
- package/dist/lib/registry.d.ts +15 -11
- package/dist/lib/registry.js +195 -25
- package/dist/lib/runtime.js +37 -2
- package/dist/lib/tools-inspect.js +156 -12
- package/dist/lib/tools-register.js +2 -1
- package/dist/lib/tools-response.js +5 -1
- package/dist/scripts/conformance.mjs +1 -0
- package/dist/skills/actors/SKILL.md +87 -65
- package/dist/skills/actors/references/diagnostics.md +44 -0
- package/dist/skills/actors/references/persistent-tools.md +74 -0
- package/dist/skills/actors/references/recipes.md +51 -0
- package/dist/skills/actors/references/runs.md +39 -0
- package/dist/skills/artifacts/SKILL.md +24 -7
- package/dist/skills/media/SKILL.md +35 -7
- package/dist/skills/project-work/SKILL.md +28 -7
- package/dist/skills/recipe-memory/SKILL.md +27 -7
- package/dist/skills/swarm/SKILL.md +56 -437
- package/dist/skills/swarm/references/development-swarm.md +118 -525
- package/dist/skills/swarm/references/review-swarms.md +115 -0
- package/docs/README.md +5 -5
- package/docs/recipe-library.md +15 -10
- package/docs/tool-registry.md +10 -4
- package/lib/prompts.ts +24 -24
- package/lib/recipes-discovery.ts +22 -1
- package/lib/recipes-references.ts +14 -6
- package/lib/registry.ts +288 -51
- package/lib/runtime.ts +41 -2
- package/lib/tools-inspect.ts +202 -10
- package/lib/tools-register.ts +4 -3
- package/lib/tools-response.ts +5 -1
- package/package.json +1 -1
- package/scripts/conformance.mjs +1 -0
- package/skills/actors/SKILL.md +87 -65
- package/skills/actors/references/diagnostics.md +44 -0
- package/skills/actors/references/persistent-tools.md +74 -0
- package/skills/actors/references/recipes.md +51 -0
- package/skills/actors/references/runs.md +39 -0
- package/skills/artifacts/SKILL.md +24 -7
- package/skills/media/SKILL.md +35 -7
- package/skills/project-work/SKILL.md +28 -7
- package/skills/recipe-memory/SKILL.md +27 -7
- package/skills/swarm/SKILL.md +56 -437
- package/skills/swarm/references/development-swarm.md +118 -525
- package/skills/swarm/references/review-swarms.md +115 -0
package/dist/lib/runtime.js
CHANGED
|
@@ -7,6 +7,7 @@ import { existsSync, watch } from "node:fs";
|
|
|
7
7
|
import { basename, dirname } from "node:path";
|
|
8
8
|
import * as Paths from "./paths.js";
|
|
9
9
|
import * as RecipesDiscovery from "./recipes-discovery.js";
|
|
10
|
+
import * as RecipesReferences from "./recipes-references.js";
|
|
10
11
|
import * as RecipesUsage from "./recipes-usage.js";
|
|
11
12
|
import * as ToolsLocal from "./tools-local.js";
|
|
12
13
|
export function createAutoToolsRuntime(deps) {
|
|
@@ -40,6 +41,21 @@ export function createAutoToolsRuntime(deps) {
|
|
|
40
41
|
template: cfg.template,
|
|
41
42
|
});
|
|
42
43
|
}
|
|
44
|
+
function registeredToolSource(cfg) {
|
|
45
|
+
const raw = cfg.sourcePath
|
|
46
|
+
? RecipesReferences.readRawRecipeConfig(cfg.sourcePath)
|
|
47
|
+
: undefined;
|
|
48
|
+
const template = raw?.template;
|
|
49
|
+
if (typeof template === "string" &&
|
|
50
|
+
/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(template)) {
|
|
51
|
+
return template;
|
|
52
|
+
}
|
|
53
|
+
if (typeof template === "string" &&
|
|
54
|
+
(template.endsWith(".json") || template.endsWith(".md"))) {
|
|
55
|
+
return `<explicit-file:${basename(template)}>`;
|
|
56
|
+
}
|
|
57
|
+
return "template";
|
|
58
|
+
}
|
|
43
59
|
function deactivateMissingRuntimeTools(activeNames) {
|
|
44
60
|
const stale = [...runtimeTools].filter((name) => !activeNames.has(name));
|
|
45
61
|
if (stale.length === 0)
|
|
@@ -105,6 +121,7 @@ export function createAutoToolsRuntime(deps) {
|
|
|
105
121
|
lines.push("Other registry diagnostics:");
|
|
106
122
|
lines.push(...other.map((warning) => `• ${warning}`));
|
|
107
123
|
}
|
|
124
|
+
lines.push("Next: inspect target=recipes view=doctor");
|
|
108
125
|
return `${lines.join("\n")}\n`;
|
|
109
126
|
}
|
|
110
127
|
function loadTools(ctx, resolutionContext) {
|
|
@@ -164,9 +181,27 @@ export function createAutoToolsRuntime(deps) {
|
|
|
164
181
|
const usage = cfg.sourcePath
|
|
165
182
|
? RecipesUsage.readRecipeUsage(cfg.sourcePath)
|
|
166
183
|
: undefined;
|
|
184
|
+
const activation = activationFor(name);
|
|
185
|
+
const args = RecipesDiscovery.summarizeRegisteredToolArgs(cfg);
|
|
167
186
|
return {
|
|
168
|
-
...
|
|
169
|
-
|
|
187
|
+
...activation,
|
|
188
|
+
activation_boundary: activation.callable_now
|
|
189
|
+
? "current_session"
|
|
190
|
+
: !activation.host_registered
|
|
191
|
+
? "host_registration"
|
|
192
|
+
: "active_tool_set",
|
|
193
|
+
persisted: Boolean(cfg.sourcePath),
|
|
194
|
+
registry_active: true,
|
|
195
|
+
source: registeredToolSource(cfg),
|
|
196
|
+
required_args: args.required,
|
|
197
|
+
optional_args: args.optional,
|
|
198
|
+
next_actions: activation.callable_now
|
|
199
|
+
? [`call tool ${name}`]
|
|
200
|
+
: [
|
|
201
|
+
`register_tool name=${name} update=true`,
|
|
202
|
+
`inspect target=tool:${name} view=status`,
|
|
203
|
+
],
|
|
204
|
+
...(usage?.launch_kind ? { launch_kind: usage.launch_kind } : {}),
|
|
170
205
|
spawn_calls: Number(usage?.spawn_calls ?? 0),
|
|
171
206
|
tool_calls: Number(usage?.tool_calls ?? 0),
|
|
172
207
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Zones: Run Recipe/Trace/Control views and runtime/recipe/tool diagnostics
|
|
4
4
|
* Owns exact inspect target/view dispatch; source projection stays in domain modules.
|
|
5
5
|
*/
|
|
6
|
-
import { statSync } from "node:fs";
|
|
6
|
+
import { existsSync, statSync } from "node:fs";
|
|
7
7
|
import { isAbsolute, join, relative } from "node:path";
|
|
8
8
|
import * as AsyncRuns from "./async-runs.js";
|
|
9
9
|
import * as ControlProjection from "./control-projection.js";
|
|
@@ -166,7 +166,105 @@ function portableSkillRecipePath(file, skill, namespaces) {
|
|
|
166
166
|
? `<active-skill:${skill}>/${relation.replaceAll("\\", "/")}`
|
|
167
167
|
: `<active-skill:${skill}>`;
|
|
168
168
|
}
|
|
169
|
-
function
|
|
169
|
+
function portableSkillRecipeDiagnosticReason(diagnostic, namespaces) {
|
|
170
|
+
let reason = diagnostic.reason;
|
|
171
|
+
if (diagnostic.file) {
|
|
172
|
+
reason = reason.replaceAll(diagnostic.file, portableSkillRecipePath(diagnostic.file, diagnostic.skill, namespaces));
|
|
173
|
+
}
|
|
174
|
+
for (const root of namespaces[diagnostic.skill] ?? []) {
|
|
175
|
+
reason = reason.replaceAll(root, `<active-skill:${diagnostic.skill}>`);
|
|
176
|
+
}
|
|
177
|
+
return reason;
|
|
178
|
+
}
|
|
179
|
+
function inspectRecipeIdentity(identity, resolutionContext, namespaces, inventory, registryStatus) {
|
|
180
|
+
const match = /^([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\/([a-z][a-z0-9]*(?:-[a-z0-9]+)*)$/u.exec(identity);
|
|
181
|
+
const skill = match?.[1] ?? "";
|
|
182
|
+
const roots = skill ? namespaces[skill] ?? [] : [];
|
|
183
|
+
const skillActive = roots.length === 1;
|
|
184
|
+
const matchingComponent = inventory.components.find((component) => component.identity === identity);
|
|
185
|
+
const matchingRejection = inventory.rejected.find((diagnostic) => diagnostic.skill === skill &&
|
|
186
|
+
(diagnostic.stem === undefined || diagnostic.stem === match?.[2]));
|
|
187
|
+
let sourceLocation;
|
|
188
|
+
let rejectedReason;
|
|
189
|
+
let resolvable = false;
|
|
190
|
+
if (!match) {
|
|
191
|
+
rejectedReason =
|
|
192
|
+
"Identity must use canonical <skill>/<recipe> syntax.";
|
|
193
|
+
}
|
|
194
|
+
else if (roots.length === 0) {
|
|
195
|
+
rejectedReason = `Active Skill Recipe not found: ${identity}`;
|
|
196
|
+
}
|
|
197
|
+
else if (roots.length > 1) {
|
|
198
|
+
rejectedReason = matchingRejection
|
|
199
|
+
? portableSkillRecipeDiagnosticReason(matchingRejection, namespaces)
|
|
200
|
+
: `Duplicate active Skill identity ${skill}`;
|
|
201
|
+
}
|
|
202
|
+
else if (matchingRejection) {
|
|
203
|
+
rejectedReason = portableSkillRecipeDiagnosticReason(matchingRejection, namespaces);
|
|
204
|
+
if (matchingRejection.file) {
|
|
205
|
+
sourceLocation = portableSkillRecipePath(matchingRejection.file, skill, namespaces);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
try {
|
|
210
|
+
const file = RecipesReferences.resolveRecipeReferencePath(identity, resolutionContext?.cwd, resolutionContext?.activeSkills);
|
|
211
|
+
if (!file || !existsSync(file)) {
|
|
212
|
+
rejectedReason = `Skill Recipe component not found: ${identity}`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
sourceLocation = portableSkillRecipePath(file, skill, namespaces);
|
|
216
|
+
const recipe = RecipesReferences.readResolvedRecipeConfig(file, [], {
|
|
217
|
+
skillContext: resolutionContext?.activeSkills,
|
|
218
|
+
});
|
|
219
|
+
if (recipe)
|
|
220
|
+
resolvable = true;
|
|
221
|
+
else {
|
|
222
|
+
rejectedReason =
|
|
223
|
+
RecipesReferences.diagnoseRawRecipeConfigFailure(file) ??
|
|
224
|
+
`Recipe could not be resolved: ${identity}`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
rejectedReason = error instanceof Error ? error.message : String(error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const componentStatus = resolvable
|
|
233
|
+
? "available"
|
|
234
|
+
: !match
|
|
235
|
+
? "invalid_identity"
|
|
236
|
+
: roots.length === 0
|
|
237
|
+
? "skill_inactive"
|
|
238
|
+
: roots.length > 1
|
|
239
|
+
? "ambiguous_skill"
|
|
240
|
+
: matchingRejection
|
|
241
|
+
? "rejected"
|
|
242
|
+
: matchingComponent
|
|
243
|
+
? "unresolvable"
|
|
244
|
+
: "missing";
|
|
245
|
+
return {
|
|
246
|
+
identity,
|
|
247
|
+
skill_active: skillActive,
|
|
248
|
+
resolvable,
|
|
249
|
+
catalog_partial: inventory.partial,
|
|
250
|
+
component_status: componentStatus,
|
|
251
|
+
...(sourceLocation ? { source_location: sourceLocation } : {}),
|
|
252
|
+
resolution_generation: resolutionContext?.generation ?? registryStatus?.resolution_generation ?? "unavailable",
|
|
253
|
+
...(rejectedReason ? { rejected_reason: rejectedReason } : {}),
|
|
254
|
+
next_actions: resolvable
|
|
255
|
+
? [
|
|
256
|
+
`spawn recipe=${identity}`,
|
|
257
|
+
`register_tool name=<tool-name> from=${identity}`,
|
|
258
|
+
]
|
|
259
|
+
: roots.length === 0 && skill
|
|
260
|
+
? [
|
|
261
|
+
`activate Skill ${skill}`,
|
|
262
|
+
`inspect target=recipes view=doctor identity=${identity}`,
|
|
263
|
+
]
|
|
264
|
+
: ["inspect target=recipes view=imports verbose=true"],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function inspectRecipes(view, input, deps, context) {
|
|
170
268
|
if (view !== "status" &&
|
|
171
269
|
view !== "summary" &&
|
|
172
270
|
view !== "doctor" &&
|
|
@@ -188,6 +286,14 @@ function inspectRecipes(view, deps, context) {
|
|
|
188
286
|
RecipesReferences.EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT;
|
|
189
287
|
const skillRecipeNamespaces = RecipesReferences.getActiveSkillRecipeNamespaces(activeSkillContext);
|
|
190
288
|
const skillInventory = RecipesReferences.inventoryActiveSkillRecipeComponents(activeSkillContext);
|
|
289
|
+
const registryStatus = deps.registryStatus?.();
|
|
290
|
+
const identity = typeof input.identity === "string" ? input.identity.trim() : "";
|
|
291
|
+
if (identity && view !== "doctor") {
|
|
292
|
+
throw new Error("inspect recipes identity is supported only with view=doctor.");
|
|
293
|
+
}
|
|
294
|
+
if (view === "doctor" && identity) {
|
|
295
|
+
return inspectRecipeIdentity(identity, resolutionContext, skillRecipeNamespaces, skillInventory, registryStatus);
|
|
296
|
+
}
|
|
191
297
|
const skillRecipeComponents = skillInventory.components.map((component) => ({
|
|
192
298
|
identity: component.identity,
|
|
193
299
|
source_kind: "active_skill_component",
|
|
@@ -196,7 +302,7 @@ function inspectRecipes(view, deps, context) {
|
|
|
196
302
|
imports: component.imports,
|
|
197
303
|
}));
|
|
198
304
|
const skillRecipeComponentDiagnostics = skillInventory.rejected.map((diagnostic) => ({
|
|
199
|
-
error: diagnostic
|
|
305
|
+
error: portableSkillRecipeDiagnosticReason(diagnostic, skillRecipeNamespaces),
|
|
200
306
|
...(diagnostic.file
|
|
201
307
|
? {
|
|
202
308
|
file: portableSkillRecipePath(diagnostic.file, diagnostic.skill, skillRecipeNamespaces),
|
|
@@ -208,9 +314,9 @@ function inspectRecipes(view, deps, context) {
|
|
|
208
314
|
const discoverySummary = RecipesDiscovery.summarizeDiscovery(discovered);
|
|
209
315
|
const summary = {
|
|
210
316
|
...discoverySummary,
|
|
211
|
-
...(
|
|
317
|
+
...(registryStatus
|
|
212
318
|
? {
|
|
213
|
-
...
|
|
319
|
+
...registryStatus,
|
|
214
320
|
watched_root: "~/.pi/agent/recipes",
|
|
215
321
|
}
|
|
216
322
|
: {}),
|
|
@@ -322,21 +428,58 @@ function inspectTool(name, view, deps) {
|
|
|
322
428
|
throw new Error("inspect tool:<name> supports view=status or view=schema.");
|
|
323
429
|
}
|
|
324
430
|
const tool = deps.getTool?.(name);
|
|
325
|
-
|
|
326
|
-
|
|
431
|
+
const status = deps.getToolStatus?.(name);
|
|
432
|
+
if (!tool && !status) {
|
|
433
|
+
throw new Error(`Registered tool not found: ${name}. Next: inspect target=recipes view=doctor; do not substitute spawn for tool invocation.`);
|
|
434
|
+
}
|
|
435
|
+
if (view === "schema" && !tool) {
|
|
436
|
+
throw new Error(`Tool "${name}" is registered but its callable schema is unavailable; callable_now=${String(status?.callable_now ?? false)}, activation_boundary=${String(status?.activation_boundary ?? "host_registration")}. Next: inspect target=tool:${name} view=status.`);
|
|
437
|
+
}
|
|
327
438
|
return {
|
|
328
439
|
name,
|
|
329
|
-
...(view === "status" ?
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
440
|
+
...(view === "status" ? status : {}),
|
|
441
|
+
...(tool
|
|
442
|
+
? {
|
|
443
|
+
description: tool.description,
|
|
444
|
+
parameters: tool.parameters,
|
|
445
|
+
promptSnippet: tool.promptSnippet,
|
|
446
|
+
}
|
|
447
|
+
: {}),
|
|
333
448
|
};
|
|
334
449
|
}
|
|
335
450
|
function compactResult(target, view, details) {
|
|
336
451
|
if (target === "runtime")
|
|
337
452
|
return compactRuntime(view, details);
|
|
453
|
+
if (target === "recipes" && typeof details.identity === "string") {
|
|
454
|
+
const lines = [
|
|
455
|
+
`recipes doctor identity=${details.identity} skill_active=${String(details.skill_active)} resolvable=${String(details.resolvable)} catalog_partial=${String(details.catalog_partial)} component_status=${String(details.component_status)}`,
|
|
456
|
+
...(details.source_location
|
|
457
|
+
? [`source=${String(details.source_location)}`]
|
|
458
|
+
: []),
|
|
459
|
+
`resolution_generation=${String(details.resolution_generation)}`,
|
|
460
|
+
...(details.rejected_reason
|
|
461
|
+
? [`rejected_reason=${String(details.rejected_reason)}`]
|
|
462
|
+
: []),
|
|
463
|
+
...(details.next_actions ?? []).map((action) => `next=${String(action)}`),
|
|
464
|
+
];
|
|
465
|
+
return `\n${lines.join("\n")}`;
|
|
466
|
+
}
|
|
338
467
|
if (target === "recipes")
|
|
339
468
|
return ToolsResponse.compactRecipeRegistry(details);
|
|
469
|
+
if (target.startsWith("tool:") && view === "status") {
|
|
470
|
+
const required = Array.isArray(details.required_args)
|
|
471
|
+
? details.required_args.map(String).join(",") || "none"
|
|
472
|
+
: "unknown";
|
|
473
|
+
const optional = Array.isArray(details.optional_args)
|
|
474
|
+
? details.optional_args.map(String).join(",") || "none"
|
|
475
|
+
: "unknown";
|
|
476
|
+
const next = Array.isArray(details.next_actions) && details.next_actions.length > 0
|
|
477
|
+
? String(details.next_actions[0])
|
|
478
|
+
: details.callable_now === true
|
|
479
|
+
? `call tool ${target.slice(5)}`
|
|
480
|
+
: `register_tool name=${target.slice(5)} update=true`;
|
|
481
|
+
return `\ntool=${target.slice(5)} source=${String(details.source ?? "unknown")} callable_now=${String(details.callable_now ?? false)} activation_boundary=${String(details.activation_boundary ?? "unknown")} required=${required} optional=${optional} launch_kind=${String(details.launch_kind ?? "none")} spawn_calls=${Number(details.spawn_calls ?? 0)} tool_calls=${Number(details.tool_calls ?? 0)} next=${next}`;
|
|
482
|
+
}
|
|
340
483
|
if (target.startsWith("tool:"))
|
|
341
484
|
return `\ntool=${target.slice(5)} view=${view}`;
|
|
342
485
|
return `\nrun=${target.slice(4)} view=${view}`;
|
|
@@ -347,6 +490,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
347
490
|
label: "Inspect",
|
|
348
491
|
description: "Inspect a Run's Recipe, Trace, or Control evidence, or runtime/recipe/tool diagnostics.",
|
|
349
492
|
parameters: Schema.objectSchema({
|
|
493
|
+
identity: Schema.stringSchema("Optional canonical <skill>/<recipe> identity for focused Recipe doctor diagnosis."),
|
|
350
494
|
lines: Schema.stringSchema("Bounded item count for Trace or recent Controls."),
|
|
351
495
|
source: Schema.stringSchema("Optional Trace source: all, lifecycle, control, process, agent, artifact, or runtime."),
|
|
352
496
|
status: Schema.stringSchema("Optional runtime Run status filter."),
|
|
@@ -363,7 +507,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
363
507
|
details = inspectRuntime(view, input, ctx, deps);
|
|
364
508
|
}
|
|
365
509
|
else if (target === "recipes") {
|
|
366
|
-
details = inspectRecipes(view, deps, ctx);
|
|
510
|
+
details = inspectRecipes(view, input, deps, ctx);
|
|
367
511
|
}
|
|
368
512
|
else {
|
|
369
513
|
const run = parseRunTarget(target);
|
|
@@ -24,7 +24,9 @@ export function createRegisterToolDefinition(deps) {
|
|
|
24
24
|
args: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.args),
|
|
25
25
|
async: booleanSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.async),
|
|
26
26
|
description: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.description),
|
|
27
|
+
defaults: looseObjectSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.defaults),
|
|
27
28
|
draft: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.draft),
|
|
29
|
+
from: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.from),
|
|
28
30
|
name: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.name),
|
|
29
31
|
template: unionSchema([
|
|
30
32
|
stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.template),
|
|
@@ -33,7 +35,6 @@ export function createRegisterToolDefinition(deps) {
|
|
|
33
35
|
nullSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.templateNull),
|
|
34
36
|
]),
|
|
35
37
|
update: booleanSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.update),
|
|
36
|
-
values: looseObjectSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.values),
|
|
37
38
|
}, []),
|
|
38
39
|
execute: async (_toolCallId, params, _signal, _onUpdate, ctx) => Registry.executeRegisterTool(params, ctx, deps),
|
|
39
40
|
};
|
|
@@ -204,6 +204,9 @@ export function recipeRegistryNextActions(summary, view) {
|
|
|
204
204
|
if (view === "doctor" && typeof topAction.action === "string") {
|
|
205
205
|
actions.push(String(topAction.action));
|
|
206
206
|
}
|
|
207
|
+
if (summary.skill_recipe_catalog_partial === true) {
|
|
208
|
+
actions.push("inspect target=recipes view=imports verbose=true");
|
|
209
|
+
}
|
|
207
210
|
if (drafts.length > 0) {
|
|
208
211
|
actions.push("inspect target=recipes view=summary verbose=true");
|
|
209
212
|
const firstPath = typeof drafts[0]?.path === "string" ? drafts[0].path : undefined;
|
|
@@ -228,12 +231,13 @@ export function compactRecipeRegistry(summary) {
|
|
|
228
231
|
const recommendations = Array.isArray(summary.recommendations)
|
|
229
232
|
? summary.recommendations.length
|
|
230
233
|
: 0;
|
|
234
|
+
const skillCatalogPartial = summary.skill_recipe_catalog_partial === true;
|
|
231
235
|
const currentPolicy = Array.isArray(summary.active)
|
|
232
236
|
? summary.active.filter((entry) => entry.current_policy).length
|
|
233
237
|
: 0;
|
|
234
238
|
const nextActions = Array.isArray(summary.next_actions)
|
|
235
239
|
? summary.next_actions
|
|
236
240
|
: [];
|
|
237
|
-
return `\nrecipes active=${active} drafts=${drafts} shadowed=${shadowed} invalid=${invalid} disabled=${disabled} current_policy=${currentPolicy} recommendations=${recommendations} diagnostics=${diagnostics}${compactNextActions(nextActions)}`;
|
|
241
|
+
return `\nrecipes active=${active} drafts=${drafts} shadowed=${shadowed} invalid=${invalid} disabled=${disabled} current_policy=${currentPolicy} skill_catalog_partial=${skillCatalogPartial} recommendations=${recommendations} diagnostics=${diagnostics}${compactNextActions(nextActions)}`;
|
|
238
242
|
}
|
|
239
243
|
export const DEFAULT_INSPECT_LINES = Limits.DEFAULT_INSPECT_LINES;
|
|
@@ -1,104 +1,126 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: actors
|
|
3
|
-
description:
|
|
3
|
+
description: Use for any non-trivial pi-actors operation, diagnosis, or development involving Recipes, persistent tools, Runs, spawn, message, inspect, Trace, Control, capability specialization, or activation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Actors
|
|
6
|
+
# Actors
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Choose the operation
|
|
9
|
+
|
|
10
|
+
Start from the intended outcome:
|
|
9
11
|
|
|
10
12
|
```text
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```
|
|
13
|
+
Run a maintained capability once
|
|
14
|
+
→ use spawn recipe=<skill>/<recipe>
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Make a maintained capability a persistent agent-callable tool
|
|
17
|
+
→ use register_tool from=<skill>/<recipe>
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
Keep the same capability but narrow caller defaults
|
|
20
|
+
→ use register_tool from=<skill>/<recipe> defaults={...}
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- `inspect`: inspect `run:<id>`, `runtime`, `recipes`, or `tool:<name>`.
|
|
22
|
-
- `register_tool`: persist a trusted capability; it does not address a running actor. Trust current-session callability only when its result says `callable_now: true`.
|
|
22
|
+
Register a trusted command directly
|
|
23
|
+
→ use register_tool template="..."
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
Build a reusable multi-node execution graph
|
|
26
|
+
→ author a Recipe with named imports
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
Run a long-lived controlled process
|
|
29
|
+
→ spawn its async Recipe, then use message and inspect
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
Coordinate several independent actors or subagents
|
|
32
|
+
→ also read the swarm Skill
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
Choose capability-specific behavior
|
|
35
|
+
→ read the owning capability Skill
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
Diagnose resolution, registration, or activation
|
|
38
|
+
→ use Inspect status/doctor flows and stop on contradictory evidence
|
|
39
|
+
```
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
Use [persistent tools](./references/persistent-tools.md), [Recipes](./references/recipes.md), [Runs](./references/runs.md), or [diagnostics](./references/diagnostics.md) only when the selected operation needs that detail.
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
## Core distinctions
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"attention": "notify"
|
|
47
|
-
}
|
|
45
|
+
Keep these boundaries explicit:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
Skill Recipe ≠ registered tool
|
|
49
|
+
spawn ≠ registered-tool invocation
|
|
50
|
+
persisted ≠ callable
|
|
51
|
+
direct delegation ≠ named import composition
|
|
52
|
+
Run Control ≠ actor chat
|
|
48
53
|
```
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool` creates or updates a persistent user tool. A tool is callable in the current session only when activation evidence says `callable_now: true`.
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
`actors` owns generic Recipe/tool/Run mechanics. The owning capability Skill owns capability-specific selection and constraints. `swarm` owns multi-actor decomposition and integration methodology.
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
## Persistent capability workflow
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
To make `media/player` callable as `music_player` with a default source:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
register_tool
|
|
65
|
+
name=music_player
|
|
66
|
+
from=media/player
|
|
67
|
+
defaults={"source":"~/Music/1MIX"}
|
|
58
68
|
```
|
|
59
69
|
|
|
60
|
-
|
|
70
|
+
Then:
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
1. Require registration to report successful resolution, validation, persistence, registry admission, host registration, activation, and `callable_now: true`.
|
|
73
|
+
2. Call the actual `music_player` tool. Do not call `spawn` and describe that as tool invocation.
|
|
74
|
+
3. Verify agent-facing evidence reports `launch_kind: "tool"`; use `inspect target=tool:music_player view=status` when usage or activation needs confirmation.
|
|
75
|
+
4. If callability is false, stop at the reported activation boundary. Preserve the logical source and diagnose it; do not substitute a Recipe spawn as proof.
|
|
63
76
|
|
|
64
|
-
|
|
77
|
+
Use direct delegation for the same maintained capability under a persistent name or narrower defaults. Use named imports only when one Recipe graph contains reusable child nodes. See [persistent tools](./references/persistent-tools.md) and [Recipes](./references/recipes.md).
|
|
65
78
|
|
|
66
|
-
|
|
79
|
+
## Local coordinator topology
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
- `trace.jsonl`: structured observations.
|
|
70
|
-
- `controls.jsonl`: durable actor-local inputs and outcomes.
|
|
71
|
-
- `control-endpoint.json`: generation-fenced service readiness.
|
|
72
|
-
- `execution.json`: command/session provenance and bounded complete-capture references.
|
|
73
|
-
- `result.json`, logs, and declared artifacts.
|
|
81
|
+
There are two distinct multi-instance shapes:
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
- A gateway-centric system owns ingress, agent-instance creation, routing, and lifecycle outside the agents.
|
|
84
|
+
- A host-coordinator system keeps the current Pi instance as the control plane; companion extensions such as Telegram provide presence, while pi-actors creates explicit local Runs for delegated work.
|
|
85
|
+
|
|
86
|
+
In host-coordinator mode, the top-level agent receives declarative outcomes, preserves user authority and global context, delegates bounded concrete execution, and owns integration plus final validation. It is not merely another worker after delegation begins. One bounded implementation worker normally runs with reasoning off; consequential output receives a separate reasoning-enabled review. Several independent participants or reviewers additionally use `swarm`.
|
|
87
|
+
|
|
88
|
+
Delegation is not mandatory for every prompt. Work inline when one short bounded act has one natural validation boundary and spawning would add more coordination than isolation, latency hiding, clean context, or continued coordinator availability can repay. For admitted delegation, prefer terminal follow-up and durable Trace/artifacts; inspect on meaningful attention, operator request, or an evidence-based overdue timer rather than busy polling.
|
|
89
|
+
|
|
90
|
+
## Run workflow
|
|
91
|
+
|
|
92
|
+
A Run is one concrete execution of a Recipe:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
Recipe --spawn--> Run
|
|
96
|
+
Run = Recipe + Trace + Control
|
|
97
|
+
```
|
|
76
98
|
|
|
77
|
-
|
|
99
|
+
1. Spawn with the exact logical Recipe identity and caller-owned values.
|
|
100
|
+
2. Retain the returned `run:<id>` and normally wait for terminal follow-up instead of polling.
|
|
101
|
+
3. Inspect `view=trace` when retained observations or attention matter.
|
|
102
|
+
4. Inspect `view=control` before diagnosing service readiness, stale work, or saturation.
|
|
103
|
+
5. Send `message` only for an action declared and consumed by that controlled Recipe.
|
|
104
|
+
6. Use terminal state, result, declared artifacts, and execution evidence to prove completion.
|
|
78
105
|
|
|
79
|
-
|
|
106
|
+
A Run exposes only `recipe`, `trace`, and `control` views. Control is bounded actor-local input, not peer messaging or chat. See [Runs](./references/runs.md).
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
2. Spawn with explicit values and retain the returned `run:<id>`.
|
|
83
|
-
3. Let short Runs finish; avoid polling.
|
|
84
|
-
4. Inspect Trace when evidence or attention requires it; its summary states whether retained history is complete.
|
|
85
|
-
5. Inspect Control capacity before diagnosing stale work or saturation, then send only declared actor-local Controls.
|
|
86
|
-
6. Use runtime kill/cancel behavior for lifecycle termination.
|
|
87
|
-
7. Inspect artifacts and execution evidence for final validation.
|
|
108
|
+
## Diagnosis and stop rules
|
|
88
109
|
|
|
89
|
-
|
|
110
|
+
When a pi-actors operation fails:
|
|
90
111
|
|
|
91
|
-
|
|
112
|
+
1. Keep the intended logical Recipe or tool identity.
|
|
113
|
+
2. Inspect the existing `recipes`, `tool:<name>`, `runtime`, or `run:<id>` surface that owns the failure.
|
|
114
|
+
3. Report resolver, registry, activation, Run, Trace, or Control truth exactly.
|
|
115
|
+
4. Retry only after the owning state is healthy.
|
|
92
116
|
|
|
93
|
-
|
|
94
|
-
- [Quorum review](../swarm/recipes/quorum-review.json)
|
|
95
|
-
- [Artifact bundle](../artifacts/recipes/bundle.json)
|
|
96
|
-
- [Music player service](../media/recipes/player.json)
|
|
97
|
-
- [Resource locker service](./recipes/resource-locker.json)
|
|
117
|
+
Stop if spawn and registry resolve the same Recipe differently. Stop if registration persists but is not callable. Stop if an operation cannot be proven through pi-actors surfaces.
|
|
98
118
|
|
|
99
|
-
|
|
119
|
+
Never recover by copying maintained Recipe args, defaults, Control, artifacts, or helper commands. Never hard-code a `{skill_dir}` replacement path. Never introduce `bash -lc`, `eval`, direct bundled-helper execution, or shell backgrounding to bypass resolution. Never call `spawn` and claim a tool call. Use [diagnostics](./references/diagnostics.md) for the safe next action.
|
|
100
120
|
|
|
101
|
-
|
|
102
|
-
- [Async Runs](../../docs/async-runs.md)
|
|
121
|
+
## When to read another Skill
|
|
103
122
|
|
|
104
|
-
Read
|
|
123
|
+
- Read the owning capability Skill when choosing or operating that capability pack.
|
|
124
|
+
- Read `swarm` in addition to `actors` for multiple actors/subagents, parallel scopes, reviewer lenses, quorum, conflict handling, or integration.
|
|
125
|
+
- For generic mechanics, this Skill outranks capability Skills and `swarm`. Report a stale Skill if it contradicts Recipe identity, registration, activation, spawn, Inspect, Trace, or Control semantics here.
|
|
126
|
+
- When changing the extension implementation itself, apply project implementation instructions after this operating protocol.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Diagnostics
|
|
2
|
+
|
|
3
|
+
Preserve the intended logical identity and diagnose through public pi-actors surfaces. Do not inspect raw registry files or implementation source as the normal first response.
|
|
4
|
+
|
|
5
|
+
## Recipe resolution or catalog failure
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
inspect target=recipes view=status
|
|
9
|
+
inspect target=recipes view=doctor identity=<skill>/<recipe>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Use the focused doctor form for one intended identity. It reports active-Skill ownership, exact resolvability, partial-catalog state, component status, portable source location, resolution generation, any rejection, and bounded next actions. Use the unfiltered doctor only for catalog-wide diagnosis. A partial catalog does not imply every exact component is unavailable. If the owning Skill is inactive, report that blocker rather than locating and running its helper manually.
|
|
13
|
+
|
|
14
|
+
## Persistent tool failure
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
inspect target=tool:<name> view=status
|
|
18
|
+
inspect target=tool:<name> view=schema
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Distinguish persistence, registry admission, host registration, active-tool membership, and `callable_now`. Confirm the source identity, effective caller schema, separate tool/spawn usage, and last launch kind when exposed.
|
|
22
|
+
|
|
23
|
+
If persistence succeeded but callability is false, do not use `spawn` and claim the tool worked. Follow the reported activation boundary or stop.
|
|
24
|
+
|
|
25
|
+
## Run failure
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
inspect target=run:<id> view=recipe
|
|
29
|
+
inspect target=run:<id> view=trace
|
|
30
|
+
inspect target=run:<id> view=control
|
|
31
|
+
inspect target=runtime view=status
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use Recipe view for captured identity/launch evidence, Trace for bounded observations, Control for readiness/capacity/stale work, and runtime status for kernel-level health. Treat retained-history completeness honestly.
|
|
35
|
+
|
|
36
|
+
## Safe failure protocol
|
|
37
|
+
|
|
38
|
+
1. Keep the exact intended Recipe/tool/Run identity.
|
|
39
|
+
2. Identify the owning public surface.
|
|
40
|
+
3. Record exact resolver, registry, activation, or Run truth.
|
|
41
|
+
4. Apply only the bounded next action returned by that owner.
|
|
42
|
+
5. Retry only after the owning state is healthy.
|
|
43
|
+
|
|
44
|
+
Stop if evidence remains contradictory or the requested operation cannot be proven. Never recover by copying maintained contracts, hard-coding installation paths, directly executing bundled helpers, adding `bash -lc` or `eval`, shell-backgrounding work, editing unrelated Skills, or relabeling a Recipe spawn as a tool call.
|