@llblab/pi-actors 0.45.1 → 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/AGENTS.md +9 -7
- package/BACKLOG.md +582 -0
- package/CHANGELOG.md +17 -1
- package/README.md +27 -13
- package/dist/fixtures/protocol/recipe-summary.json +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/async-runs.d.ts +5 -1
- package/dist/lib/async-runs.js +26 -24
- package/dist/lib/automatic-review-runtime.d.ts +2 -0
- package/dist/lib/automatic-review-runtime.js +12 -5
- package/dist/lib/draft-sleep.js +0 -1
- package/dist/lib/execution.d.ts +1 -0
- package/dist/lib/execution.js +1 -0
- package/dist/lib/extension-runtime.d.ts +1 -1
- package/dist/lib/extension-runtime.js +26 -4
- package/dist/lib/inspector.js +46 -7
- package/dist/lib/paths.d.ts +0 -1
- package/dist/lib/paths.js +0 -7
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +4 -4
- package/dist/lib/recipes-context.d.ts +12 -3
- package/dist/lib/recipes-context.js +30 -6
- package/dist/lib/recipes-discovery.d.ts +18 -0
- package/dist/lib/recipes-discovery.js +95 -22
- package/dist/lib/recipes-references.d.ts +42 -9
- package/dist/lib/recipes-references.js +248 -135
- package/dist/lib/registry.d.ts +14 -1
- package/dist/lib/registry.js +110 -87
- package/dist/lib/runtime.d.ts +30 -4
- package/dist/lib/runtime.js +74 -12
- package/dist/lib/tools-inspect.d.ts +3 -1
- package/dist/lib/tools-inspect.js +56 -9
- package/dist/lib/tools-local.d.ts +2 -0
- package/dist/lib/tools-local.js +5 -2
- package/dist/lib/tools-response.js +2 -0
- package/dist/lib/tools-spawn.d.ts +2 -0
- package/dist/lib/tools-spawn.js +24 -52
- package/dist/lib/tools.d.ts +4 -1
- package/dist/lib/tools.js +2 -0
- package/dist/scripts/build-dist.mjs +13 -4
- package/dist/skills/actors/SKILL.md +8 -8
- package/dist/skills/actors/recipes/recipe-validate.json +11 -0
- package/{recipes/utility-coordinator-lock-snapshot.json → dist/skills/actors/recipes/resource-locker-snapshot.json} +1 -3
- package/dist/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
- package/dist/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
- package/dist/skills/actors/recipes/run-summary.json +9 -0
- package/dist/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -2
- package/dist/skills/actors/scripts/run-utils.mjs +164 -0
- package/{scripts → dist/skills/actors/scripts}/validate-recipe.mjs +134 -17
- package/dist/skills/artifacts/SKILL.md +16 -0
- package/{recipes/pipeline-artifact-bundle.json → dist/skills/artifacts/recipes/bundle.json} +4 -4
- package/dist/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
- package/dist/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
- package/{recipes/pipeline-artifact-report.json → dist/skills/artifacts/recipes/report.json} +2 -2
- package/dist/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
- package/dist/skills/artifacts/scripts/artifact-utils.mjs +63 -0
- package/dist/skills/media/SKILL.md +16 -0
- package/dist/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
- package/{recipes/music-player.json → dist/skills/media/recipes/player.json} +1 -3
- package/dist/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
- package/dist/skills/media/scripts/media-utils.mjs +47 -0
- package/dist/{scripts → skills/media/scripts}/music-player.mjs +9 -3
- package/dist/skills/project-work/SKILL.md +16 -0
- package/dist/skills/project-work/recipes/changelog-section.json +11 -0
- package/{recipes/pipeline-docs-maintenance.json → dist/skills/project-work/recipes/docs-maintenance.json} +4 -4
- package/dist/skills/project-work/recipes/package-summary.json +9 -0
- package/{recipes/pipeline-release-readiness.json → dist/skills/project-work/recipes/release-readiness.json} +6 -6
- package/dist/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
- package/{recipes/pipeline-repo-health.json → dist/skills/project-work/recipes/repo-health.json} +6 -6
- package/{recipes/pipeline-async-run-ops.json → dist/skills/project-work/recipes/run-ops.json} +3 -3
- package/dist/skills/project-work/recipes/skill-summary.json +11 -0
- package/dist/skills/project-work/scripts/project-utils.mjs +89 -0
- package/dist/skills/recipe-memory/SKILL.md +16 -0
- package/dist/skills/swarm/SKILL.md +4 -4
- package/{recipes/pipeline-architect-coordinator.json → dist/skills/swarm/recipes/architect.json} +5 -5
- package/{recipes/pipeline-checkpoint-continuation.json → dist/skills/swarm/recipes/checkpoint-continuation.json} +3 -3
- package/dist/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
- package/dist/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
- package/dist/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
- package/{recipes/pipeline-research-synthesis.json → dist/skills/swarm/recipes/research-synthesis.json} +6 -6
- package/{recipes/pipeline-review-readiness.json → dist/skills/swarm/recipes/review-readiness.json} +1 -1
- package/{recipes/subagents-prompts.json → dist/skills/swarm/recipes/subagent-prompts.json} +1 -1
- package/{recipes → dist/skills/swarm/recipes}/subagent-quorum.json +1 -1
- package/dist/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
- package/docs/actor-inspector.md +4 -3
- package/docs/async-runs.md +2 -2
- package/docs/recipe-library.md +23 -23
- package/docs/releasing.md +2 -2
- package/docs/template-recipes.md +32 -14
- package/docs/tool-registry.md +10 -4
- package/fixtures/protocol/recipe-summary.json +1 -1
- package/index.ts +2 -1
- package/lib/async-runs.ts +52 -25
- package/lib/automatic-review-runtime.ts +16 -3
- package/lib/draft-sleep.ts +0 -1
- package/lib/execution.ts +2 -0
- package/lib/extension-runtime.ts +42 -4
- package/lib/inspector.ts +57 -11
- package/lib/paths.ts +0 -7
- package/lib/prompts.ts +4 -4
- package/lib/recipes-context.ts +51 -6
- package/lib/recipes-discovery.ts +164 -24
- package/lib/recipes-references.ts +359 -146
- package/lib/registry.ts +185 -94
- package/lib/runtime.ts +109 -17
- package/lib/tools-inspect.ts +77 -10
- package/lib/tools-local.ts +9 -1
- package/lib/tools-response.ts +2 -0
- package/lib/tools-spawn.ts +38 -74
- package/lib/tools.ts +4 -1
- package/package.json +2 -4
- package/scripts/build-dist.mjs +13 -4
- package/skills/actors/SKILL.md +8 -8
- package/skills/actors/recipes/recipe-validate.json +11 -0
- package/{dist/recipes/utility-coordinator-lock-snapshot.json → skills/actors/recipes/resource-locker-snapshot.json} +1 -3
- package/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
- package/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
- package/skills/actors/recipes/run-summary.json +9 -0
- package/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -2
- package/skills/actors/scripts/run-utils.mjs +164 -0
- package/{dist → skills/actors}/scripts/validate-recipe.mjs +134 -17
- package/skills/artifacts/SKILL.md +16 -0
- package/{dist/recipes/pipeline-artifact-bundle.json → skills/artifacts/recipes/bundle.json} +4 -4
- package/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
- package/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
- package/{dist/recipes/pipeline-artifact-report.json → skills/artifacts/recipes/report.json} +2 -2
- package/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
- package/skills/artifacts/scripts/artifact-utils.mjs +63 -0
- package/skills/media/SKILL.md +16 -0
- package/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
- package/{dist/recipes/music-player.json → skills/media/recipes/player.json} +1 -3
- package/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
- package/skills/media/scripts/media-utils.mjs +47 -0
- package/{scripts → skills/media/scripts}/music-player.mjs +9 -3
- package/skills/project-work/SKILL.md +16 -0
- package/skills/project-work/recipes/changelog-section.json +11 -0
- package/{dist/recipes/pipeline-docs-maintenance.json → skills/project-work/recipes/docs-maintenance.json} +4 -4
- package/skills/project-work/recipes/package-summary.json +9 -0
- package/{dist/recipes/pipeline-release-readiness.json → skills/project-work/recipes/release-readiness.json} +6 -6
- package/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
- package/{dist/recipes/pipeline-repo-health.json → skills/project-work/recipes/repo-health.json} +6 -6
- package/{dist/recipes/pipeline-async-run-ops.json → skills/project-work/recipes/run-ops.json} +3 -3
- package/skills/project-work/recipes/skill-summary.json +11 -0
- package/skills/project-work/scripts/project-utils.mjs +89 -0
- package/skills/recipe-memory/SKILL.md +16 -0
- package/skills/swarm/SKILL.md +4 -4
- package/{dist/recipes/pipeline-architect-coordinator.json → skills/swarm/recipes/architect.json} +5 -5
- package/{dist/recipes/pipeline-checkpoint-continuation.json → skills/swarm/recipes/checkpoint-continuation.json} +3 -3
- package/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
- package/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
- package/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
- package/{dist/recipes/pipeline-research-synthesis.json → skills/swarm/recipes/research-synthesis.json} +6 -6
- package/{dist/recipes/pipeline-review-readiness.json → skills/swarm/recipes/review-readiness.json} +1 -1
- package/{dist/recipes/subagents-prompts.json → skills/swarm/recipes/subagent-prompts.json} +1 -1
- package/{dist → skills/swarm}/recipes/subagent-quorum.json +1 -1
- package/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
- package/dist/recipes/utility-changelog-section.json +0 -13
- package/dist/recipes/utility-package-summary.json +0 -11
- package/dist/recipes/utility-run-summary.json +0 -11
- package/dist/recipes/utility-skill-summary.json +0 -13
- package/dist/recipes/utility-validate-recipe.json +0 -13
- package/dist/scripts/executable-block-style.d.mts +0 -1
- package/dist/scripts/executable-block-style.mjs +0 -73
- package/dist/scripts/recipe-utils.mjs +0 -404
- package/dist/scripts/release-gates.mjs +0 -281
- package/recipes/utility-changelog-section.json +0 -13
- package/recipes/utility-package-summary.json +0 -11
- package/recipes/utility-run-summary.json +0 -11
- package/recipes/utility-skill-summary.json +0 -13
- package/recipes/utility-validate-recipe.json +0 -13
- package/scripts/executable-block-style.d.mts +0 -1
- package/scripts/executable-block-style.mjs +0 -73
- package/scripts/recipe-utils.mjs +0 -404
- package/scripts/release-gates.mjs +0 -281
- /package/dist/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
- /package/dist/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
- /package/dist/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
- /package/dist/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
- /package/dist/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
- /package/dist/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
- /package/dist/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
- /package/dist/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
- /package/dist/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
- /package/dist/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
- /package/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
- /package/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
- /package/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
- /package/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
- /package/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
- /package/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
- /package/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
- /package/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
- /package/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
- /package/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
package/dist/lib/registry.js
CHANGED
|
@@ -8,9 +8,11 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
8
8
|
import * as CommandTemplates from "./command-templates.js";
|
|
9
9
|
import * as Config from "./config.js";
|
|
10
10
|
import * as ExecutionOutput from "./execution-output.js";
|
|
11
|
-
import { withFileMutationLock, writeJsonAtomic } from "./file-state.js";
|
|
11
|
+
import { withFileMutationLock, writeJsonAtomic, writeTextAtomic, } from "./file-state.js";
|
|
12
12
|
import * as Identity from "./identity.js";
|
|
13
13
|
import * as Paths from "./paths.js";
|
|
14
|
+
import * as RecipesContext from "./recipes-context.js";
|
|
15
|
+
import * as RecipesDiscovery from "./recipes-discovery.js";
|
|
14
16
|
import * as RecipesReferences from "./recipes-references.js";
|
|
15
17
|
import * as Schema from "./schema.js";
|
|
16
18
|
function textContent(text) {
|
|
@@ -58,21 +60,24 @@ function promoteDraftRecipe(name, input, ctx, deps) {
|
|
|
58
60
|
if ((existing || existsSync(targetPath)) && !input.update) {
|
|
59
61
|
throw new Error(ExecutionOutput.formatToolText(`Tool "${name}" already registered. Use update=true to overwrite.`));
|
|
60
62
|
}
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
63
|
+
const raw = RecipesReferences.readRawRecipeConfig(draftPath);
|
|
64
|
+
if (!raw) {
|
|
63
65
|
const reason = RecipesReferences.diagnoseRawRecipeConfigFailure(draftPath);
|
|
64
66
|
throw new Error(ExecutionOutput.formatToolText(`Draft recipe is invalid${reason ? `: ${reason}` : "."}`));
|
|
65
67
|
}
|
|
66
|
-
const
|
|
68
|
+
const authoredRecipe = buildAuthoredRecipe({
|
|
67
69
|
...input,
|
|
68
|
-
description: input.description ??
|
|
69
|
-
|
|
70
|
-
}, existing);
|
|
71
|
-
const
|
|
70
|
+
description: input.description ??
|
|
71
|
+
(typeof raw.description === "string" ? raw.description : undefined),
|
|
72
|
+
}, existing, raw);
|
|
73
|
+
const resolutionContext = getRegistrationResolutionContext(ctx, deps);
|
|
74
|
+
const candidate = RecipesDiscovery.admitUserRecipe(targetPath, resolutionContext, authoredRecipe);
|
|
75
|
+
if (!candidate.validated || !candidate.tool) {
|
|
76
|
+
throw new Error(ExecutionOutput.formatToolText(`Draft recipe is invalid: ${candidate.diagnostics.join("; ")}`));
|
|
77
|
+
}
|
|
72
78
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
73
|
-
writeJsonAtomic(targetPath,
|
|
74
|
-
promoted
|
|
75
|
-
promoted.sourcePath = targetPath;
|
|
79
|
+
writeJsonAtomic(targetPath, authoredRecipe);
|
|
80
|
+
const promoted = RecipesDiscovery.admitUserRecipe(targetPath, resolutionContext).tool;
|
|
76
81
|
tools.set(name, promoted);
|
|
77
82
|
deps.registerRuntimeTool(promoted);
|
|
78
83
|
deps.notify(ctx, `Promoted draft recipe: ${name}`, "info");
|
|
@@ -89,17 +94,10 @@ function promoteDraftRecipe(name, input, ctx, deps) {
|
|
|
89
94
|
},
|
|
90
95
|
};
|
|
91
96
|
}
|
|
92
|
-
function persistToolRecipe(deps,
|
|
93
|
-
const path = getToolRecipePath(deps,
|
|
97
|
+
function persistToolRecipe(deps, name, recipe) {
|
|
98
|
+
const path = getToolRecipePath(deps, name);
|
|
94
99
|
mkdirSync(dirname(path), { recursive: true });
|
|
95
|
-
writeJsonAtomic(path,
|
|
96
|
-
description: cfg.description,
|
|
97
|
-
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
98
|
-
...(cfg.storedArgs ? { args: cfg.storedArgs } : {}),
|
|
99
|
-
...(cfg.storedDefaults ? { defaults: cfg.storedDefaults } : {}),
|
|
100
|
-
...(cfg.recipe?.values ? { values: cfg.recipe.values } : {}),
|
|
101
|
-
template: cfg.template,
|
|
102
|
-
});
|
|
100
|
+
writeJsonAtomic(path, recipe);
|
|
103
101
|
return path;
|
|
104
102
|
}
|
|
105
103
|
function deleteTool(name, ctx, deps) {
|
|
@@ -130,11 +128,15 @@ function readAuthoritativeStoredTool(deps, name) {
|
|
|
130
128
|
if (!existsSync(path))
|
|
131
129
|
return { exists: false };
|
|
132
130
|
try {
|
|
133
|
-
const
|
|
131
|
+
const bytes = readFileSync(path, "utf8");
|
|
132
|
+
const recipe = JSON.parse(bytes);
|
|
133
|
+
const normalized = Config.normalizeStoredTool(name, recipe, deps.reservedToolNames);
|
|
134
134
|
if (!normalized.cfg)
|
|
135
|
-
return { exists: true };
|
|
135
|
+
return { bytes, exists: true, recipe };
|
|
136
136
|
return {
|
|
137
|
+
bytes,
|
|
137
138
|
exists: true,
|
|
139
|
+
recipe,
|
|
138
140
|
tool: { ...normalized.cfg, sourcePath: path },
|
|
139
141
|
};
|
|
140
142
|
}
|
|
@@ -161,7 +163,17 @@ function getInputTemplate(value) {
|
|
|
161
163
|
}
|
|
162
164
|
throw new Error(ExecutionOutput.formatToolText("Tool template must be a string, object, or sequence."));
|
|
163
165
|
}
|
|
164
|
-
function
|
|
166
|
+
function getRegistrationResolutionContext(ctx, deps) {
|
|
167
|
+
if (ctx &&
|
|
168
|
+
typeof ctx === "object" &&
|
|
169
|
+
"recipeResolutionContext" in ctx) {
|
|
170
|
+
const resolutionContext = ctx.recipeResolutionContext;
|
|
171
|
+
if (resolutionContext)
|
|
172
|
+
return resolutionContext;
|
|
173
|
+
}
|
|
174
|
+
return RecipesContext.createEmptyRecipeResolutionContext("offline-register-tool", getRecipeRoot(deps));
|
|
175
|
+
}
|
|
176
|
+
function buildAuthoredRecipe(input, existing, existingRecipe) {
|
|
165
177
|
const explicitArgs = input.args === undefined
|
|
166
178
|
? undefined
|
|
167
179
|
: Schema.parseToolArgDeclarations(input.args);
|
|
@@ -175,63 +187,29 @@ function buildConfig(name, input, existing) {
|
|
|
175
187
|
if (template === null) {
|
|
176
188
|
throw new Error(ExecutionOutput.formatToolText("Tool template cannot be null here."));
|
|
177
189
|
}
|
|
178
|
-
const finalTemplate = template === undefined || template === ""
|
|
190
|
+
const finalTemplate = template === undefined || template === ""
|
|
191
|
+
? existingRecipe?.template ?? existing?.template
|
|
192
|
+
: template;
|
|
179
193
|
if (!finalTemplate) {
|
|
180
194
|
throw new Error(ExecutionOutput.formatToolText("Tool template is required."));
|
|
181
195
|
}
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
? {
|
|
185
|
-
name: inputRecipe,
|
|
186
|
-
...(typeof input.async === "boolean" ? { async: input.async } : {}),
|
|
187
|
-
template: finalTemplate,
|
|
188
|
-
...(input.values && typeof input.values === "object"
|
|
189
|
-
? { values: input.values }
|
|
190
|
-
: {}),
|
|
191
|
-
}
|
|
192
|
-
: template === undefined
|
|
193
|
-
? existing?.recipe
|
|
194
|
-
: undefined;
|
|
195
|
-
const defaults = explicitArgs?.defaults ?? existing?.storedDefaults ?? {};
|
|
196
|
-
const storedArgs = explicitArgs
|
|
197
|
-
? explicitArgs.declarations
|
|
198
|
-
: existing?.storedArgs;
|
|
199
|
-
const storedDefaults = Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
200
|
-
const recipeTemplate = RecipesReferences.getRecipeTemplate(finalTemplate);
|
|
201
|
-
const argTemplate = recipeTemplate ?? finalTemplate;
|
|
202
|
-
const argTemplateConfig = typeof argTemplate === "object" && !Array.isArray(argTemplate)
|
|
203
|
-
? {
|
|
204
|
-
...argTemplate,
|
|
205
|
-
...(storedArgs !== undefined ? { args: storedArgs } : {}),
|
|
206
|
-
defaults: { ...(argTemplate.defaults ?? {}), ...defaults },
|
|
207
|
-
}
|
|
208
|
-
: {
|
|
209
|
-
args: storedArgs,
|
|
210
|
-
defaults,
|
|
211
|
-
template: argTemplate,
|
|
212
|
-
};
|
|
213
|
-
const inferredArgTypes = Schema.getTemplateArgTypes(argTemplateConfig);
|
|
214
|
-
const argTypes = {
|
|
215
|
-
...inferredArgTypes,
|
|
216
|
-
...(existing?.argTypes ?? {}),
|
|
217
|
-
...(explicitArgs?.argTypes ?? {}),
|
|
218
|
-
};
|
|
219
|
-
return {
|
|
220
|
-
name,
|
|
196
|
+
const authored = {
|
|
197
|
+
...(existingRecipe ?? {}),
|
|
221
198
|
description,
|
|
222
199
|
template: finalTemplate,
|
|
223
|
-
...(recipe ? { recipe } : {}),
|
|
224
|
-
args: RecipesReferences.isRecipeTool(finalTemplate, recipe) &&
|
|
225
|
-
storedArgs !== undefined
|
|
226
|
-
? Schema.getExplicitToolArgNames(storedArgs)
|
|
227
|
-
: RecipesReferences.isRecipeReference(finalTemplate) && !recipeTemplate
|
|
228
|
-
? Schema.getExplicitToolArgNames(storedArgs)
|
|
229
|
-
: Schema.getToolArgNames(argTemplateConfig),
|
|
230
|
-
defaults,
|
|
231
|
-
...(Object.keys(argTypes).length > 0 ? { argTypes } : {}),
|
|
232
|
-
...(storedArgs !== undefined ? { storedArgs } : {}),
|
|
233
|
-
...(storedDefaults !== undefined ? { storedDefaults } : {}),
|
|
234
200
|
};
|
|
201
|
+
if (typeof input.async === "boolean")
|
|
202
|
+
authored.async = input.async;
|
|
203
|
+
if (explicitArgs) {
|
|
204
|
+
authored.args = explicitArgs.declarations;
|
|
205
|
+
if (Object.keys(explicitArgs.defaults).length > 0)
|
|
206
|
+
authored.defaults = explicitArgs.defaults;
|
|
207
|
+
else
|
|
208
|
+
delete authored.defaults;
|
|
209
|
+
}
|
|
210
|
+
if (input.values && typeof input.values === "object")
|
|
211
|
+
authored.values = input.values;
|
|
212
|
+
return authored;
|
|
235
213
|
}
|
|
236
214
|
function executeRegisterToolUnlocked(params, ctx, deps) {
|
|
237
215
|
const input = params;
|
|
@@ -262,37 +240,82 @@ function executeRegisterToolUnlocked(params, ctx, deps) {
|
|
|
262
240
|
if (template === undefined && !existing) {
|
|
263
241
|
throw new Error(ExecutionOutput.formatToolText("Tool template is required for new registrations."));
|
|
264
242
|
}
|
|
265
|
-
const
|
|
266
|
-
|
|
243
|
+
const recipePath = getToolRecipePath(deps, name);
|
|
244
|
+
const authoredRecipe = buildAuthoredRecipe(input, existing, authoritative.recipe);
|
|
245
|
+
const resolutionContext = getRegistrationResolutionContext(ctx, deps);
|
|
246
|
+
const candidate = RecipesDiscovery.admitUserRecipe(recipePath, resolutionContext, authoredRecipe);
|
|
247
|
+
if (!candidate.validated || !candidate.tool) {
|
|
248
|
+
throw new Error(ExecutionOutput.formatToolText(`Tool recipe validation failed: ${candidate.diagnostics.join("; ")}`));
|
|
249
|
+
}
|
|
250
|
+
const activeBefore = deps.getActiveTools();
|
|
251
|
+
let persisted = false;
|
|
252
|
+
let activation;
|
|
253
|
+
let cfg;
|
|
267
254
|
try {
|
|
268
|
-
|
|
255
|
+
persistToolRecipe(deps, name, authoredRecipe);
|
|
256
|
+
persisted = true;
|
|
257
|
+
const admitted = RecipesDiscovery.admitUserRecipe(recipePath, resolutionContext);
|
|
258
|
+
if (!admitted.validated || !admitted.tool) {
|
|
259
|
+
throw new Error(`Persisted tool recipe admission failed: ${admitted.diagnostics.join("; ")}`);
|
|
260
|
+
}
|
|
261
|
+
cfg = admitted.tool;
|
|
262
|
+
tools.set(name, cfg);
|
|
263
|
+
activation = deps.registerRuntimeTool(cfg) ?? undefined;
|
|
264
|
+
if (activation &&
|
|
265
|
+
(!activation.host_registered ||
|
|
266
|
+
!activation.active_tool ||
|
|
267
|
+
!activation.callable_now)) {
|
|
268
|
+
throw new Error(`Host activation verification failed: ${JSON.stringify(activation)}`);
|
|
269
|
+
}
|
|
269
270
|
}
|
|
270
271
|
catch (error) {
|
|
271
|
-
|
|
272
|
+
if (persisted) {
|
|
273
|
+
if (authoritative.bytes !== undefined)
|
|
274
|
+
writeTextAtomic(recipePath, authoritative.bytes);
|
|
275
|
+
else if (existsSync(recipePath))
|
|
276
|
+
unlinkSync(recipePath);
|
|
277
|
+
}
|
|
278
|
+
if (existing) {
|
|
279
|
+
tools.set(name, existing);
|
|
280
|
+
deps.registerRuntimeTool(existing);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
tools.delete(name);
|
|
284
|
+
}
|
|
285
|
+
deps.setActiveTools(activeBefore);
|
|
286
|
+
throw new Error(ExecutionOutput.formatToolText(`Tool registration transaction failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
272
287
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const templateWarnings = CommandTemplates.getCommandTemplateWarnings(typeof cfg.template === "object" && !Array.isArray(cfg.template)
|
|
278
|
-
? cfg.template
|
|
279
|
-
: { template: cfg.template });
|
|
288
|
+
deps.notify(ctx, `Tool activated: ${name}`, "info");
|
|
289
|
+
const templateWarnings = CommandTemplates.getCommandTemplateWarnings(typeof cfg.recipe?.template === "object" && !Array.isArray(cfg.recipe.template)
|
|
290
|
+
? cfg.recipe.template
|
|
291
|
+
: { template: cfg.recipe?.template ?? cfg.template });
|
|
280
292
|
const warningText = templateWarnings.length > 0
|
|
281
293
|
? `\nWarnings:\n${templateWarnings.map((warning) => `- ${warning}`).join("\n")}`
|
|
282
294
|
: "";
|
|
295
|
+
const outcomeText = activation?.callable_now
|
|
296
|
+
? `${existing ? "Updated" : "Registered"} and activated tool "${name}"`
|
|
297
|
+
: `Persisted tool "${name}"; current-session activation is unverified`;
|
|
283
298
|
return {
|
|
284
299
|
content: [
|
|
285
|
-
textContent(ExecutionOutput.formatToolText(`${
|
|
300
|
+
textContent(ExecutionOutput.formatToolText(`${outcomeText} (args: ${Schema.formatToolArgs(cfg.args)}).${warningText}`)),
|
|
286
301
|
],
|
|
287
302
|
details: {
|
|
303
|
+
active_tool: activation?.active_tool ?? false,
|
|
304
|
+
activation: activation?.activation ?? "unverified",
|
|
288
305
|
args: cfg.args,
|
|
306
|
+
callable_now: activation?.callable_now ?? false,
|
|
289
307
|
config: recipePath,
|
|
290
308
|
defaults: cfg.defaults,
|
|
309
|
+
host_registered: activation?.host_registered ?? false,
|
|
310
|
+
persisted: true,
|
|
311
|
+
registry_active: tools.get(name) === cfg,
|
|
312
|
+
resolved: true,
|
|
291
313
|
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
292
314
|
...(cfg.recipe?.name ? { recipeName: cfg.recipe.name } : {}),
|
|
293
315
|
...(cfg.template ? { template: cfg.template } : {}),
|
|
294
316
|
...(templateWarnings.length > 0 ? { templateWarnings } : {}),
|
|
295
317
|
tool: name,
|
|
318
|
+
validated: true,
|
|
296
319
|
},
|
|
297
320
|
};
|
|
298
321
|
}
|
package/dist/lib/runtime.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { watch } from "node:fs";
|
|
7
7
|
import * as Config from "./config.ts";
|
|
8
8
|
import type { RegisteredToolExec } from "./execution.ts";
|
|
9
|
+
import type { RecipeResolutionContext } from "./recipes-context.ts";
|
|
9
10
|
import * as ToolsLocal from "./tools-local.ts";
|
|
10
11
|
export interface RuntimeContext {
|
|
11
12
|
hasUI: boolean;
|
|
@@ -13,22 +14,46 @@ export interface RuntimeContext {
|
|
|
13
14
|
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
14
15
|
};
|
|
15
16
|
}
|
|
17
|
+
export interface RuntimeToolActivation {
|
|
18
|
+
active_tool: boolean;
|
|
19
|
+
activation: "current_session" | "unverified";
|
|
20
|
+
callable_now: boolean;
|
|
21
|
+
host_registered: boolean;
|
|
22
|
+
}
|
|
16
23
|
export interface ToolRegistryRuntimeDeps {
|
|
17
24
|
configPath: string;
|
|
18
25
|
exec: RegisteredToolExec;
|
|
19
|
-
packagedRecipeRoot?: string;
|
|
20
26
|
recipeRoot?: string;
|
|
21
27
|
getActiveTools?: () => string[];
|
|
28
|
+
getAllTools?: () => Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
}>;
|
|
22
31
|
registerTool: (definition: ReturnType<typeof ToolsLocal.createRuntimeToolDefinition>) => void;
|
|
23
32
|
reservedToolNames: Set<string>;
|
|
24
33
|
setActiveTools?: (toolNames: string[]) => void;
|
|
25
34
|
}
|
|
35
|
+
export interface RecipeRegistryStatus {
|
|
36
|
+
active_tool_count: number;
|
|
37
|
+
last_scan_counts: {
|
|
38
|
+
active: number;
|
|
39
|
+
rejected: number;
|
|
40
|
+
scanned: number;
|
|
41
|
+
};
|
|
42
|
+
last_scan_at?: string;
|
|
43
|
+
registry_generation: number;
|
|
44
|
+
resolution_generation?: string;
|
|
45
|
+
watch_status: "closed" | "failed" | "watching_parent" | "watching_root";
|
|
46
|
+
watched_root: string;
|
|
47
|
+
}
|
|
26
48
|
export interface ToolRegistryRuntime {
|
|
49
|
+
getStatus(): RecipeRegistryStatus;
|
|
50
|
+
getToolStatus(name: string): Record<string, unknown> | undefined;
|
|
27
51
|
getToolNameBlocker(name: string): string | undefined;
|
|
28
52
|
getTools(): Map<string, Config.RegisteredTool>;
|
|
29
|
-
loadTools(ctx: RuntimeContext): void;
|
|
53
|
+
loadTools(ctx: RuntimeContext, resolutionContext?: RecipeResolutionContext): void;
|
|
30
54
|
notify(ctx: RuntimeContext, message: string, type: "info" | "warning" | "error"): void;
|
|
31
|
-
registerRuntimeTool(cfg: Config.RegisteredTool):
|
|
55
|
+
registerRuntimeTool(cfg: Config.RegisteredTool): RuntimeToolActivation;
|
|
56
|
+
setWatchStatus(status: RecipeRegistryStatus["watch_status"]): void;
|
|
32
57
|
}
|
|
33
58
|
export interface RecipeToolReloadWatcher {
|
|
34
59
|
close(): void;
|
|
@@ -37,7 +62,8 @@ export interface RecipeToolReloadWatcher {
|
|
|
37
62
|
export declare function createAutoToolsRuntime(deps: ToolRegistryRuntimeDeps): ToolRegistryRuntime;
|
|
38
63
|
export interface RecipeToolReloadWatcherDeps {
|
|
39
64
|
exists?: (path: string) => boolean;
|
|
65
|
+
getResolutionContext?: () => RecipeResolutionContext | undefined;
|
|
40
66
|
recipeRoot?: string;
|
|
41
67
|
watchPath?: typeof watch;
|
|
42
68
|
}
|
|
43
|
-
export declare function createRecipeToolReloadWatcher(runtime: Pick<ToolRegistryRuntime, "loadTools"
|
|
69
|
+
export declare function createRecipeToolReloadWatcher(runtime: Pick<ToolRegistryRuntime, "loadTools"> & Partial<Pick<ToolRegistryRuntime, "setWatchStatus">>, deps?: RecipeToolReloadWatcherDeps): RecipeToolReloadWatcher;
|
package/dist/lib/runtime.js
CHANGED
|
@@ -7,11 +7,20 @@ 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 RecipesUsage from "./recipes-usage.js";
|
|
10
11
|
import * as ToolsLocal from "./tools-local.js";
|
|
11
12
|
export function createAutoToolsRuntime(deps) {
|
|
12
13
|
const tools = new Map();
|
|
13
14
|
const runtimeToolFingerprints = new Map();
|
|
14
15
|
const runtimeTools = new Set();
|
|
16
|
+
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
17
|
+
const status = {
|
|
18
|
+
active_tool_count: 0,
|
|
19
|
+
last_scan_counts: { active: 0, rejected: 0, scanned: 0 },
|
|
20
|
+
registry_generation: 0,
|
|
21
|
+
watch_status: "closed",
|
|
22
|
+
watched_root: recipeRoot,
|
|
23
|
+
};
|
|
15
24
|
function notify(ctx, message, type) {
|
|
16
25
|
if (ctx.hasUI)
|
|
17
26
|
ctx.ui.notify(message, type);
|
|
@@ -44,13 +53,29 @@ export function createAutoToolsRuntime(deps) {
|
|
|
44
53
|
const staleSet = new Set(stale);
|
|
45
54
|
deps.setActiveTools(deps.getActiveTools().filter((name) => !staleSet.has(name)));
|
|
46
55
|
}
|
|
56
|
+
function activationFor(name) {
|
|
57
|
+
const hostRegistered = deps.getAllTools?.().some((tool) => tool.name === name) ?? false;
|
|
58
|
+
const activeTool = deps.getActiveTools?.().includes(name) ?? false;
|
|
59
|
+
return {
|
|
60
|
+
active_tool: activeTool,
|
|
61
|
+
activation: hostRegistered && activeTool ? "current_session" : "unverified",
|
|
62
|
+
callable_now: hostRegistered && activeTool,
|
|
63
|
+
host_registered: hostRegistered,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
47
66
|
function registerRuntimeTool(cfg) {
|
|
48
67
|
const fingerprint = getToolFingerprint(cfg);
|
|
49
|
-
if (runtimeToolFingerprints.get(cfg.name)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
if (runtimeToolFingerprints.get(cfg.name) !== fingerprint) {
|
|
69
|
+
deps.registerTool(ToolsLocal.createRuntimeToolDefinition(cfg, deps.exec));
|
|
70
|
+
runtimeTools.add(cfg.name);
|
|
71
|
+
runtimeToolFingerprints.set(cfg.name, fingerprint);
|
|
72
|
+
}
|
|
73
|
+
if (deps.getActiveTools && deps.setActiveTools) {
|
|
74
|
+
deps.setActiveTools([
|
|
75
|
+
...new Set([...deps.getActiveTools(), cfg.name]),
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
return activationFor(cfg.name);
|
|
54
79
|
}
|
|
55
80
|
function isStartupActionableRegistryWarning(warning) {
|
|
56
81
|
if (warning.includes(" shadows "))
|
|
@@ -69,7 +94,7 @@ export function createAutoToolsRuntime(deps) {
|
|
|
69
94
|
const other = warnings.filter((warning) => !shadowed.includes(warning) && !skipped.includes(warning));
|
|
70
95
|
const lines = ["pi-actors recipe registry warning"];
|
|
71
96
|
if (shadowed.length > 0) {
|
|
72
|
-
lines.push("User recipes
|
|
97
|
+
lines.push("User recipes shadow lower-priority user sources:");
|
|
73
98
|
lines.push(...shadowed.map((warning) => `• ${warning}`));
|
|
74
99
|
}
|
|
75
100
|
if (skipped.length > 0) {
|
|
@@ -82,13 +107,15 @@ export function createAutoToolsRuntime(deps) {
|
|
|
82
107
|
}
|
|
83
108
|
return `${lines.join("\n")}\n`;
|
|
84
109
|
}
|
|
85
|
-
function loadTools(ctx) {
|
|
110
|
+
function loadTools(ctx, resolutionContext) {
|
|
86
111
|
const warnings = [];
|
|
87
|
-
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
88
|
-
const packagedRecipeRoot = deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot();
|
|
89
112
|
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
90
|
-
{
|
|
91
|
-
|
|
113
|
+
{
|
|
114
|
+
root: recipeRoot,
|
|
115
|
+
defaultTool: true,
|
|
116
|
+
mutableUsage: true,
|
|
117
|
+
resolutionContext,
|
|
118
|
+
},
|
|
92
119
|
]);
|
|
93
120
|
warnings.push(...discovered.diagnostics);
|
|
94
121
|
tools.clear();
|
|
@@ -111,17 +138,47 @@ export function createAutoToolsRuntime(deps) {
|
|
|
111
138
|
}
|
|
112
139
|
registerRuntimeTool(cfg);
|
|
113
140
|
}
|
|
141
|
+
status.active_tool_count = tools.size;
|
|
142
|
+
status.last_scan_at = new Date().toISOString();
|
|
143
|
+
status.last_scan_counts = {
|
|
144
|
+
active: tools.size,
|
|
145
|
+
rejected: discovered.entries.filter((entry) => entry.invalid).length,
|
|
146
|
+
scanned: discovered.entries.length,
|
|
147
|
+
};
|
|
148
|
+
status.registry_generation += 1;
|
|
149
|
+
status.resolution_generation = resolutionContext?.generation;
|
|
114
150
|
const startupWarnings = warnings.filter(isStartupActionableRegistryWarning);
|
|
115
151
|
if (startupWarnings.length > 0) {
|
|
116
152
|
notify(ctx, formatRecipeToolWarnings(startupWarnings), "warning");
|
|
117
153
|
}
|
|
118
154
|
}
|
|
119
155
|
return {
|
|
156
|
+
getStatus: () => ({
|
|
157
|
+
...status,
|
|
158
|
+
last_scan_counts: { ...status.last_scan_counts },
|
|
159
|
+
}),
|
|
160
|
+
getToolStatus: (name) => {
|
|
161
|
+
const cfg = tools.get(name);
|
|
162
|
+
if (!cfg)
|
|
163
|
+
return undefined;
|
|
164
|
+
const usage = cfg.sourcePath
|
|
165
|
+
? RecipesUsage.readRecipeUsage(cfg.sourcePath)
|
|
166
|
+
: undefined;
|
|
167
|
+
return {
|
|
168
|
+
...activationFor(name),
|
|
169
|
+
launch_kind: usage?.launch_kind,
|
|
170
|
+
spawn_calls: Number(usage?.spawn_calls ?? 0),
|
|
171
|
+
tool_calls: Number(usage?.tool_calls ?? 0),
|
|
172
|
+
};
|
|
173
|
+
},
|
|
120
174
|
getToolNameBlocker,
|
|
121
175
|
getTools: () => tools,
|
|
122
176
|
loadTools,
|
|
123
177
|
notify,
|
|
124
178
|
registerRuntimeTool,
|
|
179
|
+
setWatchStatus: (watchStatus) => {
|
|
180
|
+
status.watch_status = watchStatus;
|
|
181
|
+
},
|
|
125
182
|
};
|
|
126
183
|
}
|
|
127
184
|
export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
@@ -131,6 +188,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
131
188
|
let rootWatcher;
|
|
132
189
|
let parentWatcher;
|
|
133
190
|
let failureNotified = false;
|
|
191
|
+
const setWatchStatus = (watchStatus) => runtime.setWatchStatus?.(watchStatus);
|
|
134
192
|
const close = () => {
|
|
135
193
|
const closingRoot = rootWatcher;
|
|
136
194
|
rootWatcher = undefined;
|
|
@@ -141,11 +199,13 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
141
199
|
if (reloadTimeout)
|
|
142
200
|
clearTimeout(reloadTimeout);
|
|
143
201
|
reloadTimeout = undefined;
|
|
202
|
+
setWatchStatus("closed");
|
|
144
203
|
};
|
|
145
204
|
const notifyFailure = (ctx) => {
|
|
146
205
|
if (failureNotified)
|
|
147
206
|
return;
|
|
148
207
|
failureNotified = true;
|
|
208
|
+
setWatchStatus("failed");
|
|
149
209
|
ctx.ui.notify("Recipe live reload watcher failed; restart the session or use register_tool again to refresh recipe tools.", "warning");
|
|
150
210
|
};
|
|
151
211
|
const scheduleReload = (ctx) => {
|
|
@@ -153,7 +213,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
153
213
|
if (reloadTimeout)
|
|
154
214
|
clearTimeout(reloadTimeout);
|
|
155
215
|
reloadTimeout = setTimeout(() => {
|
|
156
|
-
runtime.loadTools(ctx);
|
|
216
|
+
runtime.loadTools(ctx, deps.getResolutionContext?.());
|
|
157
217
|
ctx.ui.notify("Recipe tools refreshed from ~/.pi/agent/recipes", "info");
|
|
158
218
|
}, 150);
|
|
159
219
|
reloadTimeout.unref?.();
|
|
@@ -183,6 +243,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
183
243
|
scheduleReload(ctx);
|
|
184
244
|
});
|
|
185
245
|
parentWatcher = watcher;
|
|
246
|
+
setWatchStatus("watching_parent");
|
|
186
247
|
watcher.on("error", () => {
|
|
187
248
|
if (parentWatcher !== watcher)
|
|
188
249
|
return;
|
|
@@ -216,6 +277,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
|
|
|
216
277
|
scheduleReload(ctx);
|
|
217
278
|
});
|
|
218
279
|
rootWatcher = watcher;
|
|
280
|
+
setWatchStatus("watching_root");
|
|
219
281
|
watcher.on("error", () => {
|
|
220
282
|
if (rootWatcher !== watcher)
|
|
221
283
|
return;
|
|
@@ -3,11 +3,13 @@
|
|
|
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 * as Runtime from "./runtime.ts";
|
|
6
7
|
export interface InspectToolDeps<TContext = unknown> {
|
|
7
8
|
getRunStatus?: (runOrDir: string) => Record<string, any>;
|
|
8
9
|
getTool?: (name: string) => any | undefined;
|
|
10
|
+
getToolStatus?: (name: string) => Record<string, unknown> | undefined;
|
|
9
11
|
listRuns?: () => Array<Record<string, any>>;
|
|
10
|
-
packagedRecipeRoot?: string;
|
|
11
12
|
recipeRoot?: string;
|
|
13
|
+
registryStatus?: () => Runtime.RecipeRegistryStatus;
|
|
12
14
|
}
|
|
13
15
|
export declare function createInspectToolDefinition<TContext = unknown>(deps?: InspectToolDeps<TContext>): any;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Owns exact inspect target/view dispatch; source projection stays in domain modules.
|
|
5
5
|
*/
|
|
6
6
|
import { statSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
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";
|
|
10
10
|
import * as Inspector from "./inspector.js";
|
|
@@ -157,7 +157,16 @@ function inspectRuntime(view, input, ctx, deps) {
|
|
|
157
157
|
return runtimeTriage(ctx, deps);
|
|
158
158
|
throw new Error("inspect runtime supports view=status, view=runs, or view=triage.");
|
|
159
159
|
}
|
|
160
|
-
function
|
|
160
|
+
function portableSkillRecipePath(file, skill, namespaces) {
|
|
161
|
+
const root = namespaces[skill]?.[0];
|
|
162
|
+
if (!root)
|
|
163
|
+
return `<active-skill:${skill}>`;
|
|
164
|
+
const relation = relative(root, file);
|
|
165
|
+
return !relation.startsWith("..") && !isAbsolute(relation)
|
|
166
|
+
? `<active-skill:${skill}>/${relation.replaceAll("\\", "/")}`
|
|
167
|
+
: `<active-skill:${skill}>`;
|
|
168
|
+
}
|
|
169
|
+
function inspectRecipes(view, deps, context) {
|
|
161
170
|
if (view !== "status" &&
|
|
162
171
|
view !== "summary" &&
|
|
163
172
|
view !== "doctor" &&
|
|
@@ -171,17 +180,54 @@ function inspectRecipes(view, deps) {
|
|
|
171
180
|
}
|
|
172
181
|
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
173
182
|
{ root: recipeRoot, defaultTool: true, mutableUsage: true },
|
|
174
|
-
{ root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
|
|
175
183
|
]);
|
|
176
|
-
const
|
|
184
|
+
const resolutionContext = context && typeof context === "object" && "recipeResolutionContext" in context
|
|
185
|
+
? context.recipeResolutionContext
|
|
186
|
+
: undefined;
|
|
187
|
+
const activeSkillContext = resolutionContext?.activeSkills ??
|
|
188
|
+
RecipesReferences.EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT;
|
|
189
|
+
const skillRecipeNamespaces = RecipesReferences.getActiveSkillRecipeNamespaces(activeSkillContext);
|
|
190
|
+
const skillInventory = RecipesReferences.inventoryActiveSkillRecipeComponents(activeSkillContext);
|
|
191
|
+
const skillRecipeComponents = skillInventory.components.map((component) => ({
|
|
192
|
+
identity: component.identity,
|
|
193
|
+
source_kind: "active_skill_component",
|
|
194
|
+
skill: component.skill,
|
|
195
|
+
stem: component.stem,
|
|
196
|
+
imports: component.imports,
|
|
197
|
+
}));
|
|
198
|
+
const skillRecipeComponentDiagnostics = skillInventory.rejected.map((diagnostic) => ({
|
|
199
|
+
error: diagnostic.reason,
|
|
200
|
+
...(diagnostic.file
|
|
201
|
+
? {
|
|
202
|
+
file: portableSkillRecipePath(diagnostic.file, diagnostic.skill, skillRecipeNamespaces),
|
|
203
|
+
}
|
|
204
|
+
: {}),
|
|
205
|
+
skill: diagnostic.skill,
|
|
206
|
+
...(diagnostic.stem ? { stem: diagnostic.stem } : {}),
|
|
207
|
+
}));
|
|
208
|
+
const discoverySummary = RecipesDiscovery.summarizeDiscovery(discovered);
|
|
177
209
|
const summary = {
|
|
178
|
-
...
|
|
179
|
-
|
|
210
|
+
...discoverySummary,
|
|
211
|
+
...(deps.registryStatus
|
|
212
|
+
? {
|
|
213
|
+
...deps.registryStatus(),
|
|
214
|
+
watched_root: "~/.pi/agent/recipes",
|
|
215
|
+
}
|
|
216
|
+
: {}),
|
|
217
|
+
active: Array.isArray(discoverySummary.active)
|
|
218
|
+
? discoverySummary.active.map((entry) => ({
|
|
219
|
+
...entry,
|
|
220
|
+
source_kind: "user_registry_capability",
|
|
221
|
+
}))
|
|
222
|
+
: discoverySummary.active,
|
|
223
|
+
skill_recipe_components: skillRecipeComponents,
|
|
224
|
+
skill_recipe_component_diagnostics: skillRecipeComponentDiagnostics,
|
|
225
|
+
skill_recipe_catalog_partial: skillInventory.partial,
|
|
226
|
+
active_skill_recipe_identities: Object.keys(skillRecipeNamespaces).sort(),
|
|
180
227
|
skill_recipe_namespace_diagnostics: Object.entries(skillRecipeNamespaces)
|
|
181
228
|
.filter(([, roots]) => roots.length > 1)
|
|
182
|
-
.map(([name
|
|
229
|
+
.map(([name]) => ({
|
|
183
230
|
name,
|
|
184
|
-
roots,
|
|
185
231
|
error: `Ambiguous active Skill Recipe namespace ${name}`,
|
|
186
232
|
})),
|
|
187
233
|
drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
|
|
@@ -280,6 +326,7 @@ function inspectTool(name, view, deps) {
|
|
|
280
326
|
throw new Error(`registered tool not found: ${name}`);
|
|
281
327
|
return {
|
|
282
328
|
name,
|
|
329
|
+
...(view === "status" ? deps.getToolStatus?.(name) : {}),
|
|
283
330
|
description: tool.description,
|
|
284
331
|
parameters: tool.parameters,
|
|
285
332
|
promptSnippet: tool.promptSnippet,
|
|
@@ -316,7 +363,7 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
316
363
|
details = inspectRuntime(view, input, ctx, deps);
|
|
317
364
|
}
|
|
318
365
|
else if (target === "recipes") {
|
|
319
|
-
details = inspectRecipes(view, deps);
|
|
366
|
+
details = inspectRecipes(view, deps, ctx);
|
|
320
367
|
}
|
|
321
368
|
else {
|
|
322
369
|
const run = parseRunTarget(target);
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
import * as ModelContext from "./model-context.ts";
|
|
7
7
|
import type { RegisteredTool } from "./config.ts";
|
|
8
8
|
import * as Execution from "./execution.ts";
|
|
9
|
+
import type * as RecipeResolution from "./recipes-context.ts";
|
|
9
10
|
export interface RuntimeToolContext extends ModelContext.CurrentModelContext {
|
|
11
|
+
recipeResolutionContext?: RecipeResolution.RecipeResolutionContext;
|
|
10
12
|
cwd: string;
|
|
11
13
|
sessionManager?: {
|
|
12
14
|
getSessionId?: () => string;
|