@llblab/pi-actors 0.56.1 → 0.57.1

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 (44) hide show
  1. package/AGENTS.md +7 -7
  2. package/BACKLOG.md +4 -14
  3. package/CHANGELOG.md +13 -0
  4. package/README.md +7 -2
  5. package/dist/lib/execution-output.js +13 -2
  6. package/dist/lib/observability.js +9 -14
  7. package/dist/lib/registry.js +82 -117
  8. package/dist/lib/run-delivery.d.ts +1 -1
  9. package/dist/lib/run-delivery.js +146 -32
  10. package/dist/lib/run-ui-runtime.js +18 -27
  11. package/dist/lib/runs-process-windows-mi.d.ts +71 -0
  12. package/dist/lib/runs-process-windows-mi.js +146 -3
  13. package/dist/lib/tools-inspect.js +109 -59
  14. package/dist/lib/tools-local.js +16 -8
  15. package/dist/lib/tools-message.js +76 -65
  16. package/dist/lib/tools-response.d.ts +18 -0
  17. package/dist/lib/tools-response.js +88 -21
  18. package/dist/lib/tools-spawn.js +19 -12
  19. package/dist/skills/actors/SKILL.md +1 -1
  20. package/dist/skills/actors/references/diagnostics.md +3 -0
  21. package/dist/skills/actors/references/runs.md +1 -1
  22. package/docs/README.md +1 -0
  23. package/docs/actor-inspector.md +2 -2
  24. package/docs/agent-experience.md +51 -4
  25. package/docs/coordinator-delivery.md +2 -2
  26. package/docs/inactive-foundations-inventory.md +73 -0
  27. package/docs/inspection.md +9 -8
  28. package/docs/output-projection-map.md +19 -17
  29. package/docs/windows-process-observation.md +2 -2
  30. package/lib/execution-output.ts +13 -2
  31. package/lib/observability.ts +9 -11
  32. package/lib/registry.ts +89 -124
  33. package/lib/run-delivery.ts +130 -31
  34. package/lib/run-ui-runtime.ts +18 -34
  35. package/lib/runs-process-windows-mi.ts +167 -3
  36. package/lib/tools-inspect.ts +109 -59
  37. package/lib/tools-local.ts +26 -15
  38. package/lib/tools-message.ts +85 -58
  39. package/lib/tools-response.ts +97 -20
  40. package/lib/tools-spawn.ts +19 -12
  41. package/package.json +1 -1
  42. package/skills/actors/SKILL.md +1 -1
  43. package/skills/actors/references/diagnostics.md +3 -0
  44. package/skills/actors/references/runs.md +1 -1
package/lib/registry.ts CHANGED
@@ -172,6 +172,26 @@ function registrationNextActions(
172
172
  ];
173
173
  }
174
174
 
175
+ const REGISTRY_METADATA_BUDGET = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
176
+
177
+ type RegistryMetadata = Record<string, unknown>;
178
+
179
+ /** Bare block-YAML registry document; builders own whole-value/omission policy and the byte budget. */
180
+ function registryMetadataText(value: RegistryMetadata): string {
181
+ return (ToolsResponse.renderYamlMetadata(value, REGISTRY_METADATA_BUDGET) as
182
+ Extract<ToolsResponse.YamlMetadataRendering, { state: "available" }>).text;
183
+ }
184
+
185
+ /** One opaque selected scalar as a whole value or an explicit whole-field omission, never a clip. */
186
+ function registrySelected(key: string, value: string): ToolsResponse.YamlPart {
187
+ return ToolsResponse.selectYamlField(key, value, REGISTRY_METADATA_BUDGET);
188
+ }
189
+
190
+ /** Grow whole selected fields in priority order while the complete document still fits. */
191
+ function growRegistryDocument(render: () => RegistryMetadata, order: readonly ToolsResponse.YamlPart[]): string {
192
+ return ToolsResponse.growYamlDocument(render, order, REGISTRY_METADATA_BUDGET);
193
+ }
194
+
175
195
  /** Read-time text only: callers supply the original post-activation selections, not live ports. */
176
196
  function registrationReceiptText(data: {
177
197
  tool: string; result: "registered" | "updated" | "persisted"; source: string; callable: boolean;
@@ -235,74 +255,35 @@ function registryNoticeText(kind: "activated" | "promoted" | "deleted", name: st
235
255
  }
236
256
 
237
257
  function registrationTransactionFailureText(stage: string, generation: string, summary: string, error: string): string {
238
- const field = ToolsResponse.formatMarkdownField;
239
- const part = (key: string, value: string) => {
240
- const wellFormed = value.isWellFormed();
241
- const omitted = `- ${key}: [${wellFormed ? "omitted" : "unavailable"}]\n- ${key}_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`;
242
- const full = wellFormed ? field(key, value) : omitted;
243
- return { full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted };
244
- };
245
- const selectedGeneration = part("resolution_generation", generation);
246
- const selectedSummary = part("active_skills_summary", summary);
247
- const selectedError = part("error", error);
248
- const render = () => [field("result", "Tool registration transaction failed."), field("stage", stage),
249
- selectedGeneration.selected, selectedSummary.selected, selectedError.selected].join("\n");
258
+ const selectedGeneration = registrySelected("resolution_generation", generation);
259
+ const selectedSummary = registrySelected("active_skills_summary", summary);
260
+ const selectedError = registrySelected("error", error);
261
+ const render = (): RegistryMetadata => ({ result: "Tool registration transaction failed.", stage,
262
+ ...selectedGeneration.selected, ...selectedSummary.selected, ...selectedError.selected });
250
263
  // Keep a fitting surfaced error before large optional resolution metadata.
251
- for (const current of [selectedError, selectedGeneration, selectedSummary]) {
252
- if (current.selected === current.full) continue;
253
- const previous = current.selected;
254
- current.selected = current.full;
255
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) current.selected = previous;
256
- }
257
- return render();
264
+ return growRegistryDocument(render, [selectedError, selectedGeneration, selectedSummary]);
258
265
  }
259
266
 
260
267
  function candidateAdmissionFailureText(data: { kind: "source"; source: string; diagnostics: string; summary: string; instruction: string } | { kind: "recipe"; diagnostics: string }): string {
261
- const field = ToolsResponse.formatMarkdownField;
262
- const part = (key: string, value: string, literal = false) => {
263
- const wellFormed = value.isWellFormed();
264
- const omitted = `- ${key}: [${wellFormed ? "omitted" : "unavailable"}]\n- ${key}_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`;
265
- const full = wellFormed ? field(key, literal ? ToolsResponse.formatLiteralBlock(value) : value) : omitted;
266
- return { full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted };
267
- };
268
- const diagnostics = part("diagnostics_summary", data.diagnostics);
269
- const source = data.kind === "source" ? part("source", data.source, true) : undefined;
270
- const summary = data.kind === "source" ? part("active_skills_summary", data.summary) : undefined;
271
- const instruction = data.kind === "source" ? part("instruction", data.instruction) : undefined;
272
- const render = () => [field("field", "recipe"), ...(source ? [source.selected] : []),
273
- field("result", data.kind === "source" ? "Recipe source validation failed." : "Tool recipe validation failed."),
274
- diagnostics.selected, ...(summary ? [summary.selected] : []), ...(instruction ? [instruction.selected] : [])].join("\n");
268
+ const diagnostics = registrySelected("diagnostics_summary", data.diagnostics);
269
+ const source = data.kind === "source" ? registrySelected("source", data.source) : undefined;
270
+ const summary = data.kind === "source" ? registrySelected("active_skills_summary", data.summary) : undefined;
271
+ const instruction = data.kind === "source" ? registrySelected("instruction", data.instruction) : undefined;
272
+ const render = (): RegistryMetadata => ({ field: "recipe", ...(source ? source.selected : {}),
273
+ result: data.kind === "source" ? "Recipe source validation failed." : "Tool recipe validation failed.",
274
+ ...diagnostics.selected, ...(summary ? summary.selected : {}), ...(instruction ? instruction.selected : {}) });
275
275
  // Preserve fitting diagnostics before large optional source/context/advice fields.
276
- for (const current of [diagnostics, source, summary, instruction]) {
277
- if (!current || current.selected === current.full) continue;
278
- const previous = current.selected;
279
- current.selected = current.full;
280
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) current.selected = previous;
281
- }
282
- return render();
276
+ return growRegistryDocument(render, [diagnostics, ...(source ? [source] : []), ...(summary ? [summary] : []), ...(instruction ? [instruction] : [])]);
283
277
  }
284
278
 
285
279
  function sourceNormalizationFailureText(source: string, reason: string, summary: string, instruction: string): string {
286
- const field = ToolsResponse.formatMarkdownField;
287
- const part = (key: string, value: string, literal = false) => {
288
- const wellFormed = value.isWellFormed();
289
- const omitted = `- ${key}: [${wellFormed ? "omitted" : "unavailable"}]\n- ${key}_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`;
290
- const full = wellFormed ? field(key, literal ? ToolsResponse.formatLiteralBlock(value) : value) : omitted;
291
- return { full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted };
292
- };
293
- const selectedSource = part("source", source, true);
294
- const selectedReason = part("reason", reason);
295
- const selectedSummary = part("active_skills_summary", summary);
296
- const selectedInstruction = part("instruction", instruction);
297
- const render = () => [field("field", "from"), selectedSource.selected, field("result", "Recipe source is invalid."),
298
- selectedReason.selected, selectedSummary.selected, selectedInstruction.selected].join("\n");
299
- for (const current of [selectedSource, selectedReason, selectedSummary, selectedInstruction]) {
300
- if (current.selected === current.full) continue;
301
- const previous = current.selected;
302
- current.selected = current.full;
303
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) current.selected = previous;
304
- }
305
- return render();
280
+ const selectedSource = registrySelected("source", source);
281
+ const selectedReason = registrySelected("reason", reason);
282
+ const selectedSummary = registrySelected("active_skills_summary", summary);
283
+ const selectedInstruction = registrySelected("instruction", instruction);
284
+ const render = (): RegistryMetadata => ({ field: "from", ...selectedSource.selected, result: "Recipe source is invalid.",
285
+ ...selectedReason.selected, ...selectedSummary.selected, ...selectedInstruction.selected });
286
+ return growRegistryDocument(render, [selectedSource, selectedReason, selectedSummary, selectedInstruction]);
306
287
  }
307
288
 
308
289
  function activeSkillSummary(
@@ -319,16 +300,11 @@ function activeSkillSummary(
319
300
  }
320
301
 
321
302
  function draftPathFailureText(kind: "root" | "missing", reference: string): string {
322
- const key = kind === "root" ? "allowed_root" : "path";
323
- const render = (selected: string) => "- field: draft\n" + selected + (kind === "root"
324
- ? "\n- result: Draft must be under the allowed root.\n- instruction: Use inspect target=recipes view=summary to list drafts."
325
- : "\n- result: Draft not found.");
326
- const wellFormed = reference.isWellFormed();
327
- if (wellFormed) {
328
- const complete = render(ToolsResponse.formatMarkdownField(key, ToolsResponse.formatLiteralBlock(reference)));
329
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) return complete;
330
- }
331
- return render(`- ${key}: [${wellFormed ? "omitted" : "unavailable"}]\n- ${key}_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`);
303
+ const selected = registrySelected(kind === "root" ? "allowed_root" : "path", reference);
304
+ const render = (): RegistryMetadata => ({ field: "draft", ...selected.selected,
305
+ result: kind === "root" ? "Draft must be under the allowed root." : "Draft not found.",
306
+ ...(kind === "root" ? { instruction: "Use inspect target=recipes view=summary to list drafts." } : {}) });
307
+ return growRegistryDocument(render, [selected]);
332
308
  }
333
309
 
334
310
  function assertDraftPath<TContext>(
@@ -382,24 +358,16 @@ function draftReceiptText(result: "updated" | "registered", tool: string, callab
382
358
  }
383
359
 
384
360
  function duplicateRegistrationText(value: string): string {
385
- const render = (selected: string) => "- field: name\n" + selected + "\n- result: Tool already registered.\n- instruction: Use update=true to overwrite.";
386
- const wellFormed = value.isWellFormed();
387
- if (wellFormed) {
388
- const complete = render(ToolsResponse.formatMarkdownField("value", value));
389
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) return complete;
390
- }
391
- return render(`- value: [${wellFormed ? "omitted" : "unavailable"}]\n- value_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`);
361
+ const selected = registrySelected("value", value);
362
+ const render = (): RegistryMetadata => ({ field: "name", ...selected.selected,
363
+ result: "Tool already registered.", instruction: "Use update=true to overwrite." });
364
+ return growRegistryDocument(render, [selected]);
392
365
  }
393
366
 
394
367
  function draftRecipeFailureText(key: "reason" | "diagnostics_summary", value?: string): string {
395
- const header = "- field: draft\n- result: Draft recipe is invalid.";
396
- if (value === undefined) return header;
397
- const wellFormed = value.isWellFormed();
398
- if (wellFormed) {
399
- const complete = header + "\n" + ToolsResponse.formatMarkdownField(key, value);
400
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) return complete;
401
- }
402
- return header + `\n- ${key}: [${wellFormed ? "omitted" : "unavailable"}]\n- ${key}_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`;
368
+ const selected = value === undefined ? undefined : registrySelected(key, value);
369
+ const render = (): RegistryMetadata => ({ field: "draft", result: "Draft recipe is invalid.", ...(selected ? selected.selected : {}) });
370
+ return growRegistryDocument(render, selected ? [selected] : []);
403
371
  }
404
372
 
405
373
  function promoteDraftRecipe<TContext>(
@@ -607,9 +575,9 @@ function assertTemplateIsNotNestedRecipe(value: unknown): void {
607
575
  ))
608
576
  ) {
609
577
  throw new Error(
610
- ExecutionOutput.formatToolText(
611
- "- field: template\n- result: Nested Recipe-shaped templates are not a register_tool source mode.\n- instruction: retry with from=<skill>/<recipe> and defaults={...}.",
612
- ),
578
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "template",
579
+ result: "Nested Recipe-shaped templates are not a register_tool source mode.",
580
+ instruction: "retry with from=<skill>/<recipe> and defaults={...}." })),
613
581
  );
614
582
  }
615
583
  }
@@ -626,7 +594,7 @@ function getInputTemplate(
626
594
  });
627
595
  if (steps.length === 0)
628
596
  throw new Error(
629
- ExecutionOutput.formatToolText("- field: template\n- result: Tool template sequence is empty."),
597
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "template", result: "Tool template sequence is empty." })),
630
598
  );
631
599
  return value;
632
600
  }
@@ -637,9 +605,8 @@ function getInputTemplate(
637
605
  return value as CommandTemplates.CommandTemplateConfig;
638
606
  }
639
607
  throw new Error(
640
- ExecutionOutput.formatToolText(
641
- "- field: template\n- result: unsupported template type\n- supported_types:\n - string\n - object\n - sequence",
642
- ),
608
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "template", result: "unsupported template type",
609
+ supported_types: ["string", "object", "sequence"] })),
643
610
  );
644
611
  }
645
612
 
@@ -665,7 +632,7 @@ function normalizeDefaults(value: unknown): Record<string, unknown> | undefined
665
632
  if (value === undefined) return undefined;
666
633
  if (!value || typeof value !== "object" || Array.isArray(value)) {
667
634
  throw new Error(
668
- ExecutionOutput.formatToolText("- field: defaults\n- result: Tool defaults must be an object."),
635
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "defaults", result: "Tool defaults must be an object." })),
669
636
  );
670
637
  }
671
638
  return { ...(value as Record<string, unknown>) };
@@ -725,9 +692,8 @@ function buildAuthoredRecipe(
725
692
  const description = (input.description ?? existing?.description ?? "").trim();
726
693
  if (!description && !from) {
727
694
  throw new Error(
728
- ExecutionOutput.formatToolText(
729
- "- field: description\n- result: Tool description is required for command-template registration.",
730
- ),
695
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "description",
696
+ result: "Tool description is required for command-template registration." })),
731
697
  );
732
698
  }
733
699
  const inputDefaults = normalizeDefaults(input.defaults);
@@ -737,9 +703,10 @@ function buildAuthoredRecipe(
737
703
  );
738
704
  if (runtimeOwnedDefault) {
739
705
  throw new Error(
740
- ExecutionOutput.formatToolText(
741
- `- field: defaults\n- key: ${runtimeOwnedDefault}\n- result: Tool defaults cannot set runtime-owned input.\n- instruction: remove that default and retry register_tool; the runtime supplies it.`,
742
- ),
706
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "defaults",
707
+ ...registrySelected("key", runtimeOwnedDefault).selected,
708
+ result: "Tool defaults cannot set runtime-owned input.",
709
+ instruction: "remove that default and retry register_tool; the runtime supplies it." })),
743
710
  );
744
711
  }
745
712
  const authored: Record<string, unknown> = {
@@ -758,7 +725,7 @@ function buildAuthoredRecipe(
758
725
  const template = getInputTemplate(input.template);
759
726
  if (template === null) {
760
727
  throw new Error(
761
- ExecutionOutput.formatToolText("- field: template\n- result: Tool template cannot be null here."),
728
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "template", result: "Tool template cannot be null here." })),
762
729
  );
763
730
  }
764
731
  const finalTemplate =
@@ -767,7 +734,7 @@ function buildAuthoredRecipe(
767
734
  : template;
768
735
  if (!finalTemplate) {
769
736
  throw new Error(
770
- ExecutionOutput.formatToolText("- field: template\n- result: Tool template is required."),
737
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "template", result: "Tool template is required." })),
771
738
  );
772
739
  }
773
740
  const authored: Record<string, unknown> = {
@@ -792,9 +759,9 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
792
759
  const record = input as RegisterToolInput & Record<string, unknown>;
793
760
  if (Object.hasOwn(record, "values")) {
794
761
  throw new Error(
795
- ExecutionOutput.formatToolText(
796
- "- field: values\n- result: register_tool values was removed.\n- replacement_fields:\n - defaults\n - from\n- instruction: retry register_tool with defaults for caller inputs, or use from=<skill>/<recipe> for maintained composition.",
797
- ),
762
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "values", result: "register_tool values was removed.",
763
+ replacement_fields: ["defaults", "from"],
764
+ instruction: "retry register_tool with defaults for caller inputs, or use from=<skill>/<recipe> for maintained composition." })),
798
765
  );
799
766
  }
800
767
  const fromProvided = Object.hasOwn(record, "from");
@@ -803,9 +770,10 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
803
770
  (typeof input.from !== "string" || input.from.trim().length === 0)
804
771
  ) {
805
772
  throw new Error(
806
- ExecutionOutput.formatToolText(
807
- "- field: from\n- result: Tool from must be a non-empty Recipe reference.\n- reference_forms:\n - <skill>/<recipe>\n - explicit .json path\n - explicit .md path\n- instruction: retry register_tool with from=<skill>/<recipe> or an explicit .json/.md path.",
808
- ),
773
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "from",
774
+ result: "Tool from must be a non-empty Recipe reference.",
775
+ reference_forms: ["<skill>/<recipe>", "explicit .json path", "explicit .md path"],
776
+ instruction: "retry register_tool with from=<skill>/<recipe> or an explicit .json/.md path." })),
809
777
  );
810
778
  }
811
779
  const draftProvided =
@@ -815,9 +783,9 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
815
783
  Number(fromProvided) + Number(draftProvided) + Number(templateProvided);
816
784
  if (modeCount > 1) {
817
785
  throw new Error(
818
- ExecutionOutput.formatToolText(
819
- "- result: Use exactly one register_tool source mode.\n- source_modes:\n - from\n - template\n - draft\n- instruction: remove the extra source fields and retry register_tool.",
820
- ),
786
+ ExecutionOutput.formatToolText(registryMetadataText({ result: "Use exactly one register_tool source mode.",
787
+ source_modes: ["from", "template", "draft"],
788
+ instruction: "remove the extra source fields and retry register_tool." })),
821
789
  );
822
790
  }
823
791
  if (
@@ -825,21 +793,18 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
825
793
  (Object.hasOwn(record, "args") || Object.hasOwn(record, "async"))
826
794
  ) {
827
795
  throw new Error(
828
- ExecutionOutput.formatToolText(
829
- "- field: from\n- result: register_tool from inherits args and async behavior.\n- inherited_fields:\n - args\n - async\n- instruction: retry register_tool with from and optional defaults only; omit args and async.",
830
- ),
796
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "from",
797
+ result: "register_tool from inherits args and async behavior.",
798
+ inherited_fields: ["args", "async"],
799
+ instruction: "retry register_tool with from and optional defaults only; omit args and async." })),
831
800
  );
832
801
  }
833
802
  }
834
803
 
835
804
  function reservedRegistrationNameText(value: string): string {
836
- const render = (selected: string) => "- field: name\n" + selected + "\n- result: Reserved tool name.";
837
- const wellFormed = value.isWellFormed();
838
- if (wellFormed) {
839
- const complete = render(ToolsResponse.formatMarkdownField("value", value));
840
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) return complete;
841
- }
842
- return render(`- value: [${wellFormed ? "omitted" : "unavailable"}]\n- value_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`);
805
+ const selected = registrySelected("value", value);
806
+ const render = (): RegistryMetadata => ({ field: "name", ...selected.selected, result: "Reserved tool name." });
807
+ return growRegistryDocument(render, [selected]);
843
808
  }
844
809
 
845
810
  function executeRegisterToolUnlocked<TContext>(
@@ -856,7 +821,7 @@ function executeRegisterToolUnlocked<TContext>(
856
821
  if (!input.name) return listTools(deps);
857
822
  const name = Identity.normalizeToolName(input.name);
858
823
  if (!name)
859
- throw new Error(ExecutionOutput.formatToolText("- field: name\n- result: Invalid tool name."));
824
+ throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "name", result: "Invalid tool name." })));
860
825
  if (deps.reservedToolNames.has(name)) {
861
826
  throw new Error(
862
827
  ExecutionOutput.formatToolText(reservedRegistrationNameText(`${name}`)),
@@ -904,9 +869,9 @@ function executeRegisterToolUnlocked<TContext>(
904
869
  }
905
870
  if (template === undefined && !input.from && !existing) {
906
871
  throw new Error(
907
- ExecutionOutput.formatToolText(
908
- "- result: New registration requires exactly one source mode.\n- source_modes:\n - from\n - template\n - draft\n- instruction: retry register_tool with one source field.",
909
- ),
872
+ ExecutionOutput.formatToolText(registryMetadataText({ result: "New registration requires exactly one source mode.",
873
+ source_modes: ["from", "template", "draft"],
874
+ instruction: "retry register_tool with one source field." })),
910
875
  );
911
876
  }
912
877
  const recipePath = getToolRecipePath(deps, name);