@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/lib/registry.ts
CHANGED
|
@@ -10,9 +10,15 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
10
10
|
import * as CommandTemplates from "./command-templates.ts";
|
|
11
11
|
import * as Config from "./config.ts";
|
|
12
12
|
import * as ExecutionOutput from "./execution-output.ts";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
withFileMutationLock,
|
|
15
|
+
writeJsonAtomic,
|
|
16
|
+
writeTextAtomic,
|
|
17
|
+
} from "./file-state.ts";
|
|
14
18
|
import * as Identity from "./identity.ts";
|
|
15
19
|
import * as Paths from "./paths.ts";
|
|
20
|
+
import * as RecipesContext from "./recipes-context.ts";
|
|
21
|
+
import * as RecipesDiscovery from "./recipes-discovery.ts";
|
|
16
22
|
import * as RecipesReferences from "./recipes-references.ts";
|
|
17
23
|
import * as Schema from "./schema.ts";
|
|
18
24
|
|
|
@@ -28,16 +34,24 @@ export interface RegisterToolInput {
|
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export interface RegisterToolResultDetails {
|
|
37
|
+
active_tool?: boolean;
|
|
38
|
+
activation?: "current_session" | "unverified";
|
|
31
39
|
args?: string[];
|
|
32
40
|
async?: boolean;
|
|
41
|
+
callable_now?: boolean;
|
|
33
42
|
config?: string;
|
|
34
43
|
defaults?: Record<string, string>;
|
|
35
44
|
draft?: string;
|
|
45
|
+
host_registered?: boolean;
|
|
46
|
+
persisted?: boolean;
|
|
36
47
|
promoted?: boolean;
|
|
48
|
+
registry_active?: boolean;
|
|
49
|
+
resolved?: boolean;
|
|
37
50
|
recipeName?: string;
|
|
38
51
|
template?: CommandTemplates.CommandTemplateValue;
|
|
39
52
|
templateWarnings?: string[];
|
|
40
53
|
tool: string;
|
|
54
|
+
validated?: boolean;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
export interface RegisterToolResult {
|
|
@@ -56,7 +70,14 @@ export interface RegisterToolRuntimeDeps<TContext> {
|
|
|
56
70
|
message: string,
|
|
57
71
|
type: "info" | "warning" | "error",
|
|
58
72
|
) => void;
|
|
59
|
-
registerRuntimeTool: (cfg: Config.RegisteredTool) =>
|
|
73
|
+
registerRuntimeTool: (cfg: Config.RegisteredTool) =>
|
|
74
|
+
| {
|
|
75
|
+
active_tool: boolean;
|
|
76
|
+
activation: "current_session" | "unverified";
|
|
77
|
+
callable_now: boolean;
|
|
78
|
+
host_registered: boolean;
|
|
79
|
+
}
|
|
80
|
+
| void;
|
|
60
81
|
reservedToolNames: Set<string>;
|
|
61
82
|
setActiveTools: (toolNames: string[]) => void;
|
|
62
83
|
}
|
|
@@ -139,8 +160,8 @@ function promoteDraftRecipe<TContext>(
|
|
|
139
160
|
),
|
|
140
161
|
);
|
|
141
162
|
}
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
163
|
+
const raw = RecipesReferences.readRawRecipeConfig(draftPath);
|
|
164
|
+
if (!raw) {
|
|
144
165
|
const reason = RecipesReferences.diagnoseRawRecipeConfigFailure(draftPath);
|
|
145
166
|
throw new Error(
|
|
146
167
|
ExecutionOutput.formatToolText(
|
|
@@ -148,20 +169,35 @@ function promoteDraftRecipe<TContext>(
|
|
|
148
169
|
),
|
|
149
170
|
);
|
|
150
171
|
}
|
|
151
|
-
const
|
|
152
|
-
name,
|
|
172
|
+
const authoredRecipe = buildAuthoredRecipe(
|
|
153
173
|
{
|
|
154
174
|
...input,
|
|
155
|
-
description:
|
|
156
|
-
|
|
175
|
+
description:
|
|
176
|
+
input.description ??
|
|
177
|
+
(typeof raw.description === "string" ? raw.description : undefined),
|
|
157
178
|
},
|
|
158
179
|
existing,
|
|
180
|
+
raw,
|
|
181
|
+
);
|
|
182
|
+
const resolutionContext = getRegistrationResolutionContext(ctx, deps);
|
|
183
|
+
const candidate = RecipesDiscovery.admitUserRecipe(
|
|
184
|
+
targetPath,
|
|
185
|
+
resolutionContext,
|
|
186
|
+
authoredRecipe,
|
|
159
187
|
);
|
|
160
|
-
|
|
188
|
+
if (!candidate.validated || !candidate.tool) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
ExecutionOutput.formatToolText(
|
|
191
|
+
`Draft recipe is invalid: ${candidate.diagnostics.join("; ")}`,
|
|
192
|
+
),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
161
195
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
162
|
-
writeJsonAtomic(targetPath,
|
|
163
|
-
promoted
|
|
164
|
-
|
|
196
|
+
writeJsonAtomic(targetPath, authoredRecipe);
|
|
197
|
+
const promoted = RecipesDiscovery.admitUserRecipe(
|
|
198
|
+
targetPath,
|
|
199
|
+
resolutionContext,
|
|
200
|
+
).tool!;
|
|
165
201
|
tools.set(name, promoted);
|
|
166
202
|
deps.registerRuntimeTool(promoted);
|
|
167
203
|
deps.notify(ctx, `Promoted draft recipe: ${name}`, "info");
|
|
@@ -185,18 +221,12 @@ function promoteDraftRecipe<TContext>(
|
|
|
185
221
|
|
|
186
222
|
function persistToolRecipe<TContext>(
|
|
187
223
|
deps: RegisterToolRuntimeDeps<TContext>,
|
|
188
|
-
|
|
224
|
+
name: string,
|
|
225
|
+
recipe: Record<string, unknown>,
|
|
189
226
|
): string {
|
|
190
|
-
const path = getToolRecipePath(deps,
|
|
227
|
+
const path = getToolRecipePath(deps, name);
|
|
191
228
|
mkdirSync(dirname(path), { recursive: true });
|
|
192
|
-
writeJsonAtomic(path,
|
|
193
|
-
description: cfg.description,
|
|
194
|
-
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
195
|
-
...(cfg.storedArgs ? { args: cfg.storedArgs } : {}),
|
|
196
|
-
...(cfg.storedDefaults ? { defaults: cfg.storedDefaults } : {}),
|
|
197
|
-
...(cfg.recipe?.values ? { values: cfg.recipe.values } : {}),
|
|
198
|
-
template: cfg.template,
|
|
199
|
-
});
|
|
229
|
+
writeJsonAtomic(path, recipe);
|
|
200
230
|
return path;
|
|
201
231
|
}
|
|
202
232
|
|
|
@@ -238,18 +268,27 @@ function deleteTool<TContext>(
|
|
|
238
268
|
function readAuthoritativeStoredTool<TContext>(
|
|
239
269
|
deps: RegisterToolRuntimeDeps<TContext>,
|
|
240
270
|
name: string,
|
|
241
|
-
): {
|
|
271
|
+
): {
|
|
272
|
+
bytes?: string;
|
|
273
|
+
exists: boolean;
|
|
274
|
+
recipe?: Record<string, unknown>;
|
|
275
|
+
tool?: Config.RegisteredTool;
|
|
276
|
+
} {
|
|
242
277
|
const path = getToolRecipePath(deps, name);
|
|
243
278
|
if (!existsSync(path)) return { exists: false };
|
|
244
279
|
try {
|
|
280
|
+
const bytes = readFileSync(path, "utf8");
|
|
281
|
+
const recipe = JSON.parse(bytes);
|
|
245
282
|
const normalized = Config.normalizeStoredTool(
|
|
246
283
|
name,
|
|
247
|
-
|
|
284
|
+
recipe,
|
|
248
285
|
deps.reservedToolNames,
|
|
249
286
|
);
|
|
250
|
-
if (!normalized.cfg) return { exists: true };
|
|
287
|
+
if (!normalized.cfg) return { bytes, exists: true, recipe };
|
|
251
288
|
return {
|
|
289
|
+
bytes,
|
|
252
290
|
exists: true,
|
|
291
|
+
recipe,
|
|
253
292
|
tool: { ...normalized.cfg, sourcePath: path },
|
|
254
293
|
};
|
|
255
294
|
} catch {
|
|
@@ -285,11 +324,31 @@ function getInputTemplate(
|
|
|
285
324
|
);
|
|
286
325
|
}
|
|
287
326
|
|
|
288
|
-
function
|
|
289
|
-
|
|
327
|
+
function getRegistrationResolutionContext<TContext>(
|
|
328
|
+
ctx: TContext,
|
|
329
|
+
deps: RegisterToolRuntimeDeps<TContext>,
|
|
330
|
+
): RecipesContext.RecipeResolutionContext {
|
|
331
|
+
if (
|
|
332
|
+
ctx &&
|
|
333
|
+
typeof ctx === "object" &&
|
|
334
|
+
"recipeResolutionContext" in ctx
|
|
335
|
+
) {
|
|
336
|
+
const resolutionContext = (ctx as {
|
|
337
|
+
recipeResolutionContext?: RecipesContext.RecipeResolutionContext;
|
|
338
|
+
}).recipeResolutionContext;
|
|
339
|
+
if (resolutionContext) return resolutionContext;
|
|
340
|
+
}
|
|
341
|
+
return RecipesContext.createEmptyRecipeResolutionContext(
|
|
342
|
+
"offline-register-tool",
|
|
343
|
+
getRecipeRoot(deps),
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function buildAuthoredRecipe(
|
|
290
348
|
input: RegisterToolInput,
|
|
291
349
|
existing: Config.RegisteredTool | undefined,
|
|
292
|
-
|
|
350
|
+
existingRecipe: Record<string, unknown> | undefined,
|
|
351
|
+
): Record<string, unknown> {
|
|
293
352
|
const explicitArgs =
|
|
294
353
|
input.args === undefined
|
|
295
354
|
? undefined
|
|
@@ -311,68 +370,29 @@ function buildConfig(
|
|
|
311
370
|
);
|
|
312
371
|
}
|
|
313
372
|
const finalTemplate =
|
|
314
|
-
template === undefined || template === ""
|
|
373
|
+
template === undefined || template === ""
|
|
374
|
+
? existingRecipe?.template ?? existing?.template
|
|
375
|
+
: template;
|
|
315
376
|
if (!finalTemplate) {
|
|
316
377
|
throw new Error(
|
|
317
378
|
ExecutionOutput.formatToolText("Tool template is required."),
|
|
318
379
|
);
|
|
319
380
|
}
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
? {
|
|
323
|
-
name: inputRecipe,
|
|
324
|
-
...(typeof input.async === "boolean" ? { async: input.async } : {}),
|
|
325
|
-
template: finalTemplate,
|
|
326
|
-
...(input.values && typeof input.values === "object"
|
|
327
|
-
? { values: input.values }
|
|
328
|
-
: {}),
|
|
329
|
-
}
|
|
330
|
-
: template === undefined
|
|
331
|
-
? existing?.recipe
|
|
332
|
-
: undefined;
|
|
333
|
-
const defaults = explicitArgs?.defaults ?? existing?.storedDefaults ?? {};
|
|
334
|
-
const storedArgs = explicitArgs
|
|
335
|
-
? explicitArgs.declarations
|
|
336
|
-
: existing?.storedArgs;
|
|
337
|
-
const storedDefaults =
|
|
338
|
-
Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
339
|
-
const recipeTemplate = RecipesReferences.getRecipeTemplate(finalTemplate);
|
|
340
|
-
const argTemplate = recipeTemplate ?? finalTemplate;
|
|
341
|
-
const argTemplateConfig: CommandTemplates.CommandTemplateConfig =
|
|
342
|
-
typeof argTemplate === "object" && !Array.isArray(argTemplate)
|
|
343
|
-
? {
|
|
344
|
-
...argTemplate,
|
|
345
|
-
...(storedArgs !== undefined ? { args: storedArgs } : {}),
|
|
346
|
-
defaults: { ...(argTemplate.defaults ?? {}), ...defaults },
|
|
347
|
-
}
|
|
348
|
-
: {
|
|
349
|
-
args: storedArgs,
|
|
350
|
-
defaults,
|
|
351
|
-
template: argTemplate,
|
|
352
|
-
};
|
|
353
|
-
const inferredArgTypes = Schema.getTemplateArgTypes(argTemplateConfig);
|
|
354
|
-
const argTypes = {
|
|
355
|
-
...inferredArgTypes,
|
|
356
|
-
...(existing?.argTypes ?? {}),
|
|
357
|
-
...(explicitArgs?.argTypes ?? {}),
|
|
358
|
-
};
|
|
359
|
-
return {
|
|
360
|
-
name,
|
|
381
|
+
const authored: Record<string, unknown> = {
|
|
382
|
+
...(existingRecipe ?? {}),
|
|
361
383
|
description,
|
|
362
384
|
template: finalTemplate,
|
|
363
|
-
...(recipe ? { recipe } : {}),
|
|
364
|
-
args:
|
|
365
|
-
RecipesReferences.isRecipeTool(finalTemplate, recipe) &&
|
|
366
|
-
storedArgs !== undefined
|
|
367
|
-
? Schema.getExplicitToolArgNames(storedArgs)
|
|
368
|
-
: RecipesReferences.isRecipeReference(finalTemplate) && !recipeTemplate
|
|
369
|
-
? Schema.getExplicitToolArgNames(storedArgs)
|
|
370
|
-
: Schema.getToolArgNames(argTemplateConfig),
|
|
371
|
-
defaults,
|
|
372
|
-
...(Object.keys(argTypes).length > 0 ? { argTypes } : {}),
|
|
373
|
-
...(storedArgs !== undefined ? { storedArgs } : {}),
|
|
374
|
-
...(storedDefaults !== undefined ? { storedDefaults } : {}),
|
|
375
385
|
};
|
|
386
|
+
if (typeof input.async === "boolean") authored.async = input.async;
|
|
387
|
+
if (explicitArgs) {
|
|
388
|
+
authored.args = explicitArgs.declarations;
|
|
389
|
+
if (Object.keys(explicitArgs.defaults).length > 0)
|
|
390
|
+
authored.defaults = explicitArgs.defaults;
|
|
391
|
+
else delete authored.defaults;
|
|
392
|
+
}
|
|
393
|
+
if (input.values && typeof input.values === "object")
|
|
394
|
+
authored.values = input.values;
|
|
395
|
+
return authored;
|
|
376
396
|
}
|
|
377
397
|
|
|
378
398
|
function executeRegisterToolUnlocked<TContext>(
|
|
@@ -416,47 +436,118 @@ function executeRegisterToolUnlocked<TContext>(
|
|
|
416
436
|
),
|
|
417
437
|
);
|
|
418
438
|
}
|
|
419
|
-
const
|
|
420
|
-
|
|
439
|
+
const recipePath = getToolRecipePath(deps, name);
|
|
440
|
+
const authoredRecipe = buildAuthoredRecipe(
|
|
441
|
+
input,
|
|
442
|
+
existing,
|
|
443
|
+
authoritative.recipe,
|
|
444
|
+
);
|
|
445
|
+
const resolutionContext = getRegistrationResolutionContext(ctx, deps);
|
|
446
|
+
const candidate = RecipesDiscovery.admitUserRecipe(
|
|
447
|
+
recipePath,
|
|
448
|
+
resolutionContext,
|
|
449
|
+
authoredRecipe,
|
|
450
|
+
);
|
|
451
|
+
if (!candidate.validated || !candidate.tool) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
ExecutionOutput.formatToolText(
|
|
454
|
+
`Tool recipe validation failed: ${candidate.diagnostics.join("; ")}`,
|
|
455
|
+
),
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
const activeBefore = deps.getActiveTools();
|
|
459
|
+
let persisted = false;
|
|
460
|
+
let activation:
|
|
461
|
+
| {
|
|
462
|
+
active_tool: boolean;
|
|
463
|
+
activation: "current_session" | "unverified";
|
|
464
|
+
callable_now: boolean;
|
|
465
|
+
host_registered: boolean;
|
|
466
|
+
}
|
|
467
|
+
| undefined;
|
|
468
|
+
let cfg: Config.RegisteredTool;
|
|
421
469
|
try {
|
|
422
|
-
|
|
470
|
+
persistToolRecipe(deps, name, authoredRecipe);
|
|
471
|
+
persisted = true;
|
|
472
|
+
const admitted = RecipesDiscovery.admitUserRecipe(
|
|
473
|
+
recipePath,
|
|
474
|
+
resolutionContext,
|
|
475
|
+
);
|
|
476
|
+
if (!admitted.validated || !admitted.tool) {
|
|
477
|
+
throw new Error(
|
|
478
|
+
`Persisted tool recipe admission failed: ${admitted.diagnostics.join("; ")}`,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
cfg = admitted.tool;
|
|
482
|
+
tools.set(name, cfg);
|
|
483
|
+
activation = deps.registerRuntimeTool(cfg) ?? undefined;
|
|
484
|
+
if (
|
|
485
|
+
activation &&
|
|
486
|
+
(!activation.host_registered ||
|
|
487
|
+
!activation.active_tool ||
|
|
488
|
+
!activation.callable_now)
|
|
489
|
+
) {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`Host activation verification failed: ${JSON.stringify(activation)}`,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
423
494
|
} catch (error) {
|
|
495
|
+
if (persisted) {
|
|
496
|
+
if (authoritative.bytes !== undefined)
|
|
497
|
+
writeTextAtomic(recipePath, authoritative.bytes);
|
|
498
|
+
else if (existsSync(recipePath)) unlinkSync(recipePath);
|
|
499
|
+
}
|
|
500
|
+
if (existing) {
|
|
501
|
+
tools.set(name, existing);
|
|
502
|
+
deps.registerRuntimeTool(existing);
|
|
503
|
+
} else {
|
|
504
|
+
tools.delete(name);
|
|
505
|
+
}
|
|
506
|
+
deps.setActiveTools(activeBefore);
|
|
424
507
|
throw new Error(
|
|
425
508
|
ExecutionOutput.formatToolText(
|
|
426
|
-
`
|
|
509
|
+
`Tool registration transaction failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
427
510
|
),
|
|
428
511
|
);
|
|
429
512
|
}
|
|
430
|
-
|
|
431
|
-
tools.set(name, cfg);
|
|
432
|
-
deps.registerRuntimeTool(cfg);
|
|
433
|
-
deps.notify(ctx, `Tool persisted: ${name}`, "info");
|
|
513
|
+
deps.notify(ctx, `Tool activated: ${name}`, "info");
|
|
434
514
|
const templateWarnings = CommandTemplates.getCommandTemplateWarnings(
|
|
435
|
-
typeof cfg.template === "object" && !Array.isArray(cfg.template)
|
|
436
|
-
? cfg.template
|
|
437
|
-
: { template: cfg.template! },
|
|
515
|
+
typeof cfg.recipe?.template === "object" && !Array.isArray(cfg.recipe.template)
|
|
516
|
+
? cfg.recipe.template
|
|
517
|
+
: { template: cfg.recipe?.template ?? cfg.template! },
|
|
438
518
|
);
|
|
439
519
|
const warningText =
|
|
440
520
|
templateWarnings.length > 0
|
|
441
521
|
? `\nWarnings:\n${templateWarnings.map((warning) => `- ${warning}`).join("\n")}`
|
|
442
522
|
: "";
|
|
523
|
+
const outcomeText = activation?.callable_now
|
|
524
|
+
? `${existing ? "Updated" : "Registered"} and activated tool "${name}"`
|
|
525
|
+
: `Persisted tool "${name}"; current-session activation is unverified`;
|
|
443
526
|
return {
|
|
444
527
|
content: [
|
|
445
528
|
textContent(
|
|
446
529
|
ExecutionOutput.formatToolText(
|
|
447
|
-
`${
|
|
530
|
+
`${outcomeText} (args: ${Schema.formatToolArgs(cfg.args)}).${warningText}`,
|
|
448
531
|
),
|
|
449
532
|
),
|
|
450
533
|
],
|
|
451
534
|
details: {
|
|
535
|
+
active_tool: activation?.active_tool ?? false,
|
|
536
|
+
activation: activation?.activation ?? "unverified",
|
|
452
537
|
args: cfg.args,
|
|
538
|
+
callable_now: activation?.callable_now ?? false,
|
|
453
539
|
config: recipePath,
|
|
454
540
|
defaults: cfg.defaults,
|
|
541
|
+
host_registered: activation?.host_registered ?? false,
|
|
542
|
+
persisted: true,
|
|
543
|
+
registry_active: tools.get(name) === cfg,
|
|
544
|
+
resolved: true,
|
|
455
545
|
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
456
546
|
...(cfg.recipe?.name ? { recipeName: cfg.recipe.name } : {}),
|
|
457
547
|
...(cfg.template ? { template: cfg.template } : {}),
|
|
458
548
|
...(templateWarnings.length > 0 ? { templateWarnings } : {}),
|
|
459
549
|
tool: name,
|
|
550
|
+
validated: true,
|
|
460
551
|
},
|
|
461
552
|
};
|
|
462
553
|
}
|
package/lib/runtime.ts
CHANGED
|
@@ -10,7 +10,9 @@ import { basename, dirname } from "node:path";
|
|
|
10
10
|
import * as Config from "./config.ts";
|
|
11
11
|
import type { RegisteredToolExec } from "./execution.ts";
|
|
12
12
|
import * as Paths from "./paths.ts";
|
|
13
|
+
import type { RecipeResolutionContext } from "./recipes-context.ts";
|
|
13
14
|
import * as RecipesDiscovery from "./recipes-discovery.ts";
|
|
15
|
+
import * as RecipesUsage from "./recipes-usage.ts";
|
|
14
16
|
import * as ToolsLocal from "./tools-local.ts";
|
|
15
17
|
|
|
16
18
|
export interface RuntimeContext {
|
|
@@ -20,12 +22,19 @@ export interface RuntimeContext {
|
|
|
20
22
|
};
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
export interface RuntimeToolActivation {
|
|
26
|
+
active_tool: boolean;
|
|
27
|
+
activation: "current_session" | "unverified";
|
|
28
|
+
callable_now: boolean;
|
|
29
|
+
host_registered: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
export interface ToolRegistryRuntimeDeps {
|
|
24
33
|
configPath: string;
|
|
25
34
|
exec: RegisteredToolExec;
|
|
26
|
-
packagedRecipeRoot?: string;
|
|
27
35
|
recipeRoot?: string;
|
|
28
36
|
getActiveTools?: () => string[];
|
|
37
|
+
getAllTools?: () => Array<{ name: string }>;
|
|
29
38
|
registerTool: (
|
|
30
39
|
definition: ReturnType<typeof ToolsLocal.createRuntimeToolDefinition>,
|
|
31
40
|
) => void;
|
|
@@ -33,16 +42,32 @@ export interface ToolRegistryRuntimeDeps {
|
|
|
33
42
|
setActiveTools?: (toolNames: string[]) => void;
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
export interface RecipeRegistryStatus {
|
|
46
|
+
active_tool_count: number;
|
|
47
|
+
last_scan_counts: { active: number; rejected: number; scanned: number };
|
|
48
|
+
last_scan_at?: string;
|
|
49
|
+
registry_generation: number;
|
|
50
|
+
resolution_generation?: string;
|
|
51
|
+
watch_status: "closed" | "failed" | "watching_parent" | "watching_root";
|
|
52
|
+
watched_root: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
export interface ToolRegistryRuntime {
|
|
56
|
+
getStatus(): RecipeRegistryStatus;
|
|
57
|
+
getToolStatus(name: string): Record<string, unknown> | undefined;
|
|
37
58
|
getToolNameBlocker(name: string): string | undefined;
|
|
38
59
|
getTools(): Map<string, Config.RegisteredTool>;
|
|
39
|
-
loadTools(
|
|
60
|
+
loadTools(
|
|
61
|
+
ctx: RuntimeContext,
|
|
62
|
+
resolutionContext?: RecipeResolutionContext,
|
|
63
|
+
): void;
|
|
40
64
|
notify(
|
|
41
65
|
ctx: RuntimeContext,
|
|
42
66
|
message: string,
|
|
43
67
|
type: "info" | "warning" | "error",
|
|
44
68
|
): void;
|
|
45
|
-
registerRuntimeTool(cfg: Config.RegisteredTool):
|
|
69
|
+
registerRuntimeTool(cfg: Config.RegisteredTool): RuntimeToolActivation;
|
|
70
|
+
setWatchStatus(status: RecipeRegistryStatus["watch_status"]): void;
|
|
46
71
|
}
|
|
47
72
|
|
|
48
73
|
export interface RecipeToolReloadWatcher {
|
|
@@ -56,6 +81,14 @@ export function createAutoToolsRuntime(
|
|
|
56
81
|
const tools = new Map<string, Config.RegisteredTool>();
|
|
57
82
|
const runtimeToolFingerprints = new Map<string, string>();
|
|
58
83
|
const runtimeTools = new Set<string>();
|
|
84
|
+
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
85
|
+
const status: RecipeRegistryStatus = {
|
|
86
|
+
active_tool_count: 0,
|
|
87
|
+
last_scan_counts: { active: 0, rejected: 0, scanned: 0 },
|
|
88
|
+
registry_generation: 0,
|
|
89
|
+
watch_status: "closed",
|
|
90
|
+
watched_root: recipeRoot,
|
|
91
|
+
};
|
|
59
92
|
function notify(
|
|
60
93
|
ctx: RuntimeContext,
|
|
61
94
|
message: string,
|
|
@@ -91,12 +124,30 @@ export function createAutoToolsRuntime(
|
|
|
91
124
|
deps.getActiveTools().filter((name) => !staleSet.has(name)),
|
|
92
125
|
);
|
|
93
126
|
}
|
|
94
|
-
function
|
|
127
|
+
function activationFor(name: string): RuntimeToolActivation {
|
|
128
|
+
const hostRegistered = deps.getAllTools?.().some((tool) => tool.name === name) ?? false;
|
|
129
|
+
const activeTool = deps.getActiveTools?.().includes(name) ?? false;
|
|
130
|
+
return {
|
|
131
|
+
active_tool: activeTool,
|
|
132
|
+
activation:
|
|
133
|
+
hostRegistered && activeTool ? "current_session" : "unverified",
|
|
134
|
+
callable_now: hostRegistered && activeTool,
|
|
135
|
+
host_registered: hostRegistered,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function registerRuntimeTool(cfg: Config.RegisteredTool): RuntimeToolActivation {
|
|
95
139
|
const fingerprint = getToolFingerprint(cfg);
|
|
96
|
-
if (runtimeToolFingerprints.get(cfg.name)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
140
|
+
if (runtimeToolFingerprints.get(cfg.name) !== fingerprint) {
|
|
141
|
+
deps.registerTool(ToolsLocal.createRuntimeToolDefinition(cfg, deps.exec));
|
|
142
|
+
runtimeTools.add(cfg.name);
|
|
143
|
+
runtimeToolFingerprints.set(cfg.name, fingerprint);
|
|
144
|
+
}
|
|
145
|
+
if (deps.getActiveTools && deps.setActiveTools) {
|
|
146
|
+
deps.setActiveTools([
|
|
147
|
+
...new Set([...deps.getActiveTools(), cfg.name]),
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
return activationFor(cfg.name);
|
|
100
151
|
}
|
|
101
152
|
function isStartupActionableRegistryWarning(warning: string): boolean {
|
|
102
153
|
if (warning.includes(" shadows ")) return false;
|
|
@@ -125,7 +176,7 @@ export function createAutoToolsRuntime(
|
|
|
125
176
|
);
|
|
126
177
|
const lines = ["pi-actors recipe registry warning"];
|
|
127
178
|
if (shadowed.length > 0) {
|
|
128
|
-
lines.push("User recipes
|
|
179
|
+
lines.push("User recipes shadow lower-priority user sources:");
|
|
129
180
|
lines.push(...shadowed.map((warning) => `• ${warning}`));
|
|
130
181
|
}
|
|
131
182
|
if (skipped.length > 0) {
|
|
@@ -138,14 +189,18 @@ export function createAutoToolsRuntime(
|
|
|
138
189
|
}
|
|
139
190
|
return `${lines.join("\n")}\n`;
|
|
140
191
|
}
|
|
141
|
-
function loadTools(
|
|
192
|
+
function loadTools(
|
|
193
|
+
ctx: RuntimeContext,
|
|
194
|
+
resolutionContext?: RecipeResolutionContext,
|
|
195
|
+
) {
|
|
142
196
|
const warnings: string[] = [];
|
|
143
|
-
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
144
|
-
const packagedRecipeRoot =
|
|
145
|
-
deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot();
|
|
146
197
|
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
147
|
-
{
|
|
148
|
-
|
|
198
|
+
{
|
|
199
|
+
root: recipeRoot,
|
|
200
|
+
defaultTool: true,
|
|
201
|
+
mutableUsage: true,
|
|
202
|
+
resolutionContext,
|
|
203
|
+
},
|
|
149
204
|
]);
|
|
150
205
|
warnings.push(...discovered.diagnostics);
|
|
151
206
|
tools.clear();
|
|
@@ -168,28 +223,59 @@ export function createAutoToolsRuntime(
|
|
|
168
223
|
}
|
|
169
224
|
registerRuntimeTool(cfg);
|
|
170
225
|
}
|
|
226
|
+
status.active_tool_count = tools.size;
|
|
227
|
+
status.last_scan_at = new Date().toISOString();
|
|
228
|
+
status.last_scan_counts = {
|
|
229
|
+
active: tools.size,
|
|
230
|
+
rejected: discovered.entries.filter((entry) => entry.invalid).length,
|
|
231
|
+
scanned: discovered.entries.length,
|
|
232
|
+
};
|
|
233
|
+
status.registry_generation += 1;
|
|
234
|
+
status.resolution_generation = resolutionContext?.generation;
|
|
171
235
|
const startupWarnings = warnings.filter(isStartupActionableRegistryWarning);
|
|
172
236
|
if (startupWarnings.length > 0) {
|
|
173
237
|
notify(ctx, formatRecipeToolWarnings(startupWarnings), "warning");
|
|
174
238
|
}
|
|
175
239
|
}
|
|
176
240
|
return {
|
|
241
|
+
getStatus: () => ({
|
|
242
|
+
...status,
|
|
243
|
+
last_scan_counts: { ...status.last_scan_counts },
|
|
244
|
+
}),
|
|
245
|
+
getToolStatus: (name) => {
|
|
246
|
+
const cfg = tools.get(name);
|
|
247
|
+
if (!cfg) return undefined;
|
|
248
|
+
const usage = cfg.sourcePath
|
|
249
|
+
? RecipesUsage.readRecipeUsage(cfg.sourcePath)
|
|
250
|
+
: undefined;
|
|
251
|
+
return {
|
|
252
|
+
...activationFor(name),
|
|
253
|
+
launch_kind: usage?.launch_kind,
|
|
254
|
+
spawn_calls: Number(usage?.spawn_calls ?? 0),
|
|
255
|
+
tool_calls: Number(usage?.tool_calls ?? 0),
|
|
256
|
+
};
|
|
257
|
+
},
|
|
177
258
|
getToolNameBlocker,
|
|
178
259
|
getTools: () => tools,
|
|
179
260
|
loadTools,
|
|
180
261
|
notify,
|
|
181
262
|
registerRuntimeTool,
|
|
263
|
+
setWatchStatus: (watchStatus) => {
|
|
264
|
+
status.watch_status = watchStatus;
|
|
265
|
+
},
|
|
182
266
|
};
|
|
183
267
|
}
|
|
184
268
|
|
|
185
269
|
export interface RecipeToolReloadWatcherDeps {
|
|
186
270
|
exists?: (path: string) => boolean;
|
|
271
|
+
getResolutionContext?: () => RecipeResolutionContext | undefined;
|
|
187
272
|
recipeRoot?: string;
|
|
188
273
|
watchPath?: typeof watch;
|
|
189
274
|
}
|
|
190
275
|
|
|
191
276
|
export function createRecipeToolReloadWatcher(
|
|
192
|
-
runtime: Pick<ToolRegistryRuntime, "loadTools"
|
|
277
|
+
runtime: Pick<ToolRegistryRuntime, "loadTools"> &
|
|
278
|
+
Partial<Pick<ToolRegistryRuntime, "setWatchStatus">>,
|
|
193
279
|
deps: RecipeToolReloadWatcherDeps = {},
|
|
194
280
|
): RecipeToolReloadWatcher {
|
|
195
281
|
const pathExists = deps.exists ?? existsSync;
|
|
@@ -198,6 +284,8 @@ export function createRecipeToolReloadWatcher(
|
|
|
198
284
|
let rootWatcher: FSWatcher | undefined;
|
|
199
285
|
let parentWatcher: FSWatcher | undefined;
|
|
200
286
|
let failureNotified = false;
|
|
287
|
+
const setWatchStatus = (watchStatus: RecipeRegistryStatus["watch_status"]): void =>
|
|
288
|
+
runtime.setWatchStatus?.(watchStatus);
|
|
201
289
|
const close = (): void => {
|
|
202
290
|
const closingRoot = rootWatcher;
|
|
203
291
|
rootWatcher = undefined;
|
|
@@ -207,10 +295,12 @@ export function createRecipeToolReloadWatcher(
|
|
|
207
295
|
closingParent?.close();
|
|
208
296
|
if (reloadTimeout) clearTimeout(reloadTimeout);
|
|
209
297
|
reloadTimeout = undefined;
|
|
298
|
+
setWatchStatus("closed");
|
|
210
299
|
};
|
|
211
300
|
const notifyFailure = (ctx: RuntimeContext): void => {
|
|
212
301
|
if (failureNotified) return;
|
|
213
302
|
failureNotified = true;
|
|
303
|
+
setWatchStatus("failed");
|
|
214
304
|
ctx.ui.notify(
|
|
215
305
|
"Recipe live reload watcher failed; restart the session or use register_tool again to refresh recipe tools.",
|
|
216
306
|
"warning",
|
|
@@ -220,7 +310,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
220
310
|
failureNotified = false;
|
|
221
311
|
if (reloadTimeout) clearTimeout(reloadTimeout);
|
|
222
312
|
reloadTimeout = setTimeout(() => {
|
|
223
|
-
runtime.loadTools(ctx);
|
|
313
|
+
runtime.loadTools(ctx, deps.getResolutionContext?.());
|
|
224
314
|
ctx.ui.notify("Recipe tools refreshed from ~/.pi/agent/recipes", "info");
|
|
225
315
|
}, 150);
|
|
226
316
|
reloadTimeout.unref?.();
|
|
@@ -249,6 +339,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
249
339
|
scheduleReload(ctx);
|
|
250
340
|
});
|
|
251
341
|
parentWatcher = watcher;
|
|
342
|
+
setWatchStatus("watching_parent");
|
|
252
343
|
watcher.on("error", () => {
|
|
253
344
|
if (parentWatcher !== watcher) return;
|
|
254
345
|
parentWatcher = undefined;
|
|
@@ -278,6 +369,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
278
369
|
scheduleReload(ctx);
|
|
279
370
|
});
|
|
280
371
|
rootWatcher = watcher;
|
|
372
|
+
setWatchStatus("watching_root");
|
|
281
373
|
watcher.on("error", () => {
|
|
282
374
|
if (rootWatcher !== watcher) return;
|
|
283
375
|
rootWatcher = undefined;
|