@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
|
@@ -8,6 +8,7 @@ import * as CommandTemplates from "./command-templates.js";
|
|
|
8
8
|
import * as Paths from "./paths.js";
|
|
9
9
|
import * as Pi from "./pi.js";
|
|
10
10
|
import * as Prompts from "./prompts.js";
|
|
11
|
+
import * as RecipeResolution from "./recipes-context.js";
|
|
11
12
|
import * as RecipesReferences from "./recipes-references.js";
|
|
12
13
|
import * as RunUiRuntime from "./run-ui-runtime.js";
|
|
13
14
|
import * as Runtime from "./runtime.js";
|
|
@@ -16,10 +17,16 @@ import * as Tools from "./tools.js";
|
|
|
16
17
|
import * as ToolsResponse from "./tools-response.js";
|
|
17
18
|
export function createActorExtensionRuntime(pi) {
|
|
18
19
|
let activeRunContext;
|
|
19
|
-
const
|
|
20
|
+
const recipeResolutionContextsBySession = new Map();
|
|
20
21
|
const getRunOwnerId = Pi.getSessionId;
|
|
21
|
-
const
|
|
22
|
-
|
|
22
|
+
const getRecipeResolutionContext = (ctx) => {
|
|
23
|
+
const sessionId = getRunOwnerId(ctx);
|
|
24
|
+
const resolutionContext = recipeResolutionContextsBySession.get(sessionId);
|
|
25
|
+
if (!resolutionContext) {
|
|
26
|
+
throw new Error(`Recipe resolution context is unavailable for session ${sessionId}.`);
|
|
27
|
+
}
|
|
28
|
+
return resolutionContext;
|
|
29
|
+
};
|
|
23
30
|
const automaticReview = AutomaticReviewRuntime.createAutomaticReviewRuntime({
|
|
24
31
|
getActiveContext: () => activeRunContext,
|
|
25
32
|
getRunOwnerId,
|
|
@@ -44,7 +51,7 @@ export function createActorExtensionRuntime(pi) {
|
|
|
44
51
|
if (ctx && typeof ctx === "object") {
|
|
45
52
|
nextArgs[4] = {
|
|
46
53
|
...ctx,
|
|
47
|
-
|
|
54
|
+
recipeResolutionContext: getRecipeResolutionContext(ctx),
|
|
48
55
|
getThinkingLevel: () => pi.getThinkingLevel(),
|
|
49
56
|
};
|
|
50
57
|
}
|
|
@@ -61,6 +68,7 @@ export function createActorExtensionRuntime(pi) {
|
|
|
61
68
|
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
62
69
|
exec: CommandTemplates.execCommandTemplate,
|
|
63
70
|
getActiveTools: () => pi.getActiveTools(),
|
|
71
|
+
getAllTools: () => pi.getAllTools(),
|
|
64
72
|
registerTool: (definition) => {
|
|
65
73
|
const wrapped = withCurrentThinkingContext(definition);
|
|
66
74
|
actorToolDefinitions.set(wrapped.name, wrapped);
|
|
@@ -69,10 +77,17 @@ export function createActorExtensionRuntime(pi) {
|
|
|
69
77
|
reservedToolNames: Tools.RESERVED_TOOL_NAMES,
|
|
70
78
|
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
71
79
|
});
|
|
72
|
-
const recipeReload = Runtime.createRecipeToolReloadWatcher(runtime
|
|
80
|
+
const recipeReload = Runtime.createRecipeToolReloadWatcher(runtime, {
|
|
81
|
+
getResolutionContext: () => activeRunContext
|
|
82
|
+
? getRecipeResolutionContext(activeRunContext)
|
|
83
|
+
: undefined,
|
|
84
|
+
});
|
|
73
85
|
return {
|
|
74
86
|
beforeAgentStart(systemPrompt, skills, ctx) {
|
|
75
|
-
|
|
87
|
+
const sessionId = getRunOwnerId(ctx);
|
|
88
|
+
const resolutionContext = RecipeResolution.createRecipeResolutionContext(sessionId, ctx.cwd, RecipesReferences.createActiveSkillRecipeContext(skills));
|
|
89
|
+
recipeResolutionContextsBySession.set(sessionId, resolutionContext);
|
|
90
|
+
runtime.loadTools(ctx, resolutionContext);
|
|
76
91
|
return {
|
|
77
92
|
systemPrompt: `${systemPrompt}\n\n${Prompts.ONBOARDING_SYSTEM_PROMPT}`,
|
|
78
93
|
};
|
|
@@ -87,14 +102,15 @@ export function createActorExtensionRuntime(pi) {
|
|
|
87
102
|
automaticReview.schedule();
|
|
88
103
|
},
|
|
89
104
|
onSessionShutdown(reason, ctx) {
|
|
90
|
-
|
|
105
|
+
recipeResolutionContextsBySession.delete(getRunOwnerId(ctx));
|
|
91
106
|
activeRunContext = undefined;
|
|
92
107
|
automaticReview.close();
|
|
93
108
|
recipeReload.close();
|
|
94
109
|
runUiRuntime.shutdown(reason, ctx);
|
|
95
110
|
},
|
|
96
111
|
async onSessionStart(ctx) {
|
|
97
|
-
|
|
112
|
+
const sessionId = getRunOwnerId(ctx);
|
|
113
|
+
recipeResolutionContextsBySession.set(sessionId, RecipeResolution.createEmptyRecipeResolutionContext(sessionId, ctx.cwd));
|
|
98
114
|
ctx.ui.setWidget("zz-pi-actors-comms", undefined);
|
|
99
115
|
activeRunContext = ctx;
|
|
100
116
|
runUiRuntime.close();
|
|
@@ -104,7 +120,6 @@ export function createActorExtensionRuntime(pi) {
|
|
|
104
120
|
if (activeRunContext !== ctx)
|
|
105
121
|
return;
|
|
106
122
|
automaticReview.start(ctx);
|
|
107
|
-
runtime.loadTools(ctx);
|
|
108
123
|
runUiRuntime.start(ctx);
|
|
109
124
|
recipeReload.watch(ctx);
|
|
110
125
|
},
|
|
@@ -113,6 +128,7 @@ export function createActorExtensionRuntime(pi) {
|
|
|
113
128
|
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
114
129
|
getActiveTools: () => pi.getActiveTools(),
|
|
115
130
|
getRuntimeTool: (name) => Tools.resolveActiveRuntimeTool(name, runtime.getTools(), (activeName) => actorToolDefinitions.get(activeName)),
|
|
131
|
+
getRuntimeToolStatus: runtime.getToolStatus,
|
|
116
132
|
handleRuntimeControl: automaticReview.handleControl,
|
|
117
133
|
registryRuntime: runtime,
|
|
118
134
|
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
package/dist/lib/inspector.js
CHANGED
|
@@ -115,6 +115,7 @@ export function readActorInspectorRecipe(stateDir) {
|
|
|
115
115
|
logical_reference: primaryLogicalReference,
|
|
116
116
|
...(typeof primary?.skill === "string" ? { skill: primary.skill } : {}),
|
|
117
117
|
source_kind: primarySourceKind,
|
|
118
|
+
launch_kind: meta.launch_kind ?? meta.launch_source,
|
|
118
119
|
launch_source: meta.launch_source,
|
|
119
120
|
}),
|
|
120
121
|
launch: redactedRecord({
|
package/dist/lib/prompts.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export declare const REGISTER_TOOL_DESCRIPTION: string;
|
|
7
7
|
export declare const REGISTER_TOOL_PROMPT_SNIPPET = "Register persistent command templates as agent-callable tools";
|
|
8
8
|
export declare const REGISTER_TOOL_GUIDELINES: string[];
|
|
9
|
-
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node\u2192bun\u2192deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory:
|
|
9
|
+
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node\u2192bun\u2192deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory: valid admitted recipes are reconciled as tools across sessions; register_tool writes there and reports current activation truth.\n- Recipes own template directly and may declare metadata/defaults/imports/control/artifacts; files >1 MiB or import depth >32 fail closed.\n- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.\n- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.\n- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid internal transport vocabulary in public guidance.\n- Run state lives under ~/.pi/agent/tmp/pi-actors/runs. Inspect intentionally and avoid busy-polling. Terminal and coordinator-bound notifications queue as Pi follow-ups so concurrently completed actors can reach the coordinator after current work instead of steering between tool calls. Terminal follow-up content stays minimal: run, status, one base path, and relative artifact names only; semantic output stays in non-LLM details and run state. When a deferred actor result gates the next step, wait for its terminal follow-up; do not schedule continuation loops, repeatedly inspect, or mutate its reviewed scope while it runs. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue/stuck run.\n- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; active-Skill and explicit file Recipes remain components outside user tool discovery; offer to save successful recurring patterns only after confirmation.\n- Prefer maintained active-Skill Recipes with spawn recipe=<skill>/<recipe> before ad hoc scripts/wrappers; explicit file Recipes use exact .json/.md paths; review swarms inherit current model/thinking, preflight before fanout, and expose quorum/concurrency/TTL knobs unless explicit args are passed.\n- For any non-trivial actor use or pi-actors change, read the bundled actors skill first. Before launching multiple actors/subagents for parallel implementation, independent artifact generation, delegated audit, or review, also read the bundled swarm skill; the coordinator owns decomposition, disjoint scopes, launch correctness, integration, and final validation. For deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.";
|
|
10
10
|
export declare const REGISTER_TOOL_PARAM_DESCRIPTIONS: {
|
|
11
11
|
readonly name: "Tool name in snake_case (e.g., 'transcribe')";
|
|
12
12
|
readonly description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.";
|
package/dist/lib/prompts.js
CHANGED
|
@@ -9,7 +9,7 @@ export const REGISTER_TOOL_DESCRIPTION = "Register a persistent custom tool from
|
|
|
9
9
|
export const REGISTER_TOOL_PROMPT_SNIPPET = "Register persistent command templates as agent-callable tools";
|
|
10
10
|
export const REGISTER_TOOL_GUIDELINES = [
|
|
11
11
|
"Use register_tool to wrap trusted local commands, scripts, programs, libraries, or template recipes as persistent pi tools.",
|
|
12
|
-
"After register_tool succeeds,
|
|
12
|
+
"After register_tool succeeds, trust its callable_now and activation result; persisted definitions remain available for admission after reload.",
|
|
13
13
|
'Set template=null or template="" in register_tool to delete a persisted tool.',
|
|
14
14
|
"Set update=true in register_tool to overwrite an existing tool registration.",
|
|
15
15
|
];
|
|
@@ -18,7 +18,7 @@ export const ONBOARDING_SYSTEM_PROMPT = `pi-actors quick model:
|
|
|
18
18
|
- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.
|
|
19
19
|
- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node→bun→deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.
|
|
20
20
|
- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.
|
|
21
|
-
- ~/.pi/agent/recipes/*.json is actor muscle memory:
|
|
21
|
+
- ~/.pi/agent/recipes/*.json is actor muscle memory: valid admitted recipes are reconciled as tools across sessions; register_tool writes there and reports current activation truth.
|
|
22
22
|
- Recipes own template directly and may declare metadata/defaults/imports/control/artifacts; files >1 MiB or import depth >32 fail closed.
|
|
23
23
|
- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.
|
|
24
24
|
- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Recipe context prompt assembly.
|
|
3
|
-
* Zones: async runner prompt context, recipe provenance, LLM child launches
|
|
4
|
-
* Owns compact actor
|
|
2
|
+
* Recipe context contracts and prompt assembly.
|
|
3
|
+
* Zones: live session resolution, async runner prompt context, recipe provenance, LLM child launches
|
|
4
|
+
* Owns the immutable live Recipe environment and compact actor context appended to child-agent prompts.
|
|
5
5
|
*/
|
|
6
6
|
import type { CommandTemplateActorRecipeContext } from "./command-templates.ts";
|
|
7
|
+
import * as RecipesReferences from "./recipes-references.ts";
|
|
7
8
|
import type { TemplateRecipeContextRecord } from "./recipes-references.ts";
|
|
9
|
+
export interface RecipeResolutionContext {
|
|
10
|
+
readonly activeSkills: RecipesReferences.ActiveSkillRecipeContext;
|
|
11
|
+
readonly cwd: string;
|
|
12
|
+
readonly generation: string;
|
|
13
|
+
readonly sessionId: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function createRecipeResolutionContext(sessionId: string, cwd: string, activeSkills: RecipesReferences.ActiveSkillRecipeContext): RecipeResolutionContext;
|
|
16
|
+
export declare function createEmptyRecipeResolutionContext(sessionId: string, cwd: string): RecipeResolutionContext;
|
|
8
17
|
export interface MarkedRecipeContextRecord extends TemplateRecipeContextRecord {
|
|
9
18
|
you_are_here?: true;
|
|
10
19
|
you_are_here_path?: string;
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Recipe context prompt assembly.
|
|
3
|
-
* Zones: async runner prompt context, recipe provenance, LLM child launches
|
|
4
|
-
* Owns compact actor
|
|
2
|
+
* Recipe context contracts and prompt assembly.
|
|
3
|
+
* Zones: live session resolution, async runner prompt context, recipe provenance, LLM child launches
|
|
4
|
+
* Owns the immutable live Recipe environment and compact actor context appended to child-agent prompts.
|
|
5
5
|
*/
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
6
7
|
import { writeFileSync } from "node:fs";
|
|
7
|
-
import { basename } from "node:path";
|
|
8
|
+
import { basename, resolve } from "node:path";
|
|
9
|
+
import * as RecipesReferences from "./recipes-references.js";
|
|
10
|
+
export function createRecipeResolutionContext(sessionId, cwd, activeSkills) {
|
|
11
|
+
const normalizedSessionId = sessionId.trim();
|
|
12
|
+
if (!normalizedSessionId)
|
|
13
|
+
throw new Error("Recipe resolution session id is required.");
|
|
14
|
+
const normalizedCwd = resolve(cwd);
|
|
15
|
+
const generation = createHash("sha256")
|
|
16
|
+
.update(JSON.stringify({
|
|
17
|
+
activeSkills: RecipesReferences.getActiveSkillRecipeNamespaces(activeSkills),
|
|
18
|
+
cwd: normalizedCwd,
|
|
19
|
+
sessionId: normalizedSessionId,
|
|
20
|
+
}))
|
|
21
|
+
.digest("hex");
|
|
22
|
+
return Object.freeze({
|
|
23
|
+
activeSkills,
|
|
24
|
+
cwd: normalizedCwd,
|
|
25
|
+
generation,
|
|
26
|
+
sessionId: normalizedSessionId,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function createEmptyRecipeResolutionContext(sessionId, cwd) {
|
|
30
|
+
return createRecipeResolutionContext(sessionId, cwd, RecipesReferences.EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT);
|
|
31
|
+
}
|
|
8
32
|
function commandName(command) {
|
|
9
33
|
return basename(command).toLowerCase();
|
|
10
34
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as CommandTemplates from "./command-templates.ts";
|
|
7
7
|
import type { RegisteredTool } from "./config.ts";
|
|
8
|
+
import type { RecipeResolutionContext } from "./recipes-context.ts";
|
|
8
9
|
import type { TemplateRecipeConfig } from "./recipes-references.ts";
|
|
9
10
|
export interface DiscoveredRecipe {
|
|
10
11
|
id: string;
|
|
@@ -18,6 +19,7 @@ export interface DiscoveredRecipe {
|
|
|
18
19
|
disabled: boolean;
|
|
19
20
|
tool: boolean;
|
|
20
21
|
mutableUsage: boolean;
|
|
22
|
+
resolutionContext?: RecipeResolutionContext;
|
|
21
23
|
diagnostics: string[];
|
|
22
24
|
riskLabels: CommandTemplates.CommandTemplateRiskLabel[];
|
|
23
25
|
shadows: string[];
|
|
@@ -44,6 +46,21 @@ export interface RecipesDiscoverySource {
|
|
|
44
46
|
file?: string;
|
|
45
47
|
defaultTool?: boolean;
|
|
46
48
|
mutableUsage?: boolean;
|
|
49
|
+
resolutionContext?: RecipeResolutionContext;
|
|
50
|
+
}
|
|
51
|
+
export interface UserRecipeAdmission {
|
|
52
|
+
args?: string[];
|
|
53
|
+
argTypes?: RegisteredTool["argTypes"];
|
|
54
|
+
artifacts?: Record<string, string>;
|
|
55
|
+
async?: boolean;
|
|
56
|
+
control?: string[];
|
|
57
|
+
defaults?: Record<string, unknown>;
|
|
58
|
+
diagnostics: string[];
|
|
59
|
+
effectiveRecipe?: TemplateRecipeConfig;
|
|
60
|
+
identity: string;
|
|
61
|
+
sourcePath: string;
|
|
62
|
+
tool?: RegisteredTool;
|
|
63
|
+
validated: boolean;
|
|
47
64
|
}
|
|
48
65
|
export declare function hasBroadWindowsWriteAcl(icaclsOutput: string): boolean;
|
|
49
66
|
export declare function discoverRecipeSources(sources: RecipesDiscoverySource[]): RecipesDiscoveryResult;
|
|
@@ -52,4 +69,5 @@ export declare function createRecipeIntegrityManifest(result: RecipesDiscoveryRe
|
|
|
52
69
|
export declare function getShadowedLaunchDiagnostic(result: RecipesDiscoveryResult, id: string): Record<string, unknown> | undefined;
|
|
53
70
|
export declare function listDraftRecipes(root: string): Array<Record<string, unknown>>;
|
|
54
71
|
export declare function summarizeDiscovery(result: RecipesDiscoveryResult): Record<string, unknown>;
|
|
72
|
+
export declare function admitUserRecipe(sourcePath: string, resolutionContext: RecipeResolutionContext, authoredRecipe?: Record<string, unknown>, mutableUsage?: boolean): UserRecipeAdmission;
|
|
55
73
|
export declare function toRegisteredTool(entry: DiscoveredRecipe): RegisteredTool | undefined;
|
|
@@ -94,10 +94,12 @@ function getRecipeRiskLabels(config) {
|
|
|
94
94
|
"risk.platform_specific",
|
|
95
95
|
].filter((label) => labels.has(label));
|
|
96
96
|
}
|
|
97
|
-
function readDiscoveredRecipe(root, file, priority, defaultTool = false, mutableUsage = false) {
|
|
97
|
+
function readDiscoveredRecipe(root, file, priority, defaultTool = false, mutableUsage = false, resolutionContext) {
|
|
98
98
|
const id = RecipesReferences.getRecipeIdFromPath(file);
|
|
99
99
|
try {
|
|
100
|
-
const config = RecipesReferences.readResolvedRecipeConfig(file
|
|
100
|
+
const config = RecipesReferences.readResolvedRecipeConfig(file, [], {
|
|
101
|
+
skillContext: resolutionContext?.activeSkills,
|
|
102
|
+
});
|
|
101
103
|
const invalid = !config;
|
|
102
104
|
const disabled = config?.disabled === true;
|
|
103
105
|
return {
|
|
@@ -112,6 +114,7 @@ function readDiscoveredRecipe(root, file, priority, defaultTool = false, mutable
|
|
|
112
114
|
disabled,
|
|
113
115
|
tool: defaultTool && !disabled && !invalid,
|
|
114
116
|
mutableUsage,
|
|
117
|
+
resolutionContext,
|
|
115
118
|
diagnostics: getRecipeConfigDiagnostics(file, config),
|
|
116
119
|
riskLabels: getRecipeRiskLabels(config),
|
|
117
120
|
shadows: [],
|
|
@@ -129,6 +132,7 @@ function readDiscoveredRecipe(root, file, priority, defaultTool = false, mutable
|
|
|
129
132
|
disabled: false,
|
|
130
133
|
tool: false,
|
|
131
134
|
mutableUsage,
|
|
135
|
+
resolutionContext,
|
|
132
136
|
diagnostics: [
|
|
133
137
|
`Failed to load recipe ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
134
138
|
],
|
|
@@ -147,6 +151,7 @@ function filesForSource(source) {
|
|
|
147
151
|
file: source.file,
|
|
148
152
|
defaultTool,
|
|
149
153
|
mutableUsage,
|
|
154
|
+
resolutionContext: source.resolutionContext,
|
|
150
155
|
},
|
|
151
156
|
];
|
|
152
157
|
return source.root
|
|
@@ -155,6 +160,7 @@ function filesForSource(source) {
|
|
|
155
160
|
file,
|
|
156
161
|
defaultTool,
|
|
157
162
|
mutableUsage,
|
|
163
|
+
resolutionContext: source.resolutionContext,
|
|
158
164
|
}))
|
|
159
165
|
: [];
|
|
160
166
|
}
|
|
@@ -222,7 +228,7 @@ function getRecipeRootDiagnostics(sources) {
|
|
|
222
228
|
return diagnostics;
|
|
223
229
|
}
|
|
224
230
|
export function discoverRecipeSources(sources) {
|
|
225
|
-
const entries = sources.flatMap((source, priority) => filesForSource(source).map(({ root, file, defaultTool, mutableUsage }) => readDiscoveredRecipe(root, file, priority, defaultTool, mutableUsage)));
|
|
231
|
+
const entries = sources.flatMap((source, priority) => filesForSource(source).map(({ root, file, defaultTool, mutableUsage, resolutionContext }) => readDiscoveredRecipe(root, file, priority, defaultTool, mutableUsage, resolutionContext)));
|
|
226
232
|
const byId = new Map();
|
|
227
233
|
for (const entry of entries) {
|
|
228
234
|
const bucket = byId.get(entry.id) ?? [];
|
|
@@ -647,12 +653,8 @@ export function summarizeDiscovery(result) {
|
|
|
647
653
|
integrity_manifest: createRecipeIntegrityManifest(result),
|
|
648
654
|
};
|
|
649
655
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
return undefined;
|
|
653
|
-
const cfg = entry.config;
|
|
654
|
-
const template = entry.path;
|
|
655
|
-
const description = cfg.description ?? `Execute template recipe: ${entry.id}`;
|
|
656
|
+
function projectRegisteredTool(identity, path, cfg, mutableUsage) {
|
|
657
|
+
const description = cfg.description ?? `Execute template recipe: ${identity}`;
|
|
656
658
|
const argTemplate = cfg.template;
|
|
657
659
|
const argTemplateConfig = typeof argTemplate === "object" && !Array.isArray(argTemplate)
|
|
658
660
|
? {
|
|
@@ -669,24 +671,27 @@ export function toRegisteredTool(entry) {
|
|
|
669
671
|
return [parsed.arg, parsed.type];
|
|
670
672
|
}));
|
|
671
673
|
assertToolSafeRepeatConfig(argTemplateConfig, explicitArgTypes, { ...(cfg.defaults ?? {}), ...(cfg.values ?? {}) });
|
|
672
|
-
const
|
|
673
|
-
Schema.assertCompatibleToolArgTypes(explicitArgTypes,
|
|
674
|
+
const inferredArgTypes = Schema.getTemplateArgTypes(argTemplateConfig);
|
|
675
|
+
Schema.assertCompatibleToolArgTypes(explicitArgTypes, inferredArgTypes);
|
|
676
|
+
const effectiveArgTypes = { ...inferredArgTypes, ...explicitArgTypes };
|
|
677
|
+
const args = Schema.getToolArgNames(argTemplateConfig).filter((arg) => !RecipesReferences.isRuntimeOwnedRecipeInput(arg));
|
|
678
|
+
const callerArgSet = new Set(args);
|
|
679
|
+
const argTypes = Object.fromEntries(Object.entries(effectiveArgTypes).filter(([arg]) => callerArgSet.has(arg)));
|
|
680
|
+
const defaults = Object.fromEntries(Object.entries(cfg.defaults ?? {}).filter(([arg]) => callerArgSet.has(arg)));
|
|
681
|
+
const storedArgs = cfg.args?.filter((arg) => callerArgSet.has(Schema.parseToolArgToken(String(arg)).arg));
|
|
674
682
|
return {
|
|
675
|
-
name:
|
|
683
|
+
name: identity,
|
|
676
684
|
description,
|
|
677
|
-
template,
|
|
685
|
+
template: path,
|
|
678
686
|
recipe: cfg,
|
|
679
|
-
args
|
|
680
|
-
defaults: Object.fromEntries(Object.entries(
|
|
681
|
-
key,
|
|
682
|
-
String(value),
|
|
683
|
-
])),
|
|
687
|
+
args,
|
|
688
|
+
defaults: Object.fromEntries(Object.entries(defaults).map(([key, value]) => [key, String(value)])),
|
|
684
689
|
...(Object.keys(argTypes).length > 0 ? { argTypes } : {}),
|
|
685
|
-
...(
|
|
686
|
-
...(
|
|
687
|
-
...(
|
|
690
|
+
...(mutableUsage ? { sourcePath: path } : {}),
|
|
691
|
+
...(storedArgs ? { storedArgs } : {}),
|
|
692
|
+
...(Object.keys(defaults).length > 0
|
|
688
693
|
? {
|
|
689
|
-
storedDefaults: Object.fromEntries(Object.entries(
|
|
694
|
+
storedDefaults: Object.fromEntries(Object.entries(defaults).map(([key, value]) => [
|
|
690
695
|
key,
|
|
691
696
|
String(value),
|
|
692
697
|
])),
|
|
@@ -694,3 +699,71 @@ export function toRegisteredTool(entry) {
|
|
|
694
699
|
: {}),
|
|
695
700
|
};
|
|
696
701
|
}
|
|
702
|
+
function boundedAdmissionDiagnostic(error) {
|
|
703
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
704
|
+
return message.length <= 4096 ? message : `${message.slice(0, 4095)}…`;
|
|
705
|
+
}
|
|
706
|
+
export function admitUserRecipe(sourcePath, resolutionContext, authoredRecipe, mutableUsage = true) {
|
|
707
|
+
const identity = RecipesReferences.getRecipeIdFromPath(sourcePath);
|
|
708
|
+
try {
|
|
709
|
+
const effectiveRecipe = RecipesReferences.readResolvedRecipeConfig(sourcePath, [], {
|
|
710
|
+
...(authoredRecipe
|
|
711
|
+
? { authoredEntry: { path: sourcePath, recipe: authoredRecipe } }
|
|
712
|
+
: {}),
|
|
713
|
+
skillContext: resolutionContext.activeSkills,
|
|
714
|
+
});
|
|
715
|
+
if (!effectiveRecipe) {
|
|
716
|
+
return {
|
|
717
|
+
diagnostics: [
|
|
718
|
+
authoredRecipe
|
|
719
|
+
? "Recipe must define a valid template."
|
|
720
|
+
: RecipesReferences.diagnoseRawRecipeConfigFailure(sourcePath) ??
|
|
721
|
+
"Recipe must define a valid template.",
|
|
722
|
+
],
|
|
723
|
+
identity,
|
|
724
|
+
sourcePath,
|
|
725
|
+
validated: false,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
if (effectiveRecipe.disabled === true) {
|
|
729
|
+
return {
|
|
730
|
+
diagnostics: ["Recipe is disabled."],
|
|
731
|
+
effectiveRecipe,
|
|
732
|
+
identity,
|
|
733
|
+
sourcePath,
|
|
734
|
+
validated: false,
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
const tool = projectRegisteredTool(identity, sourcePath, effectiveRecipe, mutableUsage);
|
|
738
|
+
return {
|
|
739
|
+
args: tool.args,
|
|
740
|
+
argTypes: tool.argTypes,
|
|
741
|
+
artifacts: effectiveRecipe.artifacts,
|
|
742
|
+
async: effectiveRecipe.async === true,
|
|
743
|
+
control: effectiveRecipe.control,
|
|
744
|
+
defaults: effectiveRecipe.defaults,
|
|
745
|
+
diagnostics: [],
|
|
746
|
+
effectiveRecipe,
|
|
747
|
+
identity,
|
|
748
|
+
sourcePath,
|
|
749
|
+
tool,
|
|
750
|
+
validated: true,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
return {
|
|
755
|
+
diagnostics: [boundedAdmissionDiagnostic(error)],
|
|
756
|
+
identity,
|
|
757
|
+
sourcePath,
|
|
758
|
+
validated: false,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
export function toRegisteredTool(entry) {
|
|
763
|
+
if (!entry.tool || entry.invalid || entry.disabled || !entry.config)
|
|
764
|
+
return undefined;
|
|
765
|
+
if (entry.resolutionContext) {
|
|
766
|
+
return admitUserRecipe(entry.path, entry.resolutionContext, undefined, entry.mutableUsage).tool;
|
|
767
|
+
}
|
|
768
|
+
return projectRegisteredTool(entry.id, entry.path, entry.config, entry.mutableUsage);
|
|
769
|
+
}
|
|
@@ -56,9 +56,15 @@ export interface TemplateRecipeContextRecord {
|
|
|
56
56
|
source_kind: "active_skill_component" | "explicit_file_recipe" | "user_registry_capability";
|
|
57
57
|
}
|
|
58
58
|
export interface ReadResolvedRecipeConfigOptions {
|
|
59
|
+
authoredEntry?: {
|
|
60
|
+
path: string;
|
|
61
|
+
recipe: Record<string, unknown>;
|
|
62
|
+
};
|
|
59
63
|
includeActorRecipeContext?: boolean;
|
|
60
64
|
skillContext?: ActiveSkillRecipeContext;
|
|
61
65
|
}
|
|
66
|
+
export declare const RUNTIME_OWNED_RECIPE_INPUT_NAMES: readonly string[];
|
|
67
|
+
export declare function isRuntimeOwnedRecipeInput(name: string): boolean;
|
|
62
68
|
export interface ActiveSkillRecipeSource {
|
|
63
69
|
name: string;
|
|
64
70
|
filePath?: string;
|
|
@@ -77,6 +83,18 @@ export interface ActiveSkillRecipeComponent {
|
|
|
77
83
|
skill: string;
|
|
78
84
|
stem: string;
|
|
79
85
|
}
|
|
86
|
+
export interface SkillRecipeComponentDiagnostic {
|
|
87
|
+
file?: string;
|
|
88
|
+
reason: string;
|
|
89
|
+
skill: string;
|
|
90
|
+
stem?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface SkillRecipeComponentInventory {
|
|
93
|
+
components: ActiveSkillRecipeComponent[];
|
|
94
|
+
partial: boolean;
|
|
95
|
+
rejected: SkillRecipeComponentDiagnostic[];
|
|
96
|
+
}
|
|
97
|
+
export declare function inventoryActiveSkillRecipeComponents(context: ActiveSkillRecipeContext): SkillRecipeComponentInventory;
|
|
80
98
|
export declare function listActiveSkillRecipeComponents(context: ActiveSkillRecipeContext): ActiveSkillRecipeComponent[];
|
|
81
99
|
export declare function resolveRecipePath(value: string, baseDir?: string): string;
|
|
82
100
|
export declare function resolveRecipeReferencePath(value: unknown, baseDir?: string, context?: ActiveSkillRecipeContext): string | undefined;
|