@llblab/pi-actors 0.12.6 → 0.12.8

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
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.12.8: Usage Hint Documentation
6
+
7
+ - `[Docs]` Documented runtime actor-tool argument usage hints in README and tool-registry docs, and covered missing template-value hints separately from typed value errors. Impact: users and agents can discover the self-correction behavior without reading tests.
8
+
9
+ ## 0.12.7: Tool Argument Usage Hints
10
+
11
+ - `[Tools]` Added compact usage hints to runtime actor-tool argument errors when typed normalization or placeholder resolution fails. Impact: if an agent supplies a wrong enum/type value or misses a required template value after schema validation, the error now shows the expected call shape with required and optional fields.
12
+
5
13
  ## 0.12.6: Documentation Example Alignment
6
14
 
7
15
  - `[Docs]` Replaced remaining shader-ring recipe examples in registry and template-recipe docs with the concrete docs-review actor recipe example, aligned test fixtures, and changed async-run outbox docs to show actor message envelopes without public delivery knobs. Impact: public docs now consistently demonstrate useful actor wrapping and keep coordinator attention policy out of recipe-authored message examples.
package/README.md CHANGED
@@ -361,6 +361,7 @@ See [`docs/recipe-library.md`](./docs/recipe-library.md) for install notes and r
361
361
  - Tool args are derived from placeholders when `args` is omitted.
362
362
  - Typed arg declarations are progressive: `file:path`, `request_timeout:int=60000`, `speed:number=1.5`, `dry_run:bool=true`, `prompts:array`, and `mode:enum(check,fix)=check` can live in `args` or inline placeholders such as `{request_timeout:int=60000}`. They generate narrower tool schemas and runtime validation while existing untyped `args` and placeholders keep working.
363
363
  - `{arg=default}` inline defaults resolve after runtime values and stored `defaults`; `{arg??fallback}` handles empty/null fallback values; `{flag?--flag:}` ternaries map small truthy/falsy values to strings such as optional CLI flags.
364
+ - Runtime actor-tool argument errors include a compact usage hint when typed normalization or template value resolution fails, including example call shape plus required and optional fields.
364
365
  - `template: [...]` sequences execute left to right; each successful step passes stdout to the next step on stdin.
365
366
  - Object nodes may set `parallel: true`; children receive the same stdin and joined stdout flows to the next sequence step.
366
367
  - Parallel nodes use soft-quorum semantics: failed branches are reported as degraded coverage unless failure propagation escalates to the root.
@@ -149,6 +149,20 @@ Supported compact types are `string` (implicit), `path`, `int`, `number`, `bool`
149
149
 
150
150
  Defaults are applied before substitution, with resolution order runtime values → stored `defaults` → inline default → error. Missing required values are rejected before or during execution. Typed runtime values are normalized before substitution: `int` and `number` values become numeric strings, booleans become `true`/`false`, and enums must match one of the declared values.
151
151
 
152
+ When typed normalization or template value resolution fails at runtime, the tool error includes a compact usage hint:
153
+
154
+ ```text
155
+ Invalid arguments for tool "check_tool": Argument mode must be one of: check, fix.
156
+
157
+ Expected call shape for check_tool:
158
+ check_tool({
159
+ "file": "<file>",
160
+ "mode": "check"
161
+ })
162
+ Required: file
163
+ Optional: mode
164
+ ```
165
+
152
166
  Template recipe tools derive public arguments from the referenced or co-located command template when the recipe is available locally. Explicit `args` is still available when the public tool surface should be narrower or defaulted differently, or when a file-backed recipe is not available during registration. Runtime values are passed as `values`; async recipe tools also accept optional `run_id` to override the generated run id.
153
167
 
154
168
  ## File Argument Naming
package/lib/tools.ts CHANGED
@@ -68,6 +68,71 @@ function objectSchema(
68
68
  return { additionalProperties: false, properties, required, type: "object" };
69
69
  }
70
70
 
71
+ function sampleValueForArg(
72
+ arg: string,
73
+ type: Schema.ToolArgType | undefined,
74
+ defaults: Record<string, unknown>,
75
+ ): unknown {
76
+ if (Object.hasOwn(defaults, arg)) return defaults[arg];
77
+ if (!type || type.kind === "string") return `<${arg}>`;
78
+ if (type.kind === "path") return `./${arg}`;
79
+ if (type.kind === "int") return 1;
80
+ if (type.kind === "number") return 1.5;
81
+ if (type.kind === "bool") return true;
82
+ if (type.kind === "array") return [`<${arg}>`];
83
+ return type.values[0] ?? `<${arg}>`;
84
+ }
85
+
86
+ function shouldAddRuntimeToolUsageHint(error: unknown): boolean {
87
+ const message = error instanceof Error ? error.message : String(error);
88
+ return (
89
+ /^Argument \S+ must /.test(message) ||
90
+ /^Missing .* value: /.test(message)
91
+ );
92
+ }
93
+
94
+ function formatRuntimeToolUsageHint(
95
+ cfg: RegisteredTool,
96
+ required: string[],
97
+ includeRunId: boolean,
98
+ ): string {
99
+ const optional = cfg.args.filter((arg) => !required.includes(arg));
100
+ const example: Record<string, unknown> = {};
101
+ for (const arg of required)
102
+ example[arg] = sampleValueForArg(arg, cfg.argTypes?.[arg], cfg.defaults);
103
+ for (const arg of optional)
104
+ example[arg] = sampleValueForArg(arg, cfg.argTypes?.[arg], cfg.defaults);
105
+ if (includeRunId) example.run_id = `${cfg.name}-1`;
106
+ const lines = [
107
+ `Expected call shape for ${cfg.name}:`,
108
+ `${cfg.name}(${JSON.stringify(example, null, 2)})`,
109
+ ];
110
+ if (required.length) lines.push(`Required: ${required.join(", ")}`);
111
+ if (optional.length || includeRunId)
112
+ lines.push(
113
+ `Optional: ${[...optional, ...(includeRunId ? ["run_id"] : [])].join(", ")}`,
114
+ );
115
+ return lines.join("\n");
116
+ }
117
+
118
+ function formatRuntimeToolArgumentError(
119
+ cfg: RegisteredTool,
120
+ error: unknown,
121
+ required: string[],
122
+ includeRunId: boolean,
123
+ ): Error {
124
+ const message = error instanceof Error ? error.message : String(error);
125
+ if (!shouldAddRuntimeToolUsageHint(error))
126
+ return error instanceof Error ? error : new Error(message);
127
+ return new Error(
128
+ `Invalid arguments for tool "${cfg.name}": ${message}\n\n${formatRuntimeToolUsageHint(
129
+ cfg,
130
+ required,
131
+ includeRunId,
132
+ )}`,
133
+ );
134
+ }
135
+
71
136
  function looseObjectSchema(description: string): JsonSchema {
72
137
  return { additionalProperties: true, description, type: "object" };
73
138
  }
@@ -594,58 +659,62 @@ export function createRuntimeToolDefinition(
594
659
  _onUpdate: unknown,
595
660
  ctx: AsyncRunToolContext,
596
661
  ) {
597
- if (isAsyncRecipe) {
598
- const input = params as Record<string, unknown>;
599
- const { run_id, ...values } = input;
600
- const base = cfg.recipe ? cfg.recipe : { file: String(cfg.template) };
601
- const runId =
602
- typeof run_id === "string" && run_id.trim()
603
- ? run_id.trim()
604
- : `${cfg.name}-${Date.now()}`;
605
- const meta = AsyncRuns.startRun(
606
- {
607
- ...base,
608
- ownerId: getRunOwnerId(ctx),
609
- run_id: runId,
610
- tool: cfg.name,
611
- values: Schema.normalizeRuntimeValues(
612
- { ...(cfg.recipe?.values ?? {}), ...cfg.defaults, ...values },
613
- cfg.argTypes,
614
- ),
615
- },
616
- ctx.cwd,
617
- );
618
- return {
619
- content: [
620
- { type: "text" as const, text: compactAsyncRunStatus(meta) },
621
- ],
622
- details: meta,
623
- };
624
- }
625
- if (isRecipe && recipeTemplate) {
626
- const paramsWithDefaults = {
627
- ...(cfg.recipe?.values ?? {}),
628
- ...cfg.defaults,
629
- ...(params as Record<string, unknown>),
630
- };
631
- return Execution.executeRegisteredTool(
632
- { ...cfg, template: recipeTemplate },
633
- Schema.normalizeRuntimeValues(paramsWithDefaults, cfg.argTypes),
662
+ try {
663
+ if (isAsyncRecipe) {
664
+ const input = params as Record<string, unknown>;
665
+ const { run_id, ...values } = input;
666
+ const base = cfg.recipe ? cfg.recipe : { file: String(cfg.template) };
667
+ const runId =
668
+ typeof run_id === "string" && run_id.trim()
669
+ ? run_id.trim()
670
+ : `${cfg.name}-${Date.now()}`;
671
+ const meta = AsyncRuns.startRun(
672
+ {
673
+ ...base,
674
+ ownerId: getRunOwnerId(ctx),
675
+ run_id: runId,
676
+ tool: cfg.name,
677
+ values: Schema.normalizeRuntimeValues(
678
+ { ...(cfg.recipe?.values ?? {}), ...cfg.defaults, ...values },
679
+ cfg.argTypes,
680
+ ),
681
+ },
682
+ ctx.cwd,
683
+ );
684
+ return {
685
+ content: [
686
+ { type: "text" as const, text: compactAsyncRunStatus(meta) },
687
+ ],
688
+ details: meta,
689
+ };
690
+ }
691
+ if (isRecipe && recipeTemplate) {
692
+ const paramsWithDefaults = {
693
+ ...(cfg.recipe?.values ?? {}),
694
+ ...cfg.defaults,
695
+ ...(params as Record<string, unknown>),
696
+ };
697
+ return await Execution.executeRegisteredTool(
698
+ { ...cfg, template: recipeTemplate },
699
+ Schema.normalizeRuntimeValues(paramsWithDefaults, cfg.argTypes),
700
+ exec,
701
+ ctx.cwd,
702
+ signal,
703
+ );
704
+ }
705
+ return await Execution.executeRegisteredTool(
706
+ cfg,
707
+ Schema.normalizeRuntimeValues(
708
+ params as Record<string, unknown>,
709
+ cfg.argTypes,
710
+ ),
634
711
  exec,
635
712
  ctx.cwd,
636
713
  signal,
637
714
  );
715
+ } catch (error) {
716
+ throw formatRuntimeToolArgumentError(cfg, error, required, isAsyncRecipe);
638
717
  }
639
- return Execution.executeRegisteredTool(
640
- cfg,
641
- Schema.normalizeRuntimeValues(
642
- params as Record<string, unknown>,
643
- cfg.argTypes,
644
- ),
645
- exec,
646
- ctx.cwd,
647
- signal,
648
- );
649
718
  },
650
719
  };
651
720
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.12.6",
3
+ "version": "0.12.8",
4
4
  "private": false,
5
5
  "description": "Actor runtime and orchestrator for agent-managed local processes",
6
6
  "type": "module",