@llblab/pi-actors 0.12.6 → 0.12.7

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,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.12.7: Tool Argument Usage Hints
6
+
7
+ - `[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.
8
+
5
9
  ## 0.12.6: Documentation Example Alignment
6
10
 
7
11
  - `[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/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.7",
4
4
  "private": false,
5
5
  "description": "Actor runtime and orchestrator for agent-managed local processes",
6
6
  "type": "module",