@llblab/pi-actors 0.57.1 → 0.58.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 (49) hide show
  1. package/AGENTS.md +11 -3
  2. package/BACKLOG.md +14 -1
  3. package/CHANGELOG.md +13 -1
  4. package/README.md +7 -1
  5. package/dist/lib/file-state.d.ts +1 -0
  6. package/dist/lib/file-state.js +8 -2
  7. package/dist/lib/pi.js +1 -1
  8. package/dist/lib/prompts.d.ts +2 -1
  9. package/dist/lib/prompts.js +6 -2
  10. package/dist/lib/recipes-discovery.d.ts +4 -0
  11. package/dist/lib/recipes-discovery.js +22 -0
  12. package/dist/lib/recipes-references.d.ts +1 -0
  13. package/dist/lib/recipes-references.js +1 -1
  14. package/dist/lib/recipes-usage.js +12 -9
  15. package/dist/lib/registry.d.ts +5 -1
  16. package/dist/lib/registry.js +199 -7
  17. package/dist/lib/run-delivery.d.ts +1 -1
  18. package/dist/lib/run-delivery.js +19 -9
  19. package/dist/lib/tools-inspect.js +6 -3
  20. package/dist/lib/tools-register.js +1 -0
  21. package/dist/lib/tools-response.js +20 -16
  22. package/dist/skills/actors/SKILL.md +11 -2
  23. package/dist/skills/actors/references/persistent-tools.md +20 -2
  24. package/dist/skills/actors/references/runs.md +1 -1
  25. package/dist/skills/recipe-memory/SKILL.md +6 -0
  26. package/docs/README.md +1 -0
  27. package/docs/agent-experience.md +5 -6
  28. package/docs/capability-memory.md +121 -0
  29. package/docs/coordinator-delivery.md +2 -2
  30. package/docs/inspection.md +3 -1
  31. package/docs/output-projection-map.md +1 -1
  32. package/docs/releasing.md +5 -1
  33. package/docs/tool-registry.md +37 -1
  34. package/lib/file-state.ts +7 -3
  35. package/lib/pi.ts +1 -1
  36. package/lib/prompts.ts +7 -2
  37. package/lib/recipes-discovery.ts +26 -0
  38. package/lib/recipes-references.ts +1 -1
  39. package/lib/recipes-usage.ts +11 -8
  40. package/lib/registry.ts +195 -10
  41. package/lib/run-delivery.ts +22 -11
  42. package/lib/tools-inspect.ts +6 -3
  43. package/lib/tools-register.ts +1 -0
  44. package/lib/tools-response.ts +20 -13
  45. package/package.json +1 -1
  46. package/skills/actors/SKILL.md +11 -2
  47. package/skills/actors/references/persistent-tools.md +20 -2
  48. package/skills/actors/references/runs.md +1 -1
  49. package/skills/recipe-memory/SKILL.md +6 -0
@@ -4,9 +4,9 @@ Status: Adopted project-wide design standard. The operator has selected **JSON i
4
4
 
5
5
  ## One first AX contract
6
6
 
7
- AX is one canonical Agent Experience being prepared for its first release, not a product family of numbered drafts or user/model-selectable formats. The selected target is YAML metadata plus optional opaque authored content; the former Markdown-list key/value grammar is an implemented pre-unification layout, not another target to preserve for new output. Ordinary source and normally built package Batch admission now capture ax_records/completion_yaml_files_v1 over the unchanged completion_yaml_core_v1 carrier, and fresh attention/steer use typed block-YAML metadata plus opaque raw body text. The inactive numbered-stream path and saved frontmatter_v1/v2 retain their older layouts. Bounded source/compiled/actual-default accepted-path evidence exists; it does not establish default-disk, global lifetime-policy or loaded-host acceptance. Remaining rollout is tracked in [BACKLOG.md](../BACKLOG.md).
7
+ AX is one canonical Agent Experience being prepared for its first release, not a product family of numbered drafts or user/model-selectable formats. The selected target is YAML metadata plus optional opaque authored content; the former Markdown-list key/value grammar is an implemented pre-unification layout, not another target to preserve for new output. Ordinary source and normally built package Batch admission now capture ax_records/completion_yaml_files_v2 over the unchanged completion_yaml_core_v1 carrier, and fresh attention/steer use typed block-YAML metadata plus opaque raw body text. The inactive numbered-stream path and saved frontmatter_v1/v2 retain their older layouts. Bounded source/compiled/actual-default accepted-path evidence exists; it does not establish default-disk, global lifetime-policy or loaded-host acceptance. Remaining rollout is tracked in [BACKLOG.md](../BACKLOG.md).
8
8
 
9
- A completion group is the result sequence itself, not a container with members. Each result's YAML metadata begins index, run, state, then kind:primary for admitted primary text/final Actor response or kind:diagnostic for explicit diagnostics. The admitted Markdown follows from the left margin, outside metadata, with no #N heading, result:/preview: wrapper or whole-body literal fence. One blank separator distinguishes records. Known display loss appears only at the cut as [N characters omitted], counting Unicode scalar characters of the admitted retained source; no extra truncation ellipsis or body clipping flags/counters appear in YAML. New rooted captures keep unknown upstream completeness/privacy in header text_warnings without inventing a count or claiming a known positive loss. Retained captures preserve their original body-boundary warnings. Untracked fallback has no primary kind. Missing text stays explicit; a checkpoint retains its Actor-authored notification body. Successful automatic delivery has no extra receipt, routine outer counters, members wrapper, Batch caption, time window, status-summary copies or standing full-map instruction; real failures stay reportable. Exact source/generation/location references remain in complete model content, with whole-reference/result omission and non-done omitted states explicit. A group with one visible row stays a group. Fresh ordinary packets put minimal packet identity and relative body/marker lengths in YAML metadata, without compiler HTML tails. Older saved layouts retain their original compiler boundary/correlation framing; neither encoding grants authenticity, read permission or acceptance. Their compatibility boundary is governed separately below; never strip required historical framing. This is one new-capture contract, not a selectable AX product version.
9
+ A completion group is the result sequence itself, not a container with members. Each result's YAML metadata begins run, state, then kind:primary for admitted primary text/final Actor response or kind:diagnostic for explicit diagnostics. The admitted Markdown follows from the left margin, outside metadata, with no #N heading, result:/preview: wrapper or whole-body literal fence. One blank separator distinguishes records. Known display loss appears only at the cut as [N characters omitted], counting Unicode scalar characters of the admitted retained source; no extra truncation ellipsis or body clipping flags/counters appear in YAML. New rooted captures keep unknown upstream completeness/privacy in header text_warnings without inventing a count or claiming a known positive loss. Retained captures preserve their original body-boundary warnings. Untracked fallback has no primary kind. Missing text stays explicit; a checkpoint retains its Actor-authored notification body. Successful automatic delivery has no extra receipt, routine outer counters, members wrapper, Batch caption, time window, status-summary copies or standing full-map instruction; real failures stay reportable. Exact source/generation/location references remain in complete model content, with whole-reference/result omission and non-done omitted states explicit. Prepend the packet-local index only when two or more results are shown. Exactly one visible row omits its display index even with omitted results; omission/inventory facts and internal member/generation/correlation stay unchanged. This rule does not apply to numbered streams. Fresh ordinary packets put minimal packet identity and relative body/marker lengths in YAML metadata, without compiler HTML tails. Older saved layouts retain their original compiler boundary/correlation framing; neither encoding grants authenticity, read permission or acceptance. Their compatibility boundary is governed separately below; never strip required historical framing. This is one new-capture contract, not a selectable AX product version.
10
10
 
11
11
  Illustrative compact completion shape (not actual Run data):
12
12
 
@@ -31,13 +31,13 @@ Module was not found.
31
31
  [120 characters omitted]
32
32
  ```
33
33
 
34
- An only item uses the same first block, not a second format. Full text has no cut marker; an unavailable index is the YAML string "?", never a fabricated zero. The compact TUI renders compiler headers and markers as literal Text and each body as independent native Markdown, so an unfinished fence/list/quote cannot consume another block. New rooted headers keep navigation visible and put packet omission/inventory facts in the first header, never after the Markdown. Retained core-v1 compact views keep their original whole-reference/location hiding. Expansion or invalid/missing hints reveals the full original text. Source bodies and retained structured coverage facts are not rewritten.
34
+ An only shown item uses the same block without its packet-local index, not a second public format. Full text has no cut marker; an unavailable index is the YAML string "?", never a fabricated zero. The compact TUI renders compiler headers and markers as literal Text and each body as independent native Markdown, so an unfinished fence/list/quote cannot consume another block. New rooted headers keep navigation visible and put packet omission/inventory facts in the first header, never after the Markdown. Retained core-v1 compact views keep their original whole-reference/location hiding. Expansion or invalid/missing hints reveals the full original text. Source bodies and retained structured coverage facts are not rewritten.
35
35
 
36
36
  Storage compatibility is separate: already-captured Batch snapshots need their original codec to reproduce exact delivery text, while steer envelopes replay their saved content directly. Do not relabel, migrate or reformat existing journals/session messages to make the interface look uniform. Retained encoding identifiers are internal replay data, not alternate AX products. See [retained capture compatibility](coordinator-delivery.md#retained-capture-compatibility).
37
37
 
38
38
  ## Rooted completion files
39
39
 
40
- New source captures use a private `completion_yaml_files_v1` snapshot layout; this is replay data, not an input selector or a new product format. The YAML carrier/hint stays `completion_yaml_core_v1`. Existing snapshots keep their exact previous projection.
40
+ New source captures use a private `completion_yaml_files_v2` snapshot layout and `rooted_files_v2` service discriminator for the singleton display rule; this is replay data, not an input selector or a new product format. The YAML carrier/hint stays `completion_yaml_core_v1`. Existing snapshots, including `completion_yaml_files_v1`/`rooted_files_v1`, keep their exact previous projection and singleton indices.
41
41
 
42
42
  All runtime metadata belongs above the selected Markdown. New compact views have no service footer, appended file list or runtime warning after the body. A known cut alone adds `[N characters omitted]` at that cut; unknown loss/privacy and whole-reference omissions belong in header fields. Authored Markdown, filenames and paths keep their original language/bytes; compiler keys and purpose explanations are English.
43
43
 
@@ -49,7 +49,6 @@ Illustrative abbreviated header, not actual Run data or a complete source bindin
49
49
 
50
50
  ```text
51
51
  ---
52
- index: 0
53
52
  run: "review"
54
53
  state: "done"
55
54
  kind: "primary"
@@ -239,7 +238,7 @@ The [runtime output projection map](output-projection-map.md) names the audited
239
238
 
240
239
  The prepared `tools-response.ts` `renderYamlMetadata` foundation uses the directly declared `yaml@2.9.0` public API for selected own-data mappings/sequences. It emits bare YAML without invocation spacing or body processing, preserves scalar types/strings and refuses unsupported or over-budget whole values. Its execution ceiling is 49,152 bytes and supported nesting is at most 64 levels; these are not caller allowances or drop-in compatibility for larger existing budgets. Preflight bounds scalar/node traversal, not atomic key enumeration or arbitrary Proxy execution. Focused parser/property and shared response-owner tests cover this foundation; compact asynchronous launch receipts are the first production caller. Other surfaces and loaded-host acceptance remain separately staged; full spawn-verbose shares the final YAML style without inheriting this bounded emitter's limits.
241
240
 
242
- The following is the current pre-unification implementation inventory, not a set of alternate adopted output styles. Existing flat records, literal JSON fields, verbose JSON and retained-layout HTML compiler frames remain code facts until their owned rollout changes; the target above does not restyle or relabel stored text. This standard spans surfaces, not a new library domain. Control compact-result/public-error is now bare block-YAML metadata with native option sequences, whole selected values or explicit omission records under the existing16KiB cap, no duplicated run namespace, stage/reuse distinctions, typed selective inspect next nodes and opaque advice scalars; verbose JSON remains the machine path. Launch model/thinking groups use the concise `model` and `thinking` labels and preserve their actual source/value relationship; compact launch navigation now remains one whole native YAML value or explicit whole-row omission within16KiB including invocation-response spacing. Full spawn-verbose in source and normally built packages uses the legacy once-only JSON selection followed by clone-only YAML, without a new fixed byte/depth cap. Absent selected JSON, malformed selected text or recoverable post-selection failures withhold the whole projection with an attributable no-auto-repeat warning; original selection throws and structured details remain unchanged. Source/unit and installed compiled response checks, the strict ordinary-metadata source/packed completion journey, and all three source/packed navigation owners are accepted within their bounded domains. Native assertions remain indirect proof, not current global absence. Owning Skill byte parity does not establish a loaded-host change. The older field/inspect-action primitives remain with their unmigrated consumers; compact launch serialization now uses the YAML metadata primitive. Structured/verbose results, exact dispatch/private error causes and old completion handoff text retain their contracts. Fresh ordinary source completion admission captures canonical AX with the private completion_yaml_core_v1 layout: index-first YAML, independently rendered raw Markdown and cut-local [N characters omitted]. The minimal ordinary hint binds whole content to the YAML core, whose relative lengths derive header/body/marker boundaries without fresh HTML tails. Retained completion_frontmatter_v1 hints keep their complete ranges and original compiler frames; neither hint name is a public selector. Only valid compiler headers can hide whole reference/location rows. Markers remain literal and body-authored labels never become metadata/actions. Expansion and invalid/missing hints reveal full original text. These checks establish display consistency, not authentication, source/read scope or acceptance; display line-ending/tab handling never rewrites saved content. Routine state-equivalent summaries remain in the journal, not the new model content; meaningful summaries, loss/redaction/unavailability and reference coverage remain visible. Exact source/generation/parent bindings, whole references and fresh48KiB/64row limits include hidden technical data; retained codecs keep16KiB. The postprivacy byte budget is divided equally between candidate rows: roughly48KiB for one,24KiB each for two,16KiB each for three, less encoded service overhead. Whole admitted text uses result only when it fits without known upstream clipping; otherwise preview uses the safe prefix with exact retained-source omissions. There is no fixed16KiB result/2KiB preview cap and no render-time source IO or reallocation. Already-retained packets keep their exact captured bytes; pre-record messages keep the host renderer and retained record messages keep their original verified view. The Pi boundary recognizes the strict whole YAML core for ordinary source records, or original closed markers/anchored headers for retained captures, after private details are stripped, preserving exact owned-context acceptance without interpreting authored body labels. Actual actors guidance describes this implemented fresh-capture route; this is not a claim that a loaded host was reloaded. Fresh source urgent-steer captures use typed YAML metadata and a separate raw selected body before saving immutable envelope.content: complete final-message fitting and whole reference sequences retain16KiB/eight-slot bounds. Ordinary attention remains unbounded; bounded source/compiled/actual-default acceptance does not establish loaded-host acceptance. Existing envelopes are not reinterpreted, and no older new-message default remains. Fresh ordinary notify/followup uses one AX formatter in the existing sink: the same opaque checkpoint/body and whole first-eight reference projection, without borrowing steer's shared byte cap or changing routing. Committed history keeps its exact text; the retired presentation selector is not a compatibility API. Fresh read-time agent Trace preview leaves now also use boundary U+2026, exact admitted normalized-retained-turn omissions and separately unknown upstream loss, without changing the160UTF-16 source allowance, identifiers/chronology, detail/source data or public JSON schema. Malformed text is unknown, not repaired; no journal capture aspect is minted for a derived current read. New urgent admission/send/recovery diagnostics now also use event/attention/stage AX records, opaque raw240-unit previews and unknown loss while their persisted message-only evidence stays unchanged. A failed send leaves delivery/presentation unconfirmed, and unknown/conflicting queued recovery does not force replay. Fresh non-runtime explicit inspection also uses bare block-YAML metadata: unchanged Run/view headers, tool activation/argument sequences and Recipe registry/focused-doctor facts, whole selected source/identity/advice scalars or explicit whole-field omission, native counts/flags and one typed read-only hint or opaque `next.instruction`. The explicit `tool:* view=schema` text is generated definition metadata in the same bare YAML form with native parameter nodes when renderable; unsupported/over-ceiling schemas retain the exact legacy JSON fallback. Compact argument sequences are whole or explicitly omitted/unavailable. Verbose views bypass compact formatting and machine details keep their JSON contracts. Only the three existing closed read-only Recipe inspect literals become typed next; other advisory strings remain opaque instruction values with full next_actions retained in unchanged details/verbose JSON. Existing body/scope/budget/default-status contracts stay independent. Compact runtime status/runs/triage now also presents only its existing facts/counts as AX fields, with full owned inventory independent from bounded verbose navigation and bounded triage rows. Fresh locally owned runtime notices (callback failure, completion recovery/acceptance, steer admission/delivery/recovery diagnostics and the shutdown teardown report) are now bare block-YAML metadata with explicit receipt/report stages and native counters. UI-only notices keep complete well-formed selected fields without borrowing the model byte/depth ceilings; malformed selected text is explicitly unavailable. Existing steer preview bounds, source/errors, acceptance and native authority are unchanged. Producer-owned watcher/retirement notices now also use uncapped bare block-YAML metadata: whole selected literal paths and opaque diagnostics preserve raw signature/dedup/rearm behavior, while returned stop/reported cancellation/skip/failure remain non-closure reports with exact existing optional-notifier/error observation semantics. Inspector document/detail rendering now uses flat semantic fields, real sequence lists and opaque literal terminal values, with ephemeral compiler-label provenance instead of parsing authored colon/bullet lines as metadata. Separator-preserving native-column wrapping keeps whole text/references, source values, root stripes and existing identity/chronology/navigation/source bounds. Empty containers remain typed values; terminal line-ending/tab display policy does not rewrite source. Ten locally authored inspect refusals now render bare block-YAML metadata with true supported-value sequences, field-local whole-field coverage records and one typed read-only next, preserving their original guards and Error/dependency behavior. Only the two already-guided tool failures retain typed read-only next actions. New local error text shares16KiB including public spacing; variable fields/actions are whole or explicitly counted omitted without shortening targets, reading raw private lengths or wrapping unowned exceptions. Session/source/dependency errors keep their separate owners and old catches rather than being globally restyled. Inspector Kill confirmation now also uses captured run/state/action AX fields with whole-value paging, while both choices and closure: unconfirmed remain pinned at narrow/low viewports. Read-only PgUp/PgDn preserves original Cancel/choice/confirmation/generation behavior and adds no actuation or source permission. Inspector Kill cancellation only closes the dialog; successful actions use the selected Run's visible status without a second Notice. Failures/refusals/uncertainty remain visible, and notifications on other surfaces are not suppressed. Remaining Inspector feedback shows the complete selected opaque message directly in a modal popup over unchanged data rows, without i prefix/command or another reader. Esc dismisses; only physical overflow scrolls within that same popup. Kill uses the same centered rectangle compositor. Both use terminal-default backgrounds rather than a theme fill, two-column left content insets and top/bottom blank spacing, plus the Inspector's shared frame/key/description color grammar. On space-constrained heights, safety/choices/readable text take priority over decorative blanks. They keep the captured underlay stable through resize/invalidation and resume ordinary refresh on close, without fresh evidence/native authority. Action-port feedback pins closure uncertainty/no automatic retry without inferring native effects from message text. Fresh Kill producers now use a shared pure selected-value AX formatter: domain180-unit normalization/trim and overlay160-unit normalization/slice remain, while a cut pair retracts one unit and malformed selected text is unknown/unavailable. Dynamic legacy values state upstream completeness/privacy unproven, with unknown local clipping markers rather than private-tail counts; compiler refusals add no fake loss. ok/callback/getter/coercion/error semantics and old success suppression stay unchanged, and the popup's anchored header recognition affects color only. This does not recover discarded Markdown/causes or create a saved capture version. Trace table rows now mark actual column/line gaps with native ellipsis while preserving full selected item/detail, and compact warning captions keep incomplete-history/non-lifetime/unavailable-rank meaning visible at narrow widths. This is UI preview loss only, not new source/private character counts, rank/identity changes or a generic selector/fit rewrite. Four locally authored Control pre-dispatch guards now also use caller target/action/result and actual supported/available-action sequences: whole target/list groups fit16KiB including public spacing or carry field/entry omission counts, while malformed selected text is unavailable without repair. Original guard/getter/join/coercion observations, plain Error/host-error behavior and all unowned/dispatch exceptions stay separate; no input/status/correlation/retry/no-effects or new dispatch authority follows. Synchronous registered-command failures now present compiler-owned exit_code/killed metadata as bare block YAML, followed by a delimited header before the unchanged selected stderr/stdout bodies (or metadata-only when no stream text was selected); trim/tail selection, truncation notices, temp-file publication and exact executor failures are unchanged. The pre-dispatch argument-repair error now likewise puts owned tool/required/optional facts in a closed YAML header and keeps the original validation prose plus the verbatim JSON invocation example as unparsed body text, and local spawn pre-launch guards use bare block YAML with one whole selected value or an explicit omission record. Three local spawn pre-launch guards present field/result and a whole or explicitly omitted selected caller value as bare block YAML rather than inventing an admitted Run: unsupported state_dir, missing run: namespace and invalid id. Whole valid-scalar values fit16KiB or are omitted; malformed selections are unavailable, without repair or source/private-tail counts. Original input/method/coercion/launch behavior stays separate. The real host still rejects unsupported state_dir through its own unchanged schema before the facade, so this does not claim all SDK validation errors are AX. Namespace/id guard errors retain real host isError behavior. Successful normal registration/update-or-persisted receipts now use original tool/outcome/source/callable facts, actual required/optional argument and warning lists, a whole fenced non-template source and one prior hint. Invocation advice stays opaque instruction; only the producer-known unverified tool-status hint is typed next. Complete safety warnings have first budget priority; all variable fields/groups/actions fit16KiB including public spacing or show whole selected-field/entry omission, with malformed selections unavailable without repair. Original post-effect warning/argument/coercion/boundary observations and all transaction/details/notify/error behavior remain. This is not new callability, host activation, rollback or invocation authority. Registry current-name listing and deletion returns now also use AX: original sorted/captured names are a whole selected group with shown/omitted/total coverage, never a new page/work quota or active/callable/global registry claim. Overlarge groups are wholly omitted and malformed groups unavailable without raw-key re-coercion or repair. Deletion keeps original selected tool/result and the exact operator reload note as opaque instruction; current-map not found stays a returned result, not a new error or orphan-file cleanup. Lookup/file/deactivation/notify/errors/details remain unchanged, including post-effect failures. Draft promotion success now also uses only its prior selected name/new-update/source=draft/exact callable display/first hint as AX. It does not inherit ordinary-registration args/warnings/boundary facts or change its admission/rollback policy. Original template String conversions remain after notify, including zero/object/Symbol behavior and post-effect failures. Whole scalars/actions fit16KiB or are explicitly omitted/unavailable; only the closed producer-known read-only status hint is typed, and invocation advice is opaque. Machine details/bytes and later activation observations remain unchanged. Locally authored registry mode/name/source/template/draft/transaction refusals now render as bare block-YAML metadata documents with compiler-literal fields, native choice sequences, and each opaque selected value whole or explicitly omitted under the unchanged16KiB cap; authored text stays scalar content with escaped line breaks rather than becoming keys or actions. Registry activation/promotion/deletion notice text remains flat report/tool fields, outside this refusal migration. No supplied source values, new helper/wrapper or changed guard/descriptor/getter/trim order. The host still owns cloning, coercion and schema validation: rejected extra/object fields need not reach these AX guards, while numeric input may normalize and preserve a non-error unnamed list result. This is not new schema, no-effects or automatic retry authority. Three ordinary name/source refusals now also use AX: invalid normalized name without raw caller echo, reserved selected-name value whole or explicitly omitted/unavailable, and missing source only for a new registration with actual source choices/opaque prior advice. Original normalization/reserved-set/resolution/lookup/blocker/fault order stays unchanged; empty-name listing, existing-tool update with retained source and dependency blocker text are not reinterpreted. SDK clone/coercion/schema remains separate. Ordinary and draft duplicate-registration refusals now show their original selected name whole/omitted/unavailable plus the original update advice as opaque instruction; distinct authoritative-file/Map/exists/blocker/update predicates and original single display coercion stay unchanged. No overwrite/retry, body-read or no-effects authority follows. Template-shape helper errors now also use fixed template/result records for nested Recipe objects, empty sequences and unsupported types, with actual supported-kind lists/opaque old correction and no supplied value echo. Getter/Array/reference/key/expand semantics and external errors stay exact; null/undefined/deletion/retained-source behavior and SDK coercion remain separate. Authored-Recipe non-object defaults, missing command description, null-at-authoring and missing-final-template constants now also use fixed field/result, without new value echoes or changes to args/from/description/default/retained-source order. Front deletion, inheritance and Schema/dependency errors keep separate ownership. The delegated runtime-owned-default-key refusal now shows only its original first selected key, defaults/result and opaque old correction. Full copy/find order, eight-name membership and the delegated-only condition remain; no all-key/value disclosure, early rejection optimization or new retry authority. Two draft-path refusals now show only their old captured allowed root or resolved path as complete dynamically fenced literals, or explicit whole-reference omission/unavailability under16KiB. Refused caller path stays undisplayed in root refusal; existing containment/exists/getter/error behavior and opaque prior advice are unchanged, not new absence/native proof. Public from-normalization refusal now also uses only its old displayed source/reason/legacy summary/doctor advice as AX, preserving inner errors, catch/coercion/method order and source/hint disagreement. Source is a whole literal; reason/summary preserve opaque Markdown, and advice is instruction content. Monotone whole-field omission/unavailability fits16KiB without parsing old summary text, adding causes/counts or widening source/catch authority. Normal candidate-admission refusal now also projects only its old source-conditional displayed source/joined diagnostics/legacy summary/advice, preserving branch/coercion/join and pre-Error order. Existing4096-unit upstream admission diagnostic clipping is not widened or repaired; selected summary fields are whole/omitted/unavailable under16KiB, with fitting diagnostics first. This adds no admission policy, full diagnostics/source proof or rollback/readiness authority. Draft raw/candidate invalid-Recipe errors now likewise expose only old optional reason/joined diagnostics with fixed draft/result, preserving separate read/diagnose, truthiness/coercion, Error-before-join and legacy diagnostic bounds. No new path/body, cause, reader, policy or retry follows; whole opaque values fit16KiB or are explicitly omitted/unavailable. Outer registration transaction errors now also project only old post-cleanup stage/resolution-generation/legacy summary/error, preserving cleanup and fallible template order exactly. Resolution generation is not a Run epoch; fitting surfaced error has budget priority and opaque inner rollback/advice text is not a new guarantee. No replay, extra cause/source read or changed transaction policy follows. Registry activation/promotion/deletion notices now also use report/tool AX fields for only old compiler report identity/name, preserving callback order/info type/faults and real hasUI gate. Report wording is not callable/full-host-removal proof. UI text stays literal with native wrapping; no borrowed model16KiB cap, whole well-formed values retained and malformed selected name unavailable. RPC delivery remains fire-and-forget, and loaded UI acceptance stays separate. Recipe registry watcher warning/refresh messages now also use report and opaque instruction constants only, preserving their distinct direct ctx.ui.notify calls (no added hasUI gate), failure dedup/pending-load/callback fault order and old default-root report rather than new verified metadata. Runtime startup warnings now also preserve true category sequences/singletons and whole opaque selected entries through the existing shared field primitive. Original suppression/classification/map/push/join/coercion precedes new projection from compiler-known slots, not parsed authored labels. Trailing LF/no UI cap remain; malformed selected warning is unavailable and old doctor advice is opaque instruction. Native Inspector Run controls retain their selection grammar rather than forced Markdown: producer-specific column gaps now carry ellipses, and complete selected_run_id context uses existing Recipe/Control literal documents, separately from authored fields. Generic fit/composition, source reads, keys and action targets remain unchanged; loaded colored-terminal/phone acceptance stays gated. Other errors/notifications/public owners remain separately scoped, with live client acceptance independent. Reuse the cohesive owner of each projection:
241
+ The following is the current pre-unification implementation inventory, not a set of alternate adopted output styles. Existing flat records, literal JSON fields, verbose JSON and retained-layout HTML compiler frames remain code facts until their owned rollout changes; the target above does not restyle or relabel stored text. This standard spans surfaces, not a new library domain. Control compact-result/public-error is now bare block-YAML metadata with native option sequences, whole selected values or explicit omission records under the existing16KiB cap, no duplicated run namespace, stage/reuse distinctions, typed selective inspect next nodes and opaque advice scalars; verbose JSON remains the machine path. Launch model/thinking groups use the concise `model` and `thinking` labels and preserve their actual source/value relationship; compact launch navigation now remains one whole native YAML value or explicit whole-row omission within16KiB including invocation-response spacing. Full spawn-verbose in source and normally built packages uses the legacy once-only JSON selection followed by clone-only YAML, without a new fixed byte/depth cap. Absent selected JSON, malformed selected text or recoverable post-selection failures withhold the whole projection with an attributable no-auto-repeat warning; original selection throws and structured details remain unchanged. Source/unit and installed compiled response checks, the strict ordinary-metadata source/packed completion journey, and all three source/packed navigation owners are accepted within their bounded domains. Native assertions remain indirect proof, not current global absence. Owning Skill byte parity does not establish a loaded-host change. The older field/inspect-action primitives remain with their unmigrated consumers; compact launch serialization now uses the YAML metadata primitive. Structured/verbose results, exact dispatch/private error causes and old completion handoff text retain their contracts. Fresh ordinary source completion admission captures canonical AX with the private completion_yaml_files_v2 layout: run/state YAML with packet-local indices only for multiple shown results, independently rendered raw Markdown and cut-local [N characters omitted]. The minimal ordinary hint binds whole content to the YAML core, whose relative lengths derive header/body/marker boundaries without fresh HTML tails. Retained completion_frontmatter_v1 hints keep their complete ranges and original compiler frames; neither hint name is a public selector. Only valid compiler headers can hide whole reference/location rows. Markers remain literal and body-authored labels never become metadata/actions. Expansion and invalid/missing hints reveal full original text. These checks establish display consistency, not authentication, source/read scope or acceptance; display line-ending/tab handling never rewrites saved content. Routine state-equivalent summaries remain in the journal, not the new model content; meaningful summaries, loss/redaction/unavailability and reference coverage remain visible. Exact source/generation/parent bindings, whole references and fresh48KiB/64row limits include hidden technical data; retained codecs keep16KiB. The postprivacy byte budget is divided equally between candidate rows: roughly48KiB for one,24KiB each for two,16KiB each for three, less encoded service overhead. Whole admitted text uses result only when it fits without known upstream clipping; otherwise preview uses the safe prefix with exact retained-source omissions. There is no fixed16KiB result/2KiB preview cap and no render-time source IO or reallocation. Already-retained packets keep their exact captured bytes; pre-record messages keep the host renderer and retained record messages keep their original verified view. The Pi boundary recognizes the strict whole YAML core for ordinary source records, or original closed markers/anchored headers for retained captures, after private details are stripped, preserving exact owned-context acceptance without interpreting authored body labels. Actual actors guidance describes this implemented fresh-capture route; this is not a claim that a loaded host was reloaded. Fresh source urgent-steer captures use typed YAML metadata and a separate raw selected body before saving immutable envelope.content: complete final-message fitting and whole reference sequences retain16KiB/eight-slot bounds. Ordinary attention remains unbounded; bounded source/compiled/actual-default acceptance does not establish loaded-host acceptance. Existing envelopes are not reinterpreted, and no older new-message default remains. Fresh ordinary notify/followup uses one AX formatter in the existing sink: the same opaque checkpoint/body and whole first-eight reference projection, without borrowing steer's shared byte cap or changing routing. Committed history keeps its exact text; the retired presentation selector is not a compatibility API. Fresh read-time agent Trace preview leaves now also use boundary U+2026, exact admitted normalized-retained-turn omissions and separately unknown upstream loss, without changing the160UTF-16 source allowance, identifiers/chronology, detail/source data or public JSON schema. Malformed text is unknown, not repaired; no journal capture aspect is minted for a derived current read. New urgent admission/send/recovery diagnostics now also use event/attention/stage AX records, opaque raw240-unit previews and unknown loss while their persisted message-only evidence stays unchanged. A failed send leaves delivery/presentation unconfirmed, and unknown/conflicting queued recovery does not force replay. Fresh non-runtime explicit inspection also uses bare block-YAML metadata: unchanged Run/view headers, tool activation/argument sequences and Recipe registry/focused-doctor facts, whole selected source/identity/advice scalars or explicit whole-field omission, native counts/flags and one typed read-only hint or opaque `next.instruction`. The explicit `tool:* view=schema` text is generated definition metadata in the same bare YAML form with native parameter nodes when renderable; unsupported/over-ceiling schemas retain the exact legacy JSON fallback. Compact argument sequences are whole or explicitly omitted/unavailable. Verbose views bypass compact formatting and machine details keep their JSON contracts. Only the three existing closed read-only Recipe inspect literals become typed next; other advisory strings remain opaque instruction values with full next_actions retained in unchanged details/verbose JSON. Existing body/scope/budget/default-status contracts stay independent. Compact runtime status/runs/triage now also presents only its existing facts/counts as AX fields, with full owned inventory independent from bounded verbose navigation and bounded triage rows. Fresh locally owned runtime notices (callback failure, completion recovery/acceptance, steer admission/delivery/recovery diagnostics and the shutdown teardown report) are now bare block-YAML metadata with explicit receipt/report stages and native counters. UI-only notices keep complete well-formed selected fields without borrowing the model byte/depth ceilings; malformed selected text is explicitly unavailable. Existing steer preview bounds, source/errors, acceptance and native authority are unchanged. Producer-owned watcher/retirement notices now also use uncapped bare block-YAML metadata: whole selected literal paths and opaque diagnostics preserve raw signature/dedup/rearm behavior, while returned stop/reported cancellation/skip/failure remain non-closure reports with exact existing optional-notifier/error observation semantics. Inspector document/detail rendering now uses flat semantic fields, real sequence lists and opaque literal terminal values, with ephemeral compiler-label provenance instead of parsing authored colon/bullet lines as metadata. Separator-preserving native-column wrapping keeps whole text/references, source values, root stripes and existing identity/chronology/navigation/source bounds. Empty containers remain typed values; terminal line-ending/tab display policy does not rewrite source. Ten locally authored inspect refusals now render bare block-YAML metadata with true supported-value sequences, field-local whole-field coverage records and one typed read-only next, preserving their original guards and Error/dependency behavior. Only the two already-guided tool failures retain typed read-only next actions. New local error text shares16KiB including public spacing; variable fields/actions are whole or explicitly counted omitted without shortening targets, reading raw private lengths or wrapping unowned exceptions. Session/source/dependency errors keep their separate owners and old catches rather than being globally restyled. Inspector Kill confirmation now also uses captured run/state/action AX fields with whole-value paging, while both choices and closure: unconfirmed remain pinned at narrow/low viewports. Read-only PgUp/PgDn preserves original Cancel/choice/confirmation/generation behavior and adds no actuation or source permission. Inspector Kill cancellation only closes the dialog; successful actions use the selected Run's visible status without a second Notice. Failures/refusals/uncertainty remain visible, and notifications on other surfaces are not suppressed. Remaining Inspector feedback shows the complete selected opaque message directly in a modal popup over unchanged data rows, without i prefix/command or another reader. Esc dismisses; only physical overflow scrolls within that same popup. Kill uses the same centered rectangle compositor. Both use terminal-default backgrounds rather than a theme fill, two-column left content insets and top/bottom blank spacing, plus the Inspector's shared frame/key/description color grammar. On space-constrained heights, safety/choices/readable text take priority over decorative blanks. They keep the captured underlay stable through resize/invalidation and resume ordinary refresh on close, without fresh evidence/native authority. Action-port feedback pins closure uncertainty/no automatic retry without inferring native effects from message text. Fresh Kill producers now use a shared pure selected-value AX formatter: domain180-unit normalization/trim and overlay160-unit normalization/slice remain, while a cut pair retracts one unit and malformed selected text is unknown/unavailable. Dynamic legacy values state upstream completeness/privacy unproven, with unknown local clipping markers rather than private-tail counts; compiler refusals add no fake loss. ok/callback/getter/coercion/error semantics and old success suppression stay unchanged, and the popup's anchored header recognition affects color only. This does not recover discarded Markdown/causes or create a saved capture version. Trace table rows now mark actual column/line gaps with native ellipsis while preserving full selected item/detail, and compact warning captions keep incomplete-history/non-lifetime/unavailable-rank meaning visible at narrow widths. This is UI preview loss only, not new source/private character counts, rank/identity changes or a generic selector/fit rewrite. Four locally authored Control pre-dispatch guards now also use caller target/action/result and actual supported/available-action sequences: whole target/list groups fit16KiB including public spacing or carry field/entry omission counts, while malformed selected text is unavailable without repair. Original guard/getter/join/coercion observations, plain Error/host-error behavior and all unowned/dispatch exceptions stay separate; no input/status/correlation/retry/no-effects or new dispatch authority follows. Synchronous registered-command failures now present compiler-owned exit_code/killed metadata as bare block YAML, followed by a delimited header before the unchanged selected stderr/stdout bodies (or metadata-only when no stream text was selected); trim/tail selection, truncation notices, temp-file publication and exact executor failures are unchanged. The pre-dispatch argument-repair error now likewise puts owned tool/required/optional facts in a closed YAML header and keeps the original validation prose plus the verbatim JSON invocation example as unparsed body text, and local spawn pre-launch guards use bare block YAML with one whole selected value or an explicit omission record. Three local spawn pre-launch guards present field/result and a whole or explicitly omitted selected caller value as bare block YAML rather than inventing an admitted Run: unsupported state_dir, missing run: namespace and invalid id. Whole valid-scalar values fit16KiB or are omitted; malformed selections are unavailable, without repair or source/private-tail counts. Original input/method/coercion/launch behavior stays separate. The real host still rejects unsupported state_dir through its own unchanged schema before the facade, so this does not claim all SDK validation errors are AX. Namespace/id guard errors retain real host isError behavior. Successful normal registration/update-or-persisted receipts now use original tool/outcome/source/callable facts, actual required/optional argument and warning lists, a whole fenced non-template source and one prior hint. Invocation advice stays opaque instruction; only the producer-known unverified tool-status hint is typed next. Complete safety warnings have first budget priority; all variable fields/groups/actions fit16KiB including public spacing or show whole selected-field/entry omission, with malformed selections unavailable without repair. Original post-effect warning/argument/coercion/boundary observations and all transaction/details/notify/error behavior remain. This is not new callability, host activation, rollback or invocation authority. Registry current-name listing and deletion returns now also use AX: original sorted/captured names are a whole selected group with shown/omitted/total coverage, never a new page/work quota or active/callable/global registry claim. Overlarge groups are wholly omitted and malformed groups unavailable without raw-key re-coercion or repair. Deletion keeps original selected tool/result and the exact operator reload note as opaque instruction; current-map not found stays a returned result, not a new error or orphan-file cleanup. Lookup/file/deactivation/notify/errors/details remain unchanged, including post-effect failures. Draft promotion success now also uses only its prior selected name/new-update/source=draft/exact callable display/first hint as AX. It does not inherit ordinary-registration args/warnings/boundary facts or change its admission/rollback policy. Original template String conversions remain after notify, including zero/object/Symbol behavior and post-effect failures. Whole scalars/actions fit16KiB or are explicitly omitted/unavailable; only the closed producer-known read-only status hint is typed, and invocation advice is opaque. Machine details/bytes and later activation observations remain unchanged. Locally authored registry mode/name/source/template/draft/transaction refusals now render as bare block-YAML metadata documents with compiler-literal fields, native choice sequences, and each opaque selected value whole or explicitly omitted under the unchanged16KiB cap; authored text stays scalar content with escaped line breaks rather than becoming keys or actions. Registry activation/promotion/deletion notice text remains flat report/tool fields, outside this refusal migration. No supplied source values, new helper/wrapper or changed guard/descriptor/getter/trim order. The host still owns cloning, coercion and schema validation: rejected extra/object fields need not reach these AX guards, while numeric input may normalize and preserve a non-error unnamed list result. This is not new schema, no-effects or automatic retry authority. Three ordinary name/source refusals now also use AX: invalid normalized name without raw caller echo, reserved selected-name value whole or explicitly omitted/unavailable, and missing source only for a new registration with actual source choices/opaque prior advice. Original normalization/reserved-set/resolution/lookup/blocker/fault order stays unchanged; empty-name listing, existing-tool update with retained source and dependency blocker text are not reinterpreted. SDK clone/coercion/schema remains separate. Ordinary and draft duplicate-registration refusals now show their original selected name whole/omitted/unavailable plus the original update advice as opaque instruction; distinct authoritative-file/Map/exists/blocker/update predicates and original single display coercion stay unchanged. No overwrite/retry, body-read or no-effects authority follows. Template-shape helper errors now also use fixed template/result records for nested Recipe objects, empty sequences and unsupported types, with actual supported-kind lists/opaque old correction and no supplied value echo. Getter/Array/reference/key/expand semantics and external errors stay exact; null/undefined/deletion/retained-source behavior and SDK coercion remain separate. Authored-Recipe non-object defaults, missing command description, null-at-authoring and missing-final-template constants now also use fixed field/result, without new value echoes or changes to args/from/description/default/retained-source order. Front deletion, inheritance and Schema/dependency errors keep separate ownership. The delegated runtime-owned-default-key refusal now shows only its original first selected key, defaults/result and opaque old correction. Full copy/find order, eight-name membership and the delegated-only condition remain; no all-key/value disclosure, early rejection optimization or new retry authority. Two draft-path refusals now show only their old captured allowed root or resolved path as complete dynamically fenced literals, or explicit whole-reference omission/unavailability under16KiB. Refused caller path stays undisplayed in root refusal; existing containment/exists/getter/error behavior and opaque prior advice are unchanged, not new absence/native proof. Public from-normalization refusal now also uses only its old displayed source/reason/legacy summary/doctor advice as AX, preserving inner errors, catch/coercion/method order and source/hint disagreement. Source is a whole literal; reason/summary preserve opaque Markdown, and advice is instruction content. Monotone whole-field omission/unavailability fits16KiB without parsing old summary text, adding causes/counts or widening source/catch authority. Normal candidate-admission refusal now also projects only its old source-conditional displayed source/joined diagnostics/legacy summary/advice, preserving branch/coercion/join and pre-Error order. Existing4096-unit upstream admission diagnostic clipping is not widened or repaired; selected summary fields are whole/omitted/unavailable under16KiB, with fitting diagnostics first. This adds no admission policy, full diagnostics/source proof or rollback/readiness authority. Draft raw/candidate invalid-Recipe errors now likewise expose only old optional reason/joined diagnostics with fixed draft/result, preserving separate read/diagnose, truthiness/coercion, Error-before-join and legacy diagnostic bounds. No new path/body, cause, reader, policy or retry follows; whole opaque values fit16KiB or are explicitly omitted/unavailable. Outer registration transaction errors now also project only old post-cleanup stage/resolution-generation/legacy summary/error, preserving cleanup and fallible template order exactly. Resolution generation is not a Run epoch; fitting surfaced error has budget priority and opaque inner rollback/advice text is not a new guarantee. No replay, extra cause/source read or changed transaction policy follows. Registry activation/promotion/deletion notices now also use report/tool AX fields for only old compiler report identity/name, preserving callback order/info type/faults and real hasUI gate. Report wording is not callable/full-host-removal proof. UI text stays literal with native wrapping; no borrowed model16KiB cap, whole well-formed values retained and malformed selected name unavailable. RPC delivery remains fire-and-forget, and loaded UI acceptance stays separate. Recipe registry watcher warning/refresh messages now also use report and opaque instruction constants only, preserving their distinct direct ctx.ui.notify calls (no added hasUI gate), failure dedup/pending-load/callback fault order and old default-root report rather than new verified metadata. Runtime startup warnings now also preserve true category sequences/singletons and whole opaque selected entries through the existing shared field primitive. Original suppression/classification/map/push/join/coercion precedes new projection from compiler-known slots, not parsed authored labels. Trailing LF/no UI cap remain; malformed selected warning is unavailable and old doctor advice is opaque instruction. Native Inspector Run controls retain their selection grammar rather than forced Markdown: producer-specific column gaps now carry ellipses, and complete selected_run_id context uses existing Recipe/Control literal documents, separately from authored fields. Generic fit/composition, source reads, keys and action targets remain unchanged; loaded colored-terminal/phone acceptance stays gated. Other errors/notifications/public owners remain separately scoped, with live client acceptance independent. Reuse the cohesive owner of each projection:
243
242
 
244
243
  - `tools-message.ts`: Control receipts, refusals and conservative public error text.
245
244
  - `tools-response.ts`, `tools-spawn.ts`, `tools-local.ts`: shared response primitives and launch/runtime-tool presentation.
@@ -0,0 +1,121 @@
1
+ # Capability Memory and Tool Exposure
2
+
3
+ ## Status and Scope
4
+
5
+ Adopted design direction: preserve reusable capabilities separately from exposing named agent tools. Sync/async remains an execution contract, not a maturity score or registration rule. This document adopts the separation and its safety constraints, not a new storage format, Recipe field, public API, automatic-review action, or migration of existing Recipes.
6
+
7
+ Explicit create-only retention and read-only library inventory are implemented. Automatic retention/exposure, retained update/deletion and live portfolio migration remain unimplemented. Current tool-root discovery, automatic promote/discard decisions and ordinary tool activation behavior remain unchanged. [BACKLOG.md](../BACKLOG.md) owns remaining work; this document owns design truth, not another task list.
8
+
9
+ ## Three Independent Questions
10
+
11
+ - **Execution:** Does an invocation await the result inline or return a detached Run launch receipt?
12
+ - **Maturity:** Is this an unreviewed capture or a retained capability with stated validation evidence? Retention does not certify successful work, safety in every context, or generality.
13
+ - **Exposure:** Should the agent be offered this capability as a named tool? A retained Recipe may remain usable without occupying the tool interface.
14
+
15
+ A Recipe defines reusable execution. A tool is an agent-facing entrypoint to a capability. A Run is one concrete execution. Saving a definition, exposing a tool, activating it in the current host, launching it, and accepting its result remain separate facts.
16
+
17
+ The product goal is useful procedural memory with a selective tool interface, not the largest possible collection of callable names. More captures, launches, promotions, or registrations alone do not establish improvement.
18
+
19
+ ## Current Baseline and Its Limits
20
+
21
+ Current user-root discovery exposes valid enabled direct Recipes by location, while active Skill components already provide reusable capabilities without automatic tool exposure. Ordinary registration supports both execution modes, and public `spawn` accepts ordinary non-singleton Recipes without `async: true`; the flag is not a foreground-only restriction. Inline-template `spawn` capture produces async drafts rather than capturing maintained/file Recipe launches, so a sync-only promotion rule would exclude that capture stream.
22
+
23
+ Automatic review sees value-free shape, risk labels, launch counts and batch-local content-equality groups, not commands, descriptions, argument names/values, canonical names/paths or accepted outcomes. Its current promote/discard executor preserves captured executable content rather than generalizing a mission. Usage records launches and lineage, not successful task completion. These limits neither authorize bypassing confidentiality nor justify discarding a capability whose usefulness remains unknown.
24
+
25
+ ## Read-Only Inventory Checkpoint
26
+
27
+ The retained library stays outside nonrecursive tool-root discovery, including on older hosts. Directory separation fails safely where an unknown Recipe opt-out field could be ignored. No new Recipe field or logical-reference namespace is introduced.
28
+
29
+ Use `inspect target=recipes view=summary` and `verbose=true` for retained paths and diagnostics. The [inspection contract](./inspection.md) owns views and coverage; the [tool registry](./tool-registry.md#retain-a-new-capability-without-tool-exposure) owns the current save/discover/reuse workflow. Inventory is storage evidence, not review, callability or a running Actor, and never initializes an absent library.
30
+
31
+ ## Explicit Persistence Contract
32
+
33
+ The implemented create-only operation is `register_tool expose=false` with a new name and one template/from source. Omitted/true keeps ordinary registration. The [tool registry](./tool-registry.md#retain-a-new-capability-without-tool-exposure) owns supported inputs, source preservation, conflicts, mutation fences and withdrawal limits; this design does not define a second persistence path.
34
+
35
+ The management path is **save deliberately → find through Recipe inspection → reuse the exact Recipe reference → expose explicitly when useful → withdraw only the wrapper when appropriate**. Moving executable sources, converting existing tools/drafts, retained updates/deletion and automatic transitions require separately admitted contracts. Source/package acceptance does not reload an older host or authorize live portfolio changes.
36
+
37
+ ### Receipt and Failure Truth
38
+
39
+ Retention must be distinguishable from failed activation: `kind: retained_recipe`, `activation: not_requested`, and an exact reusable Recipe path, without global callability claims about a same-named tool. The [current receipt and storage contract](./tool-registry.md#retention-safety-and-accounting) owns fields, budgets, original causes and partial-effect handling. Unknown effects require inspection, not automatic retry or deletion.
40
+
41
+ ### One Usage Owner, Distinct Entrypoints
42
+
43
+ Reuse the parent's existing usage owner rather than create a library ledger system. Retention, exposure and withdrawal must preserve distinct entrypoint history without inventing accepted outcomes; the [usage/lineage contract](./tool-registry.md#usage-and-lineage) owns the implemented namespace and move rules. Aggregate retention bounds, expiry/disposal and semantic evidence remain prerequisites for future automatic retention, not new tool-count quotas for the manual slice.
44
+
45
+ ## Adopted Lifecycle Direction
46
+
47
+ The following is a conceptual lifecycle, not currently supported review syntax:
48
+
49
+ ```text
50
+ Unreviewed capture
51
+ ├─ Discard under an admitted retention policy
52
+ ├─ Defer when evidence is insufficient
53
+ └─ Retain as a reviewed Recipe with explicit evidence limits
54
+ ├─ Reuse by the existing Recipe execution path
55
+ └─ Deliberately expose as a named tool
56
+ └─ Resolve → validate → persist → activate → prove callable_now
57
+ ```
58
+
59
+ Sync and async Recipes can be retained or exposed. Do not leave a reviewed capability permanently classified as an unreviewed draft merely because it is async. Do not expose a synchronous one-off merely because it returns inline. Demotion of a useful tool should be able to withdraw exposure without erasing the retained capability or pretending it has never been reviewed.
60
+
61
+ Insufficient semantic evidence is **unknown**, not a low usefulness score or an instruction to delete. The future review contract must represent retain/defer separately from expose/discard and explain what evidence supports each decision. Deferral is not unlimited retention: the implementation contract must bound the retained portfolio and distinguish evidence deferral from deterministic safety rejection or explicitly admitted expiry/disposal. Missing usefulness evidence alone is not disposal authority; these limits do not weaken existing invalid/secret-touching rejection or grant new deletion permission. The exact persisted representation and review vocabulary require a concrete implementation contract; the diagram does not add action names to today's validators.
62
+
63
+ No automatic generalization or executable rewrite follows from review. Authoring a reusable capability from a one-off capture is a separate authorized change with explicit inputs, effects, and validation. Preserve the existing rule that an automatic executor derives unchanged executable content from its trusted capture.
64
+
65
+ ## When a Tool Earns Its Name
66
+
67
+ Tool exposure is justified by a stable agent-facing contract, not workload duration or whether a model runs inside it:
68
+
69
+ - **Purpose:** A recognizable recurring outcome and a name/description supported by semantic evidence, rather than an opaque content-group identity or a guessed task label.
70
+ - **Inputs:** Caller-owned typed arguments and useful parameterization; no accidental commitment to a single captured prompt, temporary file, credential, installation path, or operator-specific mission.
71
+ - **Result:** A clear deliverable and acceptance boundary. For async tools, explicitly distinguish the launch receipt from terminal completion and the requested result.
72
+ - **Effects:** Understandable filesystem/network/service effects, authority requirements, and failure or cancellation limits. Registration does not grant new permissions; launch failure may leave effects, and a stop request is not proof of scope closure.
73
+ - **Leverage:** Meaningful reduction in repeated planning or orchestration compared with existing tools and ordinary Recipe spawning. A generic arbitrary-prompt launcher that merely aliases `spawn` usually adds little.
74
+ - **Evidence:** Available validation, actual reuse, outcome acceptance, and uncertainty appropriate to the capability. Do not invent a universal call-count threshold or equate repeated launch attempts with successful reuse.
75
+
76
+ Examples are illustrative, not new registered tools:
77
+
78
+ - A parameterized async change-review capability can be a useful named tool: it launches a fresh reviewer Run and later returns the declared report.
79
+ - A one-off synchronous command with a fixed task path may be worth neither retaining nor exposing.
80
+ - A useful but infrequent pipeline may stay retained and be spawned intentionally without becoming a standing tool.
81
+ - A long-lived playback service may earn a launcher tool and declared Control without becoming a synchronous function.
82
+
83
+ A subagent-backed tool is a **capability launcher**, not a persisted live subagent or a handle to an old Run. Ordinary invocations create distinct executions; singleton compatibility and reuse remain owned by the existing Run contract. Registering every transient subagent, Run ID, or mission as a new tool defeats this distinction.
84
+
85
+ ## Review Evidence and Confidentiality
86
+
87
+ Keep automatic review read-only and value-free. Structural evidence can support eligibility checks, contract-shape comparisons, and bounded prioritization; it is not a substitute for understanding the task. Content-equality groups prove only the equality represented by the capture, not semantic equivalence or interchangeability.
88
+
89
+ A future semantic evidence channel must have a separately reviewed provenance, privacy, size, and authority contract before use. Raw commands, prompts, descriptions, paths, outcomes, and purportedly sanitized summaries are not automatically safe reviewer inputs. An operator-reviewed capability description or accepted-outcome record is a possible design input, not an available trusted carrier today. Do not let actor-authored text become admission authority or let missing evidence force an irreversible discard.
90
+
91
+ Evaluate memory quality by useful accepted reuse and reduced reconstruction cost, balanced against tool-selection ambiguity, stale contracts, runtime/review cost, and accidental exposure. Those are evaluation goals, not currently collected metrics or a requirement to add telemetry. Validate the policy on bounded controlled fixtures before claiming better agent behavior.
92
+
93
+ ## Ownership and Migration Constraints
94
+
95
+ Extend existing owners instead of creating a second execution kernel, tool registry, Recipe resolver, or parallel task list:
96
+
97
+ - **Recipe resolution/discovery:** Preserve exact Skill/file identity, imports, runtime-owned origins, validation, and the distinction between retained definitions and tool candidates. A non-tool library must remain reachable through an admitted existing Recipe execution path.
98
+ - **Registry/runtime:** Own explicit exposure, host activation, withdrawal limitations, and `callable_now` evidence. Existing explicit `register_tool` intent remains valid for sync and async Recipes; the target policy does not silently prohibit either.
99
+ - **Draft/tool review and transactions:** Own evidence-limited recommendations, immutable captures, CAS, quarantine, journals, recovery, and admission of unchanged content. Retain/defer and withdrawal need explicit replay-safe transitions before changing automatic policy.
100
+ - **Usage/lineage:** Preserve revision and lifetime evidence across retention, exposure, withdrawal, and rollback without counting a migration as a launch or inventing successful outcomes.
101
+ - **Owning Skills:** Explain only implemented operations and approved selection guidance. Do not tell an agent to use a future library path, metadata flag, review action, or public command.
102
+
103
+ The retained directory, read-only discovery and initial create-only persistence/receipt contract are implemented. Retained updates/deletion, automatic transitions, aggregate retention bounds and the semantic evidence channel remain separate unresolved implementation choices. Prefer the smallest extension of current owners; do not add a new Recipe exposure field, database, service, or eager loading of every retained Recipe into the model context. Moving a Recipe can change relative imports and `{recipe_dir}` semantics: explicit retention needs an admitted source-preservation rule before any write, not a blind move from drafts or the tool root.
104
+
105
+ Existing user tools must not be silently demoted, deleted, renamed, generalized, or re-registered during adoption. Preserve executable bytes, delegated logical references, defaults, imports, Control, artifacts, runtime origins, usage, and recovery evidence. Prepare a read-only classification first; exact portfolio changes require an admitted migration contract and the authority applicable to that portfolio. Source tests or packaging do not authorize changes to the operator's live registry or reload the running host.
106
+
107
+ ## Acceptance Properties
108
+
109
+ - **Orthogonality:** A useful async capability can be exposed; an unsuitable sync capture is not automatically exposed. Either mode can be retained without becoming a tool.
110
+ - **Evidence limits:** Structurally similar but semantically different Recipes cannot acquire an asserted purpose/usefulness solely from the same review shape. Unavailable semantic evidence stays explicit and can defer a decision without destructive loss.
111
+ - **Identity and replay:** Retain/expose/withdraw/recover preserve source and lineage, reject stale/conflicting plans, and do not create promotion/demotion churn or duplicate executable copies as new capabilities.
112
+ - **Invocation truth:** Explicit sync and async registration still works. Actual tool calls remain distinct from `spawn`; launch, eventual result, singleton attachment, and current callability retain their original meanings.
113
+ - **Compatibility:** Source and packed-package journeys preserve existing safety and explicit-registration behavior before any automatic-policy migration. No test writes into the operator's registry.
114
+ - **Product evidence:** Controlled evaluation must distinguish accepted useful reuse and selection cost from mere growth in Recipe/tool counts. Until measured, improved "muscle memory" remains a design hypothesis, not a shipped performance claim.
115
+
116
+ ## Related
117
+
118
+ - [Tool Registry](./tool-registry.md) — Current registration, discovery, activation, and automatic-review behavior.
119
+ - [Skill-Owned Recipe Components](./recipe-library.md) — Existing reusable components without automatic tool exposure.
120
+ - [Template Recipes](./template-recipes.md) — Recipe and execution contracts.
121
+ - [Project Backlog](../BACKLOG.md) — Canonical remaining implementation and acceptance work.
@@ -113,7 +113,7 @@ Phases are monotonic:
113
113
 
114
114
  Every journal mutation uses the canonical token-owned lock, expected-phase fencing, owner and generation validation, and atomic replacement. Repeated transitions are idempotent. Corrupt, oversized, foreign-owner, or stale-generation state fails closed with bounded diagnostics.
115
115
 
116
- Ordinary source completion admission has one fresh path: presentation_format=ax_records, private result_layout=completion_yaml_files_v1 and frozen text_budget, without excerpt_format or a public selector. The inactive numbered-stream kernel path keeps its separate frontmatter_v2 default. Bounded source/compiled and actual-default source/packed acceptance observes the normal index path and in-memory entry/context/provider/hint tuple. Unchanged storage/registered-renderer/accepted-history-reopen facts transfer only on the successful original-current-manager path; this is neither fresh default-disk observation nor global lifetime-policy, native-scope or loaded-host acceptance. Direct admission and ordinary run-ui-runtime use it after unchanged owner/location checks; an existing batch stays untouched. Each shown record begins YAML frontmatter with index, run, state, then kind:primary for admitted primary/final Actor response or kind:diagnostic for explicit error/failure excerpts. Untracked fallback has no kind and retains unknown completeness/privacy outside YAML. The closed header is followed by raw, unindented Markdown, with no #N heading, result:/preview: wrapper or compiler literal fence around the body. Known local loss is [N characters omitted] at the cut, not a YAML flag/count or extra ellipsis; N counts the admitted postprivacy source prefix loss, not private/native output. Unknown upstream loss remains separate. Original coverage remains structured member data; exact source/generation/location references remain whole typed nested YAML values. Routine summaries/windows/status copies/standing map instructions remain journal facts, not duplicated model content. Material omitted_results, non-done omitted_states and whole result_inventory recovery stay explicit. Each shown record has a whole run_files row or explicit omission, with unavailable reasons retained; omitted records do not leave orphan location rows. Outcomes precede lower-priority references under the total48KiB UTF-8/64candidate-row bound including framing. Prefix admission considers full-list and decimal-count discontinuities, freezes equal scalar/CRLF-safe shares without unused-share redistribution, and never clips references or rewrites a retained excerpt. Older captures retain their original16KiB/48KiB budgets.
116
+ Ordinary source completion admission has one fresh path: presentation_format=ax_records, private result_layout=completion_yaml_files_v2 and frozen text_budget, without excerpt_format or a public selector. The inactive numbered-stream kernel path keeps its separate frontmatter_v2 default. Bounded source/compiled and actual-default source/packed acceptance observes the normal index path and in-memory entry/context/provider/hint tuple. Unchanged storage/registered-renderer/accepted-history-reopen facts transfer only on the successful original-current-manager path; this is neither fresh default-disk observation nor global lifetime-policy, native-scope or loaded-host acceptance. Direct admission and ordinary run-ui-runtime use it after unchanged owner/location checks; an existing batch stays untouched. Each shown record begins YAML frontmatter with run, state, then kind:primary for admitted primary/final Actor response or kind:diagnostic for explicit error/failure excerpts. Untracked fallback has no kind and retains unknown completeness/privacy outside YAML. The closed header is followed by raw, unindented Markdown, with no #N heading, result:/preview: wrapper or compiler literal fence around the body. Known local loss is [N characters omitted] at the cut, not a YAML flag/count or extra ellipsis; N counts the admitted postprivacy source prefix loss, not private/native output. Unknown upstream loss remains separate. Original coverage remains structured member data; exact source/generation/location references remain whole typed nested YAML values. Routine summaries/windows/status copies/standing map instructions remain journal facts, not duplicated model content. Prepend a packet-local index only when two or more results are shown; exactly one shown result omits only that display field even if other results were omitted. Internal member_index, generation and correlation remain unchanged; the rooted_files_v2 discriminator gates this rule, not member count in an older snapshot or numbered-stream scope. Material omitted_results, non-done omitted_states and whole result_inventory recovery stay explicit. Each shown record has a whole run_files row or explicit omission, with unavailable reasons retained; omitted records do not leave orphan location rows. Outcomes precede lower-priority references under the total48KiB UTF-8/64candidate-row bound including framing. Prefix admission considers full-list and decimal-count discontinuities, freezes equal scalar/CRLF-safe shares without unused-share redistribution, and never clips references or rewrites a retained excerpt. Older captures retain their original16KiB/48KiB budgets.
117
117
 
118
118
  Fresh ordinary source hints carry the core format and whole-content digest; the validated YAML core carries only packet identity and relative body/marker lengths, from which boundaries are derived. It adds no fresh HTML tail or duplicate coordinate table. These are consistency facts, not source or acceptance authority.
119
119
 
@@ -131,7 +131,7 @@ Removing fresh collection of untracked bodies would leave affected old/intermedi
131
131
 
132
132
  ### Retained capture compatibility
133
133
 
134
- The numbered wire strings ax_records_v1/v2/v3, old indexed/single_result captures and result_layout records/run_headers/headings/headings_flat_omissions/headings_outcomes/headings_shared_results/headings_shared_kinds/frontmatter_v1/frontmatter_v2 are retained storage encodings, not public alternatives. Fresh ordinary source admission chooses own-data completion_yaml_files_v1 with canonical ax_records and text_budget (over the unchanged completion_yaml_core_v1 carrier); earlier completion_yaml_core_v1 snapshots retain their old layout, never single_result. Absence preserves its original indexed/singleton codec; inherited aspects never restyle data and accessors are refused without invocation. Earlier record/header/count/kind grammar and each captured16KiB/48KiB share remain exact. The first frontmatter draft retains its original bytes and hints, including unquoted unknown index ?. Retained v2 fixed that one YAML case by writing index: "?"; numeric indices and other fields/body bytes are unchanged, and the presentation-hint schema remains completion_frontmatter_v1 because its block structure is unchanged. Preserve the frozen v1 unknown-content fingerprint90e29fb36087e2620fe0cf150b4238bce8a6d1dcc68ee922a1cf71ee8604610e and prior shared-result fingerprint4428de318b8ebaa029d3588ab7e8e705b9a80fa5cf08b6a2cc7151916207e266. Journals store snapshots rather than complete text: neither an unreleased product, a YAML correction nor member count permits relabeling/migration. Pending/queued/presented replay must reproduce original model bytes and hints without operator journal scans, source refresh or message rewrites. Removing compatibility requires a separate history-lifetime decision.
134
+ The numbered wire strings ax_records_v1/v2/v3, old indexed/single_result captures and result_layout records/run_headers/headings/headings_flat_omissions/headings_outcomes/headings_shared_results/headings_shared_kinds/frontmatter_v1/frontmatter_v2 are retained storage encodings, not public alternatives. Fresh ordinary source admission chooses own-data completion_yaml_files_v2 with canonical ax_records and text_budget (over the unchanged completion_yaml_core_v1 carrier); earlier completion_yaml_core_v1 and completion_yaml_files_v1 snapshots retain their old layout and singleton indices, never single_result. Absence preserves its original indexed/singleton codec; inherited aspects never restyle data and accessors are refused without invocation. Earlier record/header/count/kind grammar and each captured16KiB/48KiB share remain exact. The first frontmatter draft retains its original bytes and hints, including unquoted unknown index ?. Retained v2 fixed that one YAML case by writing index: "?"; numeric indices and other fields/body bytes are unchanged, and the presentation-hint schema remains completion_frontmatter_v1 because its block structure is unchanged. Preserve the frozen v1 unknown-content fingerprint90e29fb36087e2620fe0cf150b4238bce8a6d1dcc68ee922a1cf71ee8604610e and prior shared-result fingerprint4428de318b8ebaa029d3588ab7e8e705b9a80fa5cf08b6a2cc7151916207e266. Journals store snapshots rather than complete text: neither an unreleased product, a YAML correction nor member count permits relabeling/migration. Pending/queued/presented replay must reproduce original model bytes and hints without operator journal scans, source refresh or message rewrites. Removing compatibility requires a separate history-lifetime decision.
135
135
 
136
136
  Steer envelopes already store complete content. Both attention and steer now have one canonical new-message formatter with no presentation selector; the redundant legacy formatting branches/path/body helpers are removed where no retained-data consumer needs them. Existing steer identity is reused before formatting and saved content bypasses the formatter in every pending/queued/presented recovery path. Removing stored Batch codecs later requires a separate retention/data-lifetime decision; an unreleased product is not permission to change previously captured delivery bytes.
137
137
 
@@ -95,12 +95,14 @@ inspect target=recipes view=imports verbose=true
95
95
  inspect target=recipes view=reviews verbose=true
96
96
  ```
97
97
 
98
- - `status` reports registry/watch state, active/shadowed/invalid/disabled user Recipes, active Skill components, drafts, usage, and diagnostics.
98
+ - `status` reports registry/watch state, active/shadowed/invalid/disabled user Recipes, active Skill components, drafts, retained-library definitions, usage, and diagnostics.
99
99
  - `summary` returns the compact discovery-oriented projection of the same current context. `registry` is the same inspection path under a second accepted view name: with identical `verbose` and other inputs it returns identical facts, details and rendering, so only `verbose` selects compact text versus structured output. The compact label stays `recipes: registry` for both names.
100
100
  - `doctor` diagnoses one canonical `<skill>/<recipe>` identity when `identity` is provided; without an identity it reports registry remediation evidence.
101
101
  - `imports` exposes active Skill namespaces and component import inventory for resolution diagnosis.
102
102
  - `reviews` exposes bounded automatic draft/tool review state and recovery guidance.
103
103
 
104
+ The compact Recipe catalog adds `retained` as a separate count for direct `.json`/`.md` definitions under `recipes/library`. Details/verbose includes `retained` rows (exact path, source_kind=user_retained_recipe, tool=false, resolved async mode, invalid/disabled state and diagnostics) and `retained_diagnostics`. Both root and library use the active session Skill context. Storage placement does not certify review, current callability or Run state; invalid/disabled definitions remain diagnostic. The missing library is empty, without initialization. Inspection performs no retention write, activation or launch. Valid enabled exact paths remain ordinary explicit-file references for `spawn` or deliberate `register_tool from` specialization. Inspection adds no mutation or logical-reference syntax; use explicit `register_tool expose=false` for supported create-only retention, and see [capability memory](./capability-memory.md#read-only-inventory-checkpoint).
105
+
104
106
  `identity` is accepted only with `view=doctor`. Skill inventory is fail-soft diagnostic evidence; exact launch and registration still resolve against the immutable active-session context.
105
107
 
106
108
  ## Tool Targets
@@ -91,7 +91,7 @@ F1.6 source-audit correction: Pi 1.0 native custom-message storage, session reco
91
91
  ### 10. Completion Projection, Compiler Frame and Native Renderer
92
92
 
93
93
  - **Owners:** `run-delivery.ts:1510` `completionRecordRow`, `:1683` `assembleFrontmatter`, `:1886` `projectCompletionRecords`, `:2110` `projectRunCompletionBatchPresentation`, `:2233` `readRunCompletionPresentation`, `:2283` retained projection, `:2328` message formatting. `run-ui-runtime.ts:68` `registerRunCompletionRenderer` is wired at `extension-runtime.ts:122`.
94
- - **Current:** Fresh ordinary `completion_yaml_files_v1` snapshots use the unchanged strict YAML-core carrier. Header files.groups stores literal roots with relative entries, source bindings and English purposes; read_first is a group/entry pointer. Unknown-base paths stay explicitly as recorded. Native compact presentation exposes those file groups and places omission/inventory facts in the first header, never after the Markdown. Only known character-cut markers remain beside a truncated body. Earlier core-v1 and historical layouts keep their exact projection and hiding/footer behavior.
94
+ - **Current:** Fresh ordinary `completion_yaml_files_v2` snapshots use the unchanged strict YAML-core carrier. Header files.groups stores literal roots with relative entries, source bindings and English purposes; read_first is a group/entry pointer. Unknown-base paths stay explicitly as recorded. Native compact presentation exposes those file groups and places omission/inventory facts in the first header, never after the Markdown. Only known character-cut markers remain beside a truncated body. Exactly one shown result omits its packet-local display index, including with omissions; multiple shown results retain indices. Internal member/generation/correlation and omission/inventory facts stay unchanged. Earlier rooted-v1, core-v1 and historical layouts keep their exact indices, projection and hiding/footer behavior; numbered streams are separate.
95
95
  - **Prepared wire core:** `run-delivery.ts` `encodeCompletionYamlCarrier` / `readCompletionYamlCarrier` provide the accepted pure/nondefault mechanics; the prototype encodes and typed native view decodes them. Ordinary source/default capture uses the rooted snapshot layout; the earlier core-v1 snapshot and inactive numbered-stream frontmatter_v2 remain separate replay/gated paths. Strict-core Pi correlation and source/packed SDK evidence bind the selected content without granting native or read authority. They encode one `_completion` packet ID/codec and relative body/marker lengths, then derive dense ranges while decoding. Canonical bare headers are independent YAML fragments separated by codec-owned blank lines, not one mapping or a standard YAML multi-document stream; body-present headers are delimited and raw parts remain opaque. All-omitted is bare service metadata, not a fake Run. Empty selected bodies are distinct from absent bodies; empty-marker-only wrappers are refused. Input uses named top-level result keys with explicit depth/work bounds; loaded-host acceptance remains F1.7/U1-gated, and meaningful service warnings must not be hidden wholesale.
96
96
  - **Prepared source projection (nondefault):** `projectRunCompletionYamlPrototype` projects current AX/frontmatter selected snapshots through one shared private fitter; it returns content, a format/digest-only hint and fitting facts, not a newly admitted or relabeled Batch. Frozen budgets/prefixes remain immutable. Canonical header/raw-part costs are measured additively, with unsupported data distinct from byte overflow; final content must agree with the unchanged core encoder/decoder. This prototype API remains data-only and source-class restricted. The ordinary rooted capture reuses internal YAML assembly and the same independently measured quotas/inventory rather than relabeling an old frozen snapshot. Native renderer support is prepared below; actual carrier acceptance remains a separate gate.
97
97
  - **Native view:** `readRunCompletionPresentation` derives frozen typed segments from the strict core and exact minimal format/digest hint; the registered renderer keeps new rooted file groups and all meaningful service facts in headers, then renders each body as independent native Markdown with literal Text metadata/markers. Retained core-v1 captures keep their earlier reference hiding and service placement. Valid-core missing/invalid hints and declared-new malformed content expose original full Text; expansion keeps that same source. Malformed content without a safely readable own new-format declaration retains historical dispatch, not a body-scanning heuristic. Headless widths/padding and old-history preservation are proved. Repo SDK 1.0.0's public `CustomMessageComponent` falls back to one Markdown component when a renderer is absent, returns undefined or throws, joining text-block arrays; a fresh component probe confirms this boundary, not session/interactive dispatch. Preserve that fallback unchanged: full content supplied to Markdown is neither independent-body rendering nor literal-visibility proof. Independent Markdown belongs only to the validated registered compact branch; registered degraded/expanded views keep original Text. Actual source-admitted native storage/context/provider/reopen and loaded terminal/phone behavior remain separate acceptance.
package/docs/releasing.md CHANGED
@@ -15,10 +15,14 @@ The adopted CI layout has three isolated test jobs on each supported OS: `instal
15
15
 
16
16
  Each job uses a separate checkout and test-agent state, Node24 and the existing single-worker/preload flags. Package validation (types, Recipe QA, committed build parity and extension import) plus dry-pack runs once per OS on `installed`, not on every shard. Local `npm test` and `npm run validate` remain complete unsharded entrypoints. The reusable workflow must wait for all nine test jobs and the single dependency audit before release publication; one successful shard never substitutes for the full gate. This layout reduces aggregate critical-path pressure, not the cost or correctness obligations of an individual native test.
17
17
 
18
- Simple tools/registration fixtures bind original runner births and actual `close`, then validate canonical success and fresh ESRCH again at deletion. Their joint acceptance phase has one10-second watchdog, replacing the old approximately2-second result/exit polling assumption; this is a fixture allowance, not a product/SDK/workload deadline. Missing post-close results fail promptly; uncertainty retains roots without signals or automatic wait inflation.
18
+ Simple tools/registration and installed-runner fixtures bind original runner births and actual `close`, then validate canonical success and fresh ESRCH again at deletion. Their joint acceptance phase has one10-second watchdog, replacing the old short result/exit polling assumptions; this is a fixture allowance, not a product/SDK/workload deadline. Missing post-close results fail promptly; uncertainty retains roots without signals or automatic wait inflation.
19
19
 
20
20
  Implementation and native CI acceptance are tracked in BACKLOG until the complete sharded candidate passes.
21
21
 
22
+ ## Local Validation
23
+
24
+ Run `npm run validate` and `npm run audit:dependencies` with Node24 selected, including after any login-shell setup. When launching validation from an Actor, clear `PI_ACTORS_DELIVERY_OWNER_ID`, `PI_ACTORS_DELIVERY_PARENT_RUN`, `PI_ACTORS_DELIVERY_PARENT_INSTANCE` and `PI_ACTORS_DELIVERY_PARENT_STATE_DIR` only in the test workload's environment. Otherwise independent SDK fixture coordinators may inherit the validation Actor's completion lineage and wait for the wrong coordinator. Keep the launcher's own lineage intact; this isolation changes neither product defaults nor test cases, the standard agent-directory preload or watchdogs.
25
+
22
26
  ## One-time npm Trusted Publisher setup
23
27
 
24
28
  Configure the existing public package `@llblab/pi-actors` on npmjs.com with a GitHub Actions Trusted Publisher using these exact values:
@@ -38,7 +38,37 @@ register_tool name=repo_check template=null
38
38
 
39
39
  An empty `template` string is the equivalent deletion form. Do not delete registry files manually. Host-visible dynamic definitions may remain visible until reload because Pi cannot unregister them, but extension-local lookup rejects the deleted definition immediately. Name collisions for creation or replacement require `update=true`. Invalid content fails before active mutation.
40
40
 
41
- Registration resolves the effective delegated contract before persistence, then reports logical `source`, effective `required_args` / `optional_args`, and distinct `persisted`, `registry_active`, `host_registered`, `active_tool`, and `callable_now` states. A callable result points to the actual generated tool as the next action. An uncallable result names its `activation_boundary` and status/doctor action without suggesting spawn as a substitute. Diagnose one maintained source with `inspect target=recipes view=doctor identity=<skill>/<recipe>`; diagnose final activation, source, schema summary, and separate spawn/tool usage with `inspect target=tool:<name> view=status`. Treat the tool as callable in the current session only when `callable_now` is true; persistence alone is not activation proof. Registration results omit raw persisted paths and executable template/config payloads.
41
+ Ordinary tool registration resolves the effective delegated contract before persistence, then reports logical `source`, effective `required_args` / `optional_args`, and distinct `persisted`, `registry_active`, `host_registered`, `active_tool`, and `callable_now` states. A callable result points to the actual generated tool as the next action. An uncallable result names its `activation_boundary` and status/doctor action without suggesting spawn as a substitute. Diagnose one maintained source with `inspect target=recipes view=doctor identity=<skill>/<recipe>`; diagnose final activation, source, schema summary, and separate spawn/tool usage with `inspect target=tool:<name> view=status`. Treat the tool as callable in the current session only when `callable_now` is true; persistence alone is not activation proof. Ordinary tool-registration results omit raw persisted paths and executable template/config payloads; retained receipts deliberately return the reusable file path, never an executable body.
42
+
43
+ ## Retain a New Capability Without Tool Exposure
44
+
45
+ ```text
46
+ register_tool name=repo_check expose=false template="make check" description="Run repository checks"
47
+ register_tool name=review_changes expose=false from=swarm/lens-review
48
+ ```
49
+
50
+ This create-only mode writes `recipes/library/<name>.json`, not a tool-root entry. The optional Boolean `expose` is registration input, not a Recipe field; omitted/true preserves ordinary registration. A new name and exactly one template/from source are required. Retained update, null/empty deletion, draft promotion and direct managed disposable-draft dependencies, including Recipe-valued template leaves, are rejected. JSON/Markdown stem conflicts, invalid source/defaults, oversized candidates and unsafe storage fail without activating a tool; a same-named exposed tool is untouched. Retaining never calls host registration/activation ports, mutates the active-tool set or launches work.
51
+
52
+ Retained `from` stores compact logical/file delegation and inherits the effective source contract, origins, imports, async, artifacts and Control without copying or moving its executable graph. It is an external dependency, not a self-contained backup or transitive lifetime guarantee. Newly authored template references resolve from their final library location.
53
+
54
+ Use the existing surfaces, supplying required caller values:
55
+
56
+ ```text
57
+ register_tool name=changes_review from=swarm/lens-review expose=false
58
+ inspect target=recipes view=summary verbose=true
59
+ spawn recipe=<exact retained path> values=<caller-owned inputs>
60
+ register_tool name=changes_review from=<exact retained path>
61
+ ```
62
+
63
+ Inspection distinguishes retained rows from tool-root candidates and drafts, using the same active Skill context. Compact output gives a retained count; verbose detail gives exact paths, resolved execution mode, invalid/disabled state and diagnostics. Reads create no directories, usage, tools or Runs; an absent library is empty. Reuse only valid enabled definitions. The final step deliberately exposes a wrapper; verify `callable_now` and actual tool invocation separately. Deleting an admitted wrapper preserves its retained source and does not stop active Runs or turn deletion of an old standalone tool into safe retention. No automatic retention, review decision, source relocation or live migration is added. See [Management Inspection](./inspection.md) for views and coverage.
64
+
65
+ ### Retention Safety and Accounting
66
+
67
+ Preflight preserves existing name/source guard order and validates the effective typed-argument/default contract and complete serialized candidate before library writes, including the existing 1 MiB Recipe limit. The tool-name reservation releases before root → library → target mutation locks. Regular non-symlink directory/file identity and containment frames, JSON/Markdown conflict checks and exact persisted-content re-admission fence publication and success. Complete staged bytes publish through an atomic no-overwrite hard link; unsupported filesystems refuse without a replacement fallback. These cooperative guards are not a filesystem sandbox or hostile-writer exclusion.
68
+
69
+ Success reports `kind: retained_recipe`, `result: retained`, `persisted: true`, `resolved: true`, `validated: true`, `activation: not_requested`, effective async mode, the exact Recipe path and required/optional argument names. It omits global `callable_now`/`active_tool` flags because a same-named tool may already exist. Metadata follows the existing whole-value YAML/omission budget with complete structured details; no executable body, secret-bearing defaults, source installation root or semantic usefulness claim is added. Its next hint is read-only Recipe inspection, not activation repair. Storage alone proves neither review, callability nor task success.
70
+
71
+ Post-publication admission, cleanup or lock-release faults can leave committed bytes. The failure boundary includes release of the outer root lock and preserves the exact original or aggregate cause privately; public effects remain unconfirmed. Rollback may remove only its own proven unchanged candidate under the original root/target fences. Do not blindly delete an uncertain candidate or replay the operation: inspect retained inventory first. Ordinary registration keeps its existing rollback and activation contract. Retention itself records no launch, success or review, and introduces no automatic expiry or migration; usage accounting remains with the existing owner below.
42
72
 
43
73
  ## Resolution
44
74
 
@@ -66,6 +96,10 @@ Async registered tools add runtime-owned optional invocation parameters after re
66
96
 
67
97
  These parameters are not Recipe-authored args. Singleton tools do not expose `run_id` because their canonical Run identity is owned by the Skill Recipe.
68
98
 
99
+ ## Retention Versus Exposure — Adopted Direction
100
+
101
+ The [capability-memory design](./capability-memory.md) owns the separation, future lifecycle and evidence gates; [BACKLOG.md](../BACKLOG.md) owns remaining work. Sync/async is execution policy, not a retention/usefulness rule: a subagent-backed tool launches a capability, and its async receipt is not the eventual result. Explicit create-only retention is implemented above; automatic retain/defer/expose transitions are not. Existing automatic promote/discard review and live portfolios remain unchanged.
102
+
69
103
  ## Automatic Review
70
104
 
71
105
  Automatic draft/tool review remains silent and mechanically fenced:
@@ -92,6 +126,8 @@ Set `PI_ACTORS_AUTOMATIC_REVIEW=off` to disable scheduling and safe-boundary act
92
126
 
93
127
  Usage and lineage live in locked metadata ledgers rather than authored Recipe files. Launch accounting briefly shares the portfolio transaction fence so quarantine cannot invalidate an already-authorized launch. Revision snapshots, rollback, demotion, rename, and identical-source deduplication retain CAS/hash evidence.
94
128
 
129
+ Retained launches use the parent Recipe root's existing `.usage` owner, not a second ledger under `library`. Private `library/<stem>` identity keeps them separate from same-stem tool-root entrypoints for JSON and Markdown alike; it is not a public Recipe reference. Retaining or exposing a definition is not a launch. Automatic missing-source/content-equality rename inference stays within the library namespace or within the existing root/draft namespace. Deliberate moves use the existing explicit lineage transaction owner; do not guess migrations or missing counts from legacy nested ledgers. Launch history does not establish useful accepted outcomes.
130
+
95
131
  ## Specializing Existing Recipes
96
132
 
97
133
  Use `register_tool from=<skill>/<recipe> defaults={...}` for one maintained capability under a persistent name or narrower defaults. Pi supplies one authoritative active-session Skill snapshot across global, project, package, settings, CLI, and extension-contributed locations. Registration resolves the source from that snapshot, stores only the logical direct-delegation reference plus caller specialization, and activates the tool from the already-resolved effective contract without ambient re-resolution. It does not copy async, args/types, Control, artifacts, helpers, or runtime-owned `{recipe_dir}`/`{skill_dir}`; symlinks and absolute helper paths are not substitutes. Named imports remain for multi-node Recipe composition, not one-source specialization. Skill Recipes remain components and are never exposed merely because their Skill is active. Install only specific capabilities; internal automatic-review Recipes must not become user-callable tools.
package/lib/file-state.ts CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { spawnSync } from "node:child_process";
8
8
  import { createHash, randomUUID } from "node:crypto";
9
- import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
9
+ import { existsSync, linkSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
10
10
  import { tmpdir } from "node:os";
11
11
  import { basename, dirname, join, parse, resolve } from "node:path";
12
12
 
@@ -297,14 +297,18 @@ function replaceFileWithRetry(source: string, target: string): void {
297
297
  export function writeTextAtomic(
298
298
  path: string,
299
299
  content: string,
300
- options: { onBeforeReplace?(): void } = {},
300
+ options: { exclusive?: boolean; onBeforeReplace?(): void } = {},
301
301
  ): void {
302
302
  mkdirSync(dirname(path), { recursive: true });
303
303
  const tempPath = `${path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
304
304
  try {
305
305
  writeFileSync(tempPath, content, "utf8");
306
306
  options.onBeforeReplace?.();
307
- replaceFileWithRetry(tempPath, path);
307
+ if (options.exclusive === true) {
308
+ // Publish a complete new file atomically without clobbering an uncooperative creator.
309
+ linkSync(tempPath, path);
310
+ unlinkSync(tempPath);
311
+ } else replaceFileWithRetry(tempPath, path);
308
312
  } catch (error) {
309
313
  try {
310
314
  unlinkSync(tempPath);
package/lib/pi.ts CHANGED
@@ -123,7 +123,7 @@ function unheaderedCompletionId(content: string): string | { malformed: true } |
123
123
  const core = RunDelivery.readCompletionYamlCarrier(content);
124
124
  if (core.state === "available") return core.packet.batchId;
125
125
  // Initial compiler headers only: never recover identity by scanning authored bodies.
126
- if (/^(?:index: (?:\d+|"\?")\nrun: [^\n]*\nstate:|_completion:\n (?:codec|batch_id):)/u.test(content)) return { malformed: true };
126
+ if (/^(?:index: (?:\d+|"\?")\nrun: [^\n]*\nstate:|run:[^\r\n]*(?:\r\n|\r|\n)state:|_completion:\n (?:codec|batch_id):)/u.test(content)) return { malformed: true };
127
127
  }
128
128
  if (typeof content === "string" && (hasCompletionFrontmatterPrefix(content) || /^(?:#|Run #|— Новый поток|- run:|- #0:\n|- result: omitted \(model byte limit\)\n|- omitted_results:)/u.test(content))) {
129
129
  return RunDelivery.readRunCompletionRecordMarker(content)?.batchId ?? { malformed: true };
package/lib/prompts.ts CHANGED
@@ -7,15 +7,18 @@
7
7
  export const REGISTER_TOOL_DESCRIPTION =
8
8
  "Register a persistent custom tool from one maintained Recipe, command template, or captured draft. " +
9
9
  "Use from for Recipe specialization, template for trusted commands, or draft for promotion. " +
10
+ "Set expose=false to retain a new template/from Recipe without tool activation. " +
10
11
  "Definitions persist under ~/.pi/agent/recipes; activation is reported separately.";
11
12
 
12
13
  export const REGISTER_TOOL_PROMPT_SNIPPET =
13
- "Register persistent Recipes or command templates as agent-callable tools";
14
+ "Persist command templates or Recipes with register_tool; use expose=false to retain without tool activation";
14
15
 
15
16
  export const REGISTER_TOOL_GUIDELINES = [
16
17
  "Use register_tool from=<skill>/<recipe> with defaults={...} to specialize a maintained Recipe without copying its contract.",
17
18
  "Use register_tool template only for trusted command templates, and use register_tool draft only for captured draft promotion.",
18
- "After register_tool succeeds, trust its callable_now and activation result; persistence alone is not callability.",
19
+ "Use register_tool expose=false to retain a new named template/from Recipe without activation; update, delete and draft promotion are unsupported in this mode.",
20
+ "A register_tool retained_recipe receipt with activation=not_requested is deliberate retention, not failed activation; reuse its exact Recipe path through spawn.",
21
+ "When register_tool requests tool exposure, trust callable_now and activation; persistence alone is not callability.",
19
22
  'Set template=null or template="" in register_tool to delete a persisted tool.',
20
23
  "Set update=true in register_tool to overwrite an existing tool registration.",
21
24
  ];
@@ -42,6 +45,8 @@ export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
42
45
  "Optional caller-owned defaults. Keys and values must satisfy the effective source or command-template argument contract.",
43
46
  draft:
44
47
  "Promote a captured draft Recipe path from ~/.pi/agent/recipes/drafts. This is a source mode; do not combine it with from or template.",
48
+ expose:
49
+ "Set false to retain a new named template/from Recipe in recipes/library without registering or activating a tool. Default true preserves ordinary tool registration. Retained update, delete and draft promotion are unsupported.",
45
50
  async:
46
51
  "Set true for a co-located async template recipe. Omit for ordinary command templates or file-backed recipe references.",
47
52
  template:
@@ -756,6 +756,32 @@ function templatePreview(value: unknown): string | undefined {
756
756
  : rendered;
757
757
  }
758
758
 
759
+ export function inventoryRetainedRecipes(
760
+ recipeRoot: string,
761
+ resolutionContext?: RecipeResolutionContext,
762
+ ): { recipes: Array<Record<string, unknown>>; diagnostics: string[] } {
763
+ const discovery = discoverRecipeSources([
764
+ { root: join(recipeRoot, "library"), resolutionContext },
765
+ ]);
766
+ return {
767
+ recipes: [...discovery.active.values()]
768
+ .map((entry) => ({
769
+ id: entry.id,
770
+ path: entry.path,
771
+ source_kind: "user_retained_recipe",
772
+ tool: false,
773
+ invalid: entry.invalid,
774
+ disabled: entry.disabled,
775
+ ...(entry.config ? { async: entry.config.async === true } : {}),
776
+ ...(entry.config?.description ? { description: entry.config.description } : {}),
777
+ ...(entry.diagnostics.length ? { diagnostics: entry.diagnostics } : {}),
778
+ ...(entry.riskLabels.length ? { risk_labels: entry.riskLabels } : {}),
779
+ }))
780
+ .sort((left, right) => left.id.localeCompare(right.id)),
781
+ diagnostics: discovery.diagnostics,
782
+ };
783
+ }
784
+
759
785
  export function listDraftRecipes(root: string): Array<Record<string, unknown>> {
760
786
  return listRecipeFiles(root).map((path) => {
761
787
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -16,7 +16,7 @@ import * as Paths from "./paths.ts";
16
16
  import * as RecipeControl from "./recipe-control.ts";
17
17
  import * as Schema from "./schema.ts";
18
18
 
19
- const MAX_RECIPE_FILE_BYTES = 1024 * 1024;
19
+ export const MAX_RECIPE_FILE_BYTES = 1024 * 1024;
20
20
  const MAX_RECIPE_IMPORT_DEPTH = 32;
21
21
 
22
22
  export interface TemplateRecipeImportBinding {