@llblab/pi-actors 0.58.1 → 0.58.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +6 -0
- package/README.md +37 -35
- package/dist/lib/draft-sleep.js +3 -3
- package/dist/lib/extension-runtime.js +7 -2
- package/dist/lib/prompts.d.ts +3 -3
- package/dist/lib/prompts.js +10 -9
- package/dist/lib/recipes-references.js +7 -7
- package/dist/lib/registry.js +27 -24
- package/dist/lib/review-control.js +1 -1
- package/dist/lib/run-evidence-policy.js +2 -2
- package/dist/lib/runtime.d.ts +3 -0
- package/dist/lib/runtime.js +24 -10
- package/dist/lib/tool-review-scheduler.js +7 -7
- package/dist/lib/tools-access.js +1 -1
- package/dist/lib/tools-inspect.js +10 -10
- package/dist/lib/tools-message.d.ts +1 -1
- package/dist/lib/tools-message.js +10 -10
- package/dist/lib/tools-register.d.ts +3 -3
- package/dist/lib/tools-register.js +4 -4
- package/dist/lib/tools-response.d.ts +1 -1
- package/dist/lib/tools-response.js +18 -18
- package/dist/lib/tools-spawn.js +1 -1
- package/dist/lib/tools.d.ts +7 -0
- package/dist/lib/tools.js +25 -0
- package/dist/skills/actors/SKILL.md +28 -20
- package/dist/skills/actors/references/diagnostics.md +11 -11
- package/dist/skills/actors/references/persistent-tools.md +19 -15
- package/dist/skills/actors/references/recipes.md +1 -1
- package/dist/skills/actors/references/runs.md +7 -7
- package/dist/skills/music-player/SKILL.md +4 -4
- package/dist/skills/music-player/scripts/playback.mjs +1 -1
- package/dist/skills/recipe-memory/SKILL.md +7 -7
- package/docs/README.md +2 -2
- package/docs/actor-inspector.md +1 -1
- package/docs/async-runs.md +13 -13
- package/docs/capability-memory.md +6 -6
- package/docs/inspection.md +21 -21
- package/docs/run-supervision.md +21 -21
- package/docs/tool-registry.md +27 -19
- package/docs/windows-process-observation.md +1 -1
- package/lib/draft-sleep.ts +3 -3
- package/lib/extension-runtime.ts +7 -5
- package/lib/prompts.ts +10 -9
- package/lib/recipes-references.ts +7 -7
- package/lib/registry.ts +26 -24
- package/lib/review-control.ts +1 -1
- package/lib/run-evidence-policy.ts +2 -2
- package/lib/runtime.ts +26 -11
- package/lib/tool-review-scheduler.ts +7 -7
- package/lib/tools-access.ts +1 -1
- package/lib/tools-inspect.ts +10 -10
- package/lib/tools-message.ts +10 -10
- package/lib/tools-register.ts +4 -4
- package/lib/tools-response.ts +18 -18
- package/lib/tools-spawn.ts +1 -1
- package/lib/tools.ts +28 -0
- package/package.json +1 -1
- package/skills/actors/SKILL.md +28 -20
- package/skills/actors/references/diagnostics.md +11 -11
- package/skills/actors/references/persistent-tools.md +19 -15
- package/skills/actors/references/recipes.md +1 -1
- package/skills/actors/references/runs.md +7 -7
- package/skills/music-player/SKILL.md +4 -4
- package/skills/music-player/scripts/playback.mjs +1 -1
- package/skills/recipe-memory/SKILL.md +7 -7
package/AGENTS.md
CHANGED
|
@@ -22,7 +22,7 @@ Run = Recipe + Trace + Control
|
|
|
22
22
|
|
|
23
23
|
An actor is any runnable local capability, including a script, tool, service, pipeline, or subagent. Recipes define actors' reusable execution. Runs are concrete actor instances and own one generation of execution and evidence. Trace owns observations. Control owns actor-local inputs for services that actually consume them. `register_tool` persists capabilities separately.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Canonical public tools are `actors_tool_register`, `actors_run_spawn`, `actors_control_apply`, and `actors_inspect`. Advertise only these names. Explicit compatibility dispatch retains old `register_tool`, `spawn`, `message`, and `inspect` calls with the same schemas/executors, without rewriting saved calls or Recipe identities. Control applies Run/runtime actions, not agent chat. New configurable tool names use their functional owner's namespace (for example `audio_transcribe`); never automatically prefix/migrate persisted Recipe names. Reject normalized host-tool collisions, including inactive foreign tools, before exposure or mutation; `update=true` is not foreign-tool overwrite permission. Agent Experience (AX) is a project-wide design principle: structured, visible, decision-relevant context that both the agent and the human can read, between raw machine data and unstructured prose. The [AX standard](docs/agent-experience.md) governs presentation across surfaces without changing their data or authority contracts.
|
|
26
26
|
|
|
27
27
|
The adopted presentation direction is **JSON structured inputs → block-YAML runtime-output metadata + unchanged authored text/Markdown**. This covers tool/launch/status/Control/inspect/error/notification/completion output, not only Actor results. Metadata-only receipts are bare YAML without `---` or a code fence; use opening/closing `---` only when a separate selected text/Markdown body follows. Framing depends on body presence, not synchronous versus asynchronous execution. Keep exactly one visual blank line between host-owned tool invocation header/arguments and response, including errors; this is boundary spacing, not a reason to rewrite selected content, echo private inputs or add YAML delimiters. Do not invent a Markdown-list pseudo-object grammar or serialize runtime objects as JSON strings inside YAML. SDK/Recipe input contracts, machine DTOs/journals/native wire formats and literal source content remain separate. YAML rollout is pending, not a claim that current emitters or a loaded host conform; preserve old saved bytes/codecs, exact content, privacy/generation fences and budgets, and gate service-framing migration on equivalent boundary/correlation/replay evidence.
|
|
28
28
|
|
|
@@ -253,4 +253,4 @@ Operator-owned staging may change concurrently through the UI. Do not gate work
|
|
|
253
253
|
- `.github/workflows/release.yml` owns the immutable sequence reusable validation → npm Trusted Publisher publication/verification → GitHub Release convergence; follow [docs/releasing.md](docs/releasing.md).
|
|
254
254
|
- Keep npm publication tokenless: use the exact npm Trusted Publisher binding and job-scoped OIDC permission, never a long-lived npm token or token fallback.
|
|
255
255
|
- Manifest-loaded packages expose bundled Skills only through `pi.skills`, preserving package resource filters and package-owned provenance. Only a checkout auto-discovered directly below a user or project Pi `extensions/` root may contribute its source Skill root through `resources_discover`; compiled-vs-source filename shape does not determine ownership.
|
|
256
|
-
-
|
|
256
|
+
- Prefer clean simplification over compatibility aliases except the explicitly adopted TOOL-NAMES migration above; preserve its legacy dispatch and saved identities. Do not generalize that exception into unrelated compatibility surfaces.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## 0.58.2: Canonical Actor Tool Names
|
|
6
|
+
|
|
7
|
+
- `Canonical Tool Surface`: New calls use `actors_tool_register`, `actors_run_spawn`, `actors_control_apply`, and `actors_inspect`. Explicit legacy dispatch shares the same schemas/executors while model declarations and ordinary callable listings advertise canonical names only. Saved calls, Recipe identities and Run semantics remain unchanged.
|
|
8
|
+
- `Control and Capability Ownership`: Control applies Run/runtime actions, not chat. Functional-owner namespaces are agent-facing naming guidance, not runtime ownership inference. Registration/discovery reject normalized foreign host collisions, including inactive tools; `update=true` grants no foreign overwrite. Own retained inactive declarations permit Recipe recreation/restoration after discovery loss. Saved names remain exact; deactivation is not complete host unregister.
|
|
9
|
+
- `Migration Evidence and Guidance`: Tool/registry properties and isolated source/npm-packed default SDK journeys cover canonical/legacy parity, saved-call preservation, loss/restoration, foreign replacement refusal and canonical-only model declarations. Skills, runtime advice, schema copy and packaged references use canonical names; historical completion framing stays exact. No live registry migration, host reload or publication is implied.
|
|
10
|
+
|
|
5
11
|
## 0.58.1: Singleton Completion Indices
|
|
6
12
|
|
|
7
13
|
- `Singleton Completion Index`: New completion packets omit the packet-local display index when exactly one result is shown, including with omitted results. Multiple shown results retain indices; omission/inventory facts, internal identity/correlation, numbered-stream behavior and historical replay remain unchanged.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Recipe --spawn--> Run
|
|
|
9
9
|
Run = Recipe + Trace + Control
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
An **actor** is any runnable local capability: a script, tool, service, pipeline, or subagent. A **Recipe** is its reusable executable definition. `
|
|
12
|
+
An **actor** is any runnable local capability: a script, tool, service, pipeline, or subagent. A **Recipe** is its reusable executable definition. `actors_run_spawn` creates a **Run**—one concrete actor instance—which captures its Recipe, appends observable **Trace**, and may consume actor-local **Control**.
|
|
13
13
|
|
|
14
14
|
## Agent Experience (AX)
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ Design notes for [portable Recipe composition bundles and CodeMode interoperabil
|
|
|
29
29
|
|
|
30
30
|
A reusable Recipe does not have to become a named tool. The [adopted capability-memory direction](docs/capability-memory.md) separates retention from tool exposure: sync/async determines execution, not usefulness, and a subagent-backed tool is a capability launcher rather than a saved live Actor. The aim is useful procedural memory with a selective tool interface, not more registered names.
|
|
31
31
|
|
|
32
|
-
Use `
|
|
32
|
+
Use `actors_tool_register expose=false` with a new name and a template/from source to save a capability in `recipes/library` without activating a tool. Its retained_recipe/not_requested receipt returns the exact Recipe path for `actors_run_spawn` or later deliberate `actors_tool_register from` exposure. Creation is exclusive; retained update, delete and draft promotion are not supported. `actors_inspect target=recipes view=summary` distinguishes retained definitions, with exact paths, modes and diagnostics in verbose detail. Automatic retained lifecycle is not implemented. Current tool-root discovery, automatic review and ordinary registration remain unchanged; structural review and launch counts alone do not prove useful successful reuse. [BACKLOG.md](BACKLOG.md) owns implementation and evidence gates; adopting the direction neither migrates existing tools nor reloads a running host.
|
|
33
33
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
@@ -49,20 +49,22 @@ The package contributes the extension and six capability-owning Skills for actor
|
|
|
49
49
|
|
|
50
50
|
## Public Tools
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Only `actors_tool_register`, `actors_run_spawn`, `actors_control_apply`, and `actors_inspect` are advertised for new calls. Explicit legacy `register_tool`, `spawn`, `message`, and `inspect` dispatch remains compatible with saved calls through the same schemas and executors. Saved Recipe identities and completion text are not migrated. Control applies Run/runtime actions, not chat. See [Tool Registry](docs/tool-registry.md#public-names-and-compatible-dispatch) for compatibility visibility, namespace and collision rules; this change does not reload an already running host.
|
|
53
|
+
|
|
54
|
+
### `actors_run_spawn`
|
|
53
55
|
|
|
54
56
|
Create a Run from an active-Skill Recipe, an explicit Recipe file, or an inline command template:
|
|
55
57
|
|
|
56
58
|
```text
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
actors_run_spawn template="sleep 30" as=run:demo
|
|
60
|
+
actors_run_spawn recipe=project-work/repo-health values={"repo":"/work/project","model":"provider/model"}
|
|
61
|
+
actors_run_spawn recipe=music-player/playback values={"source":"/music"}
|
|
62
|
+
actors_run_spawn template="make test" as=run:test
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
Use a Run when work may outlive the current turn, needs steering, fans out, produces artifacts, or must remain inspectable. Short foreground commands can remain ordinary tools.
|
|
64
66
|
|
|
65
|
-
### `
|
|
67
|
+
### `actors_control_apply`
|
|
66
68
|
|
|
67
69
|
Send one exact Control:
|
|
68
70
|
|
|
@@ -78,38 +80,38 @@ Send one exact Control:
|
|
|
78
80
|
Run targets accept Recipe-declared actor-local actions plus runtime-owned `kill`, `archive`, and `prune`, subject to Run state. Runtime targets accept only reserved review actions:
|
|
79
81
|
|
|
80
82
|
```text
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
actors_control_apply target=runtime action=review.retry input={"scope":"draft"}
|
|
84
|
+
actors_control_apply target=runtime action=review.retry input={"scope":"tool"}
|
|
85
|
+
actors_control_apply target=runtime action=review.reset input={"scope":"draft"}
|
|
86
|
+
actors_control_apply target=runtime action=review.reset input={"scope":"tool"}
|
|
85
87
|
```
|
|
86
88
|
|
|
87
89
|
Lifecycle `kill` remains runtime-owned rather than Recipe-declared.
|
|
88
90
|
|
|
89
|
-
### `
|
|
91
|
+
### `actors_inspect`
|
|
90
92
|
|
|
91
93
|
Inspect one exact management target:
|
|
92
94
|
|
|
93
95
|
```text
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
actors_inspect target=run:test view=recipe
|
|
97
|
+
actors_inspect target=run:test view=trace source=lifecycle lines=40
|
|
98
|
+
actors_inspect target=run:test view=control
|
|
99
|
+
actors_inspect target=runtime view=status
|
|
100
|
+
actors_inspect target=runtime view=runs status=failed
|
|
101
|
+
actors_inspect target=runtime view=triage
|
|
102
|
+
actors_inspect target=recipes view=status
|
|
103
|
+
actors_inspect target=recipes view=summary
|
|
104
|
+
actors_inspect target=recipes view=doctor identity=music-player/playback
|
|
105
|
+
actors_inspect target=tool:my_tool view=status
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
Recipe `summary` and `registry` are the same inspection path; only `verbose` selects compact text versus structured details. Recipe and tool views are listed above; `
|
|
108
|
+
Recipe `summary` and `registry` are the same inspection path; only `verbose` selects compact text versus structured details. Recipe and tool views are listed above; `actors_inspect` parameter help and unsupported-view refusals name the actual accepted view sequences.
|
|
107
109
|
|
|
108
|
-
A Run exposes exactly `recipe`, `trace`, and `control` views. Run-specific `
|
|
110
|
+
A Run exposes exactly `recipe`, `trace`, and `control` views. Run-specific `actors_inspect` and `actors_control_apply` require a current coordinator session whose id exactly matches the persisted non-empty Run owner; possessing `run:<id>` alone is not authorization. Missing-session, ownerless, and cross-owner access fails closed. Use `actors_inspect target=runtime view=runs` for the current session's exact-owner inventory. See the complete [management inspection matrix](./docs/inspection.md).
|
|
109
111
|
|
|
110
|
-
### `
|
|
112
|
+
### `actors_tool_register`
|
|
111
113
|
|
|
112
|
-
Persist a maintained Recipe with `
|
|
114
|
+
Persist a maintained Recipe with `actors_tool_register name=<tool> from=<skill>/<recipe> defaults={...}`, or register a trusted command through the separate `template` mode. Definitions live under `~/.pi/agent/recipes`. With `expose=false`, a new template/from definition is retained under `recipes/library` instead; its not_requested receipt makes no tool-callability claim. Treat an exposed tool as callable in the current session only when the result reports `callable_now: true`; persistence, Recipe spawning, and registered-tool invocation are distinct states.
|
|
113
115
|
|
|
114
116
|
## Recipe
|
|
115
117
|
|
|
@@ -166,7 +168,7 @@ String command-template leaves execute directly without shell interpretation. Us
|
|
|
166
168
|
}
|
|
167
169
|
```
|
|
168
170
|
|
|
169
|
-
Trace fields are exact: `id`, `ts`, `kind`, and optional `summary`, `data`, `level`, `attention`. Address, sender, recipient, reply, and routing fields fail validation. Trace is a bounded retained suffix, not an audit archive: the canonical authority appends within 2,048 events and 4 MiB or atomically keeps the newest suffix plus one warning-only `runtime.trace_compacted` marker. That marker means older history was discarded; `result.json`, `execution.json`, terminal state, and declared artifacts retain their own authority. `
|
|
171
|
+
Trace fields are exact: `id`, `ts`, `kind`, and optional `summary`, `data`, `level`, `attention`. Address, sender, recipient, reply, and routing fields fail validation. Trace is a bounded retained suffix, not an audit archive: the canonical authority appends within 2,048 events and 4 MiB or atomically keeps the newest suffix plus one warning-only `runtime.trace_compacted` marker. That marker means older history was discarded; `result.json`, `execution.json`, terminal state, and declared artifacts retain their own authority. `actors_inspect view=trace` reports whether retained history is complete. Reads are newline-safe and deterministic; first-party scripts never write this file directly.
|
|
170
172
|
|
|
171
173
|
Generic command lifecycle is Trace-only: runner-owned `command.done` records preserve completion and execution evidence but never request or project attention, including malformed legacy records carrying `steer`. There is no Recipe-level command-completion delivery switch. Semantic attention is explicit: `notify` shows visible status without a model turn, `followup` supplies ordinary checkpoint context, and `steer` requests urgent delivery at Pi's next safe assistant/tool boundary. `notify` and `followup` remain retained wake hints that may compact away, so persist recovery state or an artifact first. `steer` is admitted to the bounded owner delivery journal before `deliverAs: "steer"`, acknowledged only in exact model-bound context, and never suppresses the eventual root-terminal batch. Store large evidence in artifacts or bounded execution captures. New urgent steers keep the canonical checkpoint and whole optional references within 16 KiB, explicitly counting omitted paths instead of cutting a path; saved envelopes retain their original text. Essential text that cannot fit remains retryable before admission, not partially delivered. Ordinary notify/followup shares exact reference spelling and explicit eight-slot coverage; its wake policy is unchanged. Legacy body and steer-diagnostic clipping explicitly marks unknown omission counts when no retained privacy context admits safe numbers; it is not a new privacy or completeness guarantee.
|
|
172
174
|
|
|
@@ -174,9 +176,9 @@ Ordinary finite Runs project terminal results through one root-coordinator compl
|
|
|
174
176
|
|
|
175
177
|
Implemented UX/AX subset: completions share48KiB UTF-8 including service fields, divided equally between up to64 candidate results: nearly48KiB for one,24KiB each for two,16KiB each for three. Whole admitted text fitting its share is result; otherwise preview carries the safe prefix and exact omitted count, without a fixed16KiB result/2KiB preview cap. Completions show admitted work output or the bounded executor-selected final Actor response, never command metadata. Saved packets keep their original budget/text; source-backed prefix selection occurs only at fresh admission, not replay/render. Direct Pi text-print answers and successful composition/retry responses use the original producer privacy; observed upstream clipping stays explicit and is not confused with preview loss. Ordinary completions can carry retained primary-text, artifact-declaration inventory and diagnostic-metadata references. Follow the exact reference for clipped or omitted evidence; it records capture-time identity/hash/counts, not perpetual readability. Artifact inventories list declarations only, not proof that target files exist or were produced, and do not grant body-read permission. Diagnostic excerpts identify the selected field with literal UTF-8/Unicode counts; their source contains recorded command/error/failure-reason metadata, not complete primary output. Source loss is explicit and never replaced by guessed legacy bodies or paths. See [Run supervision](docs/run-supervision.md) for implemented boundaries and remaining acceptance gates.
|
|
176
178
|
|
|
177
|
-
Implemented A2 subset: `
|
|
179
|
+
Implemented A2 subset: `actors_inspect target=runtime view=runs verbose=true` now adds a bounded [root-first file map](docs/run-supervision.md#same-session-file-investigation) for the actual session's already cataloged directories. Follow selected locations with ordinary tools, without an original producer key. Source/npm-packed SDK fixtures resume the same saved session in a new process after real Run producers and their coordinator host have exited, then read the actual result and select recorded stdout via the mapped execution manifest with ordinary tools; another session gets no inherited branch. The read-only map exposes canonical starting-location coverage and expected format hints, not a full file inventory or format/completeness validation. Its published_sessions link locates the fixed role catalog physically under the owned Run root, even without a semantic index or producer key; this does not certify its body or historical publication. SDK checks select one controlled partial preview and also read an actual private-writer preview after its holder exits and the same saved session opens in another process. Exact tool-read paths and final byte/absence snapshots are checked, including selected-preview loss errors without cached/raw fallback; the private publisher uses controlled recorded-session sources, not genuine Actor conversations. A finite public spawn and later same-saved-session research also pass through real source/npm entrypoints, without replacement tools or copied lifecycle wiring. Corrupt catalogs stay unavailable without repair. Inventory filters observe current Run status, not the registration-time cache. Ordinary maps retain root-group recency with known failed children first; add status=failed to the verbose request for observed failed-subtree-first ordering across roots without filtering the full owned map or adding reads. Priority is partial observed data, not terminal authority. Launches and compatible singleton attachments now register after lifecycle release; catalog failure returns `catalog=unavailable` without failing or retrying the launched work. SDK fork/clone preserves inherited history but gives the new logical session no fresh map branch. Compact launch receipts now include exact-generation current-session starting locations without evidence-body or catalog reads, or explicit unavailability/whole-row omission. New completion batches freeze bounded same-session location rows at admission and render them after outcomes within the shared budget, alongside the optional current-session map command. Legacy absent/request-only packets retain their text; source/npm SDK verifies an explicitly requested result read from a terminal row. This closes representative navigation acceptance, not the release. Neither captured locations nor the request execute inspection automatically or grant copied/forked history fresh ownership. Legacy batches keep their original text; no full map is embedded into immutable outcomes. Compact/default inspection, curated previews and timing/work/native authority are unchanged.
|
|
178
180
|
|
|
179
|
-
Compact spawn/asynchronous-tool launch receipts use bare block YAML with nested file-location data and identify an existing Run reuse instead of implying another launch. Full `
|
|
181
|
+
Compact spawn/asynchronous-tool launch receipts use bare block YAML with nested file-location data and identify an existing Run reuse instead of implying another launch. Full `actors_run_spawn verbose=true` metadata remains JSON during the staged rollout; private details and input schemas keep their machine formats. When a complete direct file-location row is shown, it replaces the redundant session-wide map hint; missing, unavailable or omitted rows retain that optional investigation command. Exact references, expert details, shared bounds and saved completion-packet text are preserved.
|
|
180
182
|
|
|
181
183
|
New automatic completion packets show direct `omitted:N chars` fields for positive tracked primary/diagnostic loss, counted in Unicode code points after privacy projection. Full retained references and older saved packet text stay unchanged. This does not yet cover every legacy summary/body or attention clipping path, nor grant broader output publication.
|
|
182
184
|
|
|
@@ -186,14 +188,14 @@ Runtime argument guidance stops at the execution handoff. Failures after an exec
|
|
|
186
188
|
|
|
187
189
|
An exception does not guarantee an unapplied action: actor-local delivery can fail after handling is recorded, and retirement can fail after data has moved or been removed. Use existing owned evidence rather than replaying the action. The [partial-failure evidence map](docs/run-supervision.md#control-partial-failure-evidence-existing-facts-not-a-new-receipt-contract) distinguishes these paths from still-gated native stop receipts.
|
|
188
190
|
|
|
189
|
-
Controls persist to `controls.jsonl` before transport. One token-owned lock rejects a 65th pending Control or 1 MiB rewrite before admission, fails closed on malformed or stale-generation evidence, and atomically admits one record. Canonical transitions retain a 128-terminal tail, expected-state fencing, and 4 KiB errors. Admitted nonterminal Controls never expire automatically. Every record carries immutable `run_instance_id`. `
|
|
191
|
+
Controls persist to `controls.jsonl` before transport. One token-owned lock rejects a 65th pending Control or 1 MiB rewrite before admission, fails closed on malformed or stale-generation evidence, and atomically admits one record. Canonical transitions retain a 128-terminal tail, expected-state fencing, and 4 KiB errors. Admitted nonterminal Controls never expire automatically. Every record carries immutable `run_instance_id`. `actors_inspect view=control` reports pending capacity, saturation, stale work, journal bytes, and bounded diagnostics.
|
|
190
192
|
|
|
191
|
-
Runtime-owned lifecycle and retention actions use the same `
|
|
193
|
+
Runtime-owned lifecycle and retention actions use the same `actors_control_apply` tool:
|
|
192
194
|
|
|
193
195
|
```text
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
actors_control_apply target=run:<id> action=kill
|
|
197
|
+
actors_control_apply target=run:<id> action=archive
|
|
198
|
+
actors_control_apply target=run:<id> action=prune input={"preserve_artifacts":true}
|
|
197
199
|
```
|
|
198
200
|
|
|
199
201
|
Compact Control replies report an understandable action stage instead of dumping status objects: queued/delivered is not handled; archive/prune success is complete; a kill attempt leaves full termination explicitly unconfirmed. Only a relevant evidence hint is shown (Control for actor delivery, Trace for a stop attempt, owned inventory after identity replacement); already-terminal refusal needs no routine follow-up. Use `verbose: true` on the original request for full JSON, not a repeated destructive action. Changed-owner/generation refusals also omit the replacement status from full/verbose replies; verbosity grants no extra scope. Same-identity receipt detail remains available. Actor-local dispatch exceptions still fail, conservatively leaving delivery, handling and effects unconfirmed; do not repeat automatically. Their optional follow-up starts at fresh owned inventory, not a copied stale Control id/generation. Raw causes remain Kernel-local even with verbose; oversized request targets are wholly omitted with explicit coverage under 16 KiB. This presentation/curation does not change the legacy stop engine or prove closure.
|
package/dist/lib/draft-sleep.js
CHANGED
|
@@ -390,7 +390,7 @@ export function createDraftSleepScheduler(deps) {
|
|
|
390
390
|
...existing,
|
|
391
391
|
failedStage: processed.stage ?? "result_processing",
|
|
392
392
|
lastError: processed.error ?? "automatic draft review processing failed",
|
|
393
|
-
nextAction: "
|
|
393
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"draft\"}",
|
|
394
394
|
phase: "processing_failed",
|
|
395
395
|
processingAttempts: (existing.processingAttempts ?? 0) + 1,
|
|
396
396
|
updatedAt: now().toISOString(),
|
|
@@ -401,7 +401,7 @@ export function createDraftSleepScheduler(deps) {
|
|
|
401
401
|
...existing,
|
|
402
402
|
failedStage: processed.stage ?? "review_result",
|
|
403
403
|
lastError: processed.error ?? "automatic draft reviewer failed",
|
|
404
|
-
nextAction: "
|
|
404
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"draft\"}",
|
|
405
405
|
phase: "failed",
|
|
406
406
|
runId: undefined,
|
|
407
407
|
updatedAt: now().toISOString(),
|
|
@@ -443,7 +443,7 @@ export function createDraftSleepScheduler(deps) {
|
|
|
443
443
|
attempts: (prior?.batchId === batch.batchId ? prior.attempts : 0) + 1,
|
|
444
444
|
failedStage: "review_launch",
|
|
445
445
|
lastError: error instanceof Error ? error.message : String(error),
|
|
446
|
-
nextAction: "
|
|
446
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"draft\"}",
|
|
447
447
|
batchId: batch.batchId,
|
|
448
448
|
inputPath: batch.inputPath,
|
|
449
449
|
phase: "failed",
|
|
@@ -207,6 +207,9 @@ export function createActorExtensionRuntime(pi, supervisionPorts, completionStre
|
|
|
207
207
|
parentSession = scopeBinding;
|
|
208
208
|
closeSupervision();
|
|
209
209
|
recipeResolutionContextsBySession.set(sessionId, RecipeResolution.createEmptyRecipeResolutionContext(sessionId, ctx.cwd));
|
|
210
|
+
// Deferred compatibility dispatch needs activation only after the host binds its ports.
|
|
211
|
+
// Its loadout hook hides declarations; explicit later loadout restrictions still win.
|
|
212
|
+
pi.setActiveTools([...new Set([...pi.getActiveTools(), ...Object.keys(Tools.LEGACY_TOOL_NAMES)])]);
|
|
210
213
|
ctx.ui.setWidget("zz-pi-actors-comms", undefined);
|
|
211
214
|
activeRunContext = ctx;
|
|
212
215
|
activeRunOwnerId = sessionId;
|
|
@@ -229,7 +232,7 @@ export function createActorExtensionRuntime(pi, supervisionPorts, completionStre
|
|
|
229
232
|
recipeReload.watch(ctx);
|
|
230
233
|
},
|
|
231
234
|
registerCoreTools() {
|
|
232
|
-
|
|
235
|
+
const definitions = Tools.createCoreActorToolDefinitions({
|
|
233
236
|
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
234
237
|
getActiveTools: () => pi.getActiveTools(),
|
|
235
238
|
getRecipeResolutionContext: () => activeRunContext
|
|
@@ -240,7 +243,9 @@ export function createActorExtensionRuntime(pi, supervisionPorts, completionStre
|
|
|
240
243
|
handleRuntimeControl: automaticReview.handleControl,
|
|
241
244
|
registryRuntime: runtime,
|
|
242
245
|
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
243
|
-
})
|
|
246
|
+
});
|
|
247
|
+
const legacy = Tools.createLegacyActorToolDefinitions(definitions);
|
|
248
|
+
Pi.registerToolDefinitions(pi, [...definitions, ...legacy].map(withCurrentThinkingContext));
|
|
244
249
|
},
|
|
245
250
|
};
|
|
246
251
|
}
|
package/dist/lib/prompts.d.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* Owns LLM-facing descriptions, prompt snippets, guidelines, and parameter descriptions
|
|
5
5
|
*/
|
|
6
6
|
export declare const REGISTER_TOOL_DESCRIPTION: string;
|
|
7
|
-
export declare const REGISTER_TOOL_PROMPT_SNIPPET = "Persist command templates or Recipes with
|
|
7
|
+
export declare const REGISTER_TOOL_PROMPT_SNIPPET = "Persist command templates or Recipes with actors_tool_register; use expose=false to retain without tool activation";
|
|
8
8
|
export declare const REGISTER_TOOL_GUIDELINES: string[];
|
|
9
|
-
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors Skill routing:\n- Treat active bundled Skills as the operating authority.\n- For non-trivial pi-actors operation, diagnosis, or development, load and read the actors Skill before acting.\n- For work requiring multiple actors or subagents, additionally load and read the swarm Skill.\n- For capability-specific selection or constraints, load the owning capability Skill; actors owns generic mechanics and swarm owns multi-actor methodology.\n- Keep a Skill Recipe distinct from a registered tool and a Recipe spawn distinct from registered-tool invocation; actors owns the proof rules.\n- Treat persistence or registration as distinct from current callability; actors owns activation proof.\n- On failure or disagreement, preserve the logical Recipe identity, stop, and follow actors diagnosis; never bypass the owning Skills with copied contracts, helper paths, shell evaluation, or background-process workarounds.\n- If a capability Skill conflicts with actors about generic mechanics, follow actors and report the stale capability guidance.\n- README and docs are human-facing references, not the normal agent operating path.\n- AGENTS, source, and tests are implementation protocol and evidence; use them when changing or debugging the extension, not as substitutes for operating Skills.";
|
|
9
|
+
export declare const ONBOARDING_SYSTEM_PROMPT = "pi-actors Skill routing:\n- Treat active bundled Skills as the operating authority.\n- Route actors_tool_register, actors_run_spawn, actors_control_apply, and actors_inspect to the actors Skill; use canonical tool names for new calls.\n- For non-trivial pi-actors operation, diagnosis, or development, load and read the actors Skill before acting.\n- For work requiring multiple actors or subagents, additionally load and read the swarm Skill.\n- For capability-specific selection or constraints, load the owning capability Skill; actors owns generic mechanics and swarm owns multi-actor methodology.\n- Keep a Skill Recipe distinct from a registered tool and a Recipe spawn distinct from registered-tool invocation; actors owns the proof rules.\n- Treat persistence or registration as distinct from current callability; actors owns activation proof.\n- On failure or disagreement, preserve the logical Recipe identity, stop, and follow actors diagnosis; never bypass the owning Skills with copied contracts, helper paths, shell evaluation, or background-process workarounds.\n- If a capability Skill conflicts with actors about generic mechanics, follow actors and report the stale capability guidance.\n- README and docs are human-facing references, not the normal agent operating path.\n- AGENTS, source, and tests are implementation protocol and evidence; use them when changing or debugging the extension, not as substitutes for operating Skills.";
|
|
10
10
|
export declare const REGISTER_TOOL_PARAM_DESCRIPTIONS: {
|
|
11
|
-
readonly name: "Tool name in snake_case (e.g., '
|
|
11
|
+
readonly name: "Tool name in snake_case under its functional owner's namespace (e.g., 'audio_transcribe' or 'music_player'); keep existing persisted names when updating";
|
|
12
12
|
readonly description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.";
|
|
13
13
|
readonly from: "Recipe to specialize by canonical <skill>/<recipe> identity or explicit .json/.md path. Inherits async, args/types, source defaults, artifacts, Control, and runtime origins.";
|
|
14
14
|
readonly defaults: "Optional caller-owned defaults. Keys and values must satisfy the effective source or command-template argument contract.";
|
package/dist/lib/prompts.js
CHANGED
|
@@ -7,18 +7,19 @@ export const REGISTER_TOOL_DESCRIPTION = "Register a persistent custom tool from
|
|
|
7
7
|
"Use from for Recipe specialization, template for trusted commands, or draft for promotion. " +
|
|
8
8
|
"Set expose=false to retain a new template/from Recipe without tool activation. " +
|
|
9
9
|
"Definitions persist under ~/.pi/agent/recipes; activation is reported separately.";
|
|
10
|
-
export const REGISTER_TOOL_PROMPT_SNIPPET = "Persist command templates or Recipes with
|
|
10
|
+
export const REGISTER_TOOL_PROMPT_SNIPPET = "Persist command templates or Recipes with actors_tool_register; use expose=false to retain without tool activation";
|
|
11
11
|
export const REGISTER_TOOL_GUIDELINES = [
|
|
12
|
-
"Use
|
|
13
|
-
"Use
|
|
14
|
-
"Use
|
|
15
|
-
"A
|
|
16
|
-
"When
|
|
17
|
-
'Set template=null or template="" in
|
|
18
|
-
"Set update=true in
|
|
12
|
+
"Use actors_tool_register from=<skill>/<recipe> with defaults={...} to specialize a maintained Recipe without copying its contract.",
|
|
13
|
+
"Use actors_tool_register template only for trusted command templates, and use actors_tool_register draft only for captured draft promotion.",
|
|
14
|
+
"Use actors_tool_register expose=false to retain a new named template/from Recipe without activation; update, delete and draft promotion are unsupported in this mode.",
|
|
15
|
+
"A actors_tool_register retained_recipe receipt with activation=not_requested is deliberate retention, not failed activation; reuse its exact Recipe path through actors_run_spawn.",
|
|
16
|
+
"When actors_tool_register requests tool exposure, trust callable_now and activation; persistence alone is not callability.",
|
|
17
|
+
'Set template=null or template="" in actors_tool_register to delete a persisted tool.',
|
|
18
|
+
"Set update=true in actors_tool_register to overwrite an existing tool registration.",
|
|
19
19
|
];
|
|
20
20
|
export const ONBOARDING_SYSTEM_PROMPT = `pi-actors Skill routing:
|
|
21
21
|
- Treat active bundled Skills as the operating authority.
|
|
22
|
+
- Route actors_tool_register, actors_run_spawn, actors_control_apply, and actors_inspect to the actors Skill; use canonical tool names for new calls.
|
|
22
23
|
- For non-trivial pi-actors operation, diagnosis, or development, load and read the actors Skill before acting.
|
|
23
24
|
- For work requiring multiple actors or subagents, additionally load and read the swarm Skill.
|
|
24
25
|
- For capability-specific selection or constraints, load the owning capability Skill; actors owns generic mechanics and swarm owns multi-actor methodology.
|
|
@@ -29,7 +30,7 @@ export const ONBOARDING_SYSTEM_PROMPT = `pi-actors Skill routing:
|
|
|
29
30
|
- README and docs are human-facing references, not the normal agent operating path.
|
|
30
31
|
- AGENTS, source, and tests are implementation protocol and evidence; use them when changing or debugging the extension, not as substitutes for operating Skills.`;
|
|
31
32
|
export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
|
|
32
|
-
name: "Tool name in snake_case (e.g., '
|
|
33
|
+
name: "Tool name in snake_case under its functional owner's namespace (e.g., 'audio_transcribe' or 'music_player'); keep existing persisted names when updating",
|
|
33
34
|
description: "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.",
|
|
34
35
|
from: "Recipe to specialize by canonical <skill>/<recipe> identity or explicit .json/.md path. Inherits async, args/types, source defaults, artifacts, Control, and runtime origins.",
|
|
35
36
|
defaults: "Optional caller-owned defaults. Keys and values must satisfy the effective source or command-template argument contract.",
|
|
@@ -72,7 +72,7 @@ export function inventoryActiveSkillRecipeComponents(context) {
|
|
|
72
72
|
for (const [skill, roots] of Object.entries(context.namespaces)) {
|
|
73
73
|
if (roots.length > 1) {
|
|
74
74
|
rejected.push({
|
|
75
|
-
reason: `Duplicate active Skill identity ${skill} (${roots.length} active definitions); exact Recipe resolution is ambiguous. Next:
|
|
75
|
+
reason: `Duplicate active Skill identity ${skill} (${roots.length} active definitions); exact Recipe resolution is ambiguous. Next: actors_inspect target=recipes view=doctor identity=${skill}/<recipe>`,
|
|
76
76
|
skill,
|
|
77
77
|
});
|
|
78
78
|
continue;
|
|
@@ -175,11 +175,11 @@ function qualifiedRecipeNameForFile(file, context) {
|
|
|
175
175
|
}
|
|
176
176
|
function assertRecipeReferencePrefixWasNotRemoved(value) {
|
|
177
177
|
if (value.startsWith("std:")) {
|
|
178
|
-
throw new Error("std: Recipe references were removed in pi-actors 0.46. Use <skill>/<recipe> or an explicit .json/.md file path. Next:
|
|
178
|
+
throw new Error("std: Recipe references were removed in pi-actors 0.46. Use <skill>/<recipe> or an explicit .json/.md file path. Next: actors_inspect target=recipes view=status to identify the active owning Skill.");
|
|
179
179
|
}
|
|
180
180
|
if (value.startsWith("skill:")) {
|
|
181
181
|
const identity = value.slice("skill:".length);
|
|
182
|
-
throw new Error(`skill: Recipe references were removed in pi-actors 0.46. Use ${identity} without a prefix. Next:
|
|
182
|
+
throw new Error(`skill: Recipe references were removed in pi-actors 0.46. Use ${identity} without a prefix. Next: actors_inspect target=recipes view=doctor identity=${identity}`);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
function skillRecipeCandidates(value, context) {
|
|
@@ -189,14 +189,14 @@ function skillRecipeCandidates(value, context) {
|
|
|
189
189
|
const [, skillName, recipeName] = match;
|
|
190
190
|
const roots = [...(context.namespaces[skillName] ?? [])];
|
|
191
191
|
if (roots.length === 0)
|
|
192
|
-
throw new Error(`Active Skill Recipe not found: ${skillName}/${recipeName}. Owning Skill "${skillName}" is not active. Next: activate Skill ${skillName}, then
|
|
192
|
+
throw new Error(`Active Skill Recipe not found: ${skillName}/${recipeName}. Owning Skill "${skillName}" is not active. Next: activate Skill ${skillName}, then actors_inspect target=recipes view=doctor identity=${skillName}/${recipeName}`);
|
|
193
193
|
if (roots.length > 1)
|
|
194
|
-
throw new Error(`Duplicate active Skill identity ${skillName} (${roots.length} active definitions); ${skillName}/${recipeName} cannot resolve unambiguously. Next:
|
|
194
|
+
throw new Error(`Duplicate active Skill identity ${skillName} (${roots.length} active definitions); ${skillName}/${recipeName} cannot resolve unambiguously. Next: actors_inspect target=recipes view=doctor identity=${skillName}/${recipeName}`);
|
|
195
195
|
const root = roots[0];
|
|
196
196
|
const candidates = ["json", "md"].map((extension) => resolve(root, `${recipeName}.${extension}`));
|
|
197
197
|
const existing = candidates.filter((candidate) => existsSync(candidate));
|
|
198
198
|
if (existing.length === 0) {
|
|
199
|
-
throw new Error(`Skill Recipe component not found: ${skillName}/${recipeName}; owning Skill "${skillName}" is active. Next:
|
|
199
|
+
throw new Error(`Skill Recipe component not found: ${skillName}/${recipeName}; owning Skill "${skillName}" is active. Next: actors_inspect target=recipes view=doctor identity=${skillName}/${recipeName}`);
|
|
200
200
|
}
|
|
201
201
|
if (existing.length > 1) {
|
|
202
202
|
throw new Error(`Skill Recipe stem collision: ${skillName}/${recipeName} has both .json and .md files`);
|
|
@@ -227,7 +227,7 @@ function findOwningSkillDir(recipeFile) {
|
|
|
227
227
|
function assertRecipeHasNoDeclaredName(raw) {
|
|
228
228
|
if (!Object.hasOwn(raw, "name"))
|
|
229
229
|
return;
|
|
230
|
-
throw new Error("Recipe.name was removed in pi-actors 0.46. File-backed Recipe identity is derived from its file name. Next: remove the name field, keep the intended filename stem, then
|
|
230
|
+
throw new Error("Recipe.name was removed in pi-actors 0.46. File-backed Recipe identity is derived from its file name. Next: remove the name field, keep the intended filename stem, then actors_inspect target=recipes view=doctor.");
|
|
231
231
|
}
|
|
232
232
|
function assertRuntimeRecipeValuesNotDeclared(raw) {
|
|
233
233
|
const args = Array.isArray(raw.args)
|
package/dist/lib/registry.js
CHANGED
|
@@ -43,7 +43,7 @@ function listTools(deps) {
|
|
|
43
43
|
content: [
|
|
44
44
|
textContent(ExecutionOutput.formatToolText(registryListText(rows))),
|
|
45
45
|
],
|
|
46
|
-
details: { tool: "
|
|
46
|
+
details: { tool: "actors_tool_register" },
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
function getRecipeRoot(deps) {
|
|
@@ -65,18 +65,18 @@ function activationBoundary(activation) {
|
|
|
65
65
|
}
|
|
66
66
|
function recipeDoctorAction(source) {
|
|
67
67
|
return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(source)
|
|
68
|
-
? `
|
|
69
|
-
: "
|
|
68
|
+
? `actors_inspect target=recipes view=doctor identity=${source}`
|
|
69
|
+
: "actors_inspect target=recipes view=doctor";
|
|
70
70
|
}
|
|
71
71
|
function registrationNextActions(tool, source, callableNow) {
|
|
72
72
|
if (callableNow) {
|
|
73
73
|
return [
|
|
74
74
|
`call tool ${tool}`,
|
|
75
|
-
`
|
|
75
|
+
`actors_inspect target=tool:${tool} view=status`,
|
|
76
76
|
];
|
|
77
77
|
}
|
|
78
78
|
return [
|
|
79
|
-
`
|
|
79
|
+
`actors_inspect target=tool:${tool} view=status`,
|
|
80
80
|
...(source === "template" || source === "draft"
|
|
81
81
|
? []
|
|
82
82
|
: [recipeDoctorAction(source)]),
|
|
@@ -134,7 +134,7 @@ function registrationReceiptText(data) {
|
|
|
134
134
|
const omittedNext = omission("next_action", 1, !nextWellFormed);
|
|
135
135
|
// This producer knows only its existing status hint; invocation advice stays opaque.
|
|
136
136
|
const next = part(nextWellFormed
|
|
137
|
-
? data.callable ? { instruction: data.next } : { next: { tool: "
|
|
137
|
+
? data.callable ? { instruction: data.next } : { next: { tool: "actors_inspect", target: `tool:${data.tool}`, view: "status" } }
|
|
138
138
|
: omittedNext, omittedNext);
|
|
139
139
|
const render = () => renderPart({ ...tool.selected, result: data.result, ...source.selected, callable_now: data.callable,
|
|
140
140
|
...(data.boundary === undefined ? {} : { activation_boundary: data.boundary }), ...required.selected, ...optional.selected,
|
|
@@ -195,7 +195,7 @@ function draftPathFailureText(kind, reference) {
|
|
|
195
195
|
const selected = registrySelected(kind === "root" ? "allowed_root" : "path", reference);
|
|
196
196
|
const render = () => ({ field: "draft", ...selected.selected,
|
|
197
197
|
result: kind === "root" ? "Draft must be under the allowed root." : "Draft not found.",
|
|
198
|
-
...(kind === "root" ? { instruction: "Use
|
|
198
|
+
...(kind === "root" ? { instruction: "Use actors_inspect target=recipes view=summary to list drafts." } : {}) });
|
|
199
199
|
return growRegistryDocument(render, [selected]);
|
|
200
200
|
}
|
|
201
201
|
function assertDraftPath(deps, draft) {
|
|
@@ -226,8 +226,8 @@ function draftReceiptText(result, tool, callable, next) {
|
|
|
226
226
|
const identity = part("tool", tool, { tool });
|
|
227
227
|
const flag = part("callable_now", callable, { callable_now: callable });
|
|
228
228
|
// Only this producer's exact canonical read-only status hint is typed, never body advice.
|
|
229
|
-
const status = /^
|
|
230
|
-
const hint = part("next_action", next, status ? { next: { tool: "
|
|
229
|
+
const status = /^actors_inspect target=(tool:[a-z][a-z0-9_]{0,63}) view=status$/u.exec(next);
|
|
230
|
+
const hint = part("next_action", next, status ? { next: { tool: "actors_inspect", target: status[1], view: "status" } } : { instruction: next });
|
|
231
231
|
const render = () => renderPart({ ...identity.selected, result, source: "draft", ...flag.selected, ...hint.selected });
|
|
232
232
|
for (const current of [identity, flag, hint]) {
|
|
233
233
|
if (current.selected === current.full)
|
|
@@ -341,6 +341,9 @@ function deleteTool(name, ctx, deps) {
|
|
|
341
341
|
details: { tool: name },
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
|
+
const blocker = deps.getToolNameBlocker(name);
|
|
345
|
+
if (blocker)
|
|
346
|
+
throw new Error(ExecutionOutput.formatToolText(blocker));
|
|
344
347
|
const recipePath = getToolRecipePath(deps, name);
|
|
345
348
|
if (existsSync(recipePath))
|
|
346
349
|
unlinkSync(recipePath);
|
|
@@ -390,7 +393,7 @@ function assertTemplateIsNotNestedRecipe(value) {
|
|
|
390
393
|
(Object.keys(record).length === 1 ||
|
|
391
394
|
["artifacts", "async", "control", "description", "imports", "values"].some((key) => Object.hasOwn(record, key)))) {
|
|
392
395
|
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "template",
|
|
393
|
-
result: "Nested Recipe-shaped templates are not a
|
|
396
|
+
result: "Nested Recipe-shaped templates are not a actors_tool_register source mode.",
|
|
394
397
|
instruction: "retry with from=<skill>/<recipe> and defaults={...}." })));
|
|
395
398
|
}
|
|
396
399
|
}
|
|
@@ -437,7 +440,7 @@ function normalizeDefaults(value) {
|
|
|
437
440
|
function normalizeFromReference(value, resolutionContext) {
|
|
438
441
|
const trimmed = value.trim();
|
|
439
442
|
if (!trimmed) {
|
|
440
|
-
throw new Error(ExecutionOutput.formatToolText("Tool from must be a non-empty Recipe reference. Next: retry
|
|
443
|
+
throw new Error(ExecutionOutput.formatToolText("Tool from must be a non-empty Recipe reference. Next: retry actors_tool_register with from=<skill>/<recipe> or an explicit .json/.md path."));
|
|
441
444
|
}
|
|
442
445
|
if (trimmed.startsWith("std:")) {
|
|
443
446
|
throw new Error(ExecutionOutput.formatToolText("std: Recipe references were removed; use <skill>/<recipe> or an explicit .json/.md file path."));
|
|
@@ -472,7 +475,7 @@ function buildAuthoredRecipe(input, existing, existingRecipe) {
|
|
|
472
475
|
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "defaults",
|
|
473
476
|
...registrySelected("key", runtimeOwnedDefault).selected,
|
|
474
477
|
result: "Tool defaults cannot set runtime-owned input.",
|
|
475
|
-
instruction: "remove that default and retry
|
|
478
|
+
instruction: "remove that default and retry actors_tool_register; the runtime supplies it." })));
|
|
476
479
|
}
|
|
477
480
|
const authored = {
|
|
478
481
|
...(description ? { description } : {}),
|
|
@@ -522,9 +525,9 @@ function buildAuthoredRecipe(input, existing, existingRecipe) {
|
|
|
522
525
|
function assertRegisterToolInputModes(input) {
|
|
523
526
|
const record = input;
|
|
524
527
|
if (Object.hasOwn(record, "values")) {
|
|
525
|
-
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "values", result: "
|
|
528
|
+
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "values", result: "actors_tool_register values was removed.",
|
|
526
529
|
replacement_fields: ["defaults", "from"],
|
|
527
|
-
instruction: "retry
|
|
530
|
+
instruction: "retry actors_tool_register with defaults for caller inputs, or use from=<skill>/<recipe> for maintained composition." })));
|
|
528
531
|
}
|
|
529
532
|
const fromProvided = Object.hasOwn(record, "from");
|
|
530
533
|
if (fromProvided &&
|
|
@@ -532,22 +535,22 @@ function assertRegisterToolInputModes(input) {
|
|
|
532
535
|
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "from",
|
|
533
536
|
result: "Tool from must be a non-empty Recipe reference.",
|
|
534
537
|
reference_forms: ["<skill>/<recipe>", "explicit .json path", "explicit .md path"],
|
|
535
|
-
instruction: "retry
|
|
538
|
+
instruction: "retry actors_tool_register with from=<skill>/<recipe> or an explicit .json/.md path." })));
|
|
536
539
|
}
|
|
537
540
|
const draftProvided = typeof input.draft === "string" && input.draft.trim().length > 0;
|
|
538
541
|
const templateProvided = Object.hasOwn(record, "template");
|
|
539
542
|
const modeCount = Number(fromProvided) + Number(draftProvided) + Number(templateProvided);
|
|
540
543
|
if (modeCount > 1) {
|
|
541
|
-
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ result: "Use exactly one
|
|
544
|
+
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ result: "Use exactly one actors_tool_register source mode.",
|
|
542
545
|
source_modes: ["from", "template", "draft"],
|
|
543
|
-
instruction: "remove the extra source fields and retry
|
|
546
|
+
instruction: "remove the extra source fields and retry actors_tool_register." })));
|
|
544
547
|
}
|
|
545
548
|
if (fromProvided &&
|
|
546
549
|
(Object.hasOwn(record, "args") || Object.hasOwn(record, "async"))) {
|
|
547
550
|
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ field: "from",
|
|
548
|
-
result: "
|
|
551
|
+
result: "actors_tool_register from inherits args and async behavior.",
|
|
549
552
|
inherited_fields: ["args", "async"],
|
|
550
|
-
instruction: "retry
|
|
553
|
+
instruction: "retry actors_tool_register with from and optional defaults only; omit args and async." })));
|
|
551
554
|
}
|
|
552
555
|
}
|
|
553
556
|
function reservedRegistrationNameText(value) {
|
|
@@ -613,7 +616,7 @@ function executeRegisterToolUnlocked(params, ctx, deps) {
|
|
|
613
616
|
if (template === undefined && !input.from && !existing) {
|
|
614
617
|
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ result: "New registration requires exactly one source mode.",
|
|
615
618
|
source_modes: ["from", "template", "draft"],
|
|
616
|
-
instruction: "retry
|
|
619
|
+
instruction: "retry actors_tool_register with one source field." })));
|
|
617
620
|
}
|
|
618
621
|
const recipePath = getToolRecipePath(deps, name);
|
|
619
622
|
const authoredRecipe = buildAuthoredRecipe(input, existing, authoritative.recipe);
|
|
@@ -645,7 +648,7 @@ function executeRegisterToolUnlocked(params, ctx, deps) {
|
|
|
645
648
|
(!activation.host_registered ||
|
|
646
649
|
!activation.active_tool ||
|
|
647
650
|
!activation.callable_now)) {
|
|
648
|
-
throw new Error(`Tool "${name}" activation failed at ${activationBoundary(activation)}; host_registered=${activation.host_registered}, active_tool=${activation.active_tool}, callable_now=${activation.callable_now}. Registration was rolled back. Next:
|
|
651
|
+
throw new Error(`Tool "${name}" activation failed at ${activationBoundary(activation)}; host_registered=${activation.host_registered}, active_tool=${activation.active_tool}, callable_now=${activation.callable_now}. Registration was rolled back. Next: actors_inspect target=runtime view=status; do not use spawn as proof of tool invocation.`);
|
|
649
652
|
}
|
|
650
653
|
}
|
|
651
654
|
catch (error) {
|
|
@@ -872,11 +875,11 @@ function retainRecipe(input, ctx, deps) {
|
|
|
872
875
|
const facts = { kind: "retained_recipe", result: "retained", persisted: true, resolved: true,
|
|
873
876
|
validated: true, activation: "not_requested", async: admitted.async };
|
|
874
877
|
const text = growRegistryDocument(() => ({ ...selectedName.selected, ...facts, ...recipe.selected, ...selectedSource.selected,
|
|
875
|
-
...required.selected, ...optional.selected, next: { tool: "
|
|
878
|
+
...required.selected, ...optional.selected, next: { tool: "actors_inspect", target: "recipes", view: "summary", verbose: true } }), [recipe, selectedName, selectedSource, required, optional]);
|
|
876
879
|
const result = {
|
|
877
880
|
content: [textContent(ExecutionOutput.formatToolText(text))],
|
|
878
881
|
details: { ...facts, tool: name, recipe: path, source, required_args: args.required, optional_args: args.optional,
|
|
879
|
-
next_actions: ["
|
|
882
|
+
next_actions: ["actors_inspect target=recipes view=summary verbose=true"] },
|
|
880
883
|
};
|
|
881
884
|
stage = "release";
|
|
882
885
|
return result;
|
|
@@ -888,7 +891,7 @@ function retainRecipe(input, ctx, deps) {
|
|
|
888
891
|
const reason = error instanceof RetainedRecipeRefusal ? { reason: error.reason } : {};
|
|
889
892
|
throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ operation: "retain", stage, result: "unconfirmed",
|
|
890
893
|
effects: "unknown; inspect retained inventory before retry", ...reason,
|
|
891
|
-
next: { tool: "
|
|
894
|
+
next: { tool: "actors_inspect", target: "recipes", view: "summary", verbose: true } })), { cause: error });
|
|
892
895
|
}
|
|
893
896
|
}
|
|
894
897
|
export async function executeRegisterTool(params, ctx, deps) {
|
|
@@ -90,7 +90,7 @@ export function controlAutomaticReview(action, scope, options = {}) {
|
|
|
90
90
|
});
|
|
91
91
|
if (result.changed === true)
|
|
92
92
|
schedule(scope, options);
|
|
93
|
-
const nextActions = ["
|
|
93
|
+
const nextActions = ["actors_inspect target=recipes view=reviews"];
|
|
94
94
|
if (action === "review.retry" &&
|
|
95
95
|
scope === "tool" &&
|
|
96
96
|
(result.phase === "approved" || result.phase === "lineage_pending"))
|
|
@@ -12,8 +12,8 @@ export const TRACE_COMPACTION_VERSION = 1;
|
|
|
12
12
|
const PENDING_CONTROL_STATUSES = new Set(["queued", "delivered", "claimed"]);
|
|
13
13
|
const TERMINAL_CONTROL_STATUSES = new Set(["handled", "failed"]);
|
|
14
14
|
const CONTROL_ADMISSION_NEXT_ACTIONS = [
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"actors_inspect target=run:<id> view=control",
|
|
16
|
+
"actors_inspect target=runtime view=triage",
|
|
17
17
|
];
|
|
18
18
|
function isCount(value) {
|
|
19
19
|
return Number.isSafeInteger(value) && value >= 0;
|
package/dist/lib/runtime.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ export interface ToolRegistryRuntimeDeps {
|
|
|
27
27
|
getActiveTools?: () => string[];
|
|
28
28
|
getAllTools?: () => Array<{
|
|
29
29
|
name: string;
|
|
30
|
+
sourceInfo?: {
|
|
31
|
+
path: string;
|
|
32
|
+
};
|
|
30
33
|
}>;
|
|
31
34
|
registerTool: (definition: ReturnType<typeof ToolsLocal.createRuntimeToolDefinition>) => void;
|
|
32
35
|
reservedToolNames: Set<string>;
|