@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.
- package/AGENTS.md +10 -6
- package/CHANGELOG.md +14 -1
- package/README.md +28 -12
- package/dist/fixtures/protocol/recipe-summary.json +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +10 -110
- package/dist/lib/async-runs.d.ts +4 -1
- package/dist/lib/async-runs.js +22 -23
- 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/extension-runtime.d.ts +21 -0
- package/dist/lib/extension-runtime.js +122 -0
- package/dist/lib/inspector.js +45 -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 +2 -2
- package/dist/lib/recipes-context.js +2 -2
- package/dist/lib/recipes-references.d.ts +24 -9
- package/dist/lib/recipes-references.js +152 -126
- package/dist/lib/runtime.d.ts +0 -1
- package/dist/lib/runtime.js +1 -3
- package/dist/lib/tools-inspect.d.ts +0 -1
- package/dist/lib/tools-inspect.js +35 -8
- package/dist/lib/tools-local.d.ts +2 -0
- package/dist/lib/tools-local.js +1 -1
- package/dist/lib/tools-spawn.d.ts +2 -0
- package/dist/lib/tools-spawn.js +24 -52
- package/dist/scripts/async-runner.mjs +0 -16
- package/dist/scripts/build-dist.mjs +13 -4
- package/dist/skills/actors/SKILL.md +6 -6
- 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 -23
- package/dist/skills/actors/scripts/run-utils.mjs +164 -0
- package/{scripts → dist/skills/actors/scripts}/validate-recipe.mjs +134 -18
- 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 +30 -14
- package/docs/tool-registry.md +2 -2
- package/fixtures/protocol/recipe-summary.json +1 -1
- package/index.ts +18 -112
- package/lib/async-runs.ts +47 -24
- package/lib/automatic-review-runtime.ts +16 -3
- package/lib/draft-sleep.ts +0 -1
- package/lib/extension-runtime.ts +158 -0
- package/lib/inspector.ts +56 -11
- package/lib/paths.ts +0 -7
- package/lib/prompts.ts +2 -2
- package/lib/recipes-context.ts +2 -2
- package/lib/recipes-references.ts +223 -134
- package/lib/runtime.ts +1 -5
- package/lib/tools-inspect.ts +42 -9
- package/lib/tools-local.ts +2 -0
- package/lib/tools-spawn.ts +37 -74
- package/package.json +3 -5
- package/scripts/async-runner.mjs +0 -16
- package/scripts/build-dist.mjs +13 -4
- package/skills/actors/SKILL.md +6 -6
- 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 -23
- package/skills/actors/scripts/run-utils.mjs +164 -0
- package/{dist → skills/actors}/scripts/validate-recipe.mjs +134 -18
- 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/recipe-utils.mjs +0 -405
- package/dist/scripts/release-gates.mjs +0 -278
- 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/recipe-utils.mjs +0 -405
- package/scripts/release-gates.mjs +0 -278
- /package/dist/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
- /package/dist/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
- /package/dist/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
- /package/dist/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
- /package/dist/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
- /package/dist/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
- /package/dist/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
- /package/dist/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
- /package/dist/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
- /package/dist/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
- /package/dist/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
- /package/{recipes/utility-validation-wrapper.json → skills/actors/recipes/command-validate.json} +0 -0
- /package/{recipes/utility-jsonl-tail.json → skills/actors/recipes/jsonl-tail.json} +0 -0
- /package/{recipes/utility-run-state-files.json → skills/actors/recipes/run-state-files.json} +0 -0
- /package/{recipes/utility-playlist-scan.json → skills/media/recipes/playlist-scan.json} +0 -0
- /package/{recipes/utility-changelog-head.json → skills/project-work/recipes/changelog-head.json} +0 -0
- /package/{recipes/utility-git-log.json → skills/project-work/recipes/git-log.json} +0 -0
- /package/{recipes/utility-git-status.json → skills/project-work/recipes/git-status.json} +0 -0
- /package/{recipes/utility-markdown-index.json → skills/project-work/recipes/markdown-index.json} +0 -0
- /package/{recipes → skills/recipe-memory/recipes}/draft-review.json +0 -0
- /package/{recipes → skills/recipe-memory/recipes}/tool-review.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-artifact.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-checkpoint.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-conflict-report.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-contradiction-map.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-critic.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-evidence-map.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-followup.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-judge.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-merge.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-normalize.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-plan.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-preflight.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-prompt.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-review.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-task-card.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-tools.json +0 -0
- /package/{recipes → skills/swarm/recipes}/subagent-verify.json +0 -0
package/dist/lib/inspector.js
CHANGED
|
@@ -56,9 +56,37 @@ export function readActorInspectorRecipe(stateDir) {
|
|
|
56
56
|
!Array.isArray(primary.recipe)
|
|
57
57
|
? primary.recipe
|
|
58
58
|
: undefined;
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const stripPrivateOrigins = (value) => {
|
|
60
|
+
if (Array.isArray(value))
|
|
61
|
+
return value.map(stripPrivateOrigins);
|
|
62
|
+
if (!value || typeof value !== "object")
|
|
63
|
+
return value;
|
|
64
|
+
return Object.fromEntries(Object.entries(value)
|
|
65
|
+
.filter(([key, entry]) => entry !== undefined &&
|
|
66
|
+
key !== "recipe_dir" &&
|
|
67
|
+
key !== "skill_dir" &&
|
|
68
|
+
key !== "source_file")
|
|
69
|
+
.map(([key, entry]) => [
|
|
70
|
+
key,
|
|
71
|
+
key === "actorRecipeContext" && entry && typeof entry === "object"
|
|
72
|
+
? stripPrivateOrigins(Object.fromEntries(Object.entries(entry).filter(([contextKey]) => contextKey !== "file")))
|
|
73
|
+
: stripPrivateOrigins(entry),
|
|
74
|
+
]));
|
|
75
|
+
};
|
|
76
|
+
const redactedRecord = (value) => SessionEvidence.redactSessionEvidenceValue(stripPrivateOrigins(value));
|
|
77
|
+
const primarySourceKind = primary?.source_kind === "active_skill_component"
|
|
78
|
+
? "active_skill_component"
|
|
79
|
+
: primary?.source_kind === "user_registry_capability" ||
|
|
80
|
+
meta.launch_source === "tool"
|
|
81
|
+
? "user_registry_capability"
|
|
82
|
+
: "explicit_file_recipe";
|
|
83
|
+
const primaryLogicalReference = typeof primary?.logical_reference === "string"
|
|
84
|
+
? primary.logical_reference
|
|
85
|
+
: typeof primary?.name === "string"
|
|
86
|
+
? primary.name
|
|
87
|
+
: typeof meta.recipe === "string"
|
|
88
|
+
? meta.recipe
|
|
89
|
+
: "unknown";
|
|
62
90
|
const launchValues = meta.values && typeof meta.values === "object" && !Array.isArray(meta.values)
|
|
63
91
|
? Object.fromEntries(Object.entries(meta.values).filter(([key]) => key !== "recipe_dir" && key !== "skill_dir"))
|
|
64
92
|
: meta.values;
|
|
@@ -66,17 +94,27 @@ export function readActorInspectorRecipe(stateDir) {
|
|
|
66
94
|
composition: contexts.map((entry) => redactedRecord({
|
|
67
95
|
...(typeof entry.alias === "string" ? { alias: entry.alias } : {}),
|
|
68
96
|
depth: entry.depth,
|
|
69
|
-
file: entry.file,
|
|
70
97
|
import_path: entry.import_path,
|
|
71
|
-
|
|
72
|
-
|
|
98
|
+
logical_reference: entry.logical_reference ?? entry.name,
|
|
99
|
+
recipe_stem: entry.name,
|
|
100
|
+
role: entry.role,
|
|
101
|
+
...(typeof entry.skill === "string" ? { skill: entry.skill } : {}),
|
|
102
|
+
source_kind: entry.source_kind === "active_skill_component"
|
|
103
|
+
? "active_skill_component"
|
|
104
|
+
: entry.source_kind === "user_registry_capability" ||
|
|
105
|
+
(entry.depth === 0 && meta.launch_source === "tool")
|
|
106
|
+
? "user_registry_capability"
|
|
107
|
+
: "explicit_file_recipe",
|
|
73
108
|
})),
|
|
74
109
|
...(definition ? { definition: redactedRecord(definition) } : {}),
|
|
75
110
|
diagnostics: result.diagnostics.map((diagnostic) => diagnostic.message),
|
|
76
111
|
identity: redactedRecord({
|
|
77
112
|
run: meta.run,
|
|
78
113
|
recipe: meta.recipe,
|
|
79
|
-
|
|
114
|
+
recipe_stem: primary?.name ?? meta.recipe,
|
|
115
|
+
logical_reference: primaryLogicalReference,
|
|
116
|
+
...(typeof primary?.skill === "string" ? { skill: primary.skill } : {}),
|
|
117
|
+
source_kind: primarySourceKind,
|
|
80
118
|
launch_source: meta.launch_source,
|
|
81
119
|
}),
|
|
82
120
|
launch: redactedRecord({
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -25,4 +25,3 @@ export declare function getExtensionSkillsDir(extensionUrl: string): string;
|
|
|
25
25
|
export declare function getExistingExtensionSkillPaths(extensionUrl: string): string[];
|
|
26
26
|
export declare function getRecipeRoot(agentDir?: string): string;
|
|
27
27
|
export declare function getRecipeDraftRoot(agentDir?: string): string;
|
|
28
|
-
export declare function getPackagedRecipeRoot(): string;
|
package/dist/lib/paths.js
CHANGED
|
@@ -70,10 +70,3 @@ export function getRecipeRoot(agentDir = getAgentDir()) {
|
|
|
70
70
|
export function getRecipeDraftRoot(agentDir = getAgentDir()) {
|
|
71
71
|
return join(getRecipeRoot(agentDir), "drafts");
|
|
72
72
|
}
|
|
73
|
-
export function getPackagedRecipeRoot() {
|
|
74
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
75
|
-
const compiledRoot = resolve(here, "..", "..", "recipes");
|
|
76
|
-
if (existsSync(compiledRoot))
|
|
77
|
-
return compiledRoot;
|
|
78
|
-
return resolve(here, "..", "recipes");
|
|
79
|
-
}
|
package/dist/lib/prompts.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export declare const REGISTER_TOOL_DESCRIPTION: string;
|
|
7
7
|
export declare const REGISTER_TOOL_PROMPT_SNIPPET = "Register persistent command templates as agent-callable tools";
|
|
8
8
|
export declare const REGISTER_TOOL_GUIDELINES: string[];
|
|
9
|
-
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node\u2192bun\u2192deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.\n- Recipes own template directly and may declare metadata/defaults/imports/control/artifacts; files >1 MiB or import depth >32 fail closed.\n- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.\n- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.\n- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid internal transport vocabulary in public guidance.\n- Run state lives under ~/.pi/agent/tmp/pi-actors/runs. Inspect intentionally and avoid busy-polling. Terminal and coordinator-bound notifications queue as Pi follow-ups so concurrently completed actors can reach the coordinator after current work instead of steering between tool calls. Terminal follow-up content stays minimal: run, status, one base path, and relative artifact names only; semantic output stays in non-LLM details and run state. When a deferred actor result gates the next step, wait for its terminal follow-up; do not schedule continuation loops, repeatedly inspect, or mutate its reviewed scope while it runs. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue/stuck run.\n- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings;
|
|
9
|
+
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors quick model:\n- Local-first actor memory: persist trusted local capabilities instead of rebuilding shell recipes.\n- Layers: task -> command template -> recipe/tool -> spawn -> run:<id>; tool:<name> wraps registered capabilities.\n- Command templates stay sync and shell-free: string leaves split into executable + argv, infer .js/.mjs through node\u2192bun\u2192deno run and .sh through bash, and treat operators such as && as literal arguments; use template arrays for sequencing or an explicit trusted shell/script when shell semantics are required. Flags include args/defaults, parallel, concurrency, min_successful, when, timeout, delay, retry, failure, recover, repeat, accept_output, output.\n- Placeholders support typed/default args plus {value??fallback} and {flag?yes:no}.\n- ~/.pi/agent/recipes/*.json is actor muscle memory: every recipe there is auto-registered as an agent tool across sessions; register_tool writes there.\n- Recipes own template directly and may declare metadata/defaults/imports/control/artifacts; files >1 MiB or import depth >32 fail closed.\n- Recipe imports are local variables; imported recipes are definitions, not nested async runs; parent async:true creates one run.\n- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.\n- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid internal transport vocabulary in public guidance.\n- Run state lives under ~/.pi/agent/tmp/pi-actors/runs. Inspect intentionally and avoid busy-polling. Terminal and coordinator-bound notifications queue as Pi follow-ups so concurrently completed actors can reach the coordinator after current work instead of steering between tool calls. Terminal follow-up content stays minimal: run, status, one base path, and relative artifact names only; semantic output stays in non-LLM details and run state. When a deferred actor result gates the next step, wait for its terminal follow-up; do not schedule continuation loops, repeatedly inspect, or mutate its reviewed scope while it runs. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue/stuck run.\n- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; active-Skill and explicit file Recipes remain components outside user tool discovery; offer to save successful recurring patterns only after confirmation.\n- Prefer maintained active-Skill Recipes with spawn recipe=<skill>/<recipe> before ad hoc scripts/wrappers; explicit file Recipes use exact .json/.md paths; review swarms inherit current model/thinking, preflight before fanout, and expose quorum/concurrency/TTL knobs unless explicit args are passed.\n- For any non-trivial actor use or pi-actors change, read the bundled actors skill first. Before launching multiple actors/subagents for parallel implementation, independent artifact generation, delegated audit, or review, also read the bundled swarm skill; the coordinator owns decomposition, disjoint scopes, launch correctness, integration, and final validation. For deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.";
|
|
10
10
|
export declare const REGISTER_TOOL_PARAM_DESCRIPTIONS: {
|
|
11
11
|
readonly name: "Tool name in snake_case (e.g., 'transcribe')";
|
|
12
12
|
readonly description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.";
|
package/dist/lib/prompts.js
CHANGED
|
@@ -24,8 +24,8 @@ export const ONBOARDING_SYSTEM_PROMPT = `pi-actors quick model:
|
|
|
24
24
|
- Actor-mode trigger: if work may outlive this turn, need steering/follow-up/artifacts, run as a service, fan out, or be resumed/inspected later, use spawn -> message -> inspect instead of ad hoc shell backgrounding.
|
|
25
25
|
- Use spawn/message/inspect for actor-level start/send/observe; short foreground checks can stay ordinary tools/templates; avoid internal transport vocabulary in public guidance.
|
|
26
26
|
- Run state lives under ~/.pi/agent/tmp/pi-actors/runs. Inspect intentionally and avoid busy-polling. Terminal and coordinator-bound notifications queue as Pi follow-ups so concurrently completed actors can reach the coordinator after current work instead of steering between tool calls. Terminal follow-up content stays minimal: run, status, one base path, and relative artifact names only; semantic output stays in non-LLM details and run state. When a deferred actor result gates the next step, wait for its terminal follow-up; do not schedule continuation loops, repeatedly inspect, or mutate its reviewed scope while it runs. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue/stuck run.
|
|
27
|
-
- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings;
|
|
28
|
-
- Prefer maintained
|
|
27
|
+
- Maintain ~/.pi/agent/recipes like MEMORY.md for capabilities: keep useful tools, curate stale ones, and fix/remove/disable invalid recipes flagged by registry warnings; active-Skill and explicit file Recipes remain components outside user tool discovery; offer to save successful recurring patterns only after confirmation.
|
|
28
|
+
- Prefer maintained active-Skill Recipes with spawn recipe=<skill>/<recipe> before ad hoc scripts/wrappers; explicit file Recipes use exact .json/.md paths; review swarms inherit current model/thinking, preflight before fanout, and expose quorum/concurrency/TTL knobs unless explicit args are passed.
|
|
29
29
|
- For any non-trivial actor use or pi-actors change, read the bundled actors skill first. Before launching multiple actors/subagents for parallel implementation, independent artifact generation, delegated audit, or review, also read the bundled swarm skill; the coordinator owns decomposition, disjoint scopes, launch correctness, integration, and final validation. For deeper guidance, inspect installed extension sources/docs/recipes because README/docs are not automatically in context.`;
|
|
30
30
|
export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
|
|
31
31
|
name: "Tool name in snake_case (e.g., 'transcribe')",
|
|
@@ -78,7 +78,7 @@ export function findPiPrintPromptIndex(args) {
|
|
|
78
78
|
function matchesActorContext(record, context) {
|
|
79
79
|
if (!context)
|
|
80
80
|
return record.role === "entry";
|
|
81
|
-
if (context.file && context.file === record.
|
|
81
|
+
if (context.file && context.file === record.source_file)
|
|
82
82
|
return true;
|
|
83
83
|
if (context.name && context.name === record.name)
|
|
84
84
|
return true;
|
|
@@ -105,7 +105,7 @@ export function markRecipeContextRecords(records, context) {
|
|
|
105
105
|
}
|
|
106
106
|
export function formatRecipeContextJsonl(records, context) {
|
|
107
107
|
return markRecipeContextRecords(records, context)
|
|
108
|
-
.map((record) => JSON.stringify(record))
|
|
108
|
+
.map(({ source_file: _sourceFile, ...record }) => JSON.stringify(record))
|
|
109
109
|
.join("\n");
|
|
110
110
|
}
|
|
111
111
|
export function buildRecipeContextPromptBlock(records, context) {
|
|
@@ -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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
export declare function
|
|
68
|
-
export declare
|
|
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 {
|
|
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
|
-
|
|
17
|
-
|
|
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 =
|
|
26
|
+
const roots = rootsByName.get(name) ?? new Set();
|
|
29
27
|
roots.add(resolve(skillDir, "recipes"));
|
|
30
|
-
|
|
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
|
-
|
|
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
|
|
41
|
-
|
|
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 =
|
|
106
|
+
const root = roots[0];
|
|
50
107
|
const relation = relative(root, path);
|
|
51
|
-
if (relation &&
|
|
52
|
-
|
|
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
|
|
117
|
+
function assertRecipeReferencePrefixWasNotRemoved(value) {
|
|
57
118
|
if (value.startsWith("std:")) {
|
|
58
|
-
|
|
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 (
|
|
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
|
|
63
|
-
const
|
|
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
|
|
132
|
+
throw new Error(`Active Skill Recipe not found: ${skillName}/${recipeName}`);
|
|
71
133
|
if (roots.length > 1)
|
|
72
|
-
throw new Error(`
|
|
134
|
+
throw new Error(`Duplicate active Skill identity ${skillName}: ${roots.join(", ")}`);
|
|
73
135
|
const root = roots[0];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
83
|
-
|
|
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,
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
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,
|
|
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
|
|
158
|
-
|
|
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,
|
|
176
|
-
|
|
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:
|
|
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
|
-
|
|
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;
|
package/dist/lib/runtime.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/runtime.js
CHANGED
|
@@ -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
|
|
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;
|