@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
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* Owns detection, loading, and recipe-layer expansion for template recipe files
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
8
|
-
import {
|
|
9
|
-
import { basename, dirname, extname, isAbsolute, relative, resolve } from "node:path";
|
|
7
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
8
|
+
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
10
9
|
|
|
11
10
|
import type {
|
|
12
11
|
CommandTemplateConfig,
|
|
@@ -29,7 +28,6 @@ export interface TemplateRecipeImportBinding {
|
|
|
29
28
|
export type TemplateRecipeImport = string | TemplateRecipeImportBinding;
|
|
30
29
|
|
|
31
30
|
export interface TemplateRecipeDefinition {
|
|
32
|
-
name?: string;
|
|
33
31
|
description?: string;
|
|
34
32
|
disabled?: boolean;
|
|
35
33
|
imports?: Record<string, TemplateRecipeImport>;
|
|
@@ -57,6 +55,7 @@ export interface TemplateRecipeDefinition {
|
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
export interface TemplateRecipeConfig extends TemplateRecipeDefinition {
|
|
58
|
+
name?: string;
|
|
60
59
|
async?: boolean;
|
|
61
60
|
recipe_dir?: string;
|
|
62
61
|
skill_dir?: string;
|
|
@@ -66,7 +65,7 @@ interface ImportedRecipe {
|
|
|
66
65
|
alias: string;
|
|
67
66
|
file: string;
|
|
68
67
|
name: string;
|
|
69
|
-
config:
|
|
68
|
+
config: TemplateRecipeConfig;
|
|
70
69
|
defaults: Record<string, unknown>;
|
|
71
70
|
values: Record<string, unknown>;
|
|
72
71
|
}
|
|
@@ -74,20 +73,48 @@ interface ImportedRecipe {
|
|
|
74
73
|
export interface TemplateRecipeContextRecord {
|
|
75
74
|
alias?: string;
|
|
76
75
|
depth: number;
|
|
77
|
-
file: string;
|
|
78
76
|
import_path: string[];
|
|
77
|
+
logical_reference: string;
|
|
79
78
|
name: string;
|
|
80
|
-
qualified_name?: string;
|
|
81
79
|
recipe: Record<string, unknown>;
|
|
82
80
|
role: "entry" | "import";
|
|
81
|
+
skill?: string;
|
|
82
|
+
source_file: string;
|
|
83
|
+
source_kind:
|
|
84
|
+
| "active_skill_component"
|
|
85
|
+
| "explicit_file_recipe"
|
|
86
|
+
| "user_registry_capability";
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
export interface ReadResolvedRecipeConfigOptions {
|
|
90
|
+
authoredEntry?: { path: string; recipe: Record<string, unknown> };
|
|
86
91
|
includeActorRecipeContext?: boolean;
|
|
92
|
+
skillContext?: ActiveSkillRecipeContext;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
const
|
|
90
|
-
|
|
95
|
+
const RESERVED_AUTHORED_RECIPE_VALUE_NAMES = new Set([
|
|
96
|
+
"recipe_dir",
|
|
97
|
+
"skill_dir",
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
export const RUNTIME_OWNED_RECIPE_INPUT_NAMES = Object.freeze([
|
|
101
|
+
"owner_id",
|
|
102
|
+
"recipe_dir",
|
|
103
|
+
"run_instance_id",
|
|
104
|
+
"runtime_state_root",
|
|
105
|
+
"session_id",
|
|
106
|
+
"skill_dir",
|
|
107
|
+
"state_dir",
|
|
108
|
+
"trace_file",
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
const RUNTIME_OWNED_RECIPE_INPUT_SET = new Set(
|
|
112
|
+
RUNTIME_OWNED_RECIPE_INPUT_NAMES,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
export function isRuntimeOwnedRecipeInput(name: string): boolean {
|
|
116
|
+
return RUNTIME_OWNED_RECIPE_INPUT_SET.has(name);
|
|
117
|
+
}
|
|
91
118
|
|
|
92
119
|
export interface ActiveSkillRecipeSource {
|
|
93
120
|
name: string;
|
|
@@ -95,10 +122,14 @@ export interface ActiveSkillRecipeSource {
|
|
|
95
122
|
baseDir?: string;
|
|
96
123
|
}
|
|
97
124
|
|
|
98
|
-
export
|
|
125
|
+
export interface ActiveSkillRecipeContext {
|
|
126
|
+
readonly namespaces: Readonly<Record<string, readonly string[]>>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createActiveSkillRecipeContext(
|
|
99
130
|
skills: ActiveSkillRecipeSource[],
|
|
100
|
-
):
|
|
101
|
-
|
|
131
|
+
): ActiveSkillRecipeContext {
|
|
132
|
+
const rootsByName = new Map<string, Set<string>>();
|
|
102
133
|
for (const skill of skills) {
|
|
103
134
|
const name = skill.name.trim();
|
|
104
135
|
const skillDir = skill.baseDir
|
|
@@ -107,72 +138,236 @@ export function setActiveSkillRecipeSources(
|
|
|
107
138
|
? dirname(resolve(skill.filePath))
|
|
108
139
|
: undefined;
|
|
109
140
|
if (!name || !skillDir) continue;
|
|
110
|
-
const roots =
|
|
141
|
+
const roots = rootsByName.get(name) ?? new Set<string>();
|
|
111
142
|
roots.add(resolve(skillDir, "recipes"));
|
|
112
|
-
|
|
143
|
+
rootsByName.set(name, roots);
|
|
113
144
|
}
|
|
145
|
+
const namespaces = Object.fromEntries(
|
|
146
|
+
[...rootsByName.entries()]
|
|
147
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
148
|
+
.map(([name, roots]) => [name, Object.freeze([...roots].sort())]),
|
|
149
|
+
);
|
|
150
|
+
return Object.freeze({ namespaces: Object.freeze(namespaces) });
|
|
114
151
|
}
|
|
115
152
|
|
|
116
|
-
export
|
|
153
|
+
export const EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT =
|
|
154
|
+
createActiveSkillRecipeContext([]);
|
|
155
|
+
|
|
156
|
+
export function getActiveSkillRecipeNamespaces(
|
|
157
|
+
context: ActiveSkillRecipeContext,
|
|
158
|
+
): Record<string, string[]> {
|
|
117
159
|
return Object.fromEntries(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
160
|
+
Object.entries(context.namespaces).map(([name, roots]) => [
|
|
161
|
+
name,
|
|
162
|
+
[...roots],
|
|
163
|
+
]),
|
|
121
164
|
);
|
|
122
165
|
}
|
|
123
166
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
167
|
+
export interface ActiveSkillRecipeComponent {
|
|
168
|
+
file: string;
|
|
169
|
+
identity: string;
|
|
170
|
+
imports: Record<string, string>;
|
|
171
|
+
skill: string;
|
|
172
|
+
stem: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface SkillRecipeComponentDiagnostic {
|
|
176
|
+
file?: string;
|
|
177
|
+
reason: string;
|
|
178
|
+
skill: string;
|
|
179
|
+
stem?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface SkillRecipeComponentInventory {
|
|
183
|
+
components: ActiveSkillRecipeComponent[];
|
|
184
|
+
partial: boolean;
|
|
185
|
+
rejected: SkillRecipeComponentDiagnostic[];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function nestedRecipeFiles(root: string): string[] {
|
|
189
|
+
if (!existsSync(root)) return [];
|
|
190
|
+
return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
|
|
191
|
+
const path = join(root, entry.name);
|
|
192
|
+
if (entry.isDirectory()) return nestedRecipeFiles(path);
|
|
193
|
+
return entry.isFile() && /\.(?:json|md)$/u.test(entry.name) ? [path] : [];
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function inventoryActiveSkillRecipeComponents(
|
|
198
|
+
context: ActiveSkillRecipeContext,
|
|
199
|
+
): SkillRecipeComponentInventory {
|
|
200
|
+
const components: ActiveSkillRecipeComponent[] = [];
|
|
201
|
+
const rejected: SkillRecipeComponentDiagnostic[] = [];
|
|
202
|
+
for (const [skill, roots] of Object.entries(context.namespaces)) {
|
|
203
|
+
if (roots.length > 1) {
|
|
204
|
+
rejected.push({
|
|
205
|
+
reason: `Duplicate active Skill identity ${skill}: ${roots.join(", ")}`,
|
|
206
|
+
skill,
|
|
207
|
+
});
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const root = roots[0];
|
|
211
|
+
if (!root || !existsSync(root)) continue;
|
|
212
|
+
const direct = readdirSync(root, { withFileTypes: true });
|
|
213
|
+
for (const entry of direct.filter((candidate) => candidate.isDirectory())) {
|
|
214
|
+
const nested = nestedRecipeFiles(join(root, entry.name));
|
|
215
|
+
for (const file of nested) {
|
|
216
|
+
rejected.push({
|
|
217
|
+
file,
|
|
218
|
+
reason: `Nested Skill Recipe file is not addressable: ${file}`,
|
|
219
|
+
skill,
|
|
220
|
+
stem: getRecipeIdFromPath(file),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const files = direct
|
|
225
|
+
.filter(
|
|
226
|
+
(entry) => entry.isFile() && /\.(?:json|md)$/u.test(entry.name),
|
|
227
|
+
)
|
|
228
|
+
.map((entry) => join(root, entry.name));
|
|
229
|
+
const byStem = new Map<string, string[]>();
|
|
230
|
+
for (const file of files) {
|
|
231
|
+
const stem = getRecipeIdFromPath(file);
|
|
232
|
+
const matches = byStem.get(stem) ?? [];
|
|
233
|
+
matches.push(file);
|
|
234
|
+
byStem.set(stem, matches);
|
|
235
|
+
}
|
|
236
|
+
for (const [stem, matches] of [...byStem].sort(([a], [b]) => a.localeCompare(b))) {
|
|
237
|
+
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(stem)) {
|
|
238
|
+
rejected.push({
|
|
239
|
+
file: matches[0],
|
|
240
|
+
reason: `Invalid Skill Recipe filename stem: ${stem}`,
|
|
241
|
+
skill,
|
|
242
|
+
stem,
|
|
243
|
+
});
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (matches.length > 1) {
|
|
247
|
+
rejected.push({
|
|
248
|
+
file: matches[0],
|
|
249
|
+
reason: `Skill Recipe stem collision: ${skill}/${stem} has both .json and .md files`,
|
|
250
|
+
skill,
|
|
251
|
+
stem,
|
|
252
|
+
});
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const file = matches[0];
|
|
256
|
+
try {
|
|
257
|
+
const raw = readRawRecipeConfig(file);
|
|
258
|
+
if (!raw || !Object.hasOwn(raw, "template"))
|
|
259
|
+
throw new Error(`Invalid Skill Recipe component: ${skill}/${stem}`);
|
|
260
|
+
assertRecipeHasNoDeclaredName(raw);
|
|
261
|
+
components.push({
|
|
262
|
+
file,
|
|
263
|
+
identity: `${skill}/${stem}`,
|
|
264
|
+
imports: Object.fromEntries(
|
|
265
|
+
Object.entries(getRecipeImports(raw)).map(([alias, value]) => [
|
|
266
|
+
alias,
|
|
267
|
+
getImportFrom(value),
|
|
268
|
+
]),
|
|
269
|
+
),
|
|
270
|
+
skill,
|
|
271
|
+
stem,
|
|
272
|
+
});
|
|
273
|
+
} catch (error) {
|
|
274
|
+
rejected.push({
|
|
275
|
+
file,
|
|
276
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
277
|
+
skill,
|
|
278
|
+
stem,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
133
282
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
283
|
+
return { components, partial: rejected.length > 0, rejected };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function listActiveSkillRecipeComponents(
|
|
287
|
+
context: ActiveSkillRecipeContext,
|
|
288
|
+
): ActiveSkillRecipeComponent[] {
|
|
289
|
+
const inventory = inventoryActiveSkillRecipeComponents(context);
|
|
290
|
+
if (inventory.rejected.length > 0)
|
|
291
|
+
throw new Error(inventory.rejected[0].reason);
|
|
292
|
+
return inventory.components;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const SKILL_RECIPE_REFERENCE =
|
|
296
|
+
/^([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\/([a-z][a-z0-9]*(?:-[a-z0-9]+)*)$/;
|
|
297
|
+
|
|
298
|
+
function qualifiedRecipeNameForFile(
|
|
299
|
+
file: string,
|
|
300
|
+
context: ActiveSkillRecipeContext,
|
|
301
|
+
): string | undefined {
|
|
302
|
+
const path = resolve(file);
|
|
303
|
+
for (const [skillName, roots] of Object.entries(context.namespaces)) {
|
|
304
|
+
if (roots.length !== 1) continue;
|
|
305
|
+
const root = roots[0];
|
|
137
306
|
const relation = relative(root, path);
|
|
138
|
-
if (
|
|
139
|
-
|
|
307
|
+
if (
|
|
308
|
+
relation &&
|
|
309
|
+
dirname(relation) === "." &&
|
|
310
|
+
!relation.startsWith("..") &&
|
|
311
|
+
!isAbsolute(relation)
|
|
312
|
+
) {
|
|
313
|
+
return `${skillName}/${basename(relation, extname(relation))}`;
|
|
314
|
+
}
|
|
140
315
|
}
|
|
141
316
|
return undefined;
|
|
142
317
|
}
|
|
143
318
|
|
|
144
|
-
function
|
|
319
|
+
function assertRecipeReferencePrefixWasNotRemoved(value: string): void {
|
|
145
320
|
if (value.startsWith("std:")) {
|
|
146
|
-
|
|
147
|
-
|
|
321
|
+
throw new Error(
|
|
322
|
+
"std: Recipe references were removed in pi-actors 0.46.\nUse <skill>/<recipe> or an explicit .json/.md file path.",
|
|
148
323
|
);
|
|
149
324
|
}
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
325
|
+
if (value.startsWith("skill:")) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
"skill: Recipe references were removed in pi-actors 0.46.\nUse <skill>/<recipe> without a prefix.",
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function skillRecipeCandidates(
|
|
333
|
+
value: string,
|
|
334
|
+
context: ActiveSkillRecipeContext,
|
|
335
|
+
): string[] | undefined {
|
|
336
|
+
const match = SKILL_RECIPE_REFERENCE.exec(value);
|
|
337
|
+
if (!match) return undefined;
|
|
338
|
+
const [, skillName, recipeName] = match;
|
|
339
|
+
const roots = [...(context.namespaces[skillName] ?? [])];
|
|
158
340
|
if (roots.length === 0)
|
|
159
|
-
throw new Error(`Active Skill Recipe
|
|
341
|
+
throw new Error(`Active Skill Recipe not found: ${skillName}/${recipeName}`);
|
|
160
342
|
if (roots.length > 1)
|
|
161
343
|
throw new Error(
|
|
162
|
-
`
|
|
344
|
+
`Duplicate active Skill identity ${skillName}: ${roots.join(", ")}`,
|
|
163
345
|
);
|
|
164
346
|
const root = roots[0];
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
347
|
+
const candidates = ["json", "md"].map((extension) =>
|
|
348
|
+
resolve(root, `${recipeName}.${extension}`),
|
|
349
|
+
);
|
|
350
|
+
const existing = candidates.filter((candidate) => existsSync(candidate));
|
|
351
|
+
if (existing.length > 1) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Skill Recipe stem collision: ${skillName}/${recipeName} has both .json and .md files`,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return candidates;
|
|
172
357
|
}
|
|
173
358
|
|
|
174
|
-
function
|
|
175
|
-
|
|
359
|
+
function recipeReferenceCandidates(
|
|
360
|
+
value: string,
|
|
361
|
+
baseDir: string,
|
|
362
|
+
context: ActiveSkillRecipeContext,
|
|
363
|
+
): string[] | undefined {
|
|
364
|
+
const trimmed = value.trim();
|
|
365
|
+
if (!trimmed) return undefined;
|
|
366
|
+
assertRecipeReferencePrefixWasNotRemoved(trimmed);
|
|
367
|
+
if (trimmed.endsWith(".json") || trimmed.endsWith(".md")) {
|
|
368
|
+
return [resolve(baseDir, trimmed)];
|
|
369
|
+
}
|
|
370
|
+
return skillRecipeCandidates(trimmed, context);
|
|
176
371
|
}
|
|
177
372
|
|
|
178
373
|
function findOwningSkillDir(recipeFile: string): string | undefined {
|
|
@@ -185,11 +380,18 @@ function findOwningSkillDir(recipeFile: string): string | undefined {
|
|
|
185
380
|
}
|
|
186
381
|
}
|
|
187
382
|
|
|
383
|
+
function assertRecipeHasNoDeclaredName(raw: Record<string, unknown>): void {
|
|
384
|
+
if (!Object.hasOwn(raw, "name")) return;
|
|
385
|
+
throw new Error(
|
|
386
|
+
"Recipe.name was removed in pi-actors 0.46.\nFile-backed Recipe identity is derived from its file name.",
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
188
390
|
function assertRuntimeRecipeValuesNotDeclared(raw: Record<string, unknown>): void {
|
|
189
391
|
const args = Array.isArray(raw.args)
|
|
190
392
|
? Schema.getExplicitToolArgNames(raw.args.map(String))
|
|
191
393
|
: [];
|
|
192
|
-
for (const name of
|
|
394
|
+
for (const name of RESERVED_AUTHORED_RECIPE_VALUE_NAMES) {
|
|
193
395
|
if (
|
|
194
396
|
args.includes(name) ||
|
|
195
397
|
(isRecord(raw.defaults) && Object.hasOwn(raw.defaults, name)) ||
|
|
@@ -202,100 +404,51 @@ function assertRuntimeRecipeValuesNotDeclared(raw: Record<string, unknown>): voi
|
|
|
202
404
|
|
|
203
405
|
export function resolveRecipePath(
|
|
204
406
|
value: string,
|
|
205
|
-
|
|
407
|
+
baseDir = Paths.getRecipeRoot(),
|
|
206
408
|
): string {
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.replaceAll("{agent}", Paths.getAgentDir());
|
|
212
|
-
if (expanded.startsWith("~/")) return resolve(homedir(), expanded.slice(2));
|
|
213
|
-
if (expanded.includes("/")) return resolve(expanded);
|
|
214
|
-
return resolve(
|
|
215
|
-
recipeRoot,
|
|
216
|
-
expanded.endsWith(".json") || expanded.endsWith(".md")
|
|
217
|
-
? expanded
|
|
218
|
-
: `${expanded}.json`,
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function isBareRecipeName(value: string): boolean {
|
|
223
|
-
const trimmed = value.trim();
|
|
224
|
-
return (
|
|
225
|
-
Boolean(trimmed) &&
|
|
226
|
-
!trimmed.includes("/") &&
|
|
227
|
-
!trimmed.startsWith("~") &&
|
|
228
|
-
!trimmed.includes("{")
|
|
409
|
+
const candidates = recipeReferenceCandidates(
|
|
410
|
+
value,
|
|
411
|
+
baseDir,
|
|
412
|
+
EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT,
|
|
229
413
|
);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return [
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function recipeCandidatePaths(
|
|
239
|
-
value: string,
|
|
240
|
-
currentRecipeRoot: string,
|
|
241
|
-
): string[] {
|
|
242
|
-
const qualified = qualifiedRecipeCandidates(value.trim());
|
|
243
|
-
if (qualified) return qualified;
|
|
244
|
-
if (!isBareRecipeName(value))
|
|
245
|
-
return [resolveRecipePath(value, currentRecipeRoot)];
|
|
246
|
-
const roots = [
|
|
247
|
-
Paths.getRecipeRoot(),
|
|
248
|
-
currentRecipeRoot,
|
|
249
|
-
Paths.getPackagedRecipeRoot(),
|
|
250
|
-
];
|
|
251
|
-
return [
|
|
252
|
-
...new Set(
|
|
253
|
-
roots.flatMap((root) =>
|
|
254
|
-
recipeNameFiles(value).map((file) => resolve(root, file)),
|
|
255
|
-
),
|
|
256
|
-
),
|
|
257
|
-
];
|
|
414
|
+
if (!candidates || candidates.length !== 1) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`Recipe file reference must be an explicit .json or .md path: ${value}`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return candidates[0];
|
|
258
420
|
}
|
|
259
421
|
|
|
260
422
|
function resolveRecipeImportPath(
|
|
261
423
|
value: string,
|
|
262
|
-
|
|
424
|
+
recipeDir: string,
|
|
425
|
+
context: ActiveSkillRecipeContext,
|
|
263
426
|
): string {
|
|
264
|
-
const candidates =
|
|
427
|
+
const candidates = recipeReferenceCandidates(value, recipeDir, context);
|
|
428
|
+
if (!candidates) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Recipe import must use <skill>/<recipe> or an explicit .json/.md file path: ${value}`,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
265
433
|
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
|
266
434
|
}
|
|
267
435
|
|
|
268
436
|
export function resolveRecipeReferencePath(
|
|
269
437
|
value: unknown,
|
|
270
|
-
|
|
438
|
+
baseDir = Paths.getRecipeRoot(),
|
|
439
|
+
context = EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT,
|
|
271
440
|
): string | undefined {
|
|
272
441
|
if (typeof value !== "string") return undefined;
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
for (const path of recipeCandidatePaths(trimmed, currentRecipeRoot)) {
|
|
276
|
-
if (!existsSync(path)) continue;
|
|
277
|
-
try {
|
|
278
|
-
const raw = readRawRecipeConfig(path);
|
|
279
|
-
if (raw && typeof raw === "object" && Object.hasOwn(raw, "template"))
|
|
280
|
-
return path;
|
|
281
|
-
return path;
|
|
282
|
-
} catch {
|
|
283
|
-
return path;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return undefined;
|
|
442
|
+
const candidates = recipeReferenceCandidates(value, baseDir, context);
|
|
443
|
+
return candidates?.find((candidate) => existsSync(candidate));
|
|
287
444
|
}
|
|
288
445
|
|
|
289
446
|
export function getRecipePath(
|
|
290
447
|
value: unknown,
|
|
291
|
-
|
|
448
|
+
baseDir = Paths.getRecipeRoot(),
|
|
449
|
+
context = EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT,
|
|
292
450
|
): string | undefined {
|
|
293
|
-
|
|
294
|
-
const trimmed = value.trim();
|
|
295
|
-
if (!trimmed || hasWhitespace(trimmed)) return undefined;
|
|
296
|
-
if (trimmed.endsWith(".json") || trimmed.endsWith(".md"))
|
|
297
|
-
return resolveRecipePath(trimmed, recipeRoot);
|
|
298
|
-
return resolveRecipeReferencePath(trimmed, recipeRoot);
|
|
451
|
+
return resolveRecipeReferencePath(value, baseDir, context);
|
|
299
452
|
}
|
|
300
453
|
|
|
301
454
|
function isImportNode(value: unknown): boolean {
|
|
@@ -873,7 +1026,11 @@ function loadDelegatedRecipe(
|
|
|
873
1026
|
stack: string[],
|
|
874
1027
|
options: ReadResolvedRecipeConfigOptions,
|
|
875
1028
|
): TemplateRecipeConfig | undefined {
|
|
876
|
-
const path = resolveRecipeReferencePath(
|
|
1029
|
+
const path = resolveRecipeReferencePath(
|
|
1030
|
+
value,
|
|
1031
|
+
dirname(currentRecipeFile),
|
|
1032
|
+
options.skillContext,
|
|
1033
|
+
);
|
|
877
1034
|
if (!path) return undefined;
|
|
878
1035
|
const config = readResolvedRecipeConfig(
|
|
879
1036
|
path,
|
|
@@ -991,6 +1148,46 @@ function getDirectDelegatedRecipe(
|
|
|
991
1148
|
return undefined;
|
|
992
1149
|
}
|
|
993
1150
|
|
|
1151
|
+
function resolveDirectDelegationArgs(
|
|
1152
|
+
delegated: TemplateRecipeConfig,
|
|
1153
|
+
wrapperArgs: unknown,
|
|
1154
|
+
wrapperDefaults: Record<string, unknown> | undefined,
|
|
1155
|
+
): string[] | undefined {
|
|
1156
|
+
if (!Array.isArray(delegated.args))
|
|
1157
|
+
return Array.isArray(wrapperArgs) ? wrapperArgs.map(String) : undefined;
|
|
1158
|
+
const delegatedSpec = Schema.parseToolArgDeclarationList(delegated.args);
|
|
1159
|
+
if (delegatedSpec.error) throw new Error(delegatedSpec.error);
|
|
1160
|
+
const wrapperDeclarations = Array.isArray(wrapperArgs)
|
|
1161
|
+
? wrapperArgs.map(String)
|
|
1162
|
+
: [];
|
|
1163
|
+
const wrapperSpec = Schema.parseToolArgDeclarationList(wrapperDeclarations);
|
|
1164
|
+
if (wrapperSpec.error) throw new Error(wrapperSpec.error);
|
|
1165
|
+
const declared = new Set(delegatedSpec.args);
|
|
1166
|
+
for (const arg of wrapperSpec.args) {
|
|
1167
|
+
if (!declared.has(arg))
|
|
1168
|
+
throw new Error(`Unknown delegated Recipe argument: ${arg}`);
|
|
1169
|
+
}
|
|
1170
|
+
Schema.assertCompatibleToolArgTypes(
|
|
1171
|
+
delegatedSpec.argTypes,
|
|
1172
|
+
wrapperSpec.argTypes,
|
|
1173
|
+
);
|
|
1174
|
+
assertRecipeDefaultsDeclared(
|
|
1175
|
+
delegated.args,
|
|
1176
|
+
wrapperDefaults,
|
|
1177
|
+
"delegated Recipe default",
|
|
1178
|
+
);
|
|
1179
|
+
Schema.normalizeRuntimeValues(
|
|
1180
|
+
Object.fromEntries(
|
|
1181
|
+
Object.entries(wrapperDefaults ?? {}).filter(
|
|
1182
|
+
([, value]) =>
|
|
1183
|
+
typeof value !== "string" || !/\{[^{}]+\}/.test(value),
|
|
1184
|
+
),
|
|
1185
|
+
),
|
|
1186
|
+
delegatedSpec.argTypes,
|
|
1187
|
+
);
|
|
1188
|
+
return delegated.args;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
994
1191
|
function expandImportNodes(
|
|
995
1192
|
value: CommandTemplateValue,
|
|
996
1193
|
imports: Record<string, ImportedRecipe>,
|
|
@@ -1080,8 +1277,12 @@ export function readResolvedRecipeConfig(
|
|
|
1080
1277
|
`Recipe import depth exceeds limit ${MAX_RECIPE_IMPORT_DEPTH}: ${[...stack, path].join(" -> ")}`,
|
|
1081
1278
|
);
|
|
1082
1279
|
}
|
|
1083
|
-
const raw =
|
|
1280
|
+
const raw =
|
|
1281
|
+
options.authoredEntry?.path === path
|
|
1282
|
+
? options.authoredEntry.recipe
|
|
1283
|
+
: readRawRecipeConfig(path);
|
|
1084
1284
|
if (!raw || !Object.hasOwn(raw, "template")) return undefined;
|
|
1285
|
+
assertRecipeHasNoDeclaredName(raw);
|
|
1085
1286
|
RecipeControl.assertRecipeHasNoMailbox(raw);
|
|
1086
1287
|
assertRuntimeRecipeValuesNotDeclared(raw);
|
|
1087
1288
|
assertRecipeArgumentContract(raw as unknown as TemplateRecipeDefinition);
|
|
@@ -1090,6 +1291,7 @@ export function readResolvedRecipeConfig(
|
|
|
1090
1291
|
const importPath = resolveRecipeImportPath(
|
|
1091
1292
|
getImportFrom(binding),
|
|
1092
1293
|
dirname(path),
|
|
1294
|
+
options.skillContext ?? EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT,
|
|
1093
1295
|
);
|
|
1094
1296
|
const config = readResolvedRecipeConfig(
|
|
1095
1297
|
importPath,
|
|
@@ -1109,7 +1311,7 @@ export function readResolvedRecipeConfig(
|
|
|
1109
1311
|
imports[alias] = {
|
|
1110
1312
|
alias,
|
|
1111
1313
|
file: importPath,
|
|
1112
|
-
name:
|
|
1314
|
+
name: getRecipeIdFromPath(importPath),
|
|
1113
1315
|
config,
|
|
1114
1316
|
defaults: { ...(config.defaults ?? {}), ...(bindingDefaults ?? {}) },
|
|
1115
1317
|
values: {
|
|
@@ -1165,10 +1367,15 @@ export function readResolvedRecipeConfig(
|
|
|
1165
1367
|
role: stack.length > 0 ? "import" : "entry",
|
|
1166
1368
|
})
|
|
1167
1369
|
: expandedTemplate;
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
);
|
|
1370
|
+
const wrapperDefaults = isRecord(substituted.defaults)
|
|
1371
|
+
? substituted.defaults
|
|
1372
|
+
: undefined;
|
|
1373
|
+
const mergedDefaults = mergeDefaults(delegated?.defaults, wrapperDefaults);
|
|
1374
|
+
const effectiveArgs = delegated
|
|
1375
|
+
? resolveDirectDelegationArgs(delegated, substituted.args, wrapperDefaults)
|
|
1376
|
+
: Array.isArray(substituted.args)
|
|
1377
|
+
? (substituted.args as string[])
|
|
1378
|
+
: undefined;
|
|
1172
1379
|
const artifactSource = isRecord(substituted.artifacts)
|
|
1173
1380
|
? substituted.artifacts
|
|
1174
1381
|
: delegated?.artifacts;
|
|
@@ -1205,11 +1412,7 @@ export function readResolvedRecipeConfig(
|
|
|
1205
1412
|
? { imports: getRecipeImports(raw) }
|
|
1206
1413
|
: {}),
|
|
1207
1414
|
template: templateWithContext,
|
|
1208
|
-
...(
|
|
1209
|
-
? { args: substituted.args as string[] }
|
|
1210
|
-
: Array.isArray(delegated?.args)
|
|
1211
|
-
? { args: delegated.args }
|
|
1212
|
-
: {}),
|
|
1415
|
+
...(effectiveArgs ? { args: effectiveArgs } : {}),
|
|
1213
1416
|
...(mergedDefaults ? { defaults: mergedDefaults } : {}),
|
|
1214
1417
|
...(typeof substituted.parallel === "boolean"
|
|
1215
1418
|
? { parallel: substituted.parallel }
|
|
@@ -1286,6 +1489,7 @@ function collectRecipeContextRecords(
|
|
|
1286
1489
|
file: string,
|
|
1287
1490
|
stack: string[],
|
|
1288
1491
|
importPath: string[],
|
|
1492
|
+
context: ActiveSkillRecipeContext,
|
|
1289
1493
|
alias?: string,
|
|
1290
1494
|
): TemplateRecipeContextRecord[] {
|
|
1291
1495
|
const path = resolveRecipePath(
|
|
@@ -1302,27 +1506,35 @@ function collectRecipeContextRecords(
|
|
|
1302
1506
|
}
|
|
1303
1507
|
const raw = readRawRecipeConfig(path);
|
|
1304
1508
|
if (!raw || !Object.hasOwn(raw, "template")) return [];
|
|
1305
|
-
|
|
1509
|
+
assertRecipeHasNoDeclaredName(raw);
|
|
1510
|
+
const qualifiedName = qualifiedRecipeNameForFile(path, context);
|
|
1511
|
+
const skill = qualifiedName?.split("/", 1)[0];
|
|
1306
1512
|
const record: TemplateRecipeContextRecord = {
|
|
1307
1513
|
...(alias ? { alias } : {}),
|
|
1308
1514
|
depth: stack.length,
|
|
1309
|
-
file: path,
|
|
1310
1515
|
import_path: importPath,
|
|
1516
|
+
logical_reference: qualifiedName ?? basename(path),
|
|
1311
1517
|
name: getRecipeIdFromPath(path),
|
|
1312
|
-
...(qualifiedName ? { qualified_name: qualifiedName } : {}),
|
|
1313
1518
|
recipe: raw,
|
|
1314
1519
|
role: stack.length === 0 ? "entry" : "import",
|
|
1520
|
+
...(skill ? { skill } : {}),
|
|
1521
|
+
source_file: path,
|
|
1522
|
+
source_kind: qualifiedName
|
|
1523
|
+
? "active_skill_component"
|
|
1524
|
+
: "explicit_file_recipe",
|
|
1315
1525
|
};
|
|
1316
1526
|
const imports = getRecipeImports(raw);
|
|
1317
1527
|
const children = Object.entries(imports).flatMap(([childAlias, binding]) => {
|
|
1318
1528
|
const importFile = resolveRecipeImportPath(
|
|
1319
1529
|
getImportFrom(binding),
|
|
1320
1530
|
dirname(path),
|
|
1531
|
+
context,
|
|
1321
1532
|
);
|
|
1322
1533
|
return collectRecipeContextRecords(
|
|
1323
1534
|
importFile,
|
|
1324
1535
|
[...stack, path],
|
|
1325
1536
|
[...importPath, childAlias],
|
|
1537
|
+
context,
|
|
1326
1538
|
childAlias,
|
|
1327
1539
|
);
|
|
1328
1540
|
});
|
|
@@ -1331,8 +1543,9 @@ function collectRecipeContextRecords(
|
|
|
1331
1543
|
|
|
1332
1544
|
export function buildRecipeContextRecords(
|
|
1333
1545
|
file: string,
|
|
1546
|
+
context = EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT,
|
|
1334
1547
|
): TemplateRecipeContextRecord[] {
|
|
1335
|
-
return collectRecipeContextRecords(file, [], []);
|
|
1548
|
+
return collectRecipeContextRecords(file, [], [], context);
|
|
1336
1549
|
}
|
|
1337
1550
|
|
|
1338
1551
|
export function getRecipeTemplate(
|