@llblab/pi-actors 0.58.1 → 0.58.3

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 (73) hide show
  1. package/AGENTS.md +3 -3
  2. package/BACKLOG.md +2 -2
  3. package/CHANGELOG.md +12 -0
  4. package/README.md +38 -36
  5. package/dist/lib/draft-sleep.js +3 -3
  6. package/dist/lib/extension-runtime.js +7 -2
  7. package/dist/lib/prompts.d.ts +3 -3
  8. package/dist/lib/prompts.js +10 -9
  9. package/dist/lib/recipes-references.js +7 -7
  10. package/dist/lib/registry.js +27 -24
  11. package/dist/lib/review-control.js +1 -1
  12. package/dist/lib/run-delivery.d.ts +1 -1
  13. package/dist/lib/run-delivery.js +41 -17
  14. package/dist/lib/run-evidence-policy.js +2 -2
  15. package/dist/lib/runtime.d.ts +3 -0
  16. package/dist/lib/runtime.js +24 -10
  17. package/dist/lib/tool-review-scheduler.js +7 -7
  18. package/dist/lib/tools-access.js +1 -1
  19. package/dist/lib/tools-inspect.js +10 -10
  20. package/dist/lib/tools-message.d.ts +1 -1
  21. package/dist/lib/tools-message.js +10 -10
  22. package/dist/lib/tools-register.d.ts +3 -3
  23. package/dist/lib/tools-register.js +4 -4
  24. package/dist/lib/tools-response.d.ts +1 -1
  25. package/dist/lib/tools-response.js +42 -20
  26. package/dist/lib/tools-spawn.js +1 -1
  27. package/dist/lib/tools.d.ts +7 -0
  28. package/dist/lib/tools.js +25 -0
  29. package/dist/skills/actors/SKILL.md +29 -21
  30. package/dist/skills/actors/references/diagnostics.md +11 -11
  31. package/dist/skills/actors/references/persistent-tools.md +19 -15
  32. package/dist/skills/actors/references/recipes.md +1 -1
  33. package/dist/skills/actors/references/runs.md +10 -10
  34. package/dist/skills/music-player/SKILL.md +4 -4
  35. package/dist/skills/music-player/scripts/playback.mjs +1 -1
  36. package/dist/skills/recipe-memory/SKILL.md +7 -7
  37. package/docs/README.md +2 -2
  38. package/docs/actor-inspector.md +1 -1
  39. package/docs/agent-experience.md +8 -8
  40. package/docs/async-runs.md +13 -13
  41. package/docs/capability-memory.md +6 -6
  42. package/docs/coordinator-delivery.md +2 -2
  43. package/docs/inspection.md +21 -21
  44. package/docs/output-projection-map.md +1 -1
  45. package/docs/run-supervision.md +21 -21
  46. package/docs/tool-registry.md +27 -19
  47. package/docs/windows-process-observation.md +1 -1
  48. package/lib/draft-sleep.ts +3 -3
  49. package/lib/extension-runtime.ts +7 -5
  50. package/lib/prompts.ts +10 -9
  51. package/lib/recipes-references.ts +7 -7
  52. package/lib/registry.ts +26 -24
  53. package/lib/review-control.ts +1 -1
  54. package/lib/run-delivery.ts +34 -17
  55. package/lib/run-evidence-policy.ts +2 -2
  56. package/lib/runtime.ts +26 -11
  57. package/lib/tool-review-scheduler.ts +7 -7
  58. package/lib/tools-access.ts +1 -1
  59. package/lib/tools-inspect.ts +10 -10
  60. package/lib/tools-message.ts +10 -10
  61. package/lib/tools-register.ts +4 -4
  62. package/lib/tools-response.ts +39 -20
  63. package/lib/tools-spawn.ts +1 -1
  64. package/lib/tools.ts +28 -0
  65. package/package.json +1 -1
  66. package/skills/actors/SKILL.md +29 -21
  67. package/skills/actors/references/diagnostics.md +11 -11
  68. package/skills/actors/references/persistent-tools.md +19 -15
  69. package/skills/actors/references/recipes.md +1 -1
  70. package/skills/actors/references/runs.md +10 -10
  71. package/skills/music-player/SKILL.md +4 -4
  72. package/skills/music-player/scripts/playback.mjs +1 -1
  73. package/skills/recipe-memory/SKILL.md +7 -7
@@ -6,20 +6,28 @@
6
6
  ~/.pi/agent/recipes/*.json
7
7
  ```
8
8
 
9
- Each valid Recipe admitted against the current session context can become an agent-callable tool. `register_tool` creates, updates, promotes, or deletes these files through fenced mutation paths and reports the resulting activation state.
9
+ Each valid Recipe admitted against the current session context can become an agent-callable tool. `actors_tool_register` creates, updates, promotes, or deletes these files through fenced mutation paths and reports the resulting activation state.
10
+
11
+ ## Public names and compatible dispatch
12
+
13
+ The advertised tools are `actors_tool_register`, `actors_run_spawn`, `actors_control_apply`, and `actors_inspect`. Explicit old `register_tool` / `spawn` / `message` / `inspect` calls dispatch through the same schemas and executors. Legacy entries are deferred and active for saved native calls, with their declarations hidden through Pi's loadout hook; normal callable listings omit deferred entries, while explicit search and host registered/active inventories can still expose compatibility metadata. A deliberately restricted host loadout/permission policy remains authoritative. No saved definition, call, Recipe filename/logical reference, launch kind or completion packet is rewritten. An already loaded host is not automatically reloaded.
14
+
15
+ New configurable tools use their functional owner's namespace in snake_case, for example `audio_transcribe`, `music_player`, or `repo_check`. This agent-facing naming guidance is not runtime ownership inference and does not impose a new Recipe field or reject/migrate legacy unprefixed names. Runtime admission refuses normalized collisions against all foreign host tools, including inactive tools; `update=true` does not override foreign ownership. Discovery leaves a colliding persisted Recipe intact but does not admit its exposure or report the foreign tool as that Recipe's callable activation.
16
+
17
+ Control applies Run/runtime actions, not agent chat. All existing envelopes, activation proof and Run semantics remain unchanged.
10
18
 
11
19
  ## Registration
12
20
 
13
21
  Register a command template:
14
22
 
15
23
  ```text
16
- register_tool name=repo_check template="make check" description="Run repository checks"
24
+ actors_tool_register name=repo_check template="make check" description="Run repository checks"
17
25
  ```
18
26
 
19
27
  Specialize a maintained Recipe without copying its contract:
20
28
 
21
29
  ```text
22
- register_tool name=music_player from=music-player/playback defaults={"source":"~/Music/1MIX"}
30
+ actors_tool_register name=music_player from=music-player/playback defaults={"source":"~/Music/1MIX"}
23
31
  ```
24
32
 
25
33
  `from`, `template`, and `draft` are distinct source modes. `from` accepts exact `<skill>/<recipe>` identity or an explicit `.json` / `.md` path and inherits async behavior, args/types, source defaults, artifacts, Control, and runtime-owned origins. `defaults` may set only effective caller-owned args and must satisfy their types. `template` is only for trusted command definitions; specialization accepts caller `defaults`, while authored composition values belong in a Recipe file.
@@ -27,24 +35,24 @@ register_tool name=music_player from=music-player/playback defaults={"source":"~
27
35
  Promote an immutable captured draft only with its draft path and explicit target name:
28
36
 
29
37
  ```text
30
- register_tool name=repo_check draft=~/.pi/agent/recipes/drafts/<captured-recipe>.json
38
+ actors_tool_register name=repo_check draft=~/.pi/agent/recipes/drafts/<captured-recipe>.json
31
39
  ```
32
40
 
33
41
  Delete a persisted definition through the same fenced mutation path:
34
42
 
35
43
  ```text
36
- register_tool name=repo_check template=null
44
+ actors_tool_register name=repo_check template=null
37
45
  ```
38
46
 
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.
47
+ 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. The runtime retains its observed host-declaration name/source-path provenance separately from active Recipe membership, allowing same-name recreation or restoration after temporary discovery loss. A different source-path replacement or normalized foreign name still blocks exposure, even inactive; namespacing is not ownership proof. This host metadata check cannot distinguish replacements with identical name/source metadata. `update=true` permits replacing an existing Recipe, never a foreign host tool. Invalid content fails before active mutation.
40
48
 
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.
49
+ 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 `actors_inspect target=recipes view=doctor identity=<skill>/<recipe>`; diagnose final activation, source, schema summary, and separate spawn/tool usage with `actors_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
50
 
43
51
  ## Retain a New Capability Without Tool Exposure
44
52
 
45
53
  ```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
54
+ actors_tool_register name=repo_check expose=false template="make check" description="Run repository checks"
55
+ actors_tool_register name=review_changes expose=false from=swarm/lens-review
48
56
  ```
49
57
 
50
58
  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.
@@ -54,10 +62,10 @@ Retained `from` stores compact logical/file delegation and inherits the effectiv
54
62
  Use the existing surfaces, supplying required caller values:
55
63
 
56
64
  ```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>
65
+ actors_tool_register name=changes_review from=swarm/lens-review expose=false
66
+ actors_inspect target=recipes view=summary verbose=true
67
+ actors_run_spawn recipe=<exact retained path> values=<caller-owned inputs>
68
+ actors_tool_register name=changes_review from=<exact retained path>
61
69
  ```
62
70
 
63
71
  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.
@@ -79,13 +87,13 @@ Skill component inventory is diagnostic and fail-soft: valid components remain l
79
87
  Inspect registry state with:
80
88
 
81
89
  ```text
82
- inspect target=recipes view=status
83
- inspect target=tool:<name> view=status
90
+ actors_inspect target=recipes view=status
91
+ actors_inspect target=tool:<name> view=status
84
92
  ```
85
93
 
86
94
  Recipe inspection reports generation, scan/watch state, active, shadowed, invalid, disabled, component rejection, diagnostic, risk, usage, and review evidence. Tool status reports current activation plus separate `tool_calls` and `spawn_calls`; tool schema reports the caller-owned capability contract. Inspect the complete target/view contract in [Management Inspection](./inspection.md). A registered tool is not a running actor.
87
95
 
88
- `spawn recipe=<name>` executes a Recipe and reports `launch_kind: "spawn"`; it does not prove that a registered tool was exposed or invoked. Registered-tool execution reports `launch_kind: "tool"`.
96
+ `actors_run_spawn recipe=<name>` executes a Recipe and reports `launch_kind: "spawn"`; it does not prove that a registered tool was exposed or invoked. Registered-tool execution reports `launch_kind: "tool"`.
89
97
 
90
98
  Async registered tools add runtime-owned optional invocation parameters after resolving the Recipe contract:
91
99
 
@@ -114,8 +122,8 @@ Automatic draft/tool review remains silent and mechanically fenced:
114
122
  Reserved recovery uses runtime Controls:
115
123
 
116
124
  ```text
117
- message target=runtime action=review.retry input={"scope":"draft"}
118
- message target=runtime action=review.reset input={"scope":"tool"}
125
+ actors_control_apply target=runtime action=review.retry input={"scope":"draft"}
126
+ actors_control_apply target=runtime action=review.reset input={"scope":"tool"}
119
127
  ```
120
128
 
121
129
  Retry preserves authenticated transaction recovery. Reset rejects evidence that requires roll-forward.
@@ -130,7 +138,7 @@ Retained launches use the parent Recipe root's existing `.usage` owner, not a se
130
138
 
131
139
  ## Specializing Existing Recipes
132
140
 
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.
141
+ Use `actors_tool_register 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.
134
142
 
135
143
  ## Safety
136
144
 
@@ -14,7 +14,7 @@ The goal is cheaper fresh observation with unchanged evidence strength, not a ca
14
14
 
15
15
  ### Bounded original-runner diagnostic
16
16
 
17
- The public SDK fixture now has a temporary observer, with failure-only output, for the first `overflow` Run in each layout driver. It reserves one constructor candidate through public `node:diagnostics_channel`, matches executable/full public arguments/state directory/PID at successful `spawn`, and retains that original object's `close` callback facts. Private receipt binding follows the unchanged durable capture and requires the exact frame object and immutable signature. An unrelated first candidate makes the diagnostic unavailable; there is no replacement search, spawn interception, product hook, extra query, timer or wait.
17
+ The public SDK fixture now has a temporary observer, with failure-only output, for the first `overflow` Run in each layout driver. It reserves one constructor candidate through public `node:diagnostics_channel`, matches executable/full public arguments/state directory/PID at successful `actors_run_spawn`, and retains that original object's `close` callback facts. Private receipt binding follows the unchanged durable capture and requires the exact frame object and immutable signature. An unrelated first candidate makes the diagnostic unavailable; there is no replacement search, spawn interception, product hook, extra query, timer or wait.
18
18
 
19
19
  Optional `failure_evidence.original_runner` freezes original callback facts at settlement catch entry, captures available public runtime fields before callback revocation, attempts owned teardown, then projects frozen observations with post-attempt teardown uncertainty. The catch-only `runtime_terminal_fields` category reads `exitCode` and `signalCode` at most once each from the same retained original object: safe-integer/null yields `exit_code_set`, null/recognized builtin OS signal yields `signal_code_set`, and null/null yields `null_pair`. Invalid, read-faulted, unbound, uncovered, released or changed-capture observations yield `unavailable`; no raw code, signal or private cause is projected. Getter/catch-clock reentrancy can invalidate this new category but cannot replace frozen entry callback facts.
20
20
 
@@ -497,7 +497,7 @@ export function createDraftSleepScheduler(
497
497
  ...existing,
498
498
  failedStage: processed.stage ?? "result_processing",
499
499
  lastError: processed.error ?? "automatic draft review processing failed",
500
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"draft\"}",
500
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"draft\"}",
501
501
  phase: "processing_failed",
502
502
  processingAttempts: (existing.processingAttempts ?? 0) + 1,
503
503
  updatedAt: now().toISOString(),
@@ -508,7 +508,7 @@ export function createDraftSleepScheduler(
508
508
  ...existing,
509
509
  failedStage: processed.stage ?? "review_result",
510
510
  lastError: processed.error ?? "automatic draft reviewer failed",
511
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"draft\"}",
511
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"draft\"}",
512
512
  phase: "failed",
513
513
  runId: undefined,
514
514
  updatedAt: now().toISOString(),
@@ -551,7 +551,7 @@ export function createDraftSleepScheduler(
551
551
  attempts: (prior?.batchId === batch.batchId ? prior.attempts : 0) + 1,
552
552
  failedStage: "review_launch",
553
553
  lastError: error instanceof Error ? error.message : String(error),
554
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"draft\"}",
554
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"draft\"}",
555
555
  batchId: batch.batchId,
556
556
  inputPath: batch.inputPath,
557
557
  phase: "failed",
@@ -250,6 +250,9 @@ export function createActorExtensionRuntime(
250
250
  sessionId,
251
251
  RecipeResolution.createEmptyRecipeResolutionContext(sessionId, ctx.cwd),
252
252
  );
253
+ // Deferred compatibility dispatch needs activation only after the host binds its ports.
254
+ // Its loadout hook hides declarations; explicit later loadout restrictions still win.
255
+ pi.setActiveTools([...new Set([...pi.getActiveTools(), ...Object.keys(Tools.LEGACY_TOOL_NAMES)])]);
253
256
  ctx.ui.setWidget("zz-pi-actors-comms", undefined);
254
257
  activeRunContext = ctx;
255
258
  activeRunOwnerId = sessionId;
@@ -270,9 +273,7 @@ export function createActorExtensionRuntime(
270
273
  recipeReload.watch(ctx);
271
274
  },
272
275
  registerCoreTools() {
273
- Pi.registerToolDefinitions(
274
- pi,
275
- Tools.createCoreActorToolDefinitions<Pi.ExtensionContext>({
276
+ const definitions = Tools.createCoreActorToolDefinitions<Pi.ExtensionContext>({
276
277
  configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
277
278
  getActiveTools: () => pi.getActiveTools(),
278
279
  getRecipeResolutionContext: () =>
@@ -289,8 +290,9 @@ export function createActorExtensionRuntime(
289
290
  handleRuntimeControl: automaticReview.handleControl,
290
291
  registryRuntime: runtime,
291
292
  setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
292
- }).map(withCurrentThinkingContext),
293
- );
293
+ });
294
+ const legacy = Tools.createLegacyActorToolDefinitions(definitions);
295
+ Pi.registerToolDefinitions(pi, [...definitions, ...legacy].map(withCurrentThinkingContext));
294
296
  },
295
297
  };
296
298
  }
package/lib/prompts.ts CHANGED
@@ -11,20 +11,21 @@ export const REGISTER_TOOL_DESCRIPTION =
11
11
  "Definitions persist under ~/.pi/agent/recipes; activation is reported separately.";
12
12
 
13
13
  export const REGISTER_TOOL_PROMPT_SNIPPET =
14
- "Persist command templates or Recipes with register_tool; use expose=false to retain without tool activation";
14
+ "Persist command templates or Recipes with actors_tool_register; use expose=false to retain without tool activation";
15
15
 
16
16
  export const REGISTER_TOOL_GUIDELINES = [
17
- "Use register_tool from=<skill>/<recipe> with defaults={...} to specialize a maintained Recipe without copying its contract.",
18
- "Use register_tool template only for trusted command templates, and use register_tool draft only for captured draft promotion.",
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.",
22
- 'Set template=null or template="" in register_tool to delete a persisted tool.',
23
- "Set update=true in register_tool to overwrite an existing tool registration.",
17
+ "Use actors_tool_register from=<skill>/<recipe> with defaults={...} to specialize a maintained Recipe without copying its contract.",
18
+ "Use actors_tool_register template only for trusted command templates, and use actors_tool_register draft only for captured draft promotion.",
19
+ "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.",
20
+ "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.",
21
+ "When actors_tool_register requests tool exposure, trust callable_now and activation; persistence alone is not callability.",
22
+ 'Set template=null or template="" in actors_tool_register to delete a persisted tool.',
23
+ "Set update=true in actors_tool_register to overwrite an existing tool registration.",
24
24
  ];
25
25
 
26
26
  export const ONBOARDING_SYSTEM_PROMPT = `pi-actors Skill routing:
27
27
  - Treat active bundled Skills as the operating authority.
28
+ - Route actors_tool_register, actors_run_spawn, actors_control_apply, and actors_inspect to the actors Skill; use canonical tool names for new calls.
28
29
  - For non-trivial pi-actors operation, diagnosis, or development, load and read the actors Skill before acting.
29
30
  - For work requiring multiple actors or subagents, additionally load and read the swarm Skill.
30
31
  - For capability-specific selection or constraints, load the owning capability Skill; actors owns generic mechanics and swarm owns multi-actor methodology.
@@ -36,7 +37,7 @@ export const ONBOARDING_SYSTEM_PROMPT = `pi-actors Skill routing:
36
37
  - AGENTS, source, and tests are implementation protocol and evidence; use them when changing or debugging the extension, not as substitutes for operating Skills.`;
37
38
 
38
39
  export const REGISTER_TOOL_PARAM_DESCRIPTIONS = {
39
- name: "Tool name in snake_case (e.g., 'transcribe')",
40
+ 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",
40
41
  description:
41
42
  "Describe what the tool does for the LLM. Required unless deleting; omitted updates keep the old description.",
42
43
  from:
@@ -205,7 +205,7 @@ export function inventoryActiveSkillRecipeComponents(
205
205
  for (const [skill, roots] of Object.entries(context.namespaces)) {
206
206
  if (roots.length > 1) {
207
207
  rejected.push({
208
- reason: `Duplicate active Skill identity ${skill} (${roots.length} active definitions); exact Recipe resolution is ambiguous. Next: inspect target=recipes view=doctor identity=${skill}/<recipe>`,
208
+ 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>`,
209
209
  skill,
210
210
  });
211
211
  continue;
@@ -322,13 +322,13 @@ function qualifiedRecipeNameForFile(
322
322
  function assertRecipeReferencePrefixWasNotRemoved(value: string): void {
323
323
  if (value.startsWith("std:")) {
324
324
  throw new Error(
325
- "std: Recipe references were removed in pi-actors 0.46. Use <skill>/<recipe> or an explicit .json/.md file path. Next: inspect target=recipes view=status to identify the active owning Skill.",
325
+ "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.",
326
326
  );
327
327
  }
328
328
  if (value.startsWith("skill:")) {
329
329
  const identity = value.slice("skill:".length);
330
330
  throw new Error(
331
- `skill: Recipe references were removed in pi-actors 0.46. Use ${identity} without a prefix. Next: inspect target=recipes view=doctor identity=${identity}`,
331
+ `skill: Recipe references were removed in pi-actors 0.46. Use ${identity} without a prefix. Next: actors_inspect target=recipes view=doctor identity=${identity}`,
332
332
  );
333
333
  }
334
334
  }
@@ -343,11 +343,11 @@ function skillRecipeCandidates(
343
343
  const roots = [...(context.namespaces[skillName] ?? [])];
344
344
  if (roots.length === 0)
345
345
  throw new Error(
346
- `Active Skill Recipe not found: ${skillName}/${recipeName}. Owning Skill "${skillName}" is not active. Next: activate Skill ${skillName}, then inspect target=recipes view=doctor identity=${skillName}/${recipeName}`,
346
+ `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}`,
347
347
  );
348
348
  if (roots.length > 1)
349
349
  throw new Error(
350
- `Duplicate active Skill identity ${skillName} (${roots.length} active definitions); ${skillName}/${recipeName} cannot resolve unambiguously. Next: inspect target=recipes view=doctor identity=${skillName}/${recipeName}`,
350
+ `Duplicate active Skill identity ${skillName} (${roots.length} active definitions); ${skillName}/${recipeName} cannot resolve unambiguously. Next: actors_inspect target=recipes view=doctor identity=${skillName}/${recipeName}`,
351
351
  );
352
352
  const root = roots[0];
353
353
  const candidates = ["json", "md"].map((extension) =>
@@ -356,7 +356,7 @@ function skillRecipeCandidates(
356
356
  const existing = candidates.filter((candidate) => existsSync(candidate));
357
357
  if (existing.length === 0) {
358
358
  throw new Error(
359
- `Skill Recipe component not found: ${skillName}/${recipeName}; owning Skill "${skillName}" is active. Next: inspect target=recipes view=doctor identity=${skillName}/${recipeName}`,
359
+ `Skill Recipe component not found: ${skillName}/${recipeName}; owning Skill "${skillName}" is active. Next: actors_inspect target=recipes view=doctor identity=${skillName}/${recipeName}`,
360
360
  );
361
361
  }
362
362
  if (existing.length > 1) {
@@ -394,7 +394,7 @@ function findOwningSkillDir(recipeFile: string): string | undefined {
394
394
  function assertRecipeHasNoDeclaredName(raw: Record<string, unknown>): void {
395
395
  if (!Object.hasOwn(raw, "name")) return;
396
396
  throw new Error(
397
- "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 inspect target=recipes view=doctor.",
397
+ "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.",
398
398
  );
399
399
  }
400
400
 
package/lib/registry.ts CHANGED
@@ -124,7 +124,7 @@ function listTools<TContext>(
124
124
  ),
125
125
  ),
126
126
  ],
127
- details: { tool: "register_tool" },
127
+ details: { tool: "actors_tool_register" },
128
128
  };
129
129
  }
130
130
 
@@ -153,8 +153,8 @@ function activationBoundary(
153
153
 
154
154
  function recipeDoctorAction(source: string): string {
155
155
  return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(source)
156
- ? `inspect target=recipes view=doctor identity=${source}`
157
- : "inspect target=recipes view=doctor";
156
+ ? `actors_inspect target=recipes view=doctor identity=${source}`
157
+ : "actors_inspect target=recipes view=doctor";
158
158
  }
159
159
 
160
160
  function registrationNextActions(
@@ -165,11 +165,11 @@ function registrationNextActions(
165
165
  if (callableNow) {
166
166
  return [
167
167
  `call tool ${tool}`,
168
- `inspect target=tool:${tool} view=status`,
168
+ `actors_inspect target=tool:${tool} view=status`,
169
169
  ];
170
170
  }
171
171
  return [
172
- `inspect target=tool:${tool} view=status`,
172
+ `actors_inspect target=tool:${tool} view=status`,
173
173
  ...(source === "template" || source === "draft"
174
174
  ? []
175
175
  : [recipeDoctorAction(source)]),
@@ -236,7 +236,7 @@ function registrationReceiptText(data: {
236
236
  const omittedNext = omission("next_action", 1, !nextWellFormed);
237
237
  // This producer knows only its existing status hint; invocation advice stays opaque.
238
238
  const next = part(nextWellFormed
239
- ? data.callable ? { instruction: data.next } : { next: { tool: "inspect", target: `tool:${data.tool}`, view: "status" } }
239
+ ? data.callable ? { instruction: data.next } : { next: { tool: "actors_inspect", target: `tool:${data.tool}`, view: "status" } }
240
240
  : omittedNext, omittedNext);
241
241
  const render = () => renderPart({ ...tool.selected, result: data.result, ...source.selected, callable_now: data.callable,
242
242
  ...(data.boundary === undefined ? {} : { activation_boundary: data.boundary }), ...required.selected, ...optional.selected,
@@ -307,7 +307,7 @@ function draftPathFailureText(kind: "root" | "missing", reference: string): stri
307
307
  const selected = registrySelected(kind === "root" ? "allowed_root" : "path", reference);
308
308
  const render = (): RegistryMetadata => ({ field: "draft", ...selected.selected,
309
309
  result: kind === "root" ? "Draft must be under the allowed root." : "Draft not found.",
310
- ...(kind === "root" ? { instruction: "Use inspect target=recipes view=summary to list drafts." } : {}) });
310
+ ...(kind === "root" ? { instruction: "Use actors_inspect target=recipes view=summary to list drafts." } : {}) });
311
311
  return growRegistryDocument(render, [selected]);
312
312
  }
313
313
 
@@ -349,8 +349,8 @@ function draftReceiptText(result: "updated" | "registered", tool: string, callab
349
349
  const identity = part("tool", tool, { tool });
350
350
  const flag = part("callable_now", callable, { callable_now: callable });
351
351
  // Only this producer's exact canonical read-only status hint is typed, never body advice.
352
- const status = /^inspect target=(tool:[a-z][a-z0-9_]{0,63}) view=status$/u.exec(next);
353
- const hint = part("next_action", next, status ? { next: { tool: "inspect", target: status[1], view: "status" } } : { instruction: next });
352
+ const status = /^actors_inspect target=(tool:[a-z][a-z0-9_]{0,63}) view=status$/u.exec(next);
353
+ const hint = part("next_action", next, status ? { next: { tool: "actors_inspect", target: status[1], view: "status" } } : { instruction: next });
354
354
  const render = () => renderPart({ ...identity.selected, result, source: "draft", ...flag.selected, ...hint.selected });
355
355
  for (const current of [identity, flag, hint]) {
356
356
  if (current.selected === current.full) continue;
@@ -508,6 +508,8 @@ function deleteTool<TContext>(
508
508
  details: { tool: name },
509
509
  };
510
510
  }
511
+ const blocker = deps.getToolNameBlocker(name);
512
+ if (blocker) throw new Error(ExecutionOutput.formatToolText(blocker));
511
513
  const recipePath = getToolRecipePath(deps, name);
512
514
  if (existsSync(recipePath)) unlinkSync(recipePath);
513
515
  tools.delete(name);
@@ -580,7 +582,7 @@ function assertTemplateIsNotNestedRecipe(value: unknown): void {
580
582
  ) {
581
583
  throw new Error(
582
584
  ExecutionOutput.formatToolText(registryMetadataText({ field: "template",
583
- result: "Nested Recipe-shaped templates are not a register_tool source mode.",
585
+ result: "Nested Recipe-shaped templates are not a actors_tool_register source mode.",
584
586
  instruction: "retry with from=<skill>/<recipe> and defaults={...}." })),
585
587
  );
586
588
  }
@@ -650,7 +652,7 @@ function normalizeFromReference(
650
652
  if (!trimmed) {
651
653
  throw new Error(
652
654
  ExecutionOutput.formatToolText(
653
- "Tool from must be a non-empty Recipe reference. Next: retry register_tool with from=<skill>/<recipe> or an explicit .json/.md path.",
655
+ "Tool from must be a non-empty Recipe reference. Next: retry actors_tool_register with from=<skill>/<recipe> or an explicit .json/.md path.",
654
656
  ),
655
657
  );
656
658
  }
@@ -710,7 +712,7 @@ function buildAuthoredRecipe(
710
712
  ExecutionOutput.formatToolText(registryMetadataText({ field: "defaults",
711
713
  ...registrySelected("key", runtimeOwnedDefault).selected,
712
714
  result: "Tool defaults cannot set runtime-owned input.",
713
- instruction: "remove that default and retry register_tool; the runtime supplies it." })),
715
+ instruction: "remove that default and retry actors_tool_register; the runtime supplies it." })),
714
716
  );
715
717
  }
716
718
  const authored: Record<string, unknown> = {
@@ -763,9 +765,9 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
763
765
  const record = input as RegisterToolInput & Record<string, unknown>;
764
766
  if (Object.hasOwn(record, "values")) {
765
767
  throw new Error(
766
- ExecutionOutput.formatToolText(registryMetadataText({ field: "values", result: "register_tool values was removed.",
768
+ ExecutionOutput.formatToolText(registryMetadataText({ field: "values", result: "actors_tool_register values was removed.",
767
769
  replacement_fields: ["defaults", "from"],
768
- instruction: "retry register_tool with defaults for caller inputs, or use from=<skill>/<recipe> for maintained composition." })),
770
+ instruction: "retry actors_tool_register with defaults for caller inputs, or use from=<skill>/<recipe> for maintained composition." })),
769
771
  );
770
772
  }
771
773
  const fromProvided = Object.hasOwn(record, "from");
@@ -777,7 +779,7 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
777
779
  ExecutionOutput.formatToolText(registryMetadataText({ field: "from",
778
780
  result: "Tool from must be a non-empty Recipe reference.",
779
781
  reference_forms: ["<skill>/<recipe>", "explicit .json path", "explicit .md path"],
780
- instruction: "retry register_tool with from=<skill>/<recipe> or an explicit .json/.md path." })),
782
+ instruction: "retry actors_tool_register with from=<skill>/<recipe> or an explicit .json/.md path." })),
781
783
  );
782
784
  }
783
785
  const draftProvided =
@@ -787,9 +789,9 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
787
789
  Number(fromProvided) + Number(draftProvided) + Number(templateProvided);
788
790
  if (modeCount > 1) {
789
791
  throw new Error(
790
- ExecutionOutput.formatToolText(registryMetadataText({ result: "Use exactly one register_tool source mode.",
792
+ ExecutionOutput.formatToolText(registryMetadataText({ result: "Use exactly one actors_tool_register source mode.",
791
793
  source_modes: ["from", "template", "draft"],
792
- instruction: "remove the extra source fields and retry register_tool." })),
794
+ instruction: "remove the extra source fields and retry actors_tool_register." })),
793
795
  );
794
796
  }
795
797
  if (
@@ -798,9 +800,9 @@ function assertRegisterToolInputModes(input: RegisterToolInput): void {
798
800
  ) {
799
801
  throw new Error(
800
802
  ExecutionOutput.formatToolText(registryMetadataText({ field: "from",
801
- result: "register_tool from inherits args and async behavior.",
803
+ result: "actors_tool_register from inherits args and async behavior.",
802
804
  inherited_fields: ["args", "async"],
803
- instruction: "retry register_tool with from and optional defaults only; omit args and async." })),
805
+ instruction: "retry actors_tool_register with from and optional defaults only; omit args and async." })),
804
806
  );
805
807
  }
806
808
  }
@@ -881,7 +883,7 @@ function executeRegisterToolUnlocked<TContext>(
881
883
  throw new Error(
882
884
  ExecutionOutput.formatToolText(registryMetadataText({ result: "New registration requires exactly one source mode.",
883
885
  source_modes: ["from", "template", "draft"],
884
- instruction: "retry register_tool with one source field." })),
886
+ instruction: "retry actors_tool_register with one source field." })),
885
887
  );
886
888
  }
887
889
  const recipePath = getToolRecipePath(deps, name);
@@ -930,7 +932,7 @@ function executeRegisterToolUnlocked<TContext>(
930
932
  !activation.callable_now)
931
933
  ) {
932
934
  throw new Error(
933
- `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: inspect target=runtime view=status; do not use spawn as proof of tool invocation.`,
935
+ `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.`,
934
936
  );
935
937
  }
936
938
  } catch (error) {
@@ -1158,12 +1160,12 @@ function retainRecipe<TContext>(
1158
1160
  const facts = { kind: "retained_recipe" as const, result: "retained" as const, persisted: true, resolved: true,
1159
1161
  validated: true, activation: "not_requested" as const, async: admitted.async };
1160
1162
  const text = growRegistryDocument(() => ({ ...selectedName.selected, ...facts, ...recipe.selected, ...selectedSource.selected,
1161
- ...required.selected, ...optional.selected, next: { tool: "inspect", target: "recipes", view: "summary", verbose: true } }),
1163
+ ...required.selected, ...optional.selected, next: { tool: "actors_inspect", target: "recipes", view: "summary", verbose: true } }),
1162
1164
  [recipe, selectedName, selectedSource, required, optional]);
1163
1165
  const result = {
1164
1166
  content: [textContent(ExecutionOutput.formatToolText(text))],
1165
1167
  details: { ...facts, tool: name, recipe: path, source, required_args: args.required, optional_args: args.optional,
1166
- next_actions: ["inspect target=recipes view=summary verbose=true"] },
1168
+ next_actions: ["actors_inspect target=recipes view=summary verbose=true"] },
1167
1169
  };
1168
1170
  stage = "release";
1169
1171
  return result;
@@ -1174,7 +1176,7 @@ function retainRecipe<TContext>(
1174
1176
  const reason = error instanceof RetainedRecipeRefusal ? { reason: error.reason } : {};
1175
1177
  throw new Error(ExecutionOutput.formatToolText(registryMetadataText({ operation: "retain", stage, result: "unconfirmed",
1176
1178
  effects: "unknown; inspect retained inventory before retry", ...reason,
1177
- next: { tool: "inspect", target: "recipes", view: "summary", verbose: true } })), { cause: error });
1179
+ next: { tool: "actors_inspect", target: "recipes", view: "summary", verbose: true } })), { cause: error });
1178
1180
  }
1179
1181
  }
1180
1182
 
@@ -115,7 +115,7 @@ export function controlAutomaticReview(
115
115
  };
116
116
  });
117
117
  if (result.changed === true) schedule(scope, options);
118
- const nextActions = ["inspect target=recipes view=reviews"];
118
+ const nextActions = ["actors_inspect target=recipes view=reviews"];
119
119
  if (
120
120
  action === "review.retry" &&
121
121
  scope === "tool" &&