@llblab/pi-actors 0.56.0 → 0.56.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.
package/AGENTS.md CHANGED
@@ -194,11 +194,11 @@ Four compiler-owned Control guard errors now use private tools-message.ts:contro
194
194
 
195
195
  Only three local pre-launch spawn guards use tools-spawn.ts:spawnGuardText: unsupported state_dir, as without run: namespace and invalid id spelling. field/value identifies selected caller input, never an admitted Run, generation, launched/no-effects receipt or retry permission. Keep original state_dir/as/startsWith/slice/regexp order, including two as getter reads and target template coercion exactly once on the original error branches. The formatter must not reread input or serialize state_dir values. Whole selected strings and opaque CRLF Markdown fit16KiB including leading LF or have whole-field omission; malformed selected UTF16 is unavailable without repair or char/private-tail counts. Keep all startRun/session/Recipe/draft/navigation/success/verbose/details code outside new catches; the original post-guard launch tail stays byte-equal. Existing tools.test/invocation/SDK native fixtures remain untouched. New tools-spawn.test.ts is the exact library-owner mirror, not a feature-name exception; its two pure-guard properties run in source, explicit conformance and whole installed replay without new native work. Actual host clones arguments and rejects unsupported state_dir by its unchanged schema before execute: do not bypass validation, claim that SDK error is AX, or project direct accessor probes through cloning. Namespace/id guard errors do cross the real host isError boundary. No new module/authority; the earned tools-spawn→limits edge uses the existing shared response cap.
196
196
 
197
- Successful normal register/update-or-persisted receipt text now belongs to private registry.ts:registrationReceiptText, not a tools-register catch or a new presentation domain. Project only original post-activation tool/outcome/source/callable flag, the originally observed optional display boundary, selected required/optional args, warnings and first existing hint. Source references remain whole dynamically fenced literals, template stays a scalar; lists include singletons and opaque CRLF entries. Invocation advice is opaque instruction, never a parsed/trusted call; only the producer's existing unverified tool-status hint becomes typed read-only next. No new active/persisted/validated/default/body fields are added to compact text. Original warning map/join, outcome/boundary and Schema.formatToolArgs/template observations stay ordered before new formatting because faults can follow persistence/activation/notify. Capture the old optional boundary call in place; never query activation again for copy or repair disagreeing compact/details facts from dynamic getters. Everything in transaction/rollback/notify and details stays unchanged. Under16KiB including the old public LF, shorter full/omitted seeds grow monotonically; reserve fitting complete safety warnings before name/source/hint and large arg groups. Counts cover selected fields/list entries/actions, not raw/private chars; malformed/non-text selected values are unavailable without repair or another coercion. Preserve all14original registry declarations/scenarios/assertions/fixtures/cleanup except one approved layout expectation and pre-import test environment; add2properties and whole16installed replay. The unchanged sibling-process fixture's production new URL reference is remapped alongside direct imports, not its fixture/assertion/cleanup code. Its process-lock comparison is not new Actor/native/host readiness proof. Registry list/delete/draft/errors/notifier text remain separately scoped. The earned registry→tools-response/limits edges reuse existing field/fence/inspect and budget owners without new exports or callbacks.
197
+ Successful normal register/update-or-persisted receipt text now belongs to private registry.ts:registrationReceiptText, not a tools-register catch or a new presentation domain. Project only original post-activation tool/outcome/source/callable flag, the originally observed optional display boundary, selected required/optional args, warnings and first existing hint. Successful normal receipts use bare block YAML. Source references remain exact safely quoted scalar values, template stays a scalar, and argument/warning groups are native sequences including empty argument lists and opaque CRLF entries. The already selected normal callability Boolean stays Boolean; absent warnings stay absent. Invocation advice is opaque instruction, never a parsed/trusted call; only the producer's existing unverified tool-status hint becomes typed read-only next. No new active/persisted/validated/default/body fields are added to compact text. Original warning map/join, outcome/boundary and Schema.formatToolArgs/template observations stay ordered before new formatting because faults can follow persistence/activation/notify. Capture the old optional boundary call in place; never query activation again for copy or repair disagreeing compact/details facts from dynamic getters. Everything in transaction/rollback/notify and details stays unchanged. Under16KiB including the old public LF, shorter full/omitted seeds grow monotonically; reserve fitting complete safety warnings before name/source/hint and large arg groups. Counts cover selected fields/list entries/actions, not raw/private chars; malformed/non-text selected values are unavailable without repair or another coercion. Preserve all14original registry declarations/scenarios/assertions/fixtures/cleanup except one approved layout expectation and pre-import test environment; add2properties and whole16installed replay. The unchanged sibling-process fixture's production new URL reference is remapped alongside direct imports, not its fixture/assertion/cleanup code. Its process-lock comparison is not new Actor/native/host readiness proof. Registry list/delete/draft/errors/notifier text remain separately scoped. The earned registry→tools-response/limits edges reuse the pure YAML emitter and existing budget owner without new exports or callbacks.
198
198
 
199
- Registry list/deletion returned text stays in registry.ts. List only the original in-memory Map keys after the same sort and conditional map/join; capture old compiler-prefixed rows so rendering never coerces raw keys again. The whole selected name group fits16KiB including public LF or is wholly omitted/unavailable with selected-name coverage; do not invent a page/row/work quota or label a withheld group none. Count current selected names, not active/callable/complete-host/lifetime facts or private chars. Multiline names remain opaque and malformed selected text is not repaired. Deletion keeps original lookup→optional file unlink→map deletion→active-set update→notify order and exact partial-effect exceptions; a map miss does not authorize orphan-file deletion/deactivation. Capture the original name template conversion once at the returned-text site, preserving legacy method-bearing identity behavior without source re-coercion. Keep the exact complete-registry reload note as opaque instruction, never a typed action or permission to reload/retry. Details, notifier text, normal registration/draft/transaction/errors and all original source/npm fixtures remain independently owned.
199
+ Registry list/deletion returned text uses bare block YAML in registry.ts. Strip only the captured compiler prefix after the same in-memory Map-key sort and conditional map/join; never coerce raw keys again. Empty selection is a native empty sequence; withheld groups retain explicit scalar omission/unavailability and selected-name coverage. Reuse the shared pure emitter and include YAML escaping, indentation, trailing LF and the public separator LF in the existing cap. Fixed fallback metadata contains only compiler strings and finite array-length counts, not arbitrary inputs or getters. Source conformance does not establish compiled/package/loaded-host acceptance. The whole selected name group fits16KiB including public LF or is wholly omitted/unavailable with selected-name coverage; do not invent a page/row/work quota or label a withheld group none. Count current selected names, not active/callable/complete-host/lifetime facts or private chars. Multiline names remain opaque and malformed selected text is not repaired. Deletion keeps original lookup→optional file unlink→map deletion→active-set update→notify order and exact partial-effect exceptions; a map miss does not authorize orphan-file deletion/deactivation. Capture the original name template conversion once at the returned-text site, preserving legacy method-bearing identity behavior without source re-coercion. Keep the exact complete-registry reload note as opaque instruction, never a typed action or permission to reload/retry. Details, notifier text, normal registration/draft/transaction/errors and all original source/npm fixtures remain independently owned.
200
200
 
201
- Draft promotion returned text projects only original selected name/new-update/source=draft/callable display/first hint, not the normal registration receipt's args/warnings/boundary fields or policy. Preserve name→callable→hint template conversions exactly once at their old site after notify, including String-hint coercion, Symbol rejection and primitive/opaque faults after already committed promotion effects. Never Boolean-normalize a legacy zero/object flag display, infer hint kind from its visible value, reread activation or add ordinary-registration rollback/admission. A closed canonical tool-status hint may be typed; invocation/other advice stays opaque instruction. Whole selected scalar/action fields fit16KiB including existing LF or have field/action omission, with malformed strings unavailable without repair; counts are not hidden chars. Source path/defaults/body/args and details stay undisplayed/unchanged, including dynamic activation disagreement. Preserve original source/npm owners and separate direct nonstandard-port probes from actual SDK DTO/host readiness evidence.
201
+ Draft promotion returned text uses bare block YAML for only original selected name/new-update/source=draft/callable display/first hint, not the normal registration receipt's args/warnings/boundary fields or policy. Its already selected callable display remains a safely quoted string, including "false", "true" and "0", never a reconstructed Boolean. YAML escaping, indentation, trailing LF and the public separator LF count within the existing cap. Preserve name→callable→hint template conversions exactly once at their old site after notify, including String-hint coercion, Symbol rejection and primitive/opaque faults after already committed promotion effects. Never Boolean-normalize a legacy zero/object flag display, infer hint kind from its visible value, reread activation or add ordinary-registration rollback/admission. A closed canonical tool-status hint may be typed; invocation/other advice stays opaque instruction. Whole selected scalar/action fields fit16KiB including existing LF or have field/action omission, with malformed strings unavailable without repair; counts are not hidden chars. Source path/defaults/body/args and details stay undisplayed/unchanged, including dynamic activation disagreement. Preserve original source/npm owners and separate direct nonstandard-port probes from actual SDK DTO/host readiness evidence.
202
202
 
203
203
  Exactly four assertRegisterToolInputModes errors now use fixed AX compiler strings in place: removed values, invalid/empty from, mixed source modes and from args/async inheritance. Do not add a helper/catch, inspect supplied values or change hasOwn/getter/trim/mode-count/outer-lock observations to format these constants. Replacement/reference/source-mode/inherited-field choices are actual lists; original repair advice is opaque instruction, never typed mutating retry or no-effects proof. The same non-empty-from legacy sentence also exists in normalizeFromReference: keep that owner separate and use a unique guard context for edits. SDK clone/coercion/validation stays outside these local refusals: removed values/object-valued from may fail before execute, while numeric from is cloned/coerced to a string and an unnamed request can retain its old listing result. Do not force every wrong-typed host input through the direct guard, spoof schema or infer original caller types from post-validation params. Direct descriptor/getter/fallible-trim failures remain exact. All successful receipts and other registry errors/notices stay independently closed.
204
204
 
package/BACKLOG.md CHANGED
@@ -4,24 +4,16 @@ Canonical open work: `BACKLOG.md`. Contracts and durable constraints live in `AG
4
4
 
5
5
  Task IDs name bounded work, not execution order. The operator authorizes autonomous selection and execution of open pi-actors work in Grow Loop: choose dependency-ready high-value cohorts across this backlog without repeated routine permission questions. Dependencies, factual acceptance and specific safety/authority boundaries remain explicit; a gated task is not completed merely because another cohort can proceed. Completing a child does not complete its checkpoint.
6
6
 
7
- ## 1. Release 0.56.0 — external convergence
8
-
9
- Outcome: Recover the failed immutable-tag validation without silently changing release identity. [PR#137](https://github.com/llblab/pi-actors/pull/137) is merged; `v0.56.0` remains at `48cd1348730f8d4ae8b19ea52dc1fd61ae017622`, whose tree passed all eleven PR checks. The [tag-owned Release run](https://github.com/llblab/pi-actors/actions/runs/42440646237) subsequently failed Windows `owners-a` at the snapshot canonical-facts test. Passing PR checks did not replace this mandatory tag gate. The operator explicitly authorized a correction PR through `dev → main` and conditional replacement of this failed unpublished tag after fresh corrected-main CI and a complete zero-publication/consumption audit. This is not permission for an unchanged rerun, main rewrite or manual publication.
10
-
11
- ### R2 — Immutable publication completion
12
-
13
- Exit: Exact intended package, immutable tag and GitHub Release converge through the existing automation. A pushed tag is not a published/verified release.
14
-
15
- - [ ] **R2.3 — Validate and deliver release recovery.** Review the controlled busy-lifetime fixture correction in `tests/run-ui-runtime.test.ts`, then obtain fresh current-head full-matrix acceptance through [PR#138](https://github.com/llblab/pi-actors/pull/138), including Windows installed compiled SDK lifecycle/numbered-history. Keep production behavior, all existing assertions, deadlines, cleanup guards, versions and workflows unchanged. Preserve the existing tag until corrected protected-main CI and the immediately refreshed complete zero-publication/consumption audit pass. Then use only the operator-authorized exact leased failed-unpublished-tag replacement protocol; never rewrite main, rerun unchanged work for luck or publish manually.
16
- - [ ] **R2.4 — Verify external convergence.** After owning automation succeeds, confirm exact npm version/gitHead/package, immutable tag, published stable GitHub Release/latest and successful owning workflow identities. Visibility retries verify existing state; they never republish. Remove this release checkpoint only after both R2 exit conditions pass.
17
-
18
7
  ## Next bounded work
19
8
 
20
- - **Release:** Review and deliver the SDK busy-lifetime fixture correction through PR#138, obtain fresh current-head Windows/full-matrix acceptance, then finish the guarded R2 audit/tag-replacement/publication sequence. Do not move the existing tag early or compete with publication.
21
- - **Local implementation:** F1.3b is the next concrete projection cohort; success/list/delete/draft receipts still need migration. F1.3c, F1.3s and F1.4i remain separate attributable owners, not proof that all AX output already conforms. N1.4c also permits unrouted deterministic preparation within its reviewed contract.
9
+ - **Separate remaining owners:** F1.3c, F1.3s and F1.4i remain separate attributable owners, not proof that all AX output already conforms. N1.4c also permits unrouted deterministic preparation within its reviewed contract.
22
10
  - **Explicit gates:** U1 requires authorized loaded-host observation/reload; N1.2p needs actual Windows profile evidence, N1.3 an asset-distribution decision, and N1.6 separate activation admission. Original-clock/native-scope, macOS research and composition decisions retain their own B/C/T gates. Existing release permission does not adopt them.
23
11
  - **Evidence reuse:** Reuse the accepted three-OS SOURCE/PACKED quit witnesses for B2.4; extend only the missing descendants/triggers/same-ID/crash cases. N1.9 stays a gated unresolved risk, not an instruction to repeat the completed ownership inquiry.
24
12
 
13
+ ## Release 0.56.1
14
+
15
+ - [ ] **R3 — Complete the guarded 0.56.1 release.** Accept the exact dev-to-main PR through all nine product shards and the dependency audit; merge normally, tag the verified main commit once, then verify tag-owned validation, npm identity/package and GitHub Release convergence. Publication belongs only to release.yml; no tag replacement, manual publish, history rewrite or unrelated activation. Close only after external release evidence matches.
16
+
25
17
  ## 2. Uniform runtime presentation — adopted, rollout pending
26
18
 
27
19
  Outcome: JSON structured inputs, block-YAML runtime-output metadata and unchanged selected authored text/Markdown, as defined by [the AX contract](docs/agent-experience.md#json-input-yaml-output-opaque-content). This includes tool/spawn receipts, not only Actor results. Rollout remains partial; this checkpoint does not imply uniform emitter conformance, native continuation, package publication or a loaded-host change. Existing machine/input/source/history contracts remain separate.
@@ -30,7 +22,6 @@ Outcome: JSON structured inputs, block-YAML runtime-output metadata and unchange
30
22
 
31
23
  Exit: New owned runtime projections use YAML without pseudo-records or serialized JSON objects: bare when metadata-only, `---`-delimited headers only when a separate text body follows. Literal content, meaning, source/privacy bounds and saved messages remain intact. Service-carrier adoption has its own evidence gate; do not delete current framing to simulate completion.
32
24
 
33
- - [ ] **F1.3b — Migrate successful registry projections.** Convert registration/update, listing/deletion and draft-promotion receipts with their distinct selected facts, safety-warning priority and whole-value bounds. A separate selected body alone requires a delimited header. Preserve post-effect/coercion/transaction/callability truth and unchanged details. Verify: registry success/list/delete/draft owners and source/packed parity.
34
25
  - [ ] **F1.3c — Migrate registry-owned refusals.** Convert only locally authored mode/name/source/template/draft/transaction refusal projections. Keep original getter/coercion/fault order, private causes, opaque advice and whole-source limits; do not wrap host/dependency exceptions or infer no effects/retry. Verify: corresponding registry guard/failure properties and applicable packed witnesses.
35
26
  - [ ] **F1.3s — Separate synchronous tool metadata from selected output.** Migrate owned usage/guard/exit-label facts without parsing or reserializing stdout/stderr/report bodies or JSON invocation examples. Keep existing trim/tail/loss basis, exact executor failures and the pre-dispatch repair cutoff. Metadata plus selected text uses a delimited header; metadata-only output stays bare. Verify: tools-local/execution-output/spawn guards and packed dispatch-failure owners.
36
27
  - [ ] **F1.4 — Migrate inspection, Control and runtime notices.** Render selected structured/verbose metadata as YAML, including generated JSON Schema views; retain literal sources. Reconcile emitter execution/depth ceilings with actual caller budgets without narrowing public allowances. Preserve error/cause, request/delivery/handling/effects/closure distinctions and recipient fences. Verify: affected Control/inspect/runtime/notification owner properties.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.56.1: YAML Registry Receipts
8
+
9
+ - `Registry Receipts`: Successful registration/update, listing/deletion and draft-promotion replies now use bare block YAML with native lists, whole-value omissions and exact opaque scalars within the existing16KiB limit. Complete warnings retain priority. Normal callability stays Boolean; draft preserves its selected string display. Getter/coercion and partial-effect behavior, machine details, notices and saved messages stay unchanged. No input migration, automatic reload or retry is introduced.
10
+
7
11
  ## 0.56.0: AX, File Navigation and Runtime Safety
8
12
 
9
13
  - `AX Records`: Compact spawn/async-tool receipts, full spawn metadata and fresh attention use block YAML with opaque selected content and whole reference sequences. Metadata-only replies stay bare; bodies follow closed headers without added outer indent. Ordinary attention remains unbounded; urgent complete-message fitting keeps16KiB and later-group coverage. Other surfaces retain flat records during rollout. Source/activation contracts, machine details and saved text stay unchanged.
@@ -21,18 +21,18 @@ function textContent(text) {
21
21
  return { type: "text", text };
22
22
  }
23
23
  function registryListText(rows) {
24
- const coverage = (shown, reason) => "\n- registered_tools_coverage:" +
25
- (reason ? `\n - reason: ${reason}` : "") + `\n - shown: ${shown}\n - omitted: ${rows.length - shown}\n - total: ${rows.length}`;
24
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
25
+ const coverage = (shown, reason) => ({ ...(reason ? { reason } : {}), shown, omitted: rows.length - shown, total: rows.length });
26
26
  const malformed = rows.some(row => !row.isWellFormed());
27
27
  if (!malformed) {
28
- // Rows already contain the old compiler-owned bullet and selected key coercion.
29
- const body = rows.length ? "- registered_tools:\n" + rows.map(row => " " + row.replaceAll(/\r\n|\r|\n/gu, newline => newline + " ")).join("\n")
30
- : "- registered_tools: none";
31
- const complete = body + coverage(rows.length);
32
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1)
33
- return complete;
28
+ // Remove only the compiler prefix after the original key coercion and join.
29
+ const complete = ToolsResponse.renderYamlMetadata({ registered_tools: rows.map(row => row.slice(2)), registered_tools_coverage: coverage(rows.length) }, budget);
30
+ if (complete.state === "available")
31
+ return complete.text;
34
32
  }
35
- return `- registered_tools: [${malformed ? "unavailable" : "omitted"}]` + coverage(0, malformed ? "malformed_selected_text" : "response_too_large");
33
+ const fallback = ToolsResponse.renderYamlMetadata({ registered_tools: malformed ? "[unavailable]" : "[omitted]", registered_tools_coverage: coverage(0, malformed ? "malformed_selected_text" : "response_too_large") }, budget);
34
+ // Fixed keys/reasons and finite array-length counts are bounded far below budget.
35
+ return fallback.text;
36
36
  }
37
37
  function listTools(deps) {
38
38
  const names = [...deps.getTools().keys()].sort();
@@ -84,13 +84,20 @@ function registrationNextActions(tool, source, callableNow) {
84
84
  }
85
85
  /** Read-time text only: callers supply the original post-activation selections, not live ports. */
86
86
  function registrationReceiptText(data) {
87
- const field = ToolsResponse.formatMarkdownField;
88
- const omission = (key, count, malformed) => `- ${key}: [${malformed ? "unavailable" : "omitted"}]\n- ${key}_omission:\n - reason: ${malformed ? "malformed_selected_text" : "response_too_large"}\n - shown: 0\n - omitted: ${count}\n - total: ${count}`;
89
- const part = (full, omitted) => ({ full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted });
90
- const scalar = (key, value, literal = false) => {
87
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
88
+ const renderPart = (value) => ToolsResponse.renderYamlMetadata(value, budget);
89
+ const omission = (key, count, malformed) => ({
90
+ [key]: malformed ? "[unavailable]" : "[omitted]",
91
+ [key + "_omission"]: { reason: malformed ? "malformed_selected_text" : "response_too_large", shown: 0, omitted: count, total: count },
92
+ });
93
+ const part = (full, omitted) => {
94
+ const complete = renderPart(full), fallback = renderPart(omitted);
95
+ return { full, selected: complete.state === "available" && fallback.state === "available" && Buffer.byteLength(complete.text) <= Buffer.byteLength(fallback.text) ? full : omitted };
96
+ };
97
+ const scalar = (key, value) => {
91
98
  const wellFormed = value.isWellFormed();
92
99
  const omitted = omission(key, 1, !wellFormed);
93
- return part(wellFormed ? field(key, literal ? ToolsResponse.formatLiteralBlock(value) : value) : omitted, omitted);
100
+ return part(wellFormed ? { [key]: value } : omitted, omitted);
94
101
  };
95
102
  const sequence = (key, values) => {
96
103
  const rows = [];
@@ -101,34 +108,34 @@ function registrationReceiptText(data) {
101
108
  malformed = true;
102
109
  break;
103
110
  }
104
- rows.push(" - " + value.replaceAll(/\r\n|\r|\n/gu, newline => newline + " "));
111
+ rows.push(value);
105
112
  }
106
113
  const omitted = omission(key, values.length, malformed);
107
- return part(malformed ? omitted : rows.length ? `- ${key}:\n${rows.join("\n")}` : field(key, "none"), omitted);
114
+ return part(malformed ? omitted : { [key]: rows }, omitted);
108
115
  };
109
116
  const tool = scalar("tool", data.tool);
110
- const source = scalar("source", data.source, data.source !== "template");
117
+ const source = scalar("source", data.source);
111
118
  const required = sequence("required_args", data.required), optional = sequence("optional_args", data.optional);
112
119
  const warnings = data.warnings.length ? sequence("warnings", data.warnings) : undefined;
113
120
  const nextWellFormed = data.next.isWellFormed();
114
121
  const omittedNext = omission("next_action", 1, !nextWellFormed);
115
122
  // This producer knows only its existing status hint; invocation advice stays opaque.
116
123
  const next = part(nextWellFormed
117
- ? data.callable ? field("instruction", data.next) : ToolsResponse.formatInspectNextAction(`tool:${data.tool}`, "status")
124
+ ? data.callable ? { instruction: data.next } : { next: { tool: "inspect", target: `tool:${data.tool}`, view: "status" } }
118
125
  : omittedNext, omittedNext);
119
- const render = () => [tool.selected, field("result", data.result), source.selected, field("callable_now", data.callable ? "true" : "false"),
120
- ...(data.boundary === undefined ? [] : [field("activation_boundary", data.boundary)]), required.selected, optional.selected,
121
- ...(warnings ? [warnings.selected] : []), next.selected].join("\n");
126
+ const render = () => renderPart({ ...tool.selected, result: data.result, ...source.selected, callable_now: data.callable,
127
+ ...(data.boundary === undefined ? {} : { activation_boundary: data.boundary }), ...required.selected, ...optional.selected,
128
+ ...(warnings ? warnings.selected : {}), ...next.selected });
122
129
  // Preserve complete safety notes before large argument lists; all seeds are monotone.
123
130
  for (const current of [...(warnings ? [warnings] : []), tool, source, next, required, optional]) {
124
131
  if (current.selected === current.full)
125
132
  continue;
126
133
  const previous = current.selected;
127
134
  current.selected = current.full;
128
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1)
135
+ if (render().state !== "available")
129
136
  current.selected = previous;
130
137
  }
131
- return render();
138
+ return render().text;
132
139
  }
133
140
  function registryNoticeText(kind, name) {
134
141
  const report = { activated: "Tool activated", promoted: "Promoted draft recipe", deleted: "Deleted tool" }[kind];
@@ -244,28 +251,31 @@ function assertDraftPath(deps, draft) {
244
251
  return path;
245
252
  }
246
253
  function draftReceiptText(result, tool, callable, next) {
247
- const field = ToolsResponse.formatMarkdownField;
254
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
255
+ const renderPart = (value) => ToolsResponse.renderYamlMetadata(value, budget);
248
256
  const part = (key, value, rendered) => {
249
257
  const wellFormed = value.isWellFormed();
250
- 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`;
258
+ const omitted = { [key]: wellFormed ? "[omitted]" : "[unavailable]",
259
+ [key + "_omission"]: { reason: wellFormed ? "response_too_large" : "malformed_selected_text", shown: 0, omitted: 1, total: 1 } };
251
260
  const full = wellFormed ? rendered : omitted;
252
- return { full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted };
261
+ const complete = renderPart(full), fallback = renderPart(omitted);
262
+ return { full, selected: complete.state === "available" && fallback.state === "available" && Buffer.byteLength(complete.text) <= Buffer.byteLength(fallback.text) ? full : omitted };
253
263
  };
254
- const identity = part("tool", tool, field("tool", tool));
255
- const flag = part("callable_now", callable, field("callable_now", callable));
264
+ const identity = part("tool", tool, { tool });
265
+ const flag = part("callable_now", callable, { callable_now: callable });
256
266
  // Only this producer's exact canonical read-only status hint is typed, never body advice.
257
267
  const status = /^inspect target=(tool:[a-z][a-z0-9_]{0,63}) view=status$/u.exec(next);
258
- const hint = part("next_action", next, status ? ToolsResponse.formatInspectNextAction(status[1], "status") : field("instruction", next));
259
- const render = () => [identity.selected, field("result", result), field("source", "draft"), flag.selected, hint.selected].join("\n");
268
+ const hint = part("next_action", next, status ? { next: { tool: "inspect", target: status[1], view: "status" } } : { instruction: next });
269
+ const render = () => renderPart({ ...identity.selected, result, source: "draft", ...flag.selected, ...hint.selected });
260
270
  for (const current of [identity, flag, hint]) {
261
271
  if (current.selected === current.full)
262
272
  continue;
263
273
  const previous = current.selected;
264
274
  current.selected = current.full;
265
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1)
275
+ if (render().state !== "available")
266
276
  current.selected = previous;
267
277
  }
268
- return render();
278
+ return render().text;
269
279
  }
270
280
  function duplicateRegistrationText(value) {
271
281
  const render = (selected) => "- field: name\n" + selected + "\n- result: Tool already registered.\n- instruction: Use update=true to overwrite.";
@@ -358,17 +368,17 @@ function persistToolRecipe(deps, name, recipe) {
358
368
  return path;
359
369
  }
360
370
  function registryDeletionText(name, result) {
361
- const field = ToolsResponse.formatMarkdownField;
362
- const facts = [field("result", result), ...(result === "deleted"
363
- ? [field("instruction", "Reload to remove it from the complete registry.")] : [])];
364
- const render = (identity) => [identity, ...facts].join("\n");
371
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
372
+ const facts = { result, ...(result === "deleted" ? { instruction: "Reload to remove it from the complete registry." } : {}) };
365
373
  const wellFormed = name.isWellFormed();
366
374
  if (wellFormed) {
367
- const complete = render(field("tool", name));
368
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1)
369
- return complete;
375
+ const complete = ToolsResponse.renderYamlMetadata({ tool: name, ...facts }, budget);
376
+ if (complete.state === "available")
377
+ return complete.text;
370
378
  }
371
- return render(`- tool: [${wellFormed ? "omitted" : "unavailable"}]\n- tool_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`);
379
+ const fallback = ToolsResponse.renderYamlMetadata({ tool: wellFormed ? "[omitted]" : "[unavailable]", tool_omission: { reason: wellFormed ? "response_too_large" : "malformed_selected_text", shown: 0, omitted: 1, total: 1 }, ...facts }, budget);
380
+ // Fixed metadata is well formed and bounded far below budget.
381
+ return fallback.text;
372
382
  }
373
383
  function deleteTool(name, ctx, deps) {
374
384
  const tools = deps.getTools();
@@ -78,11 +78,11 @@ Four locally authored Control guard errors use caller target/action/result recor
78
78
 
79
79
  Local spawn guards identify unsupported state_dir or invalid as with field/result and the selected caller value, not an admitted Run. Whole values fit16KiB or show field omission; malformed text is unavailable, not repaired. The host may reject unsupported state_dir by its own schema before the facade, so not every SDK validation error has AX text. This adds no launch, no-effects receipt, retry permission, generated identity or change to Recipe/session/post-launch failures.
80
80
 
81
- Successful normal register_tool receipts now use flat tool/result/source/callable_now fields and actual argument/warning sequences. A non-template source stays one exact literal; complete warnings take priority under16KiB, and whole-field/list omissions count only selected metadata, not hidden chars. Call advice remains opaque instruction, not automatic invocation; an unverified registration keeps its existing read-only tool-status next. Persistence is not callability: rely on the existing activation evidence, never substitute spawn or infer fresh host readiness from this layout. Transaction/errors/list/delete/draft and saved messages are not restyled by this slice.
81
+ Fresh source normal register_tool receipts use bare block-YAML tool/result/source/callable_now fields and native argument/warning sequences, including empty argument lists. Normal callability remains Boolean and a non-template source stays one exact safely quoted scalar; complete warnings take priority under16KiB, and whole-field/list omissions count only selected metadata, not hidden chars. Call advice remains opaque instruction, not automatic invocation; an unverified registration keeps its existing read-only tool-status next. Persistence is not callability: rely on the existing activation evidence, never substitute spawn or infer fresh host readiness from this layout. Transaction/errors/list/delete/draft and saved messages are not restyled by this slice.
82
82
 
83
- Registry name listings now show the same selected sorted names as one list with coverage; an overlarge whole group is omitted rather than mislabeled empty, and malformed text is unavailable without repair. Counts describe selected names, not active/callable or the complete loaded-host registry. Deletion returns tool/result and preserves the manual reload note as opaque instruction: neither a current-map miss nor returned deletion proves full host removal or authorizes automatic reload/retry. Existing partial-effect exceptions and machine details remain unchanged.
83
+ Fresh source registry listings and deletion receipts use bare block YAML. Listings preserve the same selected sorted names as a native sequence with coverage; empty selection is `[]`, an overlarge whole group is explicitly omitted rather than mislabeled empty, and malformed text is unavailable without repair. Counts describe selected names, not active/callable or the complete loaded-host registry. Deletion returns tool/result and preserves the manual reload note as opaque instruction: neither a current-map miss nor returned deletion proves full host removal or authorizes automatic reload/retry. Existing partial-effect exceptions and machine details remain unchanged. Combined compiled/package acceptance and loaded-host observation remain separate; an older runtime can still return the prior flat-list text.
84
84
 
85
- Draft-promotion success uses the same field grammar only for its original selected name/new-update/source=draft/callable display and one prior hint. It adds no default/body/argument/path or activation facts and does not become an ordinary registration transaction. A zero, opaque legacy display or unavailable field is not permission to infer readiness or repair callability. Whole scalar/action omissions stay explicit; invocation advice is content and only the existing read-only status hint may be typed. Promotion callbacks/display can still fail after writes with original errors; no automatic rollback/retry/reload follows.
85
+ Fresh source draft-promotion success uses bare block YAML only for its original selected name/new-update/source=draft/callable display and one prior hint. The selected callable display remains a string, including quoted "false", "true" or "0", not reconstructed Boolean readiness. It adds no default/body/argument/path or activation facts and does not become an ordinary registration transaction. A zero, opaque legacy display or unavailable field is not permission to infer readiness or repair callability. Whole scalar/action omissions stay explicit; invocation advice is content and only the existing read-only status hint may be typed. Promotion callbacks/display can still fail after writes with original errors; no automatic rollback/retry/reload follows.
86
86
 
87
87
  Local register_tool source-mode refusals now identify field/result and actual replacement/reference/source-mode/inherited-field choices. Existing correction advice is instruction content, not automatic retry/update or a no-effects receipt. Supplied values are not echoed. The host owns clone/coercion/schema behavior before the facade, so use the actual outcome rather than assuming every wrong-typed request reaches a local AX guard. Other registry errors and all existing activation/transaction contracts remain unchanged.
88
88
 
@@ -59,7 +59,7 @@ Terminal observation -> frozen completion/attention capture
59
59
  ### 6. Registration, Listing, Deletion and Draft Promotion
60
60
 
61
61
  - **Owners:** `lib/tools-register.ts:61` delegates to Registry. `lib/registry.ts:94` `registryListText`, `:176` `registrationReceiptText`, `:352` `draftReceiptText`, `:501` `registryDeletionText` and their list/promote/delete/register transaction callers.
62
- - **Current:** Pseudo-fields and real selected sequences for tool/result/source/callability, args/warnings/names/coverage; source references may be fenced literals. Draft promotion has its own selected/coerced display facts. Delete has a manual reload note.
62
+ - **Current:** Source registration/update, listing, deletion/not-found and draft-promotion receipts use bare block YAML under the shared16KiB complete-response cap. Names/arguments/warnings are native sequences; empty selected lists stay `[]` and whole omissions/unavailability retain coverage. Normal callability is Boolean; draft keeps its selected display string. Source references stay exact scalar values, fitting warnings retain priority, and reload/invocation advice stays opaque. Independent review, compiled/package acceptance and loaded-host observation require separate evidence; source syntax does not establish them.
63
63
  - **Target / guard:** Metadata-only returns become bare YAML. Preserve actual persistence versus current callability, reuse of selected values, warning priority, whole-group omissions and source references. Do not normalize old flags into stronger activation truth. Formatting/notifier/unlink/reactivation failures may follow writes; a reload instruction is not permission to reload.
64
64
  - **Proof owners:** `tests/registry.test.ts` ordinary/draft/list/delete/activation-order families; `tests/registration-truth.test.ts` and installed registry/runtime owner replay.
65
65
 
package/lib/registry.ts CHANGED
@@ -92,17 +92,17 @@ function textContent(text: string) {
92
92
  }
93
93
 
94
94
  function registryListText(rows: readonly string[]): string {
95
- const coverage = (shown: number, reason?: string) => "\n- registered_tools_coverage:" +
96
- (reason ? `\n - reason: ${reason}` : "") + `\n - shown: ${shown}\n - omitted: ${rows.length - shown}\n - total: ${rows.length}`;
95
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
96
+ const coverage = (shown: number, reason?: string) => ({ ...(reason ? { reason } : {}), shown, omitted: rows.length - shown, total: rows.length });
97
97
  const malformed = rows.some(row => !row.isWellFormed());
98
98
  if (!malformed) {
99
- // Rows already contain the old compiler-owned bullet and selected key coercion.
100
- const body = rows.length ? "- registered_tools:\n" + rows.map(row => " " + row.replaceAll(/\r\n|\r|\n/gu, newline => newline + " ")).join("\n")
101
- : "- registered_tools: none";
102
- const complete = body + coverage(rows.length);
103
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) return complete;
99
+ // Remove only the compiler prefix after the original key coercion and join.
100
+ const complete = ToolsResponse.renderYamlMetadata({ registered_tools: rows.map(row => row.slice(2)), registered_tools_coverage: coverage(rows.length) }, budget);
101
+ if (complete.state === "available") return complete.text;
104
102
  }
105
- return `- registered_tools: [${malformed ? "unavailable" : "omitted"}]` + coverage(0, malformed ? "malformed_selected_text" : "response_too_large");
103
+ const fallback = ToolsResponse.renderYamlMetadata({ registered_tools: malformed ? "[unavailable]" : "[omitted]", registered_tools_coverage: coverage(0, malformed ? "malformed_selected_text" : "response_too_large") }, budget);
104
+ // Fixed keys/reasons and finite array-length counts are bounded far below budget.
105
+ return (fallback as Extract<ToolsResponse.YamlMetadataRendering, { state: "available" }>).text;
106
106
  }
107
107
 
108
108
  function listTools<TContext>(
@@ -178,14 +178,20 @@ function registrationReceiptText(data: {
178
178
  boundary?: string; required: readonly unknown[]; optional: readonly unknown[];
179
179
  warnings: readonly unknown[]; next: string;
180
180
  }): string {
181
- const field = ToolsResponse.formatMarkdownField;
182
- const omission = (key: string, count: number, malformed: boolean) =>
183
- `- ${key}: [${malformed ? "unavailable" : "omitted"}]\n- ${key}_omission:\n - reason: ${malformed ? "malformed_selected_text" : "response_too_large"}\n - shown: 0\n - omitted: ${count}\n - total: ${count}`;
184
- const part = (full: string, omitted: string) => ({ full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted });
185
- const scalar = (key: string, value: string, literal = false) => {
181
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
182
+ const renderPart = (value: Record<string, unknown>) => ToolsResponse.renderYamlMetadata(value, budget);
183
+ const omission = (key: string, count: number, malformed: boolean): Record<string, unknown> => ({
184
+ [key]: malformed ? "[unavailable]" : "[omitted]",
185
+ [key + "_omission"]: { reason: malformed ? "malformed_selected_text" : "response_too_large", shown: 0, omitted: count, total: count },
186
+ });
187
+ const part = (full: Record<string, unknown>, omitted: Record<string, unknown>) => {
188
+ const complete = renderPart(full), fallback = renderPart(omitted);
189
+ return { full, selected: complete.state === "available" && fallback.state === "available" && Buffer.byteLength(complete.text) <= Buffer.byteLength(fallback.text) ? full : omitted };
190
+ };
191
+ const scalar = (key: string, value: string) => {
186
192
  const wellFormed = value.isWellFormed();
187
193
  const omitted = omission(key, 1, !wellFormed);
188
- return part(wellFormed ? field(key, literal ? ToolsResponse.formatLiteralBlock(value) : value) : omitted, omitted);
194
+ return part(wellFormed ? { [key]: value } : omitted, omitted);
189
195
  };
190
196
  const sequence = (key: string, values: readonly unknown[]) => {
191
197
  const rows: string[] = [];
@@ -193,32 +199,32 @@ function registrationReceiptText(data: {
193
199
  for (let index = 0; index < values.length; index++) {
194
200
  const value = values[index];
195
201
  if (typeof value !== "string" || !value.isWellFormed()) { malformed = true; break; }
196
- rows.push(" - " + value.replaceAll(/\r\n|\r|\n/gu, newline => newline + " "));
202
+ rows.push(value);
197
203
  }
198
204
  const omitted = omission(key, values.length, malformed);
199
- return part(malformed ? omitted : rows.length ? `- ${key}:\n${rows.join("\n")}` : field(key, "none"), omitted);
205
+ return part(malformed ? omitted : { [key]: rows }, omitted);
200
206
  };
201
207
  const tool = scalar("tool", data.tool);
202
- const source = scalar("source", data.source, data.source !== "template");
208
+ const source = scalar("source", data.source);
203
209
  const required = sequence("required_args", data.required), optional = sequence("optional_args", data.optional);
204
210
  const warnings = data.warnings.length ? sequence("warnings", data.warnings) : undefined;
205
211
  const nextWellFormed = data.next.isWellFormed();
206
212
  const omittedNext = omission("next_action", 1, !nextWellFormed);
207
213
  // This producer knows only its existing status hint; invocation advice stays opaque.
208
214
  const next = part(nextWellFormed
209
- ? data.callable ? field("instruction", data.next) : ToolsResponse.formatInspectNextAction(`tool:${data.tool}`, "status")
215
+ ? data.callable ? { instruction: data.next } : { next: { tool: "inspect", target: `tool:${data.tool}`, view: "status" } }
210
216
  : omittedNext, omittedNext);
211
- const render = () => [tool.selected, field("result", data.result), source.selected, field("callable_now", data.callable ? "true" : "false"),
212
- ...(data.boundary === undefined ? [] : [field("activation_boundary", data.boundary)]), required.selected, optional.selected,
213
- ...(warnings ? [warnings.selected] : []), next.selected].join("\n");
217
+ const render = () => renderPart({ ...tool.selected, result: data.result, ...source.selected, callable_now: data.callable,
218
+ ...(data.boundary === undefined ? {} : { activation_boundary: data.boundary }), ...required.selected, ...optional.selected,
219
+ ...(warnings ? warnings.selected : {}), ...next.selected });
214
220
  // Preserve complete safety notes before large argument lists; all seeds are monotone.
215
221
  for (const current of [...(warnings ? [warnings] : []), tool, source, next, required, optional]) {
216
222
  if (current.selected === current.full) continue;
217
223
  const previous = current.selected;
218
224
  current.selected = current.full;
219
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) current.selected = previous;
225
+ if (render().state !== "available") current.selected = previous;
220
226
  }
221
- return render();
227
+ return (render() as Extract<ToolsResponse.YamlMetadataRendering, { state: "available" }>).text;
222
228
  }
223
229
 
224
230
  function registryNoticeText(kind: "activated" | "promoted" | "deleted", name: string): string {
@@ -350,26 +356,29 @@ function assertDraftPath<TContext>(
350
356
  }
351
357
 
352
358
  function draftReceiptText(result: "updated" | "registered", tool: string, callable: string, next: string): string {
353
- const field = ToolsResponse.formatMarkdownField;
354
- const part = (key: string, value: string, rendered: string) => {
359
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
360
+ const renderPart = (value: Record<string, unknown>) => ToolsResponse.renderYamlMetadata(value, budget);
361
+ const part = (key: string, value: string, rendered: Record<string, unknown>) => {
355
362
  const wellFormed = value.isWellFormed();
356
- 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`;
363
+ const omitted: Record<string, unknown> = { [key]: wellFormed ? "[omitted]" : "[unavailable]",
364
+ [key + "_omission"]: { reason: wellFormed ? "response_too_large" : "malformed_selected_text", shown: 0, omitted: 1, total: 1 } };
357
365
  const full = wellFormed ? rendered : omitted;
358
- return { full, selected: Buffer.byteLength(full) <= Buffer.byteLength(omitted) ? full : omitted };
366
+ const complete = renderPart(full), fallback = renderPart(omitted);
367
+ return { full, selected: complete.state === "available" && fallback.state === "available" && Buffer.byteLength(complete.text) <= Buffer.byteLength(fallback.text) ? full : omitted };
359
368
  };
360
- const identity = part("tool", tool, field("tool", tool));
361
- const flag = part("callable_now", callable, field("callable_now", callable));
369
+ const identity = part("tool", tool, { tool });
370
+ const flag = part("callable_now", callable, { callable_now: callable });
362
371
  // Only this producer's exact canonical read-only status hint is typed, never body advice.
363
372
  const status = /^inspect target=(tool:[a-z][a-z0-9_]{0,63}) view=status$/u.exec(next);
364
- const hint = part("next_action", next, status ? ToolsResponse.formatInspectNextAction(status[1], "status") : field("instruction", next));
365
- const render = () => [identity.selected, field("result", result), field("source", "draft"), flag.selected, hint.selected].join("\n");
373
+ const hint = part("next_action", next, status ? { next: { tool: "inspect", target: status[1], view: "status" } } : { instruction: next });
374
+ const render = () => renderPart({ ...identity.selected, result, source: "draft", ...flag.selected, ...hint.selected });
366
375
  for (const current of [identity, flag, hint]) {
367
376
  if (current.selected === current.full) continue;
368
377
  const previous = current.selected;
369
378
  current.selected = current.full;
370
- if (Buffer.byteLength(render()) > Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) current.selected = previous;
379
+ if (render().state !== "available") current.selected = previous;
371
380
  }
372
- return render();
381
+ return (render() as Extract<ToolsResponse.YamlMetadataRendering, { state: "available" }>).text;
373
382
  }
374
383
 
375
384
  function duplicateRegistrationText(value: string): string {
@@ -499,16 +508,16 @@ function persistToolRecipe<TContext>(
499
508
  }
500
509
 
501
510
  function registryDeletionText(name: string, result: "deleted" | "not found"): string {
502
- const field = ToolsResponse.formatMarkdownField;
503
- const facts = [field("result", result), ...(result === "deleted"
504
- ? [field("instruction", "Reload to remove it from the complete registry.")] : [])];
505
- const render = (identity: string) => [identity, ...facts].join("\n");
511
+ const budget = Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1;
512
+ const facts = { result, ...(result === "deleted" ? { instruction: "Reload to remove it from the complete registry." } : {}) };
506
513
  const wellFormed = name.isWellFormed();
507
514
  if (wellFormed) {
508
- const complete = render(field("tool", name));
509
- if (Buffer.byteLength(complete) <= Limits.RUN_DELIVERY_MODEL_MAX_BYTES - 1) return complete;
515
+ const complete = ToolsResponse.renderYamlMetadata({ tool: name, ...facts }, budget);
516
+ if (complete.state === "available") return complete.text;
510
517
  }
511
- return render(`- tool: [${wellFormed ? "omitted" : "unavailable"}]\n- tool_omission:\n - reason: ${wellFormed ? "response_too_large" : "malformed_selected_text"}\n - shown: 0\n - omitted: 1\n - total: 1`);
518
+ const fallback = ToolsResponse.renderYamlMetadata({ tool: wellFormed ? "[omitted]" : "[unavailable]", tool_omission: { reason: wellFormed ? "response_too_large" : "malformed_selected_text", shown: 0, omitted: 1, total: 1 }, ...facts }, budget);
519
+ // Fixed metadata is well formed and bounded far below budget.
520
+ return (fallback as Extract<ToolsResponse.YamlMetadataRendering, { state: "available" }>).text;
512
521
  }
513
522
 
514
523
  function deleteTool<TContext>(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.56.0",
3
+ "version": "0.56.1",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -78,11 +78,11 @@ Four locally authored Control guard errors use caller target/action/result recor
78
78
 
79
79
  Local spawn guards identify unsupported state_dir or invalid as with field/result and the selected caller value, not an admitted Run. Whole values fit16KiB or show field omission; malformed text is unavailable, not repaired. The host may reject unsupported state_dir by its own schema before the facade, so not every SDK validation error has AX text. This adds no launch, no-effects receipt, retry permission, generated identity or change to Recipe/session/post-launch failures.
80
80
 
81
- Successful normal register_tool receipts now use flat tool/result/source/callable_now fields and actual argument/warning sequences. A non-template source stays one exact literal; complete warnings take priority under16KiB, and whole-field/list omissions count only selected metadata, not hidden chars. Call advice remains opaque instruction, not automatic invocation; an unverified registration keeps its existing read-only tool-status next. Persistence is not callability: rely on the existing activation evidence, never substitute spawn or infer fresh host readiness from this layout. Transaction/errors/list/delete/draft and saved messages are not restyled by this slice.
81
+ Fresh source normal register_tool receipts use bare block-YAML tool/result/source/callable_now fields and native argument/warning sequences, including empty argument lists. Normal callability remains Boolean and a non-template source stays one exact safely quoted scalar; complete warnings take priority under16KiB, and whole-field/list omissions count only selected metadata, not hidden chars. Call advice remains opaque instruction, not automatic invocation; an unverified registration keeps its existing read-only tool-status next. Persistence is not callability: rely on the existing activation evidence, never substitute spawn or infer fresh host readiness from this layout. Transaction/errors/list/delete/draft and saved messages are not restyled by this slice.
82
82
 
83
- Registry name listings now show the same selected sorted names as one list with coverage; an overlarge whole group is omitted rather than mislabeled empty, and malformed text is unavailable without repair. Counts describe selected names, not active/callable or the complete loaded-host registry. Deletion returns tool/result and preserves the manual reload note as opaque instruction: neither a current-map miss nor returned deletion proves full host removal or authorizes automatic reload/retry. Existing partial-effect exceptions and machine details remain unchanged.
83
+ Fresh source registry listings and deletion receipts use bare block YAML. Listings preserve the same selected sorted names as a native sequence with coverage; empty selection is `[]`, an overlarge whole group is explicitly omitted rather than mislabeled empty, and malformed text is unavailable without repair. Counts describe selected names, not active/callable or the complete loaded-host registry. Deletion returns tool/result and preserves the manual reload note as opaque instruction: neither a current-map miss nor returned deletion proves full host removal or authorizes automatic reload/retry. Existing partial-effect exceptions and machine details remain unchanged. Combined compiled/package acceptance and loaded-host observation remain separate; an older runtime can still return the prior flat-list text.
84
84
 
85
- Draft-promotion success uses the same field grammar only for its original selected name/new-update/source=draft/callable display and one prior hint. It adds no default/body/argument/path or activation facts and does not become an ordinary registration transaction. A zero, opaque legacy display or unavailable field is not permission to infer readiness or repair callability. Whole scalar/action omissions stay explicit; invocation advice is content and only the existing read-only status hint may be typed. Promotion callbacks/display can still fail after writes with original errors; no automatic rollback/retry/reload follows.
85
+ Fresh source draft-promotion success uses bare block YAML only for its original selected name/new-update/source=draft/callable display and one prior hint. The selected callable display remains a string, including quoted "false", "true" or "0", not reconstructed Boolean readiness. It adds no default/body/argument/path or activation facts and does not become an ordinary registration transaction. A zero, opaque legacy display or unavailable field is not permission to infer readiness or repair callability. Whole scalar/action omissions stay explicit; invocation advice is content and only the existing read-only status hint may be typed. Promotion callbacks/display can still fail after writes with original errors; no automatic rollback/retry/reload follows.
86
86
 
87
87
  Local register_tool source-mode refusals now identify field/result and actual replacement/reference/source-mode/inherited-field choices. Existing correction advice is instruction content, not automatic retry/update or a no-effects receipt. Supplied values are not echoed. The host owns clone/coercion/schema behavior before the facade, so use the actual outcome rather than assuming every wrong-typed request reaches a local AX guard. Other registry errors and all existing activation/transaction contracts remain unchanged.
88
88