@llblab/pi-actors 0.16.1 → 0.16.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.3: Recipe Import Path Placeholders
4
+
5
+ - `[Template Recipes]` Added static `{repo}` and `{agent}` expansion for recipe paths, including `imports` and `from` bindings. Impact: recipes can import sibling packaged/user recipes without hard-coded absolute paths while keeping imports load-time deterministic.
6
+ - `[Docs]` Documented `{repo}` and `{agent}` import path placeholders in the template recipe standard.
7
+ - `[Package]` Bumped package and packaged skill metadata to `0.16.3` for the hotfix release.
8
+
9
+ ## 0.16.2: Recipe Registry Diagnostics Hotfix
10
+
11
+ - `[Schema]` Derived recipe tool arguments without expanding runtime-dependent repeat nodes. Impact: valid recipes using repeat expressions such as `{lenses.length}` can be exposed as tools instead of being skipped during startup schema generation.
12
+ - `[Runtime]` Replaced the dense semicolon warning with grouped recipe registry diagnostics and explicit spacing. Impact: startup diagnostics are easier to scan and do not visually run into adjacent text.
13
+ - `[Recipes]` Added a packaged `lens-swarm` recipe that composes the review coordinator without concrete model-version defaults. Impact: the standard library includes the general multi-lens review launcher instead of relying only on operator-local copies.
14
+ - `[Package]` Bumped package and packaged skill metadata to `0.16.2` for the hotfix release.
15
+
3
16
  ## 0.16.1: Recipe Registry Hotfix
4
17
 
5
18
  - `[Runtime]` Prevented invalid user recipe files from aborting extension startup when tool-schema generation fails, surfacing a warning and skipping the offending tool instead. Impact: one bad recipe in `~/.pi/agent/recipes` no longer takes down the pi-actors extension.
@@ -245,7 +245,7 @@ File-backed recipes may import other file-backed recipes at the recipe layer. Im
245
245
 
246
246
  An import binding may be either a string recipe path/name or an object with:
247
247
 
248
- - `from`: recipe path or bare name.
248
+ - `from`: recipe path or bare name. Import paths support static load-time placeholders: `{repo}` expands to the directory above the active recipe root, and `{agent}` expands to the pi agent directory. For a packaged recipe in `<repo>/recipes/name.json`, `{repo}/recipes/other.json` resolves to a sibling packaged recipe. For a user recipe in `~/.pi/agent/recipes/name.json`, `{repo}` and `{agent}` both resolve to `~/.pi/agent`.
249
249
  - `defaults`: extra default values exposed through the import.
250
250
  - `values`: explicit values for embedding that imported recipe.
251
251
 
@@ -41,6 +41,32 @@ export interface RecipeDiscoverySource {
41
41
  mutableUsage?: boolean;
42
42
  }
43
43
 
44
+ function assertToolSafeRepeatConfig(
45
+ config: unknown,
46
+ argTypes: Record<string, { kind: string }>,
47
+ defaults: Record<string, unknown>,
48
+ ): void {
49
+ if (typeof config === "string" || config === undefined || config === null) return;
50
+ if (Array.isArray(config)) {
51
+ for (const step of config) assertToolSafeRepeatConfig(step, argTypes, defaults);
52
+ return;
53
+ }
54
+ if (typeof config !== "object") return;
55
+ const node = config as { repeat?: unknown; template?: unknown; recover?: unknown };
56
+ if (typeof node.repeat === "string") {
57
+ const trimmed = node.repeat.trim();
58
+ if (!/^\d+$/.test(trimmed)) {
59
+ const match = trimmed.match(/^\{?([A-Za-z_][A-Za-z0-9_-]*)\.length\}?$/);
60
+ if (!match || (argTypes[match[1]]?.kind !== "array" && !Array.isArray(defaults[match[1]])))
61
+ throw new Error(
62
+ "Command template repeat must be a positive integer or {array.length} with an array argument/default.",
63
+ );
64
+ }
65
+ }
66
+ assertToolSafeRepeatConfig(node.template, argTypes, defaults);
67
+ assertToolSafeRepeatConfig(node.recover, argTypes, defaults);
68
+ }
69
+
44
70
  function listRecipeFiles(root: string): string[] {
45
71
  if (!existsSync(root)) return [];
46
72
  return readdirSync(root, { withFileTypes: true })
@@ -207,6 +233,13 @@ export function toRegisteredTool(entry: DiscoveredRecipe): RegisteredTool | unde
207
233
  defaults: { ...(argTemplate.defaults ?? {}), ...(cfg.defaults ?? {}) },
208
234
  }
209
235
  : { args: cfg.args, defaults: cfg.defaults ?? {}, template: argTemplate };
236
+ const explicitArgTypes = Object.fromEntries(
237
+ (cfg.args ?? []).map((arg) => {
238
+ const parsed = Schema.parseToolArgToken(String(arg));
239
+ return [parsed.arg, parsed.type];
240
+ }),
241
+ );
242
+ assertToolSafeRepeatConfig(argTemplateConfig, explicitArgTypes, cfg.defaults ?? {});
210
243
  const argTypes = Schema.getTemplateArgTypes(argTemplateConfig);
211
244
  return {
212
245
  name: entry.id,
@@ -75,11 +75,15 @@ export function resolveRecipePath(
75
75
  recipeRoot = Paths.getRecipeRoot(),
76
76
  ): string {
77
77
  const trimmed = value.trim();
78
- if (trimmed.startsWith("~/")) return resolve(homedir(), trimmed.slice(2));
79
- if (trimmed.includes("/")) return resolve(trimmed);
78
+ const repoRoot = resolve(recipeRoot, "..");
79
+ const expanded = trimmed
80
+ .replaceAll("{repo}", repoRoot)
81
+ .replaceAll("{agent}", Paths.getAgentDir());
82
+ if (expanded.startsWith("~/")) return resolve(homedir(), expanded.slice(2));
83
+ if (expanded.includes("/")) return resolve(expanded);
80
84
  return resolve(
81
85
  recipeRoot,
82
- trimmed.endsWith(".json") ? trimmed : `${trimmed}.json`,
86
+ expanded.endsWith(".json") ? expanded : `${expanded}.json`,
83
87
  );
84
88
  }
85
89
 
package/lib/runtime.ts CHANGED
@@ -98,6 +98,27 @@ export function createAutoToolsRuntime(
98
98
  runtimeTools.add(cfg.name);
99
99
  runtimeToolFingerprints.set(cfg.name, fingerprint);
100
100
  }
101
+ function formatRecipeToolWarnings(warnings: string[]): string {
102
+ const shadowed = warnings.filter((warning) => warning.includes(" shadows "));
103
+ const skipped = warnings.filter((warning) => warning.includes(" could not be exposed as a tool:"));
104
+ const other = warnings.filter(
105
+ (warning) => !shadowed.includes(warning) && !skipped.includes(warning),
106
+ );
107
+ const lines = ["pi-actors recipe registry warning"];
108
+ if (shadowed.length > 0) {
109
+ lines.push("User recipes override packaged recipes:");
110
+ lines.push(...shadowed.map((warning) => `• ${warning}`));
111
+ }
112
+ if (skipped.length > 0) {
113
+ lines.push("Recipes skipped from tool exposure:");
114
+ lines.push(...skipped.map((warning) => `• ${warning}`));
115
+ }
116
+ if (other.length > 0) {
117
+ lines.push("Other registry diagnostics:");
118
+ lines.push(...other.map((warning) => `• ${warning}`));
119
+ }
120
+ return `${lines.join("\n")}\n`;
121
+ }
101
122
  function loadTools(ctx: RuntimeContext) {
102
123
  const warnings: string[] = [];
103
124
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
@@ -138,7 +159,7 @@ export function createAutoToolsRuntime(
138
159
  registerRuntimeTool(cfg);
139
160
  }
140
161
  if (warnings.length > 0) {
141
- notify(ctx, `Recipe tools: ${warnings.join("; ")}`, "warning");
162
+ notify(ctx, formatRecipeToolWarnings(warnings), "warning");
142
163
  }
143
164
  }
144
165
  return {
package/lib/schema.ts CHANGED
@@ -261,19 +261,35 @@ function collectWhenDeclarations(
261
261
  declarations.push(parseToolArgToken(`${bareCondition[1]}:bool=false`));
262
262
  }
263
263
 
264
+ function collectCommandTemplateConfigDeclarations(
265
+ config: CommandTemplates.CommandTemplateConfig | CommandTemplates.CommandTemplateValue,
266
+ declarations: ParsedToolArgToken[],
267
+ ): void {
268
+ if (typeof config === "string") {
269
+ collectTemplatePlaceholderDeclarations(config, declarations);
270
+ return;
271
+ }
272
+ if (Array.isArray(config)) {
273
+ for (const step of config)
274
+ collectCommandTemplateConfigDeclarations(step, declarations);
275
+ return;
276
+ }
277
+ if (config.template !== undefined)
278
+ collectCommandTemplateConfigDeclarations(config.template, declarations);
279
+ if (config.recover !== undefined)
280
+ collectCommandTemplateConfigDeclarations(config.recover, declarations);
281
+ for (const field of [config.timeout, config.delay, config.retry, config.repeat]) {
282
+ if (typeof field === "string")
283
+ collectTemplatePlaceholderDeclarations(field, declarations);
284
+ }
285
+ if (typeof config.when === "string") collectWhenDeclarations(config.when, declarations);
286
+ }
287
+
264
288
  function getTemplatePlaceholderDeclarations(
265
289
  config: CommandTemplates.CommandTemplateConfig,
266
290
  ): ParsedToolArgToken[] {
267
291
  const declarations: ParsedToolArgToken[] = [];
268
- for (const step of CommandTemplates.expandCommandTemplateConfigs(config)) {
269
- collectTemplatePlaceholderDeclarations(step.template, declarations);
270
- for (const field of [step.timeout, step.delay, step.retry]) {
271
- if (typeof field === "string")
272
- collectTemplatePlaceholderDeclarations(field, declarations);
273
- }
274
- if (typeof step.when === "string")
275
- collectWhenDeclarations(step.when, declarations);
276
- }
292
+ collectCommandTemplateConfigDeclarations(config, declarations);
277
293
  return declarations;
278
294
  }
279
295
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.16.1",
3
+ "version": "0.16.3",
4
4
  "private": false,
5
5
  "description": "Actor runtime and orchestrator for agent-managed local processes",
6
6
  "keywords": [
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "lens-swarm",
3
+ "description": "General-purpose multi-lens review swarm. Launches independent reviewers by lens, then verifies, merges, judges, and normalizes the result.",
4
+ "tool": true,
5
+ "async": true,
6
+ "imports": {
7
+ "coordinator": "subagent-review-coordinator.json"
8
+ },
9
+ "args": [
10
+ "scope:string",
11
+ "lenses:array",
12
+ "model:string",
13
+ "thinking:string",
14
+ "tools:string",
15
+ "claim:string",
16
+ "evidence_policy:string",
17
+ "risk_policy:string",
18
+ "output_format:string"
19
+ ],
20
+ "defaults": {
21
+ "lenses": [
22
+ "correctness",
23
+ "architecture",
24
+ "operator UX"
25
+ ],
26
+ "thinking": "off",
27
+ "tools": "",
28
+ "claim": "The reviewed scope has clear, evidence-based findings, risks, and recommended next actions.",
29
+ "evidence_policy": "Cite inspected files, command outputs, or explicit uncertainty for every material claim.",
30
+ "risk_policy": "Preserve minority high-impact risks and ideas; separate confirmed issues from hypotheses.",
31
+ "output_format": "Markdown sections: Summary, Consensus Findings, Minority Findings, Verification, Judge Notes, Risks, Next Actions."
32
+ },
33
+ "mailbox": {
34
+ "accepts": [
35
+ "control.stop",
36
+ "control.cancel",
37
+ "control.kill"
38
+ ],
39
+ "emits": [
40
+ "review.completed",
41
+ "verification.completed",
42
+ "merge.completed",
43
+ "judge.completed",
44
+ "command.done",
45
+ "run.done",
46
+ "run.failed"
47
+ ]
48
+ },
49
+ "template": [
50
+ {
51
+ "name": "coordinator",
52
+ "values": {
53
+ "scope": "{scope}",
54
+ "lenses": "{lenses}",
55
+ "claim": "{claim}",
56
+ "reviewer_model": "{model}",
57
+ "verifier_model": "{model}",
58
+ "merger_model": "{model}",
59
+ "judge_model": "{model}",
60
+ "thinking": "{thinking}",
61
+ "tools": "{tools}",
62
+ "evidence_policy": "{evidence_policy}",
63
+ "risk_policy": "{risk_policy}",
64
+ "output_format": "{output_format}"
65
+ }
66
+ }
67
+ ]
68
+ }
@@ -2,7 +2,7 @@
2
2
  name: actors
3
3
  description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
4
4
  metadata:
5
- version: 0.16.1
5
+ version: 0.16.3
6
6
  ---
7
7
 
8
8
  # Actors (pi-actors)
@@ -216,7 +216,7 @@ Use packaged recipes by name with `spawn file=<name>` for async actors, or regis
216
216
  - Review chain: [`subagent-review`](../../recipes/subagent-review.json), [`subagent-verify`](../../recipes/subagent-verify.json), [`subagent-merge`](../../recipes/subagent-merge.json), [`subagent-judge`](../../recipes/subagent-judge.json), [`subagent-normalize`](../../recipes/subagent-normalize.json).
217
217
  - Planning/evidence: [`subagent-plan`](../../recipes/subagent-plan.json), [`subagent-task-card`](../../recipes/subagent-task-card.json), [`subagent-evidence-map`](../../recipes/subagent-evidence-map.json), [`subagent-contradiction-map`](../../recipes/subagent-contradiction-map.json), [`subagent-critic`](../../recipes/subagent-critic.json).
218
218
  - Handoffs: [`subagent-checkpoint`](../../recipes/subagent-checkpoint.json), [`subagent-followup`](../../recipes/subagent-followup.json), [`subagent-message`](../../recipes/subagent-message.json), [`subagent-artifact`](../../recipes/subagent-artifact.json), [`subagent-conflict-report`](../../recipes/subagent-conflict-report.json).
219
- - Composition: [`subagent-quorum`](../../recipes/subagent-quorum.json), [`subagent-review-coordinator`](../../recipes/subagent-review-coordinator.json).
219
+ - Composition: [`subagent-quorum`](../../recipes/subagent-quorum.json), [`subagent-review-coordinator`](../../recipes/subagent-review-coordinator.json), [`lens-swarm`](../../recipes/lens-swarm.json).
220
220
 
221
221
  ### Pipelines
222
222
 
@@ -2,7 +2,7 @@
2
2
  name: swarm
3
3
  description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
4
4
  metadata:
5
- version: 0.16.1
5
+ version: 0.16.3
6
6
  ---
7
7
 
8
8
  # Swarm