@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/docs/tool-registry.md
CHANGED
|
@@ -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. `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 `
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
118
|
-
|
|
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 `
|
|
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 `
|
|
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
|
|
package/lib/draft-sleep.ts
CHANGED
|
@@ -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: "
|
|
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: "
|
|
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: "
|
|
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",
|
package/lib/extension-runtime.ts
CHANGED
|
@@ -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.
|
|
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
|
-
})
|
|
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
|
|
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
|
|
18
|
-
"Use
|
|
19
|
-
"Use
|
|
20
|
-
"A
|
|
21
|
-
"When
|
|
22
|
-
'Set template=null or template="" in
|
|
23
|
-
"Set update=true in
|
|
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., '
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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: "
|
|
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
|
-
? `
|
|
157
|
-
: "
|
|
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
|
-
`
|
|
168
|
+
`actors_inspect target=tool:${tool} view=status`,
|
|
169
169
|
];
|
|
170
170
|
}
|
|
171
171
|
return [
|
|
172
|
-
`
|
|
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: "
|
|
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
|
|
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 = /^
|
|
353
|
-
const hint = part("next_action", next, status ? { next: { tool: "
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
768
|
+
ExecutionOutput.formatToolText(registryMetadataText({ field: "values", result: "actors_tool_register values was removed.",
|
|
767
769
|
replacement_fields: ["defaults", "from"],
|
|
768
|
-
instruction: "retry
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
803
|
+
result: "actors_tool_register from inherits args and async behavior.",
|
|
802
804
|
inherited_fields: ["args", "async"],
|
|
803
|
-
instruction: "retry
|
|
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
|
|
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:
|
|
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: "
|
|
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: ["
|
|
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: "
|
|
1179
|
+
next: { tool: "actors_inspect", target: "recipes", view: "summary", verbose: true } })), { cause: error });
|
|
1178
1180
|
}
|
|
1179
1181
|
}
|
|
1180
1182
|
|
package/lib/review-control.ts
CHANGED
|
@@ -115,7 +115,7 @@ export function controlAutomaticReview(
|
|
|
115
115
|
};
|
|
116
116
|
});
|
|
117
117
|
if (result.changed === true) schedule(scope, options);
|
|
118
|
-
const nextActions = ["
|
|
118
|
+
const nextActions = ["actors_inspect target=recipes view=reviews"];
|
|
119
119
|
if (
|
|
120
120
|
action === "review.retry" &&
|
|
121
121
|
scope === "tool" &&
|
|
@@ -69,8 +69,8 @@ export type RunControlAdmissionInput = {
|
|
|
69
69
|
const PENDING_CONTROL_STATUSES = new Set<RunControlStatus>(["queued", "delivered", "claimed"]);
|
|
70
70
|
const TERMINAL_CONTROL_STATUSES = new Set<RunControlStatus>(["handled", "failed"]);
|
|
71
71
|
const CONTROL_ADMISSION_NEXT_ACTIONS = [
|
|
72
|
-
"
|
|
73
|
-
"
|
|
72
|
+
"actors_inspect target=run:<id> view=control",
|
|
73
|
+
"actors_inspect target=runtime view=triage",
|
|
74
74
|
];
|
|
75
75
|
|
|
76
76
|
function isCount(value: number): boolean {
|
package/lib/runtime.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { existsSync, watch, type FSWatcher } from "node:fs";
|
|
|
8
8
|
import { basename, dirname } from "node:path";
|
|
9
9
|
|
|
10
10
|
import * as Config from "./config.ts";
|
|
11
|
+
import * as Identity from "./identity.ts";
|
|
11
12
|
import type { RegisteredToolExec } from "./execution.ts";
|
|
12
13
|
import * as Paths from "./paths.ts";
|
|
13
14
|
import type { RecipeResolutionContext } from "./recipes-context.ts";
|
|
@@ -36,7 +37,7 @@ export interface ToolRegistryRuntimeDeps {
|
|
|
36
37
|
exec: RegisteredToolExec;
|
|
37
38
|
recipeRoot?: string;
|
|
38
39
|
getActiveTools?: () => string[];
|
|
39
|
-
getAllTools?: () => Array<{ name: string }>;
|
|
40
|
+
getAllTools?: () => Array<{ name: string; sourceInfo?: { path: string } }>;
|
|
40
41
|
registerTool: (
|
|
41
42
|
definition: ReturnType<typeof ToolsLocal.createRuntimeToolDefinition>,
|
|
42
43
|
) => void;
|
|
@@ -83,6 +84,8 @@ export function createAutoToolsRuntime(
|
|
|
83
84
|
const tools = new Map<string, Config.RegisteredTool>();
|
|
84
85
|
const runtimeToolFingerprints = new Map<string, string>();
|
|
85
86
|
const runtimeTools = new Set<string>();
|
|
87
|
+
// Host declarations survive deactivation; retain their observed provenance separately.
|
|
88
|
+
const runtimeToolSources = new Map<string, string | undefined>();
|
|
86
89
|
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
87
90
|
const status: RecipeRegistryStatus = {
|
|
88
91
|
active_tool_count: 0,
|
|
@@ -99,9 +102,14 @@ export function createAutoToolsRuntime(
|
|
|
99
102
|
if (ctx.hasUI) ctx.ui.notify(message, type);
|
|
100
103
|
}
|
|
101
104
|
function getToolNameBlocker(name: string): string | undefined {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
:
|
|
105
|
+
const normalized = Identity.normalizeToolName(name);
|
|
106
|
+
if ([...deps.reservedToolNames].some((reserved) => Identity.normalizeToolName(reserved) === normalized))
|
|
107
|
+
return `Reserved tool name: ${name}`;
|
|
108
|
+
const foreign = deps.getAllTools?.().find((tool) =>
|
|
109
|
+
Identity.normalizeToolName(tool.name) === normalized &&
|
|
110
|
+
!(tool.name === name && runtimeToolSources.has(name) &&
|
|
111
|
+
tool.sourceInfo?.path === runtimeToolSources.get(name)));
|
|
112
|
+
return foreign ? `Host tool name collision: ${name} conflicts with ${foreign.name}` : undefined;
|
|
105
113
|
}
|
|
106
114
|
function getToolFingerprint(cfg: Config.RegisteredTool): string {
|
|
107
115
|
return JSON.stringify({
|
|
@@ -135,12 +143,15 @@ export function createAutoToolsRuntime(
|
|
|
135
143
|
function deactivateMissingRuntimeTools(activeNames: Set<string>): void {
|
|
136
144
|
const stale = [...runtimeTools].filter((name) => !activeNames.has(name));
|
|
137
145
|
if (stale.length === 0) return;
|
|
146
|
+
const hostTools = deps.getAllTools?.();
|
|
147
|
+
const staleOwned = stale.filter((name) => !hostTools || hostTools.some((tool) =>
|
|
148
|
+
tool.name === name && tool.sourceInfo?.path === runtimeToolSources.get(name)));
|
|
138
149
|
for (const name of stale) {
|
|
139
150
|
runtimeTools.delete(name);
|
|
140
151
|
runtimeToolFingerprints.delete(name);
|
|
141
152
|
}
|
|
142
153
|
if (!deps.getActiveTools || !deps.setActiveTools) return;
|
|
143
|
-
const staleSet = new Set(
|
|
154
|
+
const staleSet = new Set(staleOwned);
|
|
144
155
|
deps.setActiveTools(
|
|
145
156
|
deps.getActiveTools().filter((name) => !staleSet.has(name)),
|
|
146
157
|
);
|
|
@@ -157,10 +168,13 @@ export function createAutoToolsRuntime(
|
|
|
157
168
|
};
|
|
158
169
|
}
|
|
159
170
|
function registerRuntimeTool(cfg: Config.RegisteredTool): RuntimeToolActivation {
|
|
171
|
+
const blocker = getToolNameBlocker(cfg.name);
|
|
172
|
+
if (blocker) throw new Error(blocker);
|
|
160
173
|
const fingerprint = getToolFingerprint(cfg);
|
|
161
174
|
if (runtimeToolFingerprints.get(cfg.name) !== fingerprint) {
|
|
162
175
|
deps.registerTool(ToolsLocal.createRuntimeToolDefinition(cfg, deps.exec));
|
|
163
176
|
runtimeTools.add(cfg.name);
|
|
177
|
+
runtimeToolSources.set(cfg.name, deps.getAllTools?.().find((tool) => tool.name === cfg.name)?.sourceInfo?.path);
|
|
164
178
|
runtimeToolFingerprints.set(cfg.name, fingerprint);
|
|
165
179
|
}
|
|
166
180
|
if (deps.getActiveTools && deps.setActiveTools) {
|
|
@@ -215,7 +229,7 @@ export function createAutoToolsRuntime(
|
|
|
215
229
|
lines.push(...other.map((warning) => `• ${warning}`));
|
|
216
230
|
groups[groups.length] = ["other_diagnostics", start, lines.length];
|
|
217
231
|
}
|
|
218
|
-
lines.push("Next:
|
|
232
|
+
lines.push("Next: actors_inspect target=recipes view=doctor");
|
|
219
233
|
// Preserve the original layout join/coercion and its faults before projecting owned warning slots.
|
|
220
234
|
void `${lines.join("\n")}\n`;
|
|
221
235
|
let text = ToolsResponse.formatMarkdownField("report", "pi-actors recipe registry warning");
|
|
@@ -227,7 +241,7 @@ export function createAutoToolsRuntime(
|
|
|
227
241
|
: " - warning:\n - result: unavailable\n - reason: malformed_selected_text");
|
|
228
242
|
}
|
|
229
243
|
}
|
|
230
|
-
return text + "\n" + ToolsResponse.formatMarkdownField("instruction", "Next:
|
|
244
|
+
return text + "\n" + ToolsResponse.formatMarkdownField("instruction", "Next: actors_inspect target=recipes view=doctor") + "\n";
|
|
231
245
|
}
|
|
232
246
|
function loadTools(
|
|
233
247
|
ctx: RuntimeContext,
|
|
@@ -259,6 +273,7 @@ export function createAutoToolsRuntime(
|
|
|
259
273
|
const blocker = getToolNameBlocker(cfg.name);
|
|
260
274
|
if (blocker) {
|
|
261
275
|
warnings.push(blocker);
|
|
276
|
+
tools.delete(cfg.name);
|
|
262
277
|
continue;
|
|
263
278
|
}
|
|
264
279
|
registerRuntimeTool(cfg);
|
|
@@ -284,7 +299,7 @@ export function createAutoToolsRuntime(
|
|
|
284
299
|
}),
|
|
285
300
|
getToolStatus: (name) => {
|
|
286
301
|
const cfg = tools.get(name);
|
|
287
|
-
if (!cfg) return undefined;
|
|
302
|
+
if (!cfg || getToolNameBlocker(name)) return undefined;
|
|
288
303
|
const usage = cfg.sourcePath
|
|
289
304
|
? RecipesUsage.readRecipeUsage(cfg.sourcePath)
|
|
290
305
|
: undefined;
|
|
@@ -305,8 +320,8 @@ export function createAutoToolsRuntime(
|
|
|
305
320
|
next_actions: activation.callable_now
|
|
306
321
|
? [`call tool ${name}`]
|
|
307
322
|
: [
|
|
308
|
-
`
|
|
309
|
-
`
|
|
323
|
+
`actors_tool_register name=${name} update=true`,
|
|
324
|
+
`actors_inspect target=tool:${name} view=status`,
|
|
310
325
|
],
|
|
311
326
|
...(usage?.launch_kind ? { launch_kind: usage.launch_kind } : {}),
|
|
312
327
|
spawn_calls: Number(usage?.spawn_calls ?? 0),
|
|
@@ -372,7 +387,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
372
387
|
setWatchStatus("failed");
|
|
373
388
|
try {
|
|
374
389
|
ctx.ui.notify(
|
|
375
|
-
"- report: Recipe live reload watcher failed\n- instruction: restart the session or use
|
|
390
|
+
"- report: Recipe live reload watcher failed\n- instruction: restart the session or use actors_tool_register again to refresh recipe tools.",
|
|
376
391
|
"warning",
|
|
377
392
|
);
|
|
378
393
|
} catch (error) {
|