@llblab/pi-actors 0.46.0 → 0.46.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/BACKLOG.md +582 -0
- package/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/dist/lib/async-runs.d.ts +1 -0
- package/dist/lib/async-runs.js +4 -1
- package/dist/lib/execution.d.ts +1 -0
- package/dist/lib/execution.js +1 -0
- package/dist/lib/extension-runtime.js +25 -9
- package/dist/lib/inspector.js +1 -0
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +2 -2
- package/dist/lib/recipes-context.d.ts +12 -3
- package/dist/lib/recipes-context.js +28 -4
- package/dist/lib/recipes-discovery.d.ts +18 -0
- package/dist/lib/recipes-discovery.js +95 -22
- package/dist/lib/recipes-references.d.ts +18 -0
- package/dist/lib/recipes-references.js +120 -33
- package/dist/lib/registry.d.ts +14 -1
- package/dist/lib/registry.js +110 -87
- package/dist/lib/runtime.d.ts +30 -3
- package/dist/lib/runtime.js +73 -9
- package/dist/lib/tools-inspect.d.ts +3 -0
- package/dist/lib/tools-inspect.js +40 -20
- package/dist/lib/tools-local.d.ts +2 -2
- package/dist/lib/tools-local.js +5 -2
- package/dist/lib/tools-response.js +2 -0
- package/dist/lib/tools-spawn.d.ts +2 -2
- package/dist/lib/tools-spawn.js +1 -1
- package/dist/lib/tools.d.ts +4 -1
- package/dist/lib/tools.js +2 -0
- package/dist/skills/actors/SKILL.md +3 -3
- package/docs/template-recipes.md +3 -1
- package/docs/tool-registry.md +9 -3
- package/lib/async-runs.ts +5 -1
- package/lib/execution.ts +2 -0
- package/lib/extension-runtime.ts +33 -14
- package/lib/inspector.ts +1 -0
- package/lib/prompts.ts +2 -2
- package/lib/recipes-context.ts +49 -4
- package/lib/recipes-discovery.ts +164 -24
- package/lib/recipes-references.ts +163 -39
- package/lib/registry.ts +185 -94
- package/lib/runtime.ts +108 -12
- package/lib/tools-inspect.ts +57 -23
- package/lib/tools-local.ts +9 -3
- package/lib/tools-response.ts +2 -0
- package/lib/tools-spawn.ts +3 -2
- package/lib/tools.ts +4 -1
- package/package.json +1 -1
- package/skills/actors/SKILL.md +3 -3
package/lib/tools-inspect.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { statSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
8
|
+
import { isAbsolute, join, relative } from "node:path";
|
|
9
9
|
|
|
10
10
|
import * as AsyncRuns from "./async-runs.ts";
|
|
11
11
|
import * as ControlProjection from "./control-projection.ts";
|
|
@@ -13,12 +13,14 @@ import * as Inspector from "./inspector.ts";
|
|
|
13
13
|
import * as Limits from "./limits.ts";
|
|
14
14
|
import { computeRunControlCapacity } from "./run-evidence-policy.ts";
|
|
15
15
|
import * as Paths from "./paths.ts";
|
|
16
|
+
import type * as RecipeResolution from "./recipes-context.ts";
|
|
16
17
|
import * as RecipesDiscovery from "./recipes-discovery.ts";
|
|
17
18
|
import * as RecipesReferences from "./recipes-references.ts";
|
|
18
19
|
import * as ReviewDiagnostics from "./review-diagnostics.ts";
|
|
19
20
|
import * as RunsControls from "./runs-controls.ts";
|
|
20
21
|
import * as RunControlDelivery from "./runs-control-delivery.ts";
|
|
21
22
|
import * as RunsTrace from "./runs-trace.ts";
|
|
23
|
+
import * as Runtime from "./runtime.ts";
|
|
22
24
|
import * as RuntimeIdentity from "./runtime-identity.ts";
|
|
23
25
|
import * as RuntimeTriage from "./runtime-triage.ts";
|
|
24
26
|
import * as Schema from "./schema.ts";
|
|
@@ -32,8 +34,10 @@ const maybeJsonText = ToolsResponse.maybeJsonText;
|
|
|
32
34
|
export interface InspectToolDeps<TContext = unknown> {
|
|
33
35
|
getRunStatus?: (runOrDir: string) => Record<string, any>;
|
|
34
36
|
getTool?: (name: string) => any | undefined;
|
|
37
|
+
getToolStatus?: (name: string) => Record<string, unknown> | undefined;
|
|
35
38
|
listRuns?: () => Array<Record<string, any>>;
|
|
36
39
|
recipeRoot?: string;
|
|
40
|
+
registryStatus?: () => Runtime.RecipeRegistryStatus;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
function runtimeStatus(): Record<string, unknown> {
|
|
@@ -191,6 +195,19 @@ function inspectRuntime(
|
|
|
191
195
|
throw new Error("inspect runtime supports view=status, view=runs, or view=triage.");
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
function portableSkillRecipePath(
|
|
199
|
+
file: string,
|
|
200
|
+
skill: string,
|
|
201
|
+
namespaces: Record<string, string[]>,
|
|
202
|
+
): string {
|
|
203
|
+
const root = namespaces[skill]?.[0];
|
|
204
|
+
if (!root) return `<active-skill:${skill}>`;
|
|
205
|
+
const relation = relative(root, file);
|
|
206
|
+
return !relation.startsWith("..") && !isAbsolute(relation)
|
|
207
|
+
? `<active-skill:${skill}>/${relation.replaceAll("\\", "/")}`
|
|
208
|
+
: `<active-skill:${skill}>`;
|
|
209
|
+
}
|
|
210
|
+
|
|
194
211
|
function inspectRecipes(
|
|
195
212
|
view: string,
|
|
196
213
|
deps: InspectToolDeps,
|
|
@@ -214,37 +231,52 @@ function inspectRecipes(
|
|
|
214
231
|
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
215
232
|
{ root: recipeRoot, defaultTool: true, mutableUsage: true },
|
|
216
233
|
]);
|
|
217
|
-
const
|
|
218
|
-
context && typeof context === "object" && "
|
|
234
|
+
const resolutionContext =
|
|
235
|
+
context && typeof context === "object" && "recipeResolutionContext" in context
|
|
219
236
|
? (context as {
|
|
220
|
-
|
|
221
|
-
}).
|
|
237
|
+
recipeResolutionContext?: RecipeResolution.RecipeResolutionContext;
|
|
238
|
+
}).recipeResolutionContext
|
|
222
239
|
: undefined;
|
|
223
240
|
const activeSkillContext =
|
|
224
|
-
|
|
241
|
+
resolutionContext?.activeSkills ??
|
|
242
|
+
RecipesReferences.EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT;
|
|
225
243
|
const skillRecipeNamespaces = RecipesReferences.getActiveSkillRecipeNamespaces(
|
|
226
244
|
activeSkillContext,
|
|
227
245
|
);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
const skillInventory =
|
|
247
|
+
RecipesReferences.inventoryActiveSkillRecipeComponents(activeSkillContext);
|
|
248
|
+
const skillRecipeComponents = skillInventory.components.map((component) => ({
|
|
249
|
+
identity: component.identity,
|
|
250
|
+
source_kind: "active_skill_component",
|
|
251
|
+
skill: component.skill,
|
|
252
|
+
stem: component.stem,
|
|
253
|
+
imports: component.imports,
|
|
254
|
+
}));
|
|
255
|
+
const skillRecipeComponentDiagnostics = skillInventory.rejected.map(
|
|
256
|
+
(diagnostic) => ({
|
|
257
|
+
error: diagnostic.reason,
|
|
258
|
+
...(diagnostic.file
|
|
259
|
+
? {
|
|
260
|
+
file: portableSkillRecipePath(
|
|
261
|
+
diagnostic.file,
|
|
262
|
+
diagnostic.skill,
|
|
263
|
+
skillRecipeNamespaces,
|
|
264
|
+
),
|
|
265
|
+
}
|
|
266
|
+
: {}),
|
|
267
|
+
skill: diagnostic.skill,
|
|
268
|
+
...(diagnostic.stem ? { stem: diagnostic.stem } : {}),
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
245
271
|
const discoverySummary = RecipesDiscovery.summarizeDiscovery(discovered);
|
|
246
272
|
const summary = {
|
|
247
273
|
...discoverySummary,
|
|
274
|
+
...(deps.registryStatus
|
|
275
|
+
? {
|
|
276
|
+
...deps.registryStatus(),
|
|
277
|
+
watched_root: "~/.pi/agent/recipes",
|
|
278
|
+
}
|
|
279
|
+
: {}),
|
|
248
280
|
active: Array.isArray(discoverySummary.active)
|
|
249
281
|
? discoverySummary.active.map((entry) => ({
|
|
250
282
|
...(entry as Record<string, unknown>),
|
|
@@ -253,6 +285,7 @@ function inspectRecipes(
|
|
|
253
285
|
: discoverySummary.active,
|
|
254
286
|
skill_recipe_components: skillRecipeComponents,
|
|
255
287
|
skill_recipe_component_diagnostics: skillRecipeComponentDiagnostics,
|
|
288
|
+
skill_recipe_catalog_partial: skillInventory.partial,
|
|
256
289
|
active_skill_recipe_identities: Object.keys(skillRecipeNamespaces).sort(),
|
|
257
290
|
skill_recipe_namespace_diagnostics: Object.entries(skillRecipeNamespaces)
|
|
258
291
|
.filter(([, roots]) => roots.length > 1)
|
|
@@ -369,6 +402,7 @@ function inspectTool(
|
|
|
369
402
|
if (!tool) throw new Error(`registered tool not found: ${name}`);
|
|
370
403
|
return {
|
|
371
404
|
name,
|
|
405
|
+
...(view === "status" ? deps.getToolStatus?.(name) : {}),
|
|
372
406
|
description: tool.description,
|
|
373
407
|
parameters: tool.parameters,
|
|
374
408
|
promptSnippet: tool.promptSnippet,
|
package/lib/tools-local.ts
CHANGED
|
@@ -10,6 +10,7 @@ import * as ModelContext from "./model-context.ts";
|
|
|
10
10
|
import type { RegisteredTool } from "./config.ts";
|
|
11
11
|
import * as Execution from "./execution.ts";
|
|
12
12
|
import * as Prompts from "./prompts.ts";
|
|
13
|
+
import type * as RecipeResolution from "./recipes-context.ts";
|
|
13
14
|
import * as RecipesReferences from "./recipes-references.ts";
|
|
14
15
|
import * as RecipesUsage from "./recipes-usage.ts";
|
|
15
16
|
import * as Schema from "./schema.ts";
|
|
@@ -18,7 +19,7 @@ import * as ToolsResponse from "./tools-response.ts";
|
|
|
18
19
|
type JsonSchema = Schema.JsonSchema;
|
|
19
20
|
|
|
20
21
|
export interface RuntimeToolContext extends ModelContext.CurrentModelContext {
|
|
21
|
-
|
|
22
|
+
recipeResolutionContext?: RecipeResolution.RecipeResolutionContext;
|
|
22
23
|
cwd: string;
|
|
23
24
|
sessionManager?: { getSessionId?: () => string };
|
|
24
25
|
}
|
|
@@ -67,7 +68,11 @@ function formatRuntimeToolUsageHint(
|
|
|
67
68
|
required: string[],
|
|
68
69
|
includeRunId: boolean,
|
|
69
70
|
): string {
|
|
70
|
-
const optional = cfg.args.filter(
|
|
71
|
+
const optional = cfg.args.filter(
|
|
72
|
+
(arg) =>
|
|
73
|
+
!RecipesReferences.isRuntimeOwnedRecipeInput(arg) &&
|
|
74
|
+
!required.includes(arg),
|
|
75
|
+
);
|
|
71
76
|
const exampleDefaults = {
|
|
72
77
|
...Schema.getExplicitToolArgDefaults(cfg.recipe?.args),
|
|
73
78
|
...cfg.defaults,
|
|
@@ -176,6 +181,7 @@ export function createRuntimeToolDefinition(
|
|
|
176
181
|
? new Set(cfg.args.filter((arg) => !Object.hasOwn(cfg.defaults, arg)))
|
|
177
182
|
: Schema.getRequiredToolArgNames(requiredTemplateConfig);
|
|
178
183
|
for (const arg of cfg.args) {
|
|
184
|
+
if (RecipesReferences.isRuntimeOwnedRecipeInput(arg)) continue;
|
|
179
185
|
paramSchema[arg] = typedArgSchema(arg, cfg.argTypes?.[arg]);
|
|
180
186
|
if (requiredArgs.has(arg)) required.push(arg);
|
|
181
187
|
}
|
|
@@ -244,7 +250,7 @@ export function createRuntimeToolDefinition(
|
|
|
244
250
|
values: resolveRecipeToolValues(cfg, values, ctx),
|
|
245
251
|
},
|
|
246
252
|
ctx.cwd,
|
|
247
|
-
{ skillContext: ctx.
|
|
253
|
+
{ skillContext: ctx.recipeResolutionContext?.activeSkills },
|
|
248
254
|
);
|
|
249
255
|
return {
|
|
250
256
|
content: [
|
package/lib/tools-response.ts
CHANGED
|
@@ -106,6 +106,8 @@ export function compactAsyncRunStatus(value: unknown): string {
|
|
|
106
106
|
const result = asRecord(status.result);
|
|
107
107
|
const run = String(status.run ?? "<unknown>");
|
|
108
108
|
const tokens = [`run=${run}`, `status=${String(status.status ?? "unknown")}`];
|
|
109
|
+
if (status.launch_kind)
|
|
110
|
+
tokens.push(`launch_kind=${String(status.launch_kind)}`);
|
|
109
111
|
tokens.push(...compactModelPolicy(status.model_policy ?? progress.model_policy));
|
|
110
112
|
if (status.tool) tokens.push(`tool=${String(status.tool)}`);
|
|
111
113
|
if (status.recipe) tokens.push(`recipe=${String(status.recipe)}`);
|
package/lib/tools-spawn.ts
CHANGED
|
@@ -11,13 +11,14 @@ import * as AsyncRuns from "./async-runs.ts";
|
|
|
11
11
|
import { withFileMutationLock } from "./file-state.ts";
|
|
12
12
|
import * as ModelContext from "./model-context.ts";
|
|
13
13
|
import * as Paths from "./paths.ts";
|
|
14
|
+
import type * as RecipeResolution from "./recipes-context.ts";
|
|
14
15
|
import * as RecipesReferences from "./recipes-references.ts";
|
|
15
16
|
import * as RecipesUsage from "./recipes-usage.ts";
|
|
16
17
|
import * as Schema from "./schema.ts";
|
|
17
18
|
import * as ToolsResponse from "./tools-response.ts";
|
|
18
19
|
|
|
19
20
|
export interface SpawnToolContext extends ModelContext.CurrentModelContext {
|
|
20
|
-
|
|
21
|
+
recipeResolutionContext?: RecipeResolution.RecipeResolutionContext;
|
|
21
22
|
cwd: string;
|
|
22
23
|
sessionManager?: { getSessionId?: () => string };
|
|
23
24
|
}
|
|
@@ -184,7 +185,7 @@ export function createSpawnToolDefinition<
|
|
|
184
185
|
: {}),
|
|
185
186
|
},
|
|
186
187
|
ctx.cwd,
|
|
187
|
-
{ skillContext: ctx.
|
|
188
|
+
{ skillContext: ctx.recipeResolutionContext?.activeSkills },
|
|
188
189
|
);
|
|
189
190
|
const draftRecipe = writeSpawnDraftRecipe(input, meta);
|
|
190
191
|
const nextActions = ToolsResponse.runNextActions(meta.run);
|
package/lib/tools.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface CoreActorToolDefinitionDeps<
|
|
|
25
25
|
configPath: string;
|
|
26
26
|
getActiveTools: () => string[];
|
|
27
27
|
getRuntimeTool: (name: string) => unknown;
|
|
28
|
+
getRuntimeToolStatus: (name: string) => Record<string, unknown> | undefined;
|
|
28
29
|
handleRuntimeControl?: (
|
|
29
30
|
action: string,
|
|
30
31
|
input: unknown,
|
|
@@ -32,7 +33,7 @@ export interface CoreActorToolDefinitionDeps<
|
|
|
32
33
|
registryRuntime: Pick<
|
|
33
34
|
RegisterToolRuntimeDeps<TContext>,
|
|
34
35
|
"getToolNameBlocker" | "getTools" | "notify" | "registerRuntimeTool"
|
|
35
|
-
|
|
36
|
+
> & { getStatus(): import("./runtime.ts").RecipeRegistryStatus };
|
|
36
37
|
setActiveTools: (toolNames: string[]) => void;
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -78,6 +79,8 @@ export function createCoreActorToolDefinitions<
|
|
|
78
79
|
}),
|
|
79
80
|
ToolsInspect.createInspectToolDefinition<TContext>({
|
|
80
81
|
getTool: (name) => deps.getRuntimeTool(name),
|
|
82
|
+
getToolStatus: deps.getRuntimeToolStatus,
|
|
83
|
+
registryStatus: deps.registryRuntime.getStatus,
|
|
81
84
|
}),
|
|
82
85
|
];
|
|
83
86
|
}
|
package/package.json
CHANGED
package/skills/actors/SKILL.md
CHANGED
|
@@ -19,9 +19,9 @@ Use the swarm skill separately for decomposition, quorum design, reviewer lenses
|
|
|
19
19
|
- `spawn`: create one Run from a Recipe or inline command template.
|
|
20
20
|
- `message`: send one actor-local Control to `run:<id>`, or the reserved review actions to `runtime`.
|
|
21
21
|
- `inspect`: inspect `run:<id>`, `runtime`, `recipes`, or `tool:<name>`.
|
|
22
|
-
- `register_tool`: persist a trusted capability; it does not address a running actor.
|
|
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`.
|
|
23
23
|
|
|
24
|
-
A Run target exposes exactly three inspect views: `recipe`, `trace`, and `control`.
|
|
24
|
+
A Run target exposes exactly three inspect views: `recipe`, `trace`, and `control`. Spawning a user Recipe is still a Recipe launch (`launch_kind: "spawn"`), not evidence that its registered tool was exposed or invoked; registered-tool execution reports `launch_kind: "tool"`, and `inspect target=tool:<name> view=status` separates both usage counts.
|
|
25
25
|
|
|
26
26
|
## Recipe
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ A Recipe defines execution. It may declare named typed args, inline fallbacks, c
|
|
|
29
29
|
|
|
30
30
|
Do not declare Control for ordinary one-shot work. Runtime lifecycle actions such as `kill` stay runtime-owned and must not appear in Recipe Control declarations. Imported Recipes act as local definitions inside one Run; they do not create nested Runs unless execution explicitly spawns them.
|
|
31
31
|
|
|
32
|
-
Prefer maintained Skill-owned Recipes over ad hoc wrappers. Skill Recipe identity is `<active Skill name>/<Recipe filename stem>`; Recipe files have no top-level `name`. `SKILL.md` `name` is Pi host metadata matching its directory, not an additional pi-actors identity field. Use `<skill>/<recipe>` for an exact direct component or an explicit `.json` / `.md` path. Entry paths resolve from invocation `cwd`; relative imports resolve from the importing Recipe directory. File-backed Recipes own `{recipe_dir}` and Skill Recipes own `{skill_dir}`; callers never pass or override these origins. Skill Recipes are components, not automatic tools. Keep model, thinking, mission, concurrency, quorum, and timeout choices caller-owned unless a Recipe documents a stable policy.
|
|
32
|
+
Prefer maintained Skill-owned Recipes over ad hoc wrappers. Skill Recipe identity is `<active Skill name>/<Recipe filename stem>`; Recipe files have no top-level `name`. `SKILL.md` `name` is Pi host metadata matching its directory, not an additional pi-actors identity field. Use `<skill>/<recipe>` for an exact direct component or an explicit `.json` / `.md` path. Entry paths resolve from invocation `cwd`; relative imports resolve from the importing Recipe directory. File-backed Recipes own `{recipe_dir}` and Skill Recipes own `{skill_dir}`; callers never pass or override these origins. Skill Recipes are components, not automatic tools. A rejected component makes catalog inspection partial without blocking exact resolution of unrelated valid components in the same live session context. Keep model, thinking, mission, concurrency, quorum, and timeout choices caller-owned unless a Recipe documents a stable policy.
|
|
33
33
|
|
|
34
34
|
## Trace
|
|
35
35
|
|