@llblab/pi-actors 0.45.0 → 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 (211) hide show
  1. package/AGENTS.md +10 -6
  2. package/CHANGELOG.md +14 -1
  3. package/README.md +28 -12
  4. package/dist/fixtures/protocol/recipe-summary.json +1 -1
  5. package/dist/index.d.ts +1 -2
  6. package/dist/index.js +10 -110
  7. package/dist/lib/async-runs.d.ts +4 -1
  8. package/dist/lib/async-runs.js +22 -23
  9. package/dist/lib/automatic-review-runtime.d.ts +2 -0
  10. package/dist/lib/automatic-review-runtime.js +12 -5
  11. package/dist/lib/draft-sleep.js +0 -1
  12. package/dist/lib/extension-runtime.d.ts +21 -0
  13. package/dist/lib/extension-runtime.js +122 -0
  14. package/dist/lib/inspector.js +45 -7
  15. package/dist/lib/paths.d.ts +0 -1
  16. package/dist/lib/paths.js +0 -7
  17. package/dist/lib/prompts.d.ts +1 -1
  18. package/dist/lib/prompts.js +2 -2
  19. package/dist/lib/recipes-context.js +2 -2
  20. package/dist/lib/recipes-references.d.ts +24 -9
  21. package/dist/lib/recipes-references.js +152 -126
  22. package/dist/lib/runtime.d.ts +0 -1
  23. package/dist/lib/runtime.js +1 -3
  24. package/dist/lib/tools-inspect.d.ts +0 -1
  25. package/dist/lib/tools-inspect.js +35 -8
  26. package/dist/lib/tools-local.d.ts +2 -0
  27. package/dist/lib/tools-local.js +1 -1
  28. package/dist/lib/tools-spawn.d.ts +2 -0
  29. package/dist/lib/tools-spawn.js +24 -52
  30. package/dist/scripts/async-runner.mjs +0 -16
  31. package/dist/scripts/build-dist.mjs +13 -4
  32. package/dist/skills/actors/SKILL.md +6 -6
  33. package/dist/skills/actors/recipes/recipe-validate.json +11 -0
  34. package/{recipes/utility-coordinator-lock-snapshot.json → dist/skills/actors/recipes/resource-locker-snapshot.json} +1 -3
  35. package/dist/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
  36. package/dist/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
  37. package/dist/skills/actors/recipes/run-summary.json +9 -0
  38. package/dist/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -23
  39. package/dist/skills/actors/scripts/run-utils.mjs +164 -0
  40. package/{scripts → dist/skills/actors/scripts}/validate-recipe.mjs +134 -18
  41. package/dist/skills/artifacts/SKILL.md +16 -0
  42. package/{recipes/pipeline-artifact-bundle.json → dist/skills/artifacts/recipes/bundle.json} +4 -4
  43. package/dist/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
  44. package/dist/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
  45. package/{recipes/pipeline-artifact-report.json → dist/skills/artifacts/recipes/report.json} +2 -2
  46. package/dist/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
  47. package/dist/skills/artifacts/scripts/artifact-utils.mjs +63 -0
  48. package/dist/skills/media/SKILL.md +16 -0
  49. package/dist/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
  50. package/{recipes/music-player.json → dist/skills/media/recipes/player.json} +1 -3
  51. package/dist/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
  52. package/dist/skills/media/scripts/media-utils.mjs +47 -0
  53. package/dist/{scripts → skills/media/scripts}/music-player.mjs +9 -3
  54. package/dist/skills/project-work/SKILL.md +16 -0
  55. package/dist/skills/project-work/recipes/changelog-section.json +11 -0
  56. package/{recipes/pipeline-docs-maintenance.json → dist/skills/project-work/recipes/docs-maintenance.json} +4 -4
  57. package/dist/skills/project-work/recipes/package-summary.json +9 -0
  58. package/{recipes/pipeline-release-readiness.json → dist/skills/project-work/recipes/release-readiness.json} +6 -6
  59. package/dist/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
  60. package/{recipes/pipeline-repo-health.json → dist/skills/project-work/recipes/repo-health.json} +6 -6
  61. package/{recipes/pipeline-async-run-ops.json → dist/skills/project-work/recipes/run-ops.json} +3 -3
  62. package/dist/skills/project-work/recipes/skill-summary.json +11 -0
  63. package/dist/skills/project-work/scripts/project-utils.mjs +89 -0
  64. package/dist/skills/recipe-memory/SKILL.md +16 -0
  65. package/dist/skills/swarm/SKILL.md +4 -4
  66. package/{recipes/pipeline-architect-coordinator.json → dist/skills/swarm/recipes/architect.json} +5 -5
  67. package/{recipes/pipeline-checkpoint-continuation.json → dist/skills/swarm/recipes/checkpoint-continuation.json} +3 -3
  68. package/dist/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
  69. package/dist/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
  70. package/dist/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
  71. package/{recipes/pipeline-research-synthesis.json → dist/skills/swarm/recipes/research-synthesis.json} +6 -6
  72. package/{recipes/pipeline-review-readiness.json → dist/skills/swarm/recipes/review-readiness.json} +1 -1
  73. package/{recipes/subagents-prompts.json → dist/skills/swarm/recipes/subagent-prompts.json} +1 -1
  74. package/{recipes → dist/skills/swarm/recipes}/subagent-quorum.json +1 -1
  75. package/dist/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
  76. package/docs/actor-inspector.md +4 -3
  77. package/docs/async-runs.md +2 -2
  78. package/docs/recipe-library.md +23 -23
  79. package/docs/releasing.md +2 -2
  80. package/docs/template-recipes.md +30 -14
  81. package/docs/tool-registry.md +2 -2
  82. package/fixtures/protocol/recipe-summary.json +1 -1
  83. package/index.ts +18 -112
  84. package/lib/async-runs.ts +47 -24
  85. package/lib/automatic-review-runtime.ts +16 -3
  86. package/lib/draft-sleep.ts +0 -1
  87. package/lib/extension-runtime.ts +158 -0
  88. package/lib/inspector.ts +56 -11
  89. package/lib/paths.ts +0 -7
  90. package/lib/prompts.ts +2 -2
  91. package/lib/recipes-context.ts +2 -2
  92. package/lib/recipes-references.ts +223 -134
  93. package/lib/runtime.ts +1 -5
  94. package/lib/tools-inspect.ts +42 -9
  95. package/lib/tools-local.ts +2 -0
  96. package/lib/tools-spawn.ts +37 -74
  97. package/package.json +3 -5
  98. package/scripts/async-runner.mjs +0 -16
  99. package/scripts/build-dist.mjs +13 -4
  100. package/skills/actors/SKILL.md +6 -6
  101. package/skills/actors/recipes/recipe-validate.json +11 -0
  102. package/{dist/recipes/utility-coordinator-lock-snapshot.json → skills/actors/recipes/resource-locker-snapshot.json} +1 -3
  103. package/{recipes → skills/actors/recipes}/resource-locker.json +1 -3
  104. package/{recipes/utility-run-ops-snapshot.json → skills/actors/recipes/run-ops-snapshot.json} +1 -3
  105. package/skills/actors/recipes/run-summary.json +9 -0
  106. package/{scripts/locker.mjs → skills/actors/scripts/resource-locker.mjs} +2 -23
  107. package/skills/actors/scripts/run-utils.mjs +164 -0
  108. package/{dist → skills/actors}/scripts/validate-recipe.mjs +134 -18
  109. package/skills/artifacts/SKILL.md +16 -0
  110. package/{dist/recipes/pipeline-artifact-bundle.json → skills/artifacts/recipes/bundle.json} +4 -4
  111. package/{recipes/utility-artifact-write.json → skills/artifacts/recipes/file-write.json} +1 -3
  112. package/{recipes/utility-artifact-manifest.json → skills/artifacts/recipes/manifest.json} +1 -3
  113. package/{dist/recipes/pipeline-artifact-report.json → skills/artifacts/recipes/report.json} +2 -2
  114. package/{recipes/pipeline-artifact-write.json → skills/artifacts/recipes/write.json} +3 -3
  115. package/skills/artifacts/scripts/artifact-utils.mjs +63 -0
  116. package/skills/media/SKILL.md +16 -0
  117. package/{recipes/pipeline-media-library.json → skills/media/recipes/library.json} +2 -2
  118. package/{dist/recipes/music-player.json → skills/media/recipes/player.json} +1 -3
  119. package/{recipes/utility-playlist-build.json → skills/media/recipes/playlist-build.json} +1 -3
  120. package/skills/media/scripts/media-utils.mjs +47 -0
  121. package/{scripts → skills/media/scripts}/music-player.mjs +9 -3
  122. package/skills/project-work/SKILL.md +16 -0
  123. package/skills/project-work/recipes/changelog-section.json +11 -0
  124. package/{dist/recipes/pipeline-docs-maintenance.json → skills/project-work/recipes/docs-maintenance.json} +4 -4
  125. package/skills/project-work/recipes/package-summary.json +9 -0
  126. package/{dist/recipes/pipeline-release-readiness.json → skills/project-work/recipes/release-readiness.json} +6 -6
  127. package/{recipes/pipeline-release-summary.json → skills/project-work/recipes/release-summary.json} +5 -5
  128. package/{dist/recipes/pipeline-repo-health.json → skills/project-work/recipes/repo-health.json} +6 -6
  129. package/{dist/recipes/pipeline-async-run-ops.json → skills/project-work/recipes/run-ops.json} +3 -3
  130. package/skills/project-work/recipes/skill-summary.json +11 -0
  131. package/skills/project-work/scripts/project-utils.mjs +89 -0
  132. package/skills/recipe-memory/SKILL.md +16 -0
  133. package/skills/swarm/SKILL.md +4 -4
  134. package/{dist/recipes/pipeline-architect-coordinator.json → skills/swarm/recipes/architect.json} +5 -5
  135. package/{dist/recipes/pipeline-checkpoint-continuation.json → skills/swarm/recipes/checkpoint-continuation.json} +3 -3
  136. package/{recipes/pipeline-development-tasking.json → skills/swarm/recipes/development-tasking.json} +4 -4
  137. package/{recipes/lens-swarm.json → skills/swarm/recipes/lens-review.json} +1 -1
  138. package/{recipes/pipeline-quorum-review.json → skills/swarm/recipes/quorum-review.json} +4 -4
  139. package/{dist/recipes/pipeline-research-synthesis.json → skills/swarm/recipes/research-synthesis.json} +6 -6
  140. package/{dist/recipes/pipeline-review-readiness.json → skills/swarm/recipes/review-readiness.json} +1 -1
  141. package/{dist/recipes/subagents-prompts.json → skills/swarm/recipes/subagent-prompts.json} +1 -1
  142. package/{dist → skills/swarm}/recipes/subagent-quorum.json +1 -1
  143. package/{recipes → skills/swarm/recipes}/subagent-review-coordinator.json +6 -6
  144. package/dist/recipes/utility-changelog-section.json +0 -13
  145. package/dist/recipes/utility-package-summary.json +0 -11
  146. package/dist/recipes/utility-run-summary.json +0 -11
  147. package/dist/recipes/utility-skill-summary.json +0 -13
  148. package/dist/recipes/utility-validate-recipe.json +0 -13
  149. package/dist/scripts/recipe-utils.mjs +0 -405
  150. package/dist/scripts/release-gates.mjs +0 -278
  151. package/recipes/utility-changelog-section.json +0 -13
  152. package/recipes/utility-package-summary.json +0 -11
  153. package/recipes/utility-run-summary.json +0 -11
  154. package/recipes/utility-skill-summary.json +0 -13
  155. package/recipes/utility-validate-recipe.json +0 -13
  156. package/scripts/recipe-utils.mjs +0 -405
  157. package/scripts/release-gates.mjs +0 -278
  158. /package/dist/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
  159. /package/dist/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
  160. /package/dist/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
  161. /package/dist/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
  162. /package/dist/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
  163. /package/dist/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
  164. /package/dist/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
  165. /package/dist/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
  166. /package/dist/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
  167. /package/dist/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
  168. /package/dist/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
  169. /package/dist/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
  170. /package/dist/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
  171. /package/dist/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
  172. /package/dist/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
  173. /package/dist/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
  174. /package/dist/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
  175. /package/dist/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
  176. /package/dist/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
  177. /package/dist/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
  178. /package/dist/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
  179. /package/dist/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
  180. /package/dist/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
  181. /package/dist/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
  182. /package/dist/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
  183. /package/dist/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
  184. /package/dist/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
  185. /package/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
  186. /package/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
  187. /package/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
  188. /package/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
  189. /package/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
  190. /package/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
  191. /package/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
  192. /package/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
  193. /package/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
  194. /package/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
  195. /package/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
  196. /package/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
  197. /package/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
  198. /package/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
  199. /package/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
  200. /package/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
  201. /package/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
  202. /package/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
  203. /package/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
  204. /package/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
  205. /package/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
  206. /package/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
  207. /package/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
  208. /package/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
  209. /package/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
  210. /package/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
  211. /package/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
package/lib/runtime.ts CHANGED
@@ -23,7 +23,6 @@ export interface RuntimeContext {
23
23
  export interface ToolRegistryRuntimeDeps {
24
24
  configPath: string;
25
25
  exec: RegisteredToolExec;
26
- packagedRecipeRoot?: string;
27
26
  recipeRoot?: string;
28
27
  getActiveTools?: () => string[];
29
28
  registerTool: (
@@ -125,7 +124,7 @@ export function createAutoToolsRuntime(
125
124
  );
126
125
  const lines = ["pi-actors recipe registry warning"];
127
126
  if (shadowed.length > 0) {
128
- lines.push("User recipes override packaged recipes:");
127
+ lines.push("User recipes shadow lower-priority user sources:");
129
128
  lines.push(...shadowed.map((warning) => `• ${warning}`));
130
129
  }
131
130
  if (skipped.length > 0) {
@@ -141,11 +140,8 @@ export function createAutoToolsRuntime(
141
140
  function loadTools(ctx: RuntimeContext) {
142
141
  const warnings: string[] = [];
143
142
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
144
- const packagedRecipeRoot =
145
- deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot();
146
143
  const discovered = RecipesDiscovery.discoverRecipeSources([
147
144
  { root: recipeRoot, defaultTool: true, mutableUsage: true },
148
- { root: packagedRecipeRoot },
149
145
  ]);
150
146
  warnings.push(...discovered.diagnostics);
151
147
  tools.clear();
@@ -33,7 +33,6 @@ export interface InspectToolDeps<TContext = unknown> {
33
33
  getRunStatus?: (runOrDir: string) => Record<string, any>;
34
34
  getTool?: (name: string) => any | undefined;
35
35
  listRuns?: () => Array<Record<string, any>>;
36
- packagedRecipeRoot?: string;
37
36
  recipeRoot?: string;
38
37
  }
39
38
 
@@ -195,6 +194,7 @@ function inspectRuntime(
195
194
  function inspectRecipes(
196
195
  view: string,
197
196
  deps: InspectToolDeps,
197
+ context: unknown,
198
198
  ): Record<string, unknown> {
199
199
  if (
200
200
  view !== "status" &&
@@ -213,18 +213,51 @@ function inspectRecipes(
213
213
  }
214
214
  const discovered = RecipesDiscovery.discoverRecipeSources([
215
215
  { root: recipeRoot, defaultTool: true, mutableUsage: true },
216
- { root: deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot() },
217
216
  ]);
218
- const skillRecipeNamespaces =
219
- RecipesReferences.getActiveSkillRecipeNamespaces();
217
+ const skillContext =
218
+ context && typeof context === "object" && "activeSkillRecipeContext" in context
219
+ ? (context as {
220
+ activeSkillRecipeContext?: RecipesReferences.ActiveSkillRecipeContext;
221
+ }).activeSkillRecipeContext
222
+ : undefined;
223
+ const activeSkillContext =
224
+ skillContext ?? RecipesReferences.EMPTY_ACTIVE_SKILL_RECIPE_CONTEXT;
225
+ const skillRecipeNamespaces = RecipesReferences.getActiveSkillRecipeNamespaces(
226
+ activeSkillContext,
227
+ );
228
+ let skillRecipeComponents: Array<Record<string, unknown>> = [];
229
+ const skillRecipeComponentDiagnostics: Array<Record<string, unknown>> = [];
230
+ try {
231
+ skillRecipeComponents = RecipesReferences.listActiveSkillRecipeComponents(
232
+ activeSkillContext,
233
+ ).map((component) => ({
234
+ identity: component.identity,
235
+ source_kind: "active_skill_component",
236
+ skill: component.skill,
237
+ stem: component.stem,
238
+ imports: component.imports,
239
+ }));
240
+ } catch (error) {
241
+ skillRecipeComponentDiagnostics.push({
242
+ error: error instanceof Error ? error.message : String(error),
243
+ });
244
+ }
245
+ const discoverySummary = RecipesDiscovery.summarizeDiscovery(discovered);
220
246
  const summary = {
221
- ...RecipesDiscovery.summarizeDiscovery(discovered),
222
- skill_recipe_namespaces: skillRecipeNamespaces,
247
+ ...discoverySummary,
248
+ active: Array.isArray(discoverySummary.active)
249
+ ? discoverySummary.active.map((entry) => ({
250
+ ...(entry as Record<string, unknown>),
251
+ source_kind: "user_registry_capability",
252
+ }))
253
+ : discoverySummary.active,
254
+ skill_recipe_components: skillRecipeComponents,
255
+ skill_recipe_component_diagnostics: skillRecipeComponentDiagnostics,
256
+ active_skill_recipe_identities: Object.keys(skillRecipeNamespaces).sort(),
223
257
  skill_recipe_namespace_diagnostics: Object.entries(skillRecipeNamespaces)
224
258
  .filter(([, roots]) => roots.length > 1)
225
- .map(([name, roots]) => ({
259
+ .map(([name]) => ({
226
260
  name,
227
- roots,
228
261
  error: `Ambiguous active Skill Recipe namespace ${name}`,
229
262
  })),
230
263
  drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
@@ -386,7 +419,7 @@ export function createInspectToolDefinition<TContext = unknown>(
386
419
  if (target === "runtime") {
387
420
  details = inspectRuntime(view, input, ctx, deps);
388
421
  } else if (target === "recipes") {
389
- details = inspectRecipes(view, deps);
422
+ details = inspectRecipes(view, deps, ctx);
390
423
  } else {
391
424
  const run = parseRunTarget(target);
392
425
  if (run) details = inspectRun(run, view, input, ctx, deps);
@@ -18,6 +18,7 @@ import * as ToolsResponse from "./tools-response.ts";
18
18
  type JsonSchema = Schema.JsonSchema;
19
19
 
20
20
  export interface RuntimeToolContext extends ModelContext.CurrentModelContext {
21
+ activeSkillRecipeContext?: RecipesReferences.ActiveSkillRecipeContext;
21
22
  cwd: string;
22
23
  sessionManager?: { getSessionId?: () => string };
23
24
  }
@@ -243,6 +244,7 @@ export function createRuntimeToolDefinition(
243
244
  values: resolveRecipeToolValues(cfg, values, ctx),
244
245
  },
245
246
  ctx.cwd,
247
+ { skillContext: ctx.activeSkillRecipeContext },
246
248
  );
247
249
  return {
248
250
  content: [
@@ -11,12 +11,13 @@ import * as AsyncRuns from "./async-runs.ts";
11
11
  import { withFileMutationLock } from "./file-state.ts";
12
12
  import * as ModelContext from "./model-context.ts";
13
13
  import * as Paths from "./paths.ts";
14
- import * as RecipesDiscovery from "./recipes-discovery.ts";
14
+ import * as RecipesReferences from "./recipes-references.ts";
15
15
  import * as RecipesUsage from "./recipes-usage.ts";
16
16
  import * as Schema from "./schema.ts";
17
17
  import * as ToolsResponse from "./tools-response.ts";
18
18
 
19
19
  export interface SpawnToolContext extends ModelContext.CurrentModelContext {
20
+ activeSkillRecipeContext?: RecipesReferences.ActiveSkillRecipeContext;
20
21
  cwd: string;
21
22
  sessionManager?: { getSessionId?: () => string };
22
23
  }
@@ -28,22 +29,6 @@ function getRunOwnerId(ctx: SpawnToolContext): string | undefined {
28
29
  return ctx.sessionManager?.getSessionId?.();
29
30
  }
30
31
 
31
- function shadowedRecipeLaunchDiagnostic(
32
- recipe: unknown,
33
- ): Record<string, unknown> | undefined {
34
- if (
35
- typeof recipe !== "string" ||
36
- recipe.includes("/") ||
37
- recipe.includes("~")
38
- )
39
- return undefined;
40
- const discovery = RecipesDiscovery.discoverRecipeSources([
41
- { root: Paths.getRecipeRoot(), defaultTool: true, mutableUsage: true },
42
- { root: Paths.getPackagedRecipeRoot(), defaultTool: false },
43
- ]);
44
- return RecipesDiscovery.getShadowedLaunchDiagnostic(discovery, recipe);
45
- }
46
-
47
32
  function draftRecipeName(run: string): string {
48
33
  return `${run.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "spawn"}.json`;
49
34
  }
@@ -93,22 +78,6 @@ function writeSpawnDraftRecipe(
93
78
  return path;
94
79
  }
95
80
 
96
- function enhanceSpawnRecipeError(error: unknown, recipe: unknown): Error {
97
- const diagnostic = shadowedRecipeLaunchDiagnostic(recipe);
98
- if (!diagnostic)
99
- return error instanceof Error ? error : new Error(String(error));
100
- const original = error instanceof Error ? error.message : String(error);
101
- return Object.assign(
102
- new Error(
103
- `${original} reason=${diagnostic.reason} active_path=${diagnostic.active_path} blocked_fallback=${diagnostic.blocked_fallback} hint=${diagnostic.hint}`,
104
- ),
105
- {
106
- ...diagnostic,
107
- original_error: original,
108
- },
109
- );
110
- }
111
-
112
81
  function runIdFromTarget(target: string | undefined): string | undefined {
113
82
  if (!target) return undefined;
114
83
  if (!target.startsWith("run:")) {
@@ -138,11 +107,9 @@ export function createSpawnToolDefinition<
138
107
  "Optional Run identity in exact run:<id> form.",
139
108
  ),
140
109
  file: Schema.stringSchema(
141
- "Optional template Recipe reference. Supports paths, bare user/adjacent/std names, std:<name>, and skill:<skill>/<recipe>.",
142
- ),
143
- recipe: Schema.stringSchema(
144
- "Alias for file; qualified Skill/std or ordinary Recipe reference to spawn.",
110
+ "Optional Recipe reference as <skill>/<recipe> or an explicit .json/.md file path.",
145
111
  ),
112
+ recipe: Schema.stringSchema("Alias for file."),
146
113
  template: Schema.unionSchema([
147
114
  Schema.stringSchema("Inline command template string"),
148
115
  Schema.arraySchema(
@@ -186,43 +153,39 @@ export function createSpawnToolDefinition<
186
153
  : typeof input.recipe === "string"
187
154
  ? input.recipe
188
155
  : undefined;
189
- let meta: AsyncRuns.AsyncRunMeta;
190
- try {
191
- meta = AsyncRuns.startRun(
192
- {
193
- file: recipe,
194
- launch_source: "spawn",
195
- launch_correlation: { tool_call_id: toolCallId },
196
- ownerId: getRunOwnerId(ctx),
197
- ...(input.transport_context
198
- ? { transport_context: asRecord(input.transport_context) } : {}),
199
- run_id: runId,
200
- ...(input.template !== undefined
201
- ? {
202
- template:
203
- input.template as AsyncRuns.AsyncRunStartParams["template"],
204
- }
205
- : {}),
206
- values: ModelContext.withCurrentModelValues(
207
- asRecord(input.values),
208
- ctx,
209
- ),
210
- ...(input.artifacts &&
211
- typeof input.artifacts === "object" &&
212
- !Array.isArray(input.artifacts)
213
- ? {
214
- artifacts: input.artifacts as Record<
215
- string,
216
- AsyncRuns.RunArtifactDeclaration
217
- >,
218
- }
219
- : {}),
220
- },
221
- ctx.cwd,
222
- );
223
- } catch (error) {
224
- throw enhanceSpawnRecipeError(error, recipe);
225
- }
156
+ const meta = AsyncRuns.startRun(
157
+ {
158
+ file: recipe,
159
+ launch_source: "spawn",
160
+ launch_correlation: { tool_call_id: toolCallId },
161
+ ownerId: getRunOwnerId(ctx),
162
+ ...(input.transport_context
163
+ ? { transport_context: asRecord(input.transport_context) } : {}),
164
+ run_id: runId,
165
+ ...(input.template !== undefined
166
+ ? {
167
+ template:
168
+ input.template as AsyncRuns.AsyncRunStartParams["template"],
169
+ }
170
+ : {}),
171
+ values: ModelContext.withCurrentModelValues(
172
+ asRecord(input.values),
173
+ ctx,
174
+ ),
175
+ ...(input.artifacts &&
176
+ typeof input.artifacts === "object" &&
177
+ !Array.isArray(input.artifacts)
178
+ ? {
179
+ artifacts: input.artifacts as Record<
180
+ string,
181
+ AsyncRuns.RunArtifactDeclaration
182
+ >,
183
+ }
184
+ : {}),
185
+ },
186
+ ctx.cwd,
187
+ { skillContext: ctx.activeSkillRecipeContext },
188
+ );
226
189
  const draftRecipe = writeSpawnDraftRecipe(input, meta);
227
190
  const nextActions = ToolsResponse.runNextActions(meta.run);
228
191
  const details = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.45.0",
3
+ "version": "0.46.0",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -30,8 +30,7 @@
30
30
  "test": "node --experimental-strip-types --test --test-concurrency=1 tests/*.test.ts",
31
31
  "test:preservation": "node --experimental-strip-types --test --test-concurrency=1 tests/async-runs.test.ts tests/observability.test.ts tests/session-evidence.test.ts tests/inspector-security.test.ts tests/inspector-overlay.test.ts tests/review-control.test.ts",
32
32
  "pack:dry": "npm pack --dry-run",
33
- "recipes:qa": "node scripts/validate-recipe.mjs recipes --all --qa --summary",
34
- "release:validate": "npm run validate && node scripts/release-gates.mjs",
33
+ "recipes:qa": "node skills/actors/scripts/validate-recipe.mjs skills --skills --qa --summary",
35
34
  "validate": "npx tsc --noEmit && npm run recipes:qa && npm run build && npm run check && npm test && npm run conformance && npm run pack:dry",
36
35
  "audit:dependencies": "npm audit --audit-level=high --omit=peer",
37
36
  "build": "node scripts/build-dist.mjs",
@@ -42,7 +41,6 @@
42
41
  "dist",
43
42
  "lib",
44
43
  "scripts",
45
- "recipes",
46
44
  "skills",
47
45
  "README.md",
48
46
  "AGENTS.md",
@@ -65,7 +63,7 @@
65
63
  "sourceSkills": [
66
64
  "./skills"
67
65
  ],
68
- "image": "https://github.com/llblab/pi-actors/raw/main/banner.jpg"
66
+ "image": "https://raw.githubusercontent.com/llblab/pi-actors/main/banner.jpg"
69
67
  },
70
68
  "peerDependencies": {
71
69
  "@earendil-works/pi-coding-agent": "*",
@@ -74,7 +74,6 @@ function summarizeCommandDetail(commandDetail) {
74
74
 
75
75
  export async function runAsyncRunner(stateDir = process.argv[2]) {
76
76
  if (!stateDir) throw new Error("missing state dir");
77
-
78
77
  const runPath = join(stateDir, "run.json");
79
78
  const progressPath = join(stateDir, "progress.json");
80
79
  const resultPath = join(stateDir, "result.json");
@@ -83,11 +82,9 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
83
82
  const stderrPath = join(stateDir, "stderr.log");
84
83
  const executionPath = join(stateDir, "execution.json");
85
84
  const meta = JSON.parse(readFileSync(runPath, "utf8"));
86
-
87
85
  function event(kind, data = {}) {
88
86
  appendRunTraceEvent(stateDir, { kind, data });
89
87
  }
90
-
91
88
  function observation(kind, summary, data = {}, delivery = "log", level = "info") {
92
89
  appendRunTraceEvent(stateDir, {
93
90
  kind,
@@ -97,7 +94,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
97
94
  ...(level === "error" ? { level: "error" } : {}),
98
95
  });
99
96
  }
100
-
101
97
  function progress(phase, extra = {}) {
102
98
  writeJsonAtomic(progressPath, {
103
99
  ...(meta.model_policy ? { model_policy: meta.model_policy } : {}),
@@ -106,7 +102,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
106
102
  ...extra,
107
103
  });
108
104
  }
109
-
110
105
  let activeSubagents = 0;
111
106
  let completedSubagents = 0;
112
107
  let promptCounter = 0;
@@ -115,7 +110,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
115
110
  const evidenceRecords = [];
116
111
  const stageOccurrences = new Map();
117
112
  let reportEvidence;
118
-
119
113
  function writeExecutionManifest(status) {
120
114
  writeJsonAtomic(executionPath, {
121
115
  version: 1,
@@ -129,7 +123,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
129
123
  updated_at: new Date().toISOString(),
130
124
  });
131
125
  }
132
-
133
126
  function commandSessionFiles(sessionDir) {
134
127
  if (!sessionDir) return [];
135
128
  try {
@@ -141,7 +134,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
141
134
  return [];
142
135
  }
143
136
  }
144
-
145
137
  function commandEvidenceStartRecord({
146
138
  commandDetail,
147
139
  commandId,
@@ -181,7 +173,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
181
173
  : "not_required",
182
174
  };
183
175
  }
184
-
185
176
  function commandEvidenceRecord({
186
177
  captureDir,
187
178
  commandDetail,
@@ -273,7 +264,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
273
264
  : "rejected",
274
265
  };
275
266
  }
276
-
277
267
  function auditReviewReport(text) {
278
268
  const required = evidenceRecords
279
269
  .filter((record) =>
@@ -294,11 +284,9 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
294
284
  complete_allowed: missing.length === 0,
295
285
  };
296
286
  }
297
-
298
287
  function getCommandDoneDelivery(result) {
299
288
  return result.code !== 0 || activeSubagents > 0 ? "followup" : "log";
300
289
  }
301
-
302
290
  function progressRunning() {
303
291
  progress("running", {
304
292
  activeSubagents,
@@ -306,14 +294,12 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
306
294
  failures: subagentFailures,
307
295
  });
308
296
  }
309
-
310
297
  function promptFilePath() {
311
298
  promptCounter += 1;
312
299
  const dir = join(stateDir, "prompts");
313
300
  mkdirSync(dir, { recursive: true });
314
301
  return join(dir, `command-${String(promptCounter).padStart(3, "0")}.md`);
315
302
  }
316
-
317
303
  function readPromptText(promptFile) {
318
304
  if (!promptFile) return undefined;
319
305
  try {
@@ -322,7 +308,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
322
308
  return undefined;
323
309
  }
324
310
  }
325
-
326
311
  async function observedExec(command, args, options) {
327
312
  captureCounter += 1;
328
313
  const commandId = `command-${String(captureCounter).padStart(3, "0")}`;
@@ -485,7 +470,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
485
470
  progressRunning();
486
471
  return result;
487
472
  }
488
-
489
473
  try {
490
474
  event("run.runner.start", { pid: process.pid });
491
475
  progressRunning();
@@ -40,12 +40,21 @@ writeFileSync(
40
40
  "utf8",
41
41
  );
42
42
 
43
- for (const dir of ["scripts", "recipes", "fixtures", "skills"]) {
43
+ for (const dir of ["scripts", "fixtures", "skills"]) {
44
44
  cpSync(dir, join("dist", dir), { recursive: true });
45
45
  }
46
46
 
47
- const builtScripts = readdirSync(join("dist", "scripts"))
48
- .filter((name) => name.endsWith(".mjs"))
49
- .map((name) => join("dist", "scripts", name));
47
+ function listModuleScripts(root) {
48
+ return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
49
+ const path = join(root, entry.name);
50
+ if (entry.isDirectory()) return listModuleScripts(path);
51
+ return entry.isFile() && entry.name.endsWith(".mjs") ? [path] : [];
52
+ });
53
+ }
54
+
55
+ const builtScripts = [
56
+ ...listModuleScripts(join("dist", "scripts")),
57
+ ...listModuleScripts(join("dist", "skills")),
58
+ ];
50
59
 
51
60
  run(process.execPath, ["--check", ...builtScripts]);
@@ -29,7 +29,7 @@ A Recipe defines execution. It may declare named typed args, inline fallbacks, c
29
29
 
30
30
  Do not declare Control for ordinary one-shot work. Runtime lifecycle actions such as `kill` stay runtime-owned and must not appear in Recipe Control declarations. Imported Recipes act as local definitions inside one Run; they do not create nested Runs unless execution explicitly spawns them.
31
31
 
32
- Prefer maintained packaged Recipes over ad hoc wrappers. Use `std:<recipe>` for exact packaged lookup and `skill:<skill>/<recipe-path>` for a component bundled with a Pi-active Skill. File-backed Recipes own `{recipe_dir}` and Skill Recipes own `{skill_dir}`; callers never pass or override these origins. Skill Recipes are components, not automatic tools. Keep model, thinking, mission, concurrency, quorum, and timeout choices caller-owned unless a Recipe documents a stable policy.
32
+ Prefer maintained Skill-owned Recipes over ad hoc wrappers. Skill Recipe identity is `<active Skill name>/<Recipe filename stem>`; Recipe files have no top-level `name`. `SKILL.md` `name` is Pi host metadata matching its directory, not an additional pi-actors identity field. Use `<skill>/<recipe>` for an exact direct component or an explicit `.json` / `.md` path. Entry paths resolve from invocation `cwd`; relative imports resolve from the importing Recipe directory. File-backed Recipes own `{recipe_dir}` and Skill Recipes own `{skill_dir}`; callers never pass or override these origins. Skill Recipes are components, not automatic tools. Keep model, thinking, mission, concurrency, quorum, and timeout choices caller-owned unless a Recipe documents a stable policy.
33
33
 
34
34
  ## Trace
35
35
 
@@ -90,11 +90,11 @@ If work may outlive the current turn, needs steering, produces artifacts, fans o
90
90
 
91
91
  ## Top Recipes
92
92
 
93
- - [Repository health](../../recipes/pipeline-repo-health.json)
94
- - [Quorum review](../../recipes/pipeline-quorum-review.json)
95
- - [Artifact bundle](../../recipes/pipeline-artifact-bundle.json)
96
- - [Music player service](../../recipes/music-player.json)
97
- - [Resource locker service](../../recipes/resource-locker.json)
93
+ - [Repository health](../project-work/recipes/repo-health.json)
94
+ - [Quorum review](../swarm/recipes/quorum-review.json)
95
+ - [Artifact bundle](../artifacts/recipes/bundle.json)
96
+ - [Music player service](../media/recipes/player.json)
97
+ - [Resource locker service](./recipes/resource-locker.json)
98
98
 
99
99
  ## Deep References
100
100
 
@@ -0,0 +1,11 @@
1
+ {
2
+ "args": [
3
+ "target:path",
4
+ "all:bool"
5
+ ],
6
+ "defaults": {
7
+ "target": "recipes",
8
+ "all": "true"
9
+ },
10
+ "template": "{skill_dir}/scripts/validate-recipe.mjs {target} {all?--all:}"
11
+ }
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "args": [
3
- "repo:path",
4
3
  "state_dir:path",
5
4
  "lines:int"
6
5
  ],
7
6
  "defaults": {
8
- "repo": "~/.pi/agent/extensions/pi-actors",
9
7
  "state_dir": "~/.pi/agent/tmp/pi-actors/runs/coordinator-locker",
10
8
  "lines": "20"
11
9
  },
12
- "template": "{repo}/scripts/locker.mjs snapshot --state-dir {state_dir} --lines {lines}"
10
+ "template": "{skill_dir}/scripts/resource-locker.mjs snapshot --state-dir {state_dir} --lines {lines}"
13
11
  }
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "async": true,
3
3
  "args": [
4
- "repo:path",
5
4
  "lease_ms:int"
6
5
  ],
7
6
  "defaults": {
8
- "repo": "~/.pi/agent/extensions/pi-actors",
9
7
  "lease_ms": "600000"
10
8
  },
11
9
  "control": [
@@ -23,5 +21,5 @@
23
21
  "locks": "{state_dir}/locks.json",
24
22
  "journal": "{state_dir}/journal.jsonl"
25
23
  },
26
- "template": "{repo}/scripts/locker.mjs serve --state-dir {state_dir} --lease-ms {lease_ms}"
24
+ "template": "{skill_dir}/scripts/resource-locker.mjs serve --state-dir {state_dir} --lease-ms {lease_ms}"
27
25
  }
@@ -1,17 +1,15 @@
1
1
  {
2
2
  "args": [
3
- "repo:path",
4
3
  "state_root:path",
5
4
  "run_id:string",
6
5
  "lines:int",
7
6
  "stale_minutes:int"
8
7
  ],
9
8
  "defaults": {
10
- "repo": "~/.pi/agent/extensions/pi-actors",
11
9
  "state_root": "~/.pi/agent/tmp/pi-actors/runs",
12
10
  "run_id": "music",
13
11
  "lines": "80",
14
12
  "stale_minutes": "60"
15
13
  },
16
- "template": "{repo}/scripts/recipe-utils.mjs run-ops-snapshot {state_root} {run_id} {lines} {stale_minutes}"
14
+ "template": "{skill_dir}/scripts/run-utils.mjs run-ops-snapshot {state_root} {run_id} {lines} {stale_minutes}"
17
15
  }
@@ -0,0 +1,9 @@
1
+ {
2
+ "args": [
3
+ "state_root:path"
4
+ ],
5
+ "defaults": {
6
+ "state_root": "~/.pi/agent/tmp/pi-actors/runs"
7
+ },
8
+ "template": "{skill_dir}/scripts/run-utils.mjs run-summary {state_root}"
9
+ }