@llblab/pi-actors 0.45.1 → 0.46.0

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.
Files changed (212) hide show
  1. package/AGENTS.md +9 -7
  2. package/CHANGELOG.md +8 -1
  3. package/README.md +26 -12
  4. package/dist/fixtures/protocol/recipe-summary.json +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lib/async-runs.d.ts +4 -1
  7. package/dist/lib/async-runs.js +22 -23
  8. package/dist/lib/automatic-review-runtime.d.ts +2 -0
  9. package/dist/lib/automatic-review-runtime.js +12 -5
  10. package/dist/lib/draft-sleep.js +0 -1
  11. package/dist/lib/extension-runtime.d.ts +1 -1
  12. package/dist/lib/extension-runtime.js +8 -2
  13. package/dist/lib/inspector.js +45 -7
  14. package/dist/lib/paths.d.ts +0 -1
  15. package/dist/lib/paths.js +0 -7
  16. package/dist/lib/prompts.d.ts +1 -1
  17. package/dist/lib/prompts.js +2 -2
  18. package/dist/lib/recipes-context.js +2 -2
  19. package/dist/lib/recipes-references.d.ts +24 -9
  20. package/dist/lib/recipes-references.js +152 -126
  21. package/dist/lib/runtime.d.ts +0 -1
  22. package/dist/lib/runtime.js +1 -3
  23. package/dist/lib/tools-inspect.d.ts +0 -1
  24. package/dist/lib/tools-inspect.js +35 -8
  25. package/dist/lib/tools-local.d.ts +2 -0
  26. package/dist/lib/tools-local.js +1 -1
  27. package/dist/lib/tools-spawn.d.ts +2 -0
  28. package/dist/lib/tools-spawn.js +24 -52
  29. package/dist/scripts/build-dist.mjs +13 -4
  30. package/dist/skills/actors/SKILL.md +6 -6
  31. package/dist/skills/actors/recipes/recipe-validate.json +11 -0
  32. package/{recipes/utility-coordinator-lock-snapshot.json → dist/skills/actors/recipes/resource-locker-snapshot.json} +1 -3
  33. package/dist/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
  34. package/dist/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
  35. package/dist/skills/actors/recipes/run-summary.json +9 -0
  36. package/dist/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -2
  37. package/dist/skills/actors/scripts/run-utils.mjs +164 -0
  38. package/{scripts → dist/skills/actors/scripts}/validate-recipe.mjs +134 -17
  39. package/dist/skills/artifacts/SKILL.md +16 -0
  40. package/{recipes/pipeline-artifact-bundle.json → dist/skills/artifacts/recipes/bundle.json} +4 -4
  41. package/dist/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
  42. package/dist/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
  43. package/{recipes/pipeline-artifact-report.json → dist/skills/artifacts/recipes/report.json} +2 -2
  44. package/dist/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
  45. package/dist/skills/artifacts/scripts/artifact-utils.mjs +63 -0
  46. package/dist/skills/media/SKILL.md +16 -0
  47. package/dist/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
  48. package/{recipes/music-player.json → dist/skills/media/recipes/player.json} +1 -3
  49. package/dist/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
  50. package/dist/skills/media/scripts/media-utils.mjs +47 -0
  51. package/dist/{scripts → skills/media/scripts}/music-player.mjs +9 -3
  52. package/dist/skills/project-work/SKILL.md +16 -0
  53. package/dist/skills/project-work/recipes/changelog-section.json +11 -0
  54. package/{recipes/pipeline-docs-maintenance.json → dist/skills/project-work/recipes/docs-maintenance.json} +4 -4
  55. package/dist/skills/project-work/recipes/package-summary.json +9 -0
  56. package/{recipes/pipeline-release-readiness.json → dist/skills/project-work/recipes/release-readiness.json} +6 -6
  57. package/dist/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
  58. package/{recipes/pipeline-repo-health.json → dist/skills/project-work/recipes/repo-health.json} +6 -6
  59. package/{recipes/pipeline-async-run-ops.json → dist/skills/project-work/recipes/run-ops.json} +3 -3
  60. package/dist/skills/project-work/recipes/skill-summary.json +11 -0
  61. package/dist/skills/project-work/scripts/project-utils.mjs +89 -0
  62. package/dist/skills/recipe-memory/SKILL.md +16 -0
  63. package/dist/skills/swarm/SKILL.md +4 -4
  64. package/{recipes/pipeline-architect-coordinator.json → dist/skills/swarm/recipes/architect.json} +5 -5
  65. package/{recipes/pipeline-checkpoint-continuation.json → dist/skills/swarm/recipes/checkpoint-continuation.json} +3 -3
  66. package/dist/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
  67. package/dist/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
  68. package/dist/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
  69. package/{recipes/pipeline-research-synthesis.json → dist/skills/swarm/recipes/research-synthesis.json} +6 -6
  70. package/{recipes/pipeline-review-readiness.json → dist/skills/swarm/recipes/review-readiness.json} +1 -1
  71. package/{recipes/subagents-prompts.json → dist/skills/swarm/recipes/subagent-prompts.json} +1 -1
  72. package/{recipes → dist/skills/swarm/recipes}/subagent-quorum.json +1 -1
  73. package/dist/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
  74. package/docs/actor-inspector.md +4 -3
  75. package/docs/async-runs.md +2 -2
  76. package/docs/recipe-library.md +23 -23
  77. package/docs/releasing.md +2 -2
  78. package/docs/template-recipes.md +30 -14
  79. package/docs/tool-registry.md +2 -2
  80. package/fixtures/protocol/recipe-summary.json +1 -1
  81. package/index.ts +2 -1
  82. package/lib/async-runs.ts +47 -24
  83. package/lib/automatic-review-runtime.ts +16 -3
  84. package/lib/draft-sleep.ts +0 -1
  85. package/lib/extension-runtime.ts +21 -2
  86. package/lib/inspector.ts +56 -11
  87. package/lib/paths.ts +0 -7
  88. package/lib/prompts.ts +2 -2
  89. package/lib/recipes-context.ts +2 -2
  90. package/lib/recipes-references.ts +223 -134
  91. package/lib/runtime.ts +1 -5
  92. package/lib/tools-inspect.ts +42 -9
  93. package/lib/tools-local.ts +2 -0
  94. package/lib/tools-spawn.ts +37 -74
  95. package/package.json +2 -4
  96. package/scripts/build-dist.mjs +13 -4
  97. package/skills/actors/SKILL.md +6 -6
  98. package/skills/actors/recipes/recipe-validate.json +11 -0
  99. package/{dist/recipes/utility-coordinator-lock-snapshot.json → skills/actors/recipes/resource-locker-snapshot.json} +1 -3
  100. package/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
  101. package/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
  102. package/skills/actors/recipes/run-summary.json +9 -0
  103. package/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -2
  104. package/skills/actors/scripts/run-utils.mjs +164 -0
  105. package/{dist → skills/actors}/scripts/validate-recipe.mjs +134 -17
  106. package/skills/artifacts/SKILL.md +16 -0
  107. package/{dist/recipes/pipeline-artifact-bundle.json → skills/artifacts/recipes/bundle.json} +4 -4
  108. package/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
  109. package/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
  110. package/{dist/recipes/pipeline-artifact-report.json → skills/artifacts/recipes/report.json} +2 -2
  111. package/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
  112. package/skills/artifacts/scripts/artifact-utils.mjs +63 -0
  113. package/skills/media/SKILL.md +16 -0
  114. package/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
  115. package/{dist/recipes/music-player.json → skills/media/recipes/player.json} +1 -3
  116. package/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
  117. package/skills/media/scripts/media-utils.mjs +47 -0
  118. package/{scripts → skills/media/scripts}/music-player.mjs +9 -3
  119. package/skills/project-work/SKILL.md +16 -0
  120. package/skills/project-work/recipes/changelog-section.json +11 -0
  121. package/{dist/recipes/pipeline-docs-maintenance.json → skills/project-work/recipes/docs-maintenance.json} +4 -4
  122. package/skills/project-work/recipes/package-summary.json +9 -0
  123. package/{dist/recipes/pipeline-release-readiness.json → skills/project-work/recipes/release-readiness.json} +6 -6
  124. package/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
  125. package/{dist/recipes/pipeline-repo-health.json → skills/project-work/recipes/repo-health.json} +6 -6
  126. package/{dist/recipes/pipeline-async-run-ops.json → skills/project-work/recipes/run-ops.json} +3 -3
  127. package/skills/project-work/recipes/skill-summary.json +11 -0
  128. package/skills/project-work/scripts/project-utils.mjs +89 -0
  129. package/skills/recipe-memory/SKILL.md +16 -0
  130. package/skills/swarm/SKILL.md +4 -4
  131. package/{dist/recipes/pipeline-architect-coordinator.json → skills/swarm/recipes/architect.json} +5 -5
  132. package/{dist/recipes/pipeline-checkpoint-continuation.json → skills/swarm/recipes/checkpoint-continuation.json} +3 -3
  133. package/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
  134. package/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
  135. package/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
  136. package/{dist/recipes/pipeline-research-synthesis.json → skills/swarm/recipes/research-synthesis.json} +6 -6
  137. package/{dist/recipes/pipeline-review-readiness.json → skills/swarm/recipes/review-readiness.json} +1 -1
  138. package/{dist/recipes/subagents-prompts.json → skills/swarm/recipes/subagent-prompts.json} +1 -1
  139. package/{dist → skills/swarm}/recipes/subagent-quorum.json +1 -1
  140. package/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
  141. package/dist/recipes/utility-changelog-section.json +0 -13
  142. package/dist/recipes/utility-package-summary.json +0 -11
  143. package/dist/recipes/utility-run-summary.json +0 -11
  144. package/dist/recipes/utility-skill-summary.json +0 -13
  145. package/dist/recipes/utility-validate-recipe.json +0 -13
  146. package/dist/scripts/executable-block-style.d.mts +0 -1
  147. package/dist/scripts/executable-block-style.mjs +0 -73
  148. package/dist/scripts/recipe-utils.mjs +0 -404
  149. package/dist/scripts/release-gates.mjs +0 -281
  150. package/recipes/utility-changelog-section.json +0 -13
  151. package/recipes/utility-package-summary.json +0 -11
  152. package/recipes/utility-run-summary.json +0 -11
  153. package/recipes/utility-skill-summary.json +0 -13
  154. package/recipes/utility-validate-recipe.json +0 -13
  155. package/scripts/executable-block-style.d.mts +0 -1
  156. package/scripts/executable-block-style.mjs +0 -73
  157. package/scripts/recipe-utils.mjs +0 -404
  158. package/scripts/release-gates.mjs +0 -281
  159. /package/dist/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
  160. /package/dist/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
  161. /package/dist/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
  162. /package/dist/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
  163. /package/dist/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
  164. /package/dist/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
  165. /package/dist/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
  166. /package/dist/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
  167. /package/dist/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
  168. /package/dist/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
  169. /package/dist/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
  170. /package/dist/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
  171. /package/dist/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
  172. /package/dist/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
  173. /package/dist/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
  174. /package/dist/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
  175. /package/dist/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
  176. /package/dist/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
  177. /package/dist/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
  178. /package/dist/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
  179. /package/dist/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
  180. /package/dist/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
  181. /package/dist/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
  182. /package/dist/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
  183. /package/dist/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
  184. /package/dist/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
  185. /package/dist/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
  186. /package/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
  187. /package/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
  188. /package/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
  189. /package/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
  190. /package/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
  191. /package/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
  192. /package/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
  193. /package/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
  194. /package/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
  195. /package/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
  196. /package/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
  197. /package/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
  198. /package/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
  199. /package/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
  200. /package/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
  201. /package/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
  202. /package/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
  203. /package/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
  204. /package/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
  205. /package/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
  206. /package/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
  207. /package/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
  208. /package/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
  209. /package/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
  210. /package/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
  211. /package/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
  212. /package/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
@@ -12,7 +12,6 @@ export interface TemplateRecipeImportBinding {
12
12
  }
13
13
  export type TemplateRecipeImport = string | TemplateRecipeImportBinding;
14
14
  export interface TemplateRecipeDefinition {
15
- name?: string;
16
15
  description?: string;
17
16
  disabled?: boolean;
18
17
  imports?: Record<string, TemplateRecipeImport>;
@@ -39,6 +38,7 @@ export interface TemplateRecipeDefinition {
39
38
  usage?: Record<string, unknown>;
40
39
  }
41
40
  export interface TemplateRecipeConfig extends TemplateRecipeDefinition {
41
+ name?: string;
42
42
  async?: boolean;
43
43
  recipe_dir?: string;
44
44
  skill_dir?: string;
@@ -46,31 +46,46 @@ export interface TemplateRecipeConfig extends TemplateRecipeDefinition {
46
46
  export interface TemplateRecipeContextRecord {
47
47
  alias?: string;
48
48
  depth: number;
49
- file: string;
50
49
  import_path: string[];
50
+ logical_reference: string;
51
51
  name: string;
52
- qualified_name?: string;
53
52
  recipe: Record<string, unknown>;
54
53
  role: "entry" | "import";
54
+ skill?: string;
55
+ source_file: string;
56
+ source_kind: "active_skill_component" | "explicit_file_recipe" | "user_registry_capability";
55
57
  }
56
58
  export interface ReadResolvedRecipeConfigOptions {
57
59
  includeActorRecipeContext?: boolean;
60
+ skillContext?: ActiveSkillRecipeContext;
58
61
  }
59
62
  export interface ActiveSkillRecipeSource {
60
63
  name: string;
61
64
  filePath?: string;
62
65
  baseDir?: string;
63
66
  }
64
- export declare function setActiveSkillRecipeSources(skills: ActiveSkillRecipeSource[]): void;
65
- export declare function getActiveSkillRecipeNamespaces(): Record<string, string[]>;
66
- export declare function resolveRecipePath(value: string, recipeRoot?: string): string;
67
- export declare function resolveRecipeReferencePath(value: unknown, currentRecipeRoot?: string): string | undefined;
68
- export declare function getRecipePath(value: unknown, recipeRoot?: string): string | undefined;
67
+ export interface ActiveSkillRecipeContext {
68
+ readonly namespaces: Readonly<Record<string, readonly string[]>>;
69
+ }
70
+ export declare function createActiveSkillRecipeContext(skills: ActiveSkillRecipeSource[]): ActiveSkillRecipeContext;
71
+ export declare const EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT: ActiveSkillRecipeContext;
72
+ export declare function getActiveSkillRecipeNamespaces(context: ActiveSkillRecipeContext): Record<string, string[]>;
73
+ export interface ActiveSkillRecipeComponent {
74
+ file: string;
75
+ identity: string;
76
+ imports: Record<string, string>;
77
+ skill: string;
78
+ stem: string;
79
+ }
80
+ export declare function listActiveSkillRecipeComponents(context: ActiveSkillRecipeContext): ActiveSkillRecipeComponent[];
81
+ export declare function resolveRecipePath(value: string, baseDir?: string): string;
82
+ export declare function resolveRecipeReferencePath(value: unknown, baseDir?: string, context?: ActiveSkillRecipeContext): string | undefined;
83
+ export declare function getRecipePath(value: unknown, baseDir?: string, context?: ActiveSkillRecipeContext): string | undefined;
69
84
  export declare function diagnoseRawRecipeConfigFailure(path: string): string | undefined;
70
85
  export declare function readRawRecipeConfig(path: string): Record<string, unknown> | undefined;
71
86
  export declare function getRecipeIdFromPath(file: string): string;
72
87
  export declare function readResolvedRecipeConfig(file: string, stack?: string[], options?: ReadResolvedRecipeConfigOptions): TemplateRecipeConfig | undefined;
73
- export declare function buildRecipeContextRecords(file: string): TemplateRecipeContextRecord[];
88
+ export declare function buildRecipeContextRecords(file: string, context?: ActiveSkillRecipeContext): TemplateRecipeContextRecord[];
74
89
  export declare function getRecipeTemplate(value: unknown): CommandTemplateValue | undefined;
75
90
  export declare function isRecipeReference(value: unknown): boolean;
76
91
  export declare function isAsyncRecipeReference(value: unknown): boolean;
@@ -3,9 +3,8 @@
3
3
  * Zones: registry config, async runs, path resolution
4
4
  * Owns detection, loading, and recipe-layer expansion for template recipe files
5
5
  */
6
- import { existsSync, readFileSync, statSync } from "node:fs";
7
- import { homedir } from "node:os";
8
- import { basename, dirname, extname, isAbsolute, relative, resolve } from "node:path";
6
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
7
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
9
8
  import * as CommandTemplates from "./command-templates.js";
10
9
  import * as Paths from "./paths.js";
11
10
  import * as RecipeControl from "./recipe-control.js";
@@ -13,9 +12,8 @@ import * as Schema from "./schema.js";
13
12
  const MAX_RECIPE_FILE_BYTES = 1024 * 1024;
14
13
  const MAX_RECIPE_IMPORT_DEPTH = 32;
15
14
  const RUNTIME_RECIPE_VALUE_NAMES = new Set(["recipe_dir", "skill_dir"]);
16
- const activeSkillRecipeRoots = new Map();
17
- export function setActiveSkillRecipeSources(skills) {
18
- activeSkillRecipeRoots.clear();
15
+ export function createActiveSkillRecipeContext(skills) {
16
+ const rootsByName = new Map();
19
17
  for (const skill of skills) {
20
18
  const name = skill.name.trim();
21
19
  const skillDir = skill.baseDir
@@ -25,62 +23,132 @@ export function setActiveSkillRecipeSources(skills) {
25
23
  : undefined;
26
24
  if (!name || !skillDir)
27
25
  continue;
28
- const roots = activeSkillRecipeRoots.get(name) ?? new Set();
26
+ const roots = rootsByName.get(name) ?? new Set();
29
27
  roots.add(resolve(skillDir, "recipes"));
30
- activeSkillRecipeRoots.set(name, roots);
28
+ rootsByName.set(name, roots);
31
29
  }
32
- }
33
- export function getActiveSkillRecipeNamespaces() {
34
- return Object.fromEntries([...activeSkillRecipeRoots.entries()]
30
+ const namespaces = Object.fromEntries([...rootsByName.entries()]
35
31
  .sort(([left], [right]) => left.localeCompare(right))
36
- .map(([name, roots]) => [name, [...roots].sort()]));
32
+ .map(([name, roots]) => [name, Object.freeze([...roots].sort())]));
33
+ return Object.freeze({ namespaces: Object.freeze(namespaces) });
34
+ }
35
+ export const EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT = createActiveSkillRecipeContext([]);
36
+ export function getActiveSkillRecipeNamespaces(context) {
37
+ return Object.fromEntries(Object.entries(context.namespaces).map(([name, roots]) => [
38
+ name,
39
+ [...roots],
40
+ ]));
41
+ }
42
+ function nestedRecipeFiles(root) {
43
+ if (!existsSync(root))
44
+ return [];
45
+ return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
46
+ const path = join(root, entry.name);
47
+ if (entry.isDirectory())
48
+ return nestedRecipeFiles(path);
49
+ return entry.isFile() && /\.(?:json|md)$/u.test(entry.name) ? [path] : [];
50
+ });
51
+ }
52
+ export function listActiveSkillRecipeComponents(context) {
53
+ const components = [];
54
+ for (const [skill, roots] of Object.entries(context.namespaces)) {
55
+ if (roots.length > 1)
56
+ throw new Error(`Duplicate active Skill identity ${skill}: ${roots.join(", ")}`);
57
+ const root = roots[0];
58
+ if (!root || !existsSync(root))
59
+ continue;
60
+ const direct = readdirSync(root, { withFileTypes: true });
61
+ for (const entry of direct.filter((candidate) => candidate.isDirectory())) {
62
+ const nested = nestedRecipeFiles(join(root, entry.name));
63
+ if (nested.length > 0)
64
+ throw new Error(`Nested Skill Recipe file is not addressable: ${nested[0]}`);
65
+ }
66
+ const files = direct
67
+ .filter((entry) => entry.isFile() && /\.(?:json|md)$/u.test(entry.name))
68
+ .map((entry) => join(root, entry.name));
69
+ const byStem = new Map();
70
+ for (const file of files) {
71
+ const stem = getRecipeIdFromPath(file);
72
+ const matches = byStem.get(stem) ?? [];
73
+ matches.push(file);
74
+ byStem.set(stem, matches);
75
+ }
76
+ for (const [stem, matches] of [...byStem].sort(([a], [b]) => a.localeCompare(b))) {
77
+ if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(stem))
78
+ throw new Error(`Invalid Skill Recipe filename stem: ${stem}`);
79
+ if (matches.length > 1)
80
+ throw new Error(`Skill Recipe stem collision: ${skill}/${stem} has both .json and .md files`);
81
+ const file = matches[0];
82
+ const raw = readRawRecipeConfig(file);
83
+ if (!raw || !Object.hasOwn(raw, "template"))
84
+ throw new Error(`Invalid Skill Recipe component: ${skill}/${stem}`);
85
+ assertRecipeHasNoDeclaredName(raw);
86
+ components.push({
87
+ file,
88
+ identity: `${skill}/${stem}`,
89
+ imports: Object.fromEntries(Object.entries(getRecipeImports(raw)).map(([alias, value]) => [
90
+ alias,
91
+ getImportFrom(value),
92
+ ])),
93
+ skill,
94
+ stem,
95
+ });
96
+ }
97
+ }
98
+ return components;
37
99
  }
38
- function qualifiedRecipeNameForFile(file) {
100
+ const SKILL_RECIPE_REFERENCE = /^([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\/([a-z][a-z0-9]*(?:-[a-z0-9]+)*)$/;
101
+ function qualifiedRecipeNameForFile(file, context) {
39
102
  const path = resolve(file);
40
- const stdRelation = relative(Paths.getPackagedRecipeRoot(), path);
41
- if (stdRelation &&
42
- !stdRelation.startsWith("..") &&
43
- !isAbsolute(stdRelation)) {
44
- return `std:${stdRelation.replace(/\.(?:json|md)$/u, "")}`;
45
- }
46
- for (const [skillName, roots] of activeSkillRecipeRoots) {
47
- if (roots.size !== 1)
103
+ for (const [skillName, roots] of Object.entries(context.namespaces)) {
104
+ if (roots.length !== 1)
48
105
  continue;
49
- const root = [...roots][0];
106
+ const root = roots[0];
50
107
  const relation = relative(root, path);
51
- if (relation && !relation.startsWith("..") && !isAbsolute(relation))
52
- return `skill:${skillName}/${relation.replaceAll("\\", "/").replace(/\.(?:json|md)$/u, "")}`;
108
+ if (relation &&
109
+ dirname(relation) === "." &&
110
+ !relation.startsWith("..") &&
111
+ !isAbsolute(relation)) {
112
+ return `${skillName}/${basename(relation, extname(relation))}`;
113
+ }
53
114
  }
54
115
  return undefined;
55
116
  }
56
- function qualifiedRecipeCandidates(value) {
117
+ function assertRecipeReferencePrefixWasNotRemoved(value) {
57
118
  if (value.startsWith("std:")) {
58
- return recipeNameFiles(value.slice(4)).map((file) => resolve(Paths.getPackagedRecipeRoot(), file));
119
+ throw new Error("std: Recipe references were removed in pi-actors 0.46.\nUse <skill>/<recipe> or an explicit .json/.md file path.");
59
120
  }
60
- if (!value.startsWith("skill:"))
121
+ if (value.startsWith("skill:")) {
122
+ throw new Error("skill: Recipe references were removed in pi-actors 0.46.\nUse <skill>/<recipe> without a prefix.");
123
+ }
124
+ }
125
+ function skillRecipeCandidates(value, context) {
126
+ const match = SKILL_RECIPE_REFERENCE.exec(value);
127
+ if (!match)
61
128
  return undefined;
62
- const qualified = value.slice(6);
63
- const separator = qualified.indexOf("/");
64
- if (separator <= 0 || separator === qualified.length - 1)
65
- throw new Error(`Invalid Skill Recipe reference: ${value}`);
66
- const skillName = qualified.slice(0, separator);
67
- const recipeName = qualified.slice(separator + 1);
68
- const roots = [...(activeSkillRecipeRoots.get(skillName) ?? [])];
129
+ const [, skillName, recipeName] = match;
130
+ const roots = [...(context.namespaces[skillName] ?? [])];
69
131
  if (roots.length === 0)
70
- throw new Error(`Active Skill Recipe namespace not found: ${skillName}`);
132
+ throw new Error(`Active Skill Recipe not found: ${skillName}/${recipeName}`);
71
133
  if (roots.length > 1)
72
- throw new Error(`Ambiguous active Skill Recipe namespace ${skillName}: ${roots.join(", ")}`);
134
+ throw new Error(`Duplicate active Skill identity ${skillName}: ${roots.join(", ")}`);
73
135
  const root = roots[0];
74
- return recipeNameFiles(recipeName).map((file) => {
75
- const candidate = resolve(root, file);
76
- const relation = relative(root, candidate);
77
- if (!relation || relation.startsWith("..") || isAbsolute(relation))
78
- throw new Error(`Skill Recipe reference escapes its recipes root: ${value}`);
79
- return candidate;
80
- });
136
+ const candidates = ["json", "md"].map((extension) => resolve(root, `${recipeName}.${extension}`));
137
+ const existing = candidates.filter((candidate) => existsSync(candidate));
138
+ if (existing.length > 1) {
139
+ throw new Error(`Skill Recipe stem collision: ${skillName}/${recipeName} has both .json and .md files`);
140
+ }
141
+ return candidates;
81
142
  }
82
- function hasWhitespace(value) {
83
- return /\s/.test(value);
143
+ function recipeReferenceCandidates(value, baseDir, context) {
144
+ const trimmed = value.trim();
145
+ if (!trimmed)
146
+ return undefined;
147
+ assertRecipeReferencePrefixWasNotRemoved(trimmed);
148
+ if (trimmed.endsWith(".json") || trimmed.endsWith(".md")) {
149
+ return [resolve(baseDir, trimmed)];
150
+ }
151
+ return skillRecipeCandidates(trimmed, context);
84
152
  }
85
153
  function findOwningSkillDir(recipeFile) {
86
154
  let current = dirname(recipeFile);
@@ -93,6 +161,11 @@ function findOwningSkillDir(recipeFile) {
93
161
  current = parent;
94
162
  }
95
163
  }
164
+ function assertRecipeHasNoDeclaredName(raw) {
165
+ if (!Object.hasOwn(raw, "name"))
166
+ return;
167
+ throw new Error("Recipe.name was removed in pi-actors 0.46.\nFile-backed Recipe identity is derived from its file name.");
168
+ }
96
169
  function assertRuntimeRecipeValuesNotDeclared(raw) {
97
170
  const args = Array.isArray(raw.args)
98
171
  ? Schema.getExplicitToolArgNames(raw.args.map(String))
@@ -105,82 +178,28 @@ function assertRuntimeRecipeValuesNotDeclared(raw) {
105
178
  }
106
179
  }
107
180
  }
108
- export function resolveRecipePath(value, recipeRoot = Paths.getRecipeRoot()) {
109
- const trimmed = value.trim();
110
- const repoRoot = resolve(recipeRoot, "..");
111
- const expanded = trimmed
112
- .replaceAll("{repo}", repoRoot)
113
- .replaceAll("{agent}", Paths.getAgentDir());
114
- if (expanded.startsWith("~/"))
115
- return resolve(homedir(), expanded.slice(2));
116
- if (expanded.includes("/"))
117
- return resolve(expanded);
118
- return resolve(recipeRoot, expanded.endsWith(".json") || expanded.endsWith(".md")
119
- ? expanded
120
- : `${expanded}.json`);
121
- }
122
- function isBareRecipeName(value) {
123
- const trimmed = value.trim();
124
- return (Boolean(trimmed) &&
125
- !trimmed.includes("/") &&
126
- !trimmed.startsWith("~") &&
127
- !trimmed.includes("{"));
181
+ export function resolveRecipePath(value, baseDir = Paths.getRecipeRoot()) {
182
+ const candidates = recipeReferenceCandidates(value, baseDir, EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT);
183
+ if (!candidates || candidates.length !== 1) {
184
+ throw new Error(`Recipe file reference must be an explicit .json or .md path: ${value}`);
185
+ }
186
+ return candidates[0];
128
187
  }
129
- function recipeNameFiles(value) {
130
- const trimmed = value.trim();
131
- if (trimmed.endsWith(".json") || trimmed.endsWith(".md"))
132
- return [trimmed];
133
- return [`${trimmed}.json`, `${trimmed}.md`];
134
- }
135
- function recipeCandidatePaths(value, currentRecipeRoot) {
136
- const qualified = qualifiedRecipeCandidates(value.trim());
137
- if (qualified)
138
- return qualified;
139
- if (!isBareRecipeName(value))
140
- return [resolveRecipePath(value, currentRecipeRoot)];
141
- const roots = [
142
- Paths.getRecipeRoot(),
143
- currentRecipeRoot,
144
- Paths.getPackagedRecipeRoot(),
145
- ];
146
- return [
147
- ...new Set(roots.flatMap((root) => recipeNameFiles(value).map((file) => resolve(root, file)))),
148
- ];
149
- }
150
- function resolveRecipeImportPath(value, currentRecipeRoot) {
151
- const candidates = recipeCandidatePaths(value, currentRecipeRoot);
188
+ function resolveRecipeImportPath(value, recipeDir, context) {
189
+ const candidates = recipeReferenceCandidates(value, recipeDir, context);
190
+ if (!candidates) {
191
+ throw new Error(`Recipe import must use <skill>/<recipe> or an explicit .json/.md file path: ${value}`);
192
+ }
152
193
  return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
153
194
  }
154
- export function resolveRecipeReferencePath(value, currentRecipeRoot = Paths.getRecipeRoot()) {
195
+ export function resolveRecipeReferencePath(value, baseDir = Paths.getRecipeRoot(), context = EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT) {
155
196
  if (typeof value !== "string")
156
197
  return undefined;
157
- const trimmed = value.trim();
158
- if (!trimmed || hasWhitespace(trimmed))
159
- return undefined;
160
- for (const path of recipeCandidatePaths(trimmed, currentRecipeRoot)) {
161
- if (!existsSync(path))
162
- continue;
163
- try {
164
- const raw = readRawRecipeConfig(path);
165
- if (raw && typeof raw === "object" && Object.hasOwn(raw, "template"))
166
- return path;
167
- return path;
168
- }
169
- catch {
170
- return path;
171
- }
172
- }
173
- return undefined;
198
+ const candidates = recipeReferenceCandidates(value, baseDir, context);
199
+ return candidates?.find((candidate) => existsSync(candidate));
174
200
  }
175
- export function getRecipePath(value, recipeRoot = Paths.getRecipeRoot()) {
176
- if (typeof value !== "string")
177
- return undefined;
178
- const trimmed = value.trim();
179
- if (!trimmed || hasWhitespace(trimmed))
180
- return undefined;
181
- if (trimmed.endsWith(".json") || trimmed.endsWith(".md"))
182
- return resolveRecipePath(trimmed, recipeRoot);
183
- return resolveRecipeReferencePath(trimmed, recipeRoot);
201
+ export function getRecipePath(value, baseDir = Paths.getRecipeRoot(), context = EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT) {
202
+ return resolveRecipeReferencePath(value, baseDir, context);
184
203
  }
185
204
  function isImportNode(value) {
186
205
  if (!isRecord(value) || Object.hasOwn(value, "template"))
@@ -655,7 +674,7 @@ function withActorRecipeContext(value, context) {
655
674
  return { actorRecipeContext: context, template: value };
656
675
  }
657
676
  function loadDelegatedRecipe(value, currentRecipeFile, stack, options) {
658
- const path = resolveRecipeReferencePath(value, dirname(currentRecipeFile));
677
+ const path = resolveRecipeReferencePath(value, dirname(currentRecipeFile), options.skillContext);
659
678
  if (!path)
660
679
  return undefined;
661
680
  const config = readResolvedRecipeConfig(path, [...stack, currentRecipeFile], options);
@@ -767,12 +786,13 @@ export function readResolvedRecipeConfig(file, stack = [], options = {}) {
767
786
  const raw = readRawRecipeConfig(path);
768
787
  if (!raw || !Object.hasOwn(raw, "template"))
769
788
  return undefined;
789
+ assertRecipeHasNoDeclaredName(raw);
770
790
  RecipeControl.assertRecipeHasNoMailbox(raw);
771
791
  assertRuntimeRecipeValuesNotDeclared(raw);
772
792
  assertRecipeArgumentContract(raw);
773
793
  const imports = {};
774
794
  for (const [alias, binding] of Object.entries(getRecipeImports(raw))) {
775
- const importPath = resolveRecipeImportPath(getImportFrom(binding), dirname(path));
795
+ const importPath = resolveRecipeImportPath(getImportFrom(binding), dirname(path), options.skillContext ?? EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT);
776
796
  const config = readResolvedRecipeConfig(importPath, [...stack, path], options);
777
797
  if (!config)
778
798
  throw new Error(`Recipe import not found: ${alias}`);
@@ -782,7 +802,7 @@ export function readResolvedRecipeConfig(file, stack = [], options = {}) {
782
802
  imports[alias] = {
783
803
  alias,
784
804
  file: importPath,
785
- name: config.name ?? alias,
805
+ name: getRecipeIdFromPath(importPath),
786
806
  config,
787
807
  defaults: { ...(config.defaults ?? {}), ...(bindingDefaults ?? {}) },
788
808
  values: {
@@ -930,7 +950,7 @@ export function readResolvedRecipeConfig(file, stack = [], options = {}) {
930
950
  ...(isRecord(substituted.usage) ? { usage: substituted.usage } : {}),
931
951
  };
932
952
  }
933
- function collectRecipeContextRecords(file, stack, importPath, alias) {
953
+ function collectRecipeContextRecords(file, stack, importPath, context, alias) {
934
954
  const path = resolveRecipePath(file, stack.length > 0 ? dirname(stack.at(-1)) : Paths.getRecipeRoot());
935
955
  if (stack.includes(path)) {
936
956
  throw new Error(`Cyclic recipe import: ${[...stack, path].join(" -> ")}`);
@@ -941,26 +961,32 @@ function collectRecipeContextRecords(file, stack, importPath, alias) {
941
961
  const raw = readRawRecipeConfig(path);
942
962
  if (!raw || !Object.hasOwn(raw, "template"))
943
963
  return [];
944
- const qualifiedName = qualifiedRecipeNameForFile(path);
964
+ assertRecipeHasNoDeclaredName(raw);
965
+ const qualifiedName = qualifiedRecipeNameForFile(path, context);
966
+ const skill = qualifiedName?.split("/", 1)[0];
945
967
  const record = {
946
968
  ...(alias ? { alias } : {}),
947
969
  depth: stack.length,
948
- file: path,
949
970
  import_path: importPath,
971
+ logical_reference: qualifiedName ?? basename(path),
950
972
  name: getRecipeIdFromPath(path),
951
- ...(qualifiedName ? { qualified_name: qualifiedName } : {}),
952
973
  recipe: raw,
953
974
  role: stack.length === 0 ? "entry" : "import",
975
+ ...(skill ? { skill } : {}),
976
+ source_file: path,
977
+ source_kind: qualifiedName
978
+ ? "active_skill_component"
979
+ : "explicit_file_recipe",
954
980
  };
955
981
  const imports = getRecipeImports(raw);
956
982
  const children = Object.entries(imports).flatMap(([childAlias, binding]) => {
957
- const importFile = resolveRecipeImportPath(getImportFrom(binding), dirname(path));
958
- return collectRecipeContextRecords(importFile, [...stack, path], [...importPath, childAlias], childAlias);
983
+ const importFile = resolveRecipeImportPath(getImportFrom(binding), dirname(path), context);
984
+ return collectRecipeContextRecords(importFile, [...stack, path], [...importPath, childAlias], context, childAlias);
959
985
  });
960
986
  return [record, ...children];
961
987
  }
962
- export function buildRecipeContextRecords(file) {
963
- return collectRecipeContextRecords(file, [], []);
988
+ export function buildRecipeContextRecords(file, context = EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT) {
989
+ return collectRecipeContextRecords(file, [], [], context);
964
990
  }
965
991
  export function getRecipeTemplate(value) {
966
992
  return readRecipeConfig(value)?.template;
@@ -16,7 +16,6 @@ export interface RuntimeContext {
16
16
  export interface ToolRegistryRuntimeDeps {
17
17
  configPath: string;
18
18
  exec: RegisteredToolExec;
19
- packagedRecipeRoot?: string;
20
19
  recipeRoot?: string;
21
20
  getActiveTools?: () => string[];
22
21
  registerTool: (definition: ReturnType<typeof ToolsLocal.createRuntimeToolDefinition>) => void;
@@ -69,7 +69,7 @@ export function createAutoToolsRuntime(deps) {
69
69
  const other = warnings.filter((warning) => !shadowed.includes(warning) && !skipped.includes(warning));
70
70
  const lines = ["pi-actors recipe registry warning"];
71
71
  if (shadowed.length > 0) {
72
- lines.push("User recipes override packaged recipes:");
72
+ lines.push("User recipes shadow lower-priority user sources:");
73
73
  lines.push(...shadowed.map((warning) => `• ${warning}`));
74
74
  }
75
75
  if (skipped.length > 0) {
@@ -85,10 +85,8 @@ export function createAutoToolsRuntime(deps) {
85
85
  function loadTools(ctx) {
86
86
  const warnings = [];
87
87
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
88
- const packagedRecipeRoot = deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot();
89
88
  const discovered = RecipesDiscovery.discoverRecipeSources([
90
89
  { root: recipeRoot, defaultTool: true, mutableUsage: true },
91
- { root: packagedRecipeRoot },
92
90
  ]);
93
91
  warnings.push(...discovered.diagnostics);
94
92
  tools.clear();
@@ -7,7 +7,6 @@ export interface InspectToolDeps<TContext = unknown> {
7
7
  getRunStatus?: (runOrDir: string) => Record<string, any>;
8
8
  getTool?: (name: string) => any | undefined;
9
9
  listRuns?: () => Array<Record<string, any>>;
10
- packagedRecipeRoot?: string;
11
10
  recipeRoot?: string;
12
11
  }
13
12
  export declare function createInspectToolDefinition<TContext = unknown>(deps?: InspectToolDeps<TContext>): any;
@@ -157,7 +157,7 @@ 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 inspectRecipes(view, deps) {
160
+ function inspectRecipes(view, deps, context) {
161
161
  if (view !== "status" &&
162
162
  view !== "summary" &&
163
163
  view !== "doctor" &&
@@ -171,17 +171,44 @@ function inspectRecipes(view, deps) {
171
171
  }
172
172
  const discovered = RecipesDiscovery.discoverRecipeSources([
173
173
  { root: recipeRoot, defaultTool: true, mutableUsage: true },
174
- { root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
175
174
  ]);
176
- const skillRecipeNamespaces = RecipesReferences.getActiveSkillRecipeNamespaces();
175
+ const skillContext = context && typeof context === "object" && "activeSkillRecipeContext" in context
176
+ ? context.activeSkillRecipeContext
177
+ : undefined;
178
+ const activeSkillContext = skillContext ?? RecipesReferences.EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT;
179
+ const skillRecipeNamespaces = RecipesReferences.getActiveSkillRecipeNamespaces(activeSkillContext);
180
+ let skillRecipeComponents = [];
181
+ const skillRecipeComponentDiagnostics = [];
182
+ try {
183
+ skillRecipeComponents = RecipesReferences.listActiveSkillRecipeComponents(activeSkillContext).map((component) => ({
184
+ identity: component.identity,
185
+ source_kind: "active_skill_component",
186
+ skill: component.skill,
187
+ stem: component.stem,
188
+ imports: component.imports,
189
+ }));
190
+ }
191
+ catch (error) {
192
+ skillRecipeComponentDiagnostics.push({
193
+ error: error instanceof Error ? error.message : String(error),
194
+ });
195
+ }
196
+ const discoverySummary = RecipesDiscovery.summarizeDiscovery(discovered);
177
197
  const summary = {
178
- ...RecipesDiscovery.summarizeDiscovery(discovered),
179
- skill_recipe_namespaces: skillRecipeNamespaces,
198
+ ...discoverySummary,
199
+ active: Array.isArray(discoverySummary.active)
200
+ ? discoverySummary.active.map((entry) => ({
201
+ ...entry,
202
+ source_kind: "user_registry_capability",
203
+ }))
204
+ : discoverySummary.active,
205
+ skill_recipe_components: skillRecipeComponents,
206
+ skill_recipe_component_diagnostics: skillRecipeComponentDiagnostics,
207
+ active_skill_recipe_identities: Object.keys(skillRecipeNamespaces).sort(),
180
208
  skill_recipe_namespace_diagnostics: Object.entries(skillRecipeNamespaces)
181
209
  .filter(([, roots]) => roots.length > 1)
182
- .map(([name, roots]) => ({
210
+ .map(([name]) => ({
183
211
  name,
184
- roots,
185
212
  error: `Ambiguous active Skill Recipe namespace ${name}`,
186
213
  })),
187
214
  drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
@@ -316,7 +343,7 @@ export function createInspectToolDefinition(deps = {}) {
316
343
  details = inspectRuntime(view, input, ctx, deps);
317
344
  }
318
345
  else if (target === "recipes") {
319
- details = inspectRecipes(view, deps);
346
+ details = inspectRecipes(view, deps, ctx);
320
347
  }
321
348
  else {
322
349
  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 * as RecipesReferences from "./recipes-references.ts";
9
10
  export interface RuntimeToolContext extends ModelContext.CurrentModelContext {
11
+ activeSkillRecipeContext?: RecipesReferences.ActiveSkillRecipeContext;
10
12
  cwd: string;
11
13
  sessionManager?: {
12
14
  getSessionId?: () => string;
@@ -160,7 +160,7 @@ export function createRuntimeToolDefinition(cfg, exec) {
160
160
  tool: cfg.name,
161
161
  policy_values: ModelContext.withCurrentModelValues({ ...(cfg.recipe?.values ?? {}), ...values }, ctx),
162
162
  values: resolveRecipeToolValues(cfg, values, ctx),
163
- }, ctx.cwd);
163
+ }, ctx.cwd, { skillContext: ctx.activeSkillRecipeContext });
164
164
  return {
165
165
  content: [
166
166
  {
@@ -4,7 +4,9 @@
4
4
  * Owns the public spawn execution path for run-backed actors
5
5
  */
6
6
  import * as ModelContext from "./model-context.ts";
7
+ import * as RecipesReferences from "./recipes-references.ts";
7
8
  export interface SpawnToolContext extends ModelContext.CurrentModelContext {
9
+ activeSkillRecipeContext?: RecipesReferences.ActiveSkillRecipeContext;
8
10
  cwd: string;
9
11
  sessionManager?: {
10
12
  getSessionId?: () => string;