@llblab/pi-actors 0.56.1 → 0.57.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.
@@ -30,7 +30,6 @@ import * as ToolsResponse from "./tools-response.ts";
30
30
  import * as TraceProjection from "./trace-projection.ts";
31
31
 
32
32
  const asRecord = ToolsResponse.asRecord;
33
- const maybeJsonText = ToolsResponse.maybeJsonText;
34
33
 
35
34
  export interface InspectToolDeps<TContext = unknown> {
36
35
  getRunStatus?: (runOrDir: string) => Record<string, any>;
@@ -185,16 +184,44 @@ function runtimeTriage(
185
184
  };
186
185
  }
187
186
 
187
+ /** Compact metadata-only documents share the same emitter/omission policy as other receipts. */
188
+ function compactDocument(value: Record<string, unknown>): string {
189
+ return ToolsResponse.compactYamlDocument(value);
190
+ }
191
+
192
+ /** Generated definition metadata is YAML like other owned output; the machine JSON stays in details. */
193
+ function schemaDocument(details: Record<string, unknown>): string {
194
+ // Absent definition fields stay absent exactly as JSON.stringify omitted them; values are native nodes.
195
+ const selected: Record<string, unknown> = {};
196
+ for (const key of ["name", "description", "parameters", "promptSnippet"])
197
+ if (details[key] !== undefined) selected[key] = details[key];
198
+ // The shared emitter's own execution ceiling is the only wider allowance; a pathological schema
199
+ // keeps the exact legacy JSON text rather than being clipped or partly reformatted.
200
+ const rendered = ToolsResponse.renderYamlMetadata(selected, 49152);
201
+ return rendered.state === "available"
202
+ ? `\n${rendered.text}` : `\n${JSON.stringify(details, null, 2)}`;
203
+ }
204
+
205
+ function compactSelected(key: string, value: string): ToolsResponse.YamlPart {
206
+ return ToolsResponse.selectYamlField(key, value, Limits.RUN_DELIVERY_MODEL_MAX_BYTES);
207
+ }
208
+
209
+ function compactArguments(key: string, value: unknown): ToolsResponse.YamlPart {
210
+ const selected = Array.isArray(value) && value.length
211
+ ? value.map(item => String(item)) : Array.isArray(value) ? "none" : "unknown";
212
+ return ToolsResponse.selectYamlField(key, selected, Limits.RUN_DELIVERY_MODEL_MAX_BYTES);
213
+ }
214
+
188
215
  function compactRuntime(view: string, details: Record<string, unknown>): string {
189
- const fields = view === "runs" ? { runs: (details.runs as unknown[] | undefined)?.length ?? 0 }
216
+ const count = (value: unknown) => (Array.isArray(value) ? value.length : 0);
217
+ return compactDocument(view === "runs" ? { runtime: view, runs: count(details.runs) }
190
218
  : view === "triage" ? {
191
- failed: (details.failed_runs as unknown[] | undefined)?.length ?? 0,
219
+ runtime: view, failed: count(details.failed_runs),
192
220
  pending_controls: Number(details.pending_control_count ?? 0), stale_controls: Number(details.stale_control_count ?? 0),
193
221
  backpressured: Number(details.backpressured_run_count ?? 0), incomplete_trace: Number(details.trace_diagnostic_count ?? 0),
194
- attention: (details.attention_events as unknown[] | undefined)?.length ?? 0,
195
- } : { version: String(details.version), state_schema: String(details.state_schema), automatic_review: String(details.automatic_review) };
196
- return "\n" + [ToolsResponse.formatMarkdownField("runtime", view),
197
- ...Object.entries(fields).map(([key, value]) => ToolsResponse.formatMarkdownField(key, String(value)))].join("\n");
222
+ attention: count(details.attention_events),
223
+ } : { runtime: view, version: String(details.version), state_schema: String(details.state_schema),
224
+ automatic_review: String(details.automatic_review) });
198
225
  }
199
226
 
200
227
  // Projects local refusals only; does not intercept other throws or change existing catches.
@@ -202,28 +229,39 @@ function formatInspectFailure(
202
229
  fields: readonly (readonly [key: string, value: string | readonly string[], omittable?: true])[],
203
230
  next?: readonly [target: string, view: string],
204
231
  ): string {
205
- // The unchanged public adapter adds exactly one leading LF to these Error messages.
206
- const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
207
- const omitted = (key: string) => `- ${key}:\n - state: omitted\n - reason: response_too_large\n - shown: 0\n - omitted: 1\n - total: 1`;
208
- const field = ([key, value]: (typeof fields)[number]) => typeof value === "string"
209
- ? ToolsResponse.formatMarkdownField(key, value)
210
- : `- ${key}:\n${value.map(item => ` - ${item}`).join("\n")}`;
211
- // Unmarked fields are bounded compiler literals/lists. Short values seed full so admission only grows.
212
- const formatted = fields.map(field);
213
- const projected = fields.map((item, index) => {
214
- const full = formatted[index]!, fallback = omitted(item[0]);
215
- return item[2] && Buffer.byteLength(full, "utf8") > Buffer.byteLength(fallback, "utf8") ? fallback : full;
216
- });
217
- let action = next ? ToolsResponse.formatInspectNextAction(next[0], next[1]) : "";
218
- const text = () => [...projected, ...(action ? [action] : [])].join("\n");
219
- if (next && Buffer.byteLength(text(), "utf8") > budget) action = omitted("next_action");
220
- fields.forEach((item, index) => {
221
- if (!item[2]) return;
222
- const fallback = projected[index]!;
223
- projected[index] = formatted[index]!;
224
- if (Buffer.byteLength(text(), "utf8") > budget) projected[index] = fallback;
232
+ // The unchanged public adapter replaces this document's trailing LF with its one leading LF.
233
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES;
234
+ // Field-local coverage record: an unfit selected scalar/sequence is omitted whole, never clipped.
235
+ const omitted = { state: "omitted", reason: "response_too_large", shown: 0, omitted: 1, total: 1 };
236
+ const render = (value: Record<string, unknown>): string | undefined => {
237
+ const rendered = ToolsResponse.renderYamlMetadata(value, budget);
238
+ return rendered.state === "available" ? rendered.text : undefined;
239
+ };
240
+ const parts = fields.map(([key, value, omittable]) => {
241
+ const full: Record<string, unknown> = typeof value === "string" ? { [key]: value } : { [key]: [...value] };
242
+ const fallback: Record<string, unknown> = { [key]: omitted };
243
+ // Unmarked fields are bounded compiler literals/lists. Short values seed full so admission only grows.
244
+ const complete = render(full);
245
+ const selected = omittable && (complete === undefined || Buffer.byteLength(complete) > Buffer.byteLength(render(fallback) ?? ""))
246
+ ? fallback : full;
247
+ return { omittable, full, selected };
225
248
  });
226
- return text();
249
+ // Only producer-known read-only inspect literals become a typed next, never a parsed action.
250
+ const typedNext = next ? { next: { tool: "inspect", target: next[0], view: next[1] } } : undefined;
251
+ const nextFallback = { next_action: omitted };
252
+ const document = () => ({ ...Object.assign({}, ...parts.map((part) => part.selected)),
253
+ ...(typedNext ?? nextFallback) });
254
+ let selectedNext = typedNext;
255
+ if (next && render(document()) === undefined) selectedNext = undefined;
256
+ const nextValue = () => (next ? (selectedNext ?? nextFallback) : {});
257
+ for (const part of parts) {
258
+ if (!part.omittable) continue;
259
+ const previous = part.selected;
260
+ part.selected = part.full;
261
+ if (render({ ...Object.assign({}, ...parts.map((item) => item.selected)), ...nextValue() }) === undefined) part.selected = previous;
262
+ }
263
+ const text = render({ ...Object.assign({}, ...parts.map((item) => item.selected)), ...nextValue() });
264
+ return (text ?? "").replace(/\n$/u, "");
227
265
  }
228
266
 
229
267
  function inspectRuntime(
@@ -387,11 +425,13 @@ function inspectRecipeIdentity(
387
425
  }
388
426
 
389
427
  function inspectRecipes(
390
- view: string,
428
+ requestedView: string,
391
429
  input: Record<string, unknown>,
392
430
  deps: InspectToolDeps,
393
431
  context: unknown,
394
432
  ): Record<string, unknown> {
433
+ // `registry` and `summary` name one inspection path; only `verbose` selects compact versus full output.
434
+ const view = requestedView === "registry" ? "summary" : requestedView;
395
435
  if (
396
436
  view !== "status" &&
397
437
  view !== "summary" &&
@@ -401,7 +441,7 @@ function inspectRecipes(
401
441
  ) {
402
442
  throw new Error(formatInspectFailure([
403
443
  ["target_kind", "recipes"], ["result", "unsupported view"],
404
- ["supported_views", ["status", "summary", "doctor", "imports", "reviews"]],
444
+ ["supported_views", ["status", "summary", "registry", "doctor", "imports", "reviews"]],
405
445
  ]));
406
446
  }
407
447
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
@@ -637,37 +677,47 @@ function inspectTool(
637
677
  function compactResult(target: string, view: string, details: Record<string, unknown>): string {
638
678
  if (target === "runtime") return compactRuntime(view, details);
639
679
  if (target === "recipes" && typeof details.identity === "string") {
640
- const fields = [ToolsResponse.formatMarkdownField("recipes", "doctor"),
641
- ...["identity", "skill_active", "resolvable", "catalog_partial", "component_status"]
642
- .map(key => ToolsResponse.formatMarkdownField(key, String(details[key]))),
643
- ...(details.source_location ? [ToolsResponse.formatMarkdownField("source_location", ToolsResponse.formatLiteralBlock(String(details.source_location)))] : []),
644
- ToolsResponse.formatMarkdownField("resolution_generation", String(details.resolution_generation)),
645
- ...(details.rejected_reason ? [ToolsResponse.formatMarkdownField("rejected_reason", String(details.rejected_reason))] : [])];
680
+ const identity = compactSelected("identity", String(details.identity));
681
+ const location = typeof details.source_location === "string"
682
+ ? compactSelected("source_location", details.source_location) : undefined;
683
+ const generation = compactSelected("resolution_generation", String(details.resolution_generation));
684
+ const rejected = typeof details.rejected_reason === "string"
685
+ ? compactSelected("rejected_reason", details.rejected_reason) : undefined;
646
686
  const actions = details.next_actions as unknown[] | undefined;
647
- if (actions?.length) fields.push(ToolsResponse.formatInspectionHint(String(actions[0])));
648
- return "\n" + fields.join("\n");
687
+ const hint = actions?.length
688
+ ? ToolsResponse.nextActionMetadata(String(actions[0]), Limits.RUN_DELIVERY_MODEL_MAX_BYTES) : undefined;
689
+ const parts = [identity, ...(location ? [location] : []), generation, ...(rejected ? [rejected] : []), ...(hint ? [hint] : [])];
690
+ const render = (): Record<string, unknown> => ({ recipes: "doctor", ...identity.selected,
691
+ skill_active: String(details.skill_active), resolvable: String(details.resolvable),
692
+ catalog_partial: String(details.catalog_partial), component_status: String(details.component_status),
693
+ ...(location ? location.selected : {}), ...generation.selected, ...(rejected ? rejected.selected : {}),
694
+ ...(hint ? hint.selected : {}) });
695
+ return `\n${ToolsResponse.growYamlDocument(render, parts, Limits.RUN_DELIVERY_MODEL_MAX_BYTES).replace(/\n$/u, "")}`;
649
696
  }
650
697
  if (target === "recipes") return ToolsResponse.compactRecipeRegistry(details);
651
698
  if (target.startsWith("tool:") && view === "status") {
652
- const argumentsField = (key: string, value: unknown): string => Array.isArray(value) && value.length
653
- ? "- " + key + ":\n" + value.map(item => " - " + String(item).replace(/\r\n|\r|\n/gu, match => match + " ")).join("\n")
654
- : ToolsResponse.formatMarkdownField(key, Array.isArray(value) ? "none" : "unknown");
699
+ const name = target.slice(5);
700
+ const tool = compactSelected("tool", name);
701
+ const source = compactSelected("source", String(details.source ?? "unknown"));
702
+ const boundary = compactSelected("activation_boundary", String(details.activation_boundary ?? "unknown"));
655
703
  const next = Array.isArray(details.next_actions) && details.next_actions.length > 0
656
704
  ? String(details.next_actions[0])
657
705
  : details.callable_now === true
658
- ? `call tool ${target.slice(5)}`
659
- : `register_tool name=${target.slice(5)} update=true`;
660
- return "\n" + [ToolsResponse.formatMarkdownField("tool", target.slice(5)),
661
- ToolsResponse.formatMarkdownField("source", String(details.source ?? "unknown")),
662
- ToolsResponse.formatMarkdownField("callable_now", String(details.callable_now ?? false)),
663
- ToolsResponse.formatMarkdownField("activation_boundary", String(details.activation_boundary ?? "unknown")),
664
- argumentsField("required_args", details.required_args), argumentsField("optional_args", details.optional_args),
665
- ToolsResponse.formatMarkdownField("launch_kind", String(details.launch_kind ?? "none")),
666
- ToolsResponse.formatMarkdownField("spawn_calls", String(Number(details.spawn_calls ?? 0))),
667
- ToolsResponse.formatMarkdownField("tool_calls", String(Number(details.tool_calls ?? 0))), ToolsResponse.formatInspectionHint(next)].join("\n");
706
+ ? `call tool ${name}`
707
+ : `register_tool name=${name} update=true`;
708
+ const hint = ToolsResponse.nextActionMetadata(next, Limits.RUN_DELIVERY_MODEL_MAX_BYTES);
709
+ const required = compactArguments("required_args", details.required_args);
710
+ const optional = compactArguments("optional_args", details.optional_args);
711
+ const render = (): Record<string, unknown> => ({ ...tool.selected, ...source.selected,
712
+ callable_now: details.callable_now === true, ...boundary.selected,
713
+ ...required.selected, ...optional.selected,
714
+ launch_kind: String(details.launch_kind ?? "none"),
715
+ spawn_calls: Number(details.spawn_calls ?? 0), tool_calls: Number(details.tool_calls ?? 0),
716
+ ...hint.selected });
717
+ return `\n${ToolsResponse.growYamlDocument(render, [tool, source, boundary, required, optional, hint], Limits.RUN_DELIVERY_MODEL_MAX_BYTES).replace(/\n$/u, "")}`;
668
718
  }
669
- if (target.startsWith("tool:")) return "\n" + ToolsResponse.formatMarkdownField("tool", target.slice(5)) + "\n" + ToolsResponse.formatMarkdownField("view", view);
670
- return "\n" + ToolsResponse.formatMarkdownField("run", target.slice(4)) + "\n" + ToolsResponse.formatMarkdownField("view", view);
719
+ if (target.startsWith("tool:")) return compactDocument({ tool: target.slice(5), view });
720
+ return compactDocument({ run: target.slice(4), view });
671
721
  }
672
722
 
673
723
  export function createInspectToolDefinition<TContext = unknown>(
@@ -691,7 +741,7 @@ export function createInspectToolDefinition<TContext = unknown>(
691
741
  target: Schema.stringSchema("Target: run:<id>, runtime, recipes, or tool:<name>."),
692
742
  verbose: Schema.booleanSchema("Return full JSON instead of compact text."),
693
743
  view: Schema.stringSchema(
694
- "Run view recipe|trace|control, or target-specific management view.",
744
+ "Run view recipe|trace|control, or one target-specific management view. runtime status|runs|triage; recipes status|summary|doctor|imports|reviews, where registry aliases summary; tool status|schema.",
695
745
  ),
696
746
  },
697
747
  ["target", "view"],
@@ -726,11 +776,11 @@ export function createInspectToolDefinition<TContext = unknown>(
726
776
  return {
727
777
  content: [{
728
778
  type: "text" as const,
729
- text: maybeJsonText(
730
- details,
731
- input.verbose === true || (target.startsWith("tool:") && view === "schema"),
732
- compactResult(target, view, details),
733
- ),
779
+ text: input.verbose === true
780
+ ? ToolsResponse.jsonText(details)
781
+ : target.startsWith("tool:") && view === "schema"
782
+ ? schemaDocument(details)
783
+ : compactResult(target, view, details),
734
784
  }],
735
785
  details,
736
786
  };
@@ -6,6 +6,7 @@
6
6
 
7
7
  import * as AsyncRuns from "./async-runs.ts";
8
8
  import * as CommandTemplates from "./command-templates.ts";
9
+ import * as Limits from "./limits.ts";
9
10
  import * as ModelContext from "./model-context.ts";
10
11
  import type { RegisteredTool } from "./config.ts";
11
12
  import * as Execution from "./execution.ts";
@@ -66,6 +67,25 @@ function shouldAddRuntimeToolUsageHint(error: unknown): boolean {
66
67
  );
67
68
  }
68
69
 
70
+ /** Owned usage facts only; the JSON invocation example stays unparsed instructional body text. */
71
+ function runtimeToolUsageMetadata(
72
+ cfg: RegisteredTool,
73
+ required: string[],
74
+ includeRunId: boolean,
75
+ ): string {
76
+ const optional = cfg.args.filter(
77
+ (arg) =>
78
+ !RecipesReferences.isRuntimeOwnedRecipeInput(arg) &&
79
+ !required.includes(arg),
80
+ );
81
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES;
82
+ const tool = ToolsResponse.selectYamlField("tool", cfg.name, budget);
83
+ const requiredPart = ToolsResponse.selectYamlField("required", required, budget);
84
+ const optionalPart = ToolsResponse.selectYamlField("optional", [...optional, ...(includeRunId ? ["run_id"] : [])], budget);
85
+ return ToolsResponse.growYamlDocument(() => ({ ...tool.selected, ...requiredPart.selected, ...optionalPart.selected }),
86
+ [tool, requiredPart, optionalPart], budget);
87
+ }
88
+
69
89
  function formatRuntimeToolUsageHint(
70
90
  cfg: RegisteredTool,
71
91
  required: string[],
@@ -87,16 +107,10 @@ function formatRuntimeToolUsageHint(
87
107
  for (const arg of optional)
88
108
  example[arg] = sampleValueForArg(arg, cfg.argTypes?.[arg], exampleDefaults);
89
109
  if (includeRunId) example.run_id = `${cfg.name}-1`;
90
- const lines = [
110
+ return [
91
111
  `Expected call shape for ${cfg.name}:`,
92
112
  `${cfg.name}(${JSON.stringify(example, null, 2)})`,
93
- ];
94
- if (required.length) lines.push(`Required: ${required.join(", ")}`);
95
- if (optional.length || includeRunId)
96
- lines.push(
97
- `Optional: ${[...optional, ...(includeRunId ? ["run_id"] : [])].join(", ")}`,
98
- );
99
- return lines.join("\n");
113
+ ].join("\n");
100
114
  }
101
115
 
102
116
  function resolveRecipeToolValues(
@@ -134,13 +148,10 @@ function formatRuntimeToolArgumentError(
134
148
  const message = error instanceof Error ? error.message : String(error);
135
149
  if (!shouldAddRuntimeToolUsageHint(error))
136
150
  return error instanceof Error ? error : new Error(message);
137
- return new Error(
138
- `Invalid arguments for tool "${cfg.name}": ${message}\n\n${formatRuntimeToolUsageHint(
139
- cfg,
140
- required,
141
- includeRunId,
142
- )}`,
143
- );
151
+ // Whole usage groups fit the header budget or carry explicit omission; the JSON example stays opaque.
152
+ const header = runtimeToolUsageMetadata(cfg, required, includeRunId);
153
+ return new Error(ToolsResponse.withLeadingLineBreak(ToolsResponse.frameMetadataDocument(header,
154
+ `Invalid arguments for tool "${cfg.name}": ${message}\n\n${formatRuntimeToolUsageHint(cfg, required, includeRunId)}`)));
144
155
  }
145
156
 
146
157
  export function createRuntimeToolDefinition(
@@ -17,7 +17,54 @@ function asRecord(value: unknown): Record<string, unknown> {
17
17
  : {};
18
18
  }
19
19
 
20
- const controlField = ToolsResponse.formatMarkdownField;
20
+ const CONTROL_BUDGET = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
21
+
22
+ type ControlDocument = Record<string, unknown>;
23
+ type ControlPart = { full: ControlDocument; selected: ControlDocument };
24
+
25
+ function controlMetadata(value: ControlDocument): string {
26
+ return (ToolsResponse.renderYamlMetadata(value, CONTROL_BUDGET) as
27
+ Extract<ToolsResponse.YamlMetadataRendering, { state: "available" }>).text;
28
+ }
29
+
30
+ /** One opaque selected scalar as a whole value or an explicit whole-field omission, never a clip. */
31
+ function controlSelected(key: string, value: string): ControlPart {
32
+ return ToolsResponse.selectYamlField(key, value, CONTROL_BUDGET);
33
+ }
34
+
35
+ /** Compiler-selected option strings stay a native sequence; malformed selections stay unavailable. */
36
+ function controlChoices(options: { key: "supported_actions" | "available_actions"; values: readonly string[] }): ControlPart {
37
+ const key = options.key, values = options.values;
38
+ // Values were selected by the original string filter, not a new action grammar.
39
+ const wellFormed = values.every((value) => typeof value === "string" && value.isWellFormed());
40
+ const omitted: ControlDocument = { [key]: wellFormed ? "[omitted]" : "[unavailable]",
41
+ [key + "_omission"]: { reason: wellFormed ? "response_too_large" : "malformed_selected_text",
42
+ shown: 0, omitted: values.length, total: values.length } };
43
+ const full: ControlDocument = !wellFormed ? omitted : values.length ? { [key]: [...values] } : { [key]: "none" };
44
+ const complete = ToolsResponse.renderYamlMetadata(full, CONTROL_BUDGET);
45
+ return { full, selected: complete.state === "available" && Buffer.byteLength(complete.text) <= Buffer.byteLength(controlMetadata(omitted)) ? full : omitted };
46
+ }
47
+
48
+ /** Grow whole selected fields in priority order while the complete document still fits. */
49
+ function controlDocument(render: () => ControlDocument, order: readonly ControlPart[]): string {
50
+ return ToolsResponse.growYamlDocument(render, order, CONTROL_BUDGET);
51
+ }
52
+
53
+ /** Only producer-known read-only inspect literals become typed next; other advice stays opaque content. */
54
+ function controlNextPart(action: string): ControlPart {
55
+ const inspect = /^inspect target=([^\s]+) view=([^\s]+)$/u.exec(action);
56
+ if (inspect) {
57
+ return ToolsResponse.selectYamlNode("next", { tool: "inspect", target: inspect[1], view: inspect[2] }, CONTROL_BUDGET);
58
+ }
59
+ const wellFormed = action.isWellFormed();
60
+ const omitted: ControlDocument = { next: { instruction: wellFormed ? "[omitted]" : "[unavailable]",
61
+ instruction_omission: { reason: wellFormed ? "response_too_large" : "malformed_selected_text", shown: 0, omitted: 1, total: 1 } } };
62
+ const full: ControlDocument = wellFormed ? { next: { instruction: action } } : omitted;
63
+ const complete = wellFormed ? ToolsResponse.renderYamlMetadata(full, CONTROL_BUDGET) : undefined;
64
+ const selected = complete?.state === "available" && Buffer.byteLength(complete.text) <= Buffer.byteLength(controlMetadata(omitted))
65
+ ? full : omitted;
66
+ return { full, selected };
67
+ }
21
68
 
22
69
  /** Only the four compiler-owned guard sites below; never wraps dispatch/dependency errors. */
23
70
  function controlGuardText(
@@ -26,37 +73,12 @@ function controlGuardText(
26
73
  options?: { key: "supported_actions" | "available_actions"; values: readonly string[] },
27
74
  ): string {
28
75
  const key = request.target === "runtime" ? "target" : "run";
29
- const identity = controlField(key, request.target === "runtime" ? request.target : request.target.slice(4));
30
- const omittedIdentity = `- ${key}: [omitted]\n- ${key}_omission:\n - reason: response_too_large\n - shown: 0\n - omitted: 1\n - total: 1`;
31
- const fixed = [controlField("action", request.action), controlField("result", result)];
32
- let fullOptions = "", omittedOptions = "";
33
- if (options) {
34
- const rows: string[] = [];
35
- let malformed = false;
36
- // Values were selected by the original string filter, not by a new action grammar.
37
- for (let index = 0; index < options.values.length; index++) {
38
- const value = options.values[index];
39
- if (!value.isWellFormed()) { malformed = true; break; }
40
- rows.push(" - " + value.replaceAll(/\r\n|\r|\n/gu, newline => newline + " "));
41
- }
42
- omittedOptions = `- ${options.key}: [${malformed ? "unavailable" : "omitted"}]\n- ${options.key}_omission:\n - reason: ${malformed ? "malformed_selected_text" : "response_too_large"}\n - shown: 0\n - omitted: ${options.values.length}\n - total: ${options.values.length}`;
43
- fullOptions = malformed ? omittedOptions : rows.length ? `- ${options.key}:\n${rows.join("\n")}` : controlField(options.key, "none");
44
- }
45
- // Start with the shorter complete-or-omitted rendering: adding an omission scaffold must
46
- // never manufacture overflow when the complete small field would fit. Then grow only.
47
- let selectedIdentity = Buffer.byteLength(identity) <= Buffer.byteLength(omittedIdentity) ? identity : omittedIdentity;
48
- let selectedOptions = Buffer.byteLength(fullOptions) <= Buffer.byteLength(omittedOptions) ? fullOptions : omittedOptions;
49
- const render = (target: string, choices: string) => ToolsResponse.withLeadingLineBreak(
50
- [target, ...fixed, ...(options ? [choices] : [])].join("\n"));
51
- if (Buffer.byteLength(render(identity, selectedOptions)) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES) selectedIdentity = identity;
52
- if (options && Buffer.byteLength(render(selectedIdentity, fullOptions)) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES) selectedOptions = fullOptions;
53
- return render(selectedIdentity, selectedOptions);
54
- }
55
-
56
- function controlNext(action: string): string {
57
- const inspect = /^inspect target=([^\s]+) view=([^\s]+)$/u.exec(action);
58
- return inspect ? ToolsResponse.formatInspectNextAction(inspect[1], inspect[2])
59
- : "- next:\n" + controlField("instruction", action, 1);
76
+ const identity = controlSelected(key, request.target === "runtime" ? request.target : request.target.slice(4));
77
+ const choices = options ? controlChoices(options) : undefined;
78
+ const render = (): ControlDocument => ({ ...identity.selected, action: request.action, result,
79
+ ...(choices ? choices.selected : {}) });
80
+ // Whole identity first, then the complete option sequence: the shorter seed must never manufacture overflow.
81
+ return ToolsResponse.withLeadingLineBreak(controlDocument(render, choices ? [identity, choices] : [identity]));
60
82
  }
61
83
 
62
84
  /** Pure first-layer copy; legacy stopped/killed flags are not scope-closure proof. */
@@ -64,32 +86,40 @@ export function compactControlResult(
64
86
  request: Control.ControlRequest,
65
87
  result: Record<string, unknown>,
66
88
  ): string {
67
- const fields = [request.target.startsWith("run:") ? controlField("run", request.target.slice(4)) : controlField("target", request.target),
68
- controlField("action", request.action)];
89
+ const key = request.target.startsWith("run:") ? "run" : "target";
90
+ const identity = controlSelected(key, key === "run" ? request.target.slice(4) : request.target);
91
+ const facts: ControlDocument = {};
69
92
  if (result.stopped === false || result.archived === false || result.pruned === false) {
70
- fields.push(controlField("result", "not applied"));
71
- if (typeof result.reason === "string") fields.push(controlField("reason", result.reason));
72
- const observed = asRecord(result.status);
73
- if (result.reason === "not running" && typeof observed.status === "string") fields.push(controlField("state", observed.status));
93
+ facts.result = "not applied";
74
94
  } else if (request.action === "kill" && result.stopped === true) {
75
- fields.push(controlField("result", "kill attempted"), controlField("termination", "unconfirmed"));
95
+ facts.result = "kill attempted";
96
+ facts.termination = "unconfirmed";
76
97
  } else if (result.archived === true) {
77
- fields.push(controlField("result", "archived"));
98
+ facts.result = "archived";
78
99
  } else if (result.pruned === true) {
79
- fields.push(controlField("result", "pruned"));
100
+ facts.result = "pruned";
80
101
  } else if (result.delivery === "queued" || result.delivery === "delivered") {
81
- fields.push(controlField("delivery", result.delivery), controlField("handling", "unconfirmed"));
102
+ facts.delivery = result.delivery;
103
+ facts.handling = "unconfirmed";
82
104
  } else {
83
- fields.push(controlField(typeof result.delivery === "string" ? "delivery" : "result",
84
- typeof result.delivery === "string" ? result.delivery : typeof result.status === "string" ? result.status : "accepted"));
85
- if (typeof result.reason === "string") fields.push(controlField("reason", result.reason));
105
+ facts[typeof result.delivery === "string" ? "delivery" : "result"] =
106
+ typeof result.delivery === "string" ? result.delivery : typeof result.status === "string" ? result.status : "accepted";
86
107
  }
87
- if (typeof result.control_id === "string") fields.push(controlField("control_id", result.control_id));
108
+ const notApplied = result.stopped === false || result.archived === false || result.pruned === false;
109
+ const reason = typeof result.reason === "string" ? controlSelected("reason", result.reason) : undefined;
110
+ const observed = asRecord(result.status);
111
+ const state = notApplied && result.reason === "not running" && typeof observed.status === "string"
112
+ ? controlSelected("state", observed.status) : undefined;
113
+ const controlId = typeof result.control_id === "string" ? controlSelected("control_id", result.control_id) : undefined;
88
114
  const actions = Array.isArray(result.next_actions) ? result.next_actions
89
115
  : request.target.startsWith("run:") ? runNextActions(request.target.slice(4), result) : [];
90
116
  const next = actions.find((action): action is string => typeof action === "string" && action.length > 0);
91
- if (next) fields.push(controlNext(next));
92
- return ToolsResponse.withLeadingLineBreak(fields.join("\n"));
117
+ const nextPart = next === undefined ? undefined : controlNextPart(next);
118
+ const parts = [identity, ...(reason ? [reason] : []), ...(state ? [state] : []), ...(controlId ? [controlId] : []), ...(nextPart ? [nextPart] : [])];
119
+ const render = (): ControlDocument => ({ ...identity.selected, action: request.action, ...facts,
120
+ ...(reason ? reason.selected : {}), ...(state ? state.selected : {}), ...(controlId ? controlId.selected : {}),
121
+ ...(nextPart ? nextPart.selected : {}) });
122
+ return ToolsResponse.withLeadingLineBreak(controlDocument(render, parts));
93
123
  }
94
124
 
95
125
  function runNextActions(run: string, result: Record<string, unknown>): string[] {
@@ -228,16 +258,13 @@ export function createControlToolDefinition<TContext = unknown>(
228
258
  );
229
259
  } catch (cause) {
230
260
  // Admission-era correlation is not current reply-scope proof after an awaited dispatch.
231
- const summary = [controlField("action", request.action), controlField("result", "request failed"),
232
- controlField("delivery", "unconfirmed"), controlField("handling", "unconfirmed"),
233
- controlField("warning", "Effects may already exist.\nDo not repeat automatically."),
234
- controlNext("inspect target=runtime view=runs")].join("\n");
235
- // Reserve both line breaks; display one whole caller-owned id or explicit whole-id omission.
236
- const identity = controlField("run", run);
237
- const target = Buffer.byteLength(identity) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - Buffer.byteLength(summary) - 2
238
- ? identity
239
- : "- run: [omitted]\n- run_omission:\n - reason: response_too_large\n - shown: 0\n - omitted: 1\n - total: 1";
240
- throw new Error(ToolsResponse.withLeadingLineBreak(target + "\n" + summary), { cause });
261
+ // Display one whole caller-owned id or an explicit whole-id omission; the warning stays literal.
262
+ const identity = controlSelected("run", run);
263
+ const next = controlNextPart("inspect target=runtime view=runs");
264
+ const render = (): ControlDocument => ({ ...identity.selected, action: request.action,
265
+ result: "request failed", delivery: "unconfirmed", handling: "unconfirmed",
266
+ warning: "Effects may already exist.\nDo not repeat automatically.", ...next.selected });
267
+ throw new Error(ToolsResponse.withLeadingLineBreak(controlDocument(render, [identity])), { cause });
241
268
  }
242
269
  }
243
270
  // A refused identity change must not publish the replacement's status, even in verbose/details.