@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.
Files changed (66) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +37 -35
  4. package/dist/lib/draft-sleep.js +3 -3
  5. package/dist/lib/extension-runtime.js +7 -2
  6. package/dist/lib/prompts.d.ts +3 -3
  7. package/dist/lib/prompts.js +10 -9
  8. package/dist/lib/recipes-references.js +7 -7
  9. package/dist/lib/registry.js +27 -24
  10. package/dist/lib/review-control.js +1 -1
  11. package/dist/lib/run-evidence-policy.js +2 -2
  12. package/dist/lib/runtime.d.ts +3 -0
  13. package/dist/lib/runtime.js +24 -10
  14. package/dist/lib/tool-review-scheduler.js +7 -7
  15. package/dist/lib/tools-access.js +1 -1
  16. package/dist/lib/tools-inspect.js +10 -10
  17. package/dist/lib/tools-message.d.ts +1 -1
  18. package/dist/lib/tools-message.js +10 -10
  19. package/dist/lib/tools-register.d.ts +3 -3
  20. package/dist/lib/tools-register.js +4 -4
  21. package/dist/lib/tools-response.d.ts +1 -1
  22. package/dist/lib/tools-response.js +18 -18
  23. package/dist/lib/tools-spawn.js +1 -1
  24. package/dist/lib/tools.d.ts +7 -0
  25. package/dist/lib/tools.js +25 -0
  26. package/dist/skills/actors/SKILL.md +28 -20
  27. package/dist/skills/actors/references/diagnostics.md +11 -11
  28. package/dist/skills/actors/references/persistent-tools.md +19 -15
  29. package/dist/skills/actors/references/recipes.md +1 -1
  30. package/dist/skills/actors/references/runs.md +7 -7
  31. package/dist/skills/music-player/SKILL.md +4 -4
  32. package/dist/skills/music-player/scripts/playback.mjs +1 -1
  33. package/dist/skills/recipe-memory/SKILL.md +7 -7
  34. package/docs/README.md +2 -2
  35. package/docs/actor-inspector.md +1 -1
  36. package/docs/async-runs.md +13 -13
  37. package/docs/capability-memory.md +6 -6
  38. package/docs/inspection.md +21 -21
  39. package/docs/run-supervision.md +21 -21
  40. package/docs/tool-registry.md +27 -19
  41. package/docs/windows-process-observation.md +1 -1
  42. package/lib/draft-sleep.ts +3 -3
  43. package/lib/extension-runtime.ts +7 -5
  44. package/lib/prompts.ts +10 -9
  45. package/lib/recipes-references.ts +7 -7
  46. package/lib/registry.ts +26 -24
  47. package/lib/review-control.ts +1 -1
  48. package/lib/run-evidence-policy.ts +2 -2
  49. package/lib/runtime.ts +26 -11
  50. package/lib/tool-review-scheduler.ts +7 -7
  51. package/lib/tools-access.ts +1 -1
  52. package/lib/tools-inspect.ts +10 -10
  53. package/lib/tools-message.ts +10 -10
  54. package/lib/tools-register.ts +4 -4
  55. package/lib/tools-response.ts +18 -18
  56. package/lib/tools-spawn.ts +1 -1
  57. package/lib/tools.ts +28 -0
  58. package/package.json +1 -1
  59. package/skills/actors/SKILL.md +28 -20
  60. package/skills/actors/references/diagnostics.md +11 -11
  61. package/skills/actors/references/persistent-tools.md +19 -15
  62. package/skills/actors/references/recipes.md +1 -1
  63. package/skills/actors/references/runs.md +7 -7
  64. package/skills/music-player/SKILL.md +4 -4
  65. package/skills/music-player/scripts/playback.mjs +1 -1
  66. package/skills/recipe-memory/SKILL.md +7 -7
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { existsSync, watch } from "node:fs";
7
7
  import { basename, dirname } from "node:path";
8
+ import * as Identity from "./identity.js";
8
9
  import * as Paths from "./paths.js";
9
10
  import * as RecipesDiscovery from "./recipes-discovery.js";
10
11
  import * as RecipesReferences from "./recipes-references.js";
@@ -15,6 +16,8 @@ export function createAutoToolsRuntime(deps) {
15
16
  const tools = new Map();
16
17
  const runtimeToolFingerprints = new Map();
17
18
  const runtimeTools = new Set();
19
+ // Host declarations survive deactivation; retain their observed provenance separately.
20
+ const runtimeToolSources = new Map();
18
21
  const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
19
22
  const status = {
20
23
  active_tool_count: 0,
@@ -28,9 +31,13 @@ export function createAutoToolsRuntime(deps) {
28
31
  ctx.ui.notify(message, type);
29
32
  }
30
33
  function getToolNameBlocker(name) {
31
- return deps.reservedToolNames.has(name)
32
- ? `Reserved tool name: ${name}`
33
- : undefined;
34
+ const normalized = Identity.normalizeToolName(name);
35
+ if ([...deps.reservedToolNames].some((reserved) => Identity.normalizeToolName(reserved) === normalized))
36
+ return `Reserved tool name: ${name}`;
37
+ const foreign = deps.getAllTools?.().find((tool) => Identity.normalizeToolName(tool.name) === normalized &&
38
+ !(tool.name === name && runtimeToolSources.has(name) &&
39
+ tool.sourceInfo?.path === runtimeToolSources.get(name)));
40
+ return foreign ? `Host tool name collision: ${name} conflicts with ${foreign.name}` : undefined;
34
41
  }
35
42
  function getToolFingerprint(cfg) {
36
43
  return JSON.stringify({
@@ -61,13 +68,15 @@ export function createAutoToolsRuntime(deps) {
61
68
  const stale = [...runtimeTools].filter((name) => !activeNames.has(name));
62
69
  if (stale.length === 0)
63
70
  return;
71
+ const hostTools = deps.getAllTools?.();
72
+ const staleOwned = stale.filter((name) => !hostTools || hostTools.some((tool) => tool.name === name && tool.sourceInfo?.path === runtimeToolSources.get(name)));
64
73
  for (const name of stale) {
65
74
  runtimeTools.delete(name);
66
75
  runtimeToolFingerprints.delete(name);
67
76
  }
68
77
  if (!deps.getActiveTools || !deps.setActiveTools)
69
78
  return;
70
- const staleSet = new Set(stale);
79
+ const staleSet = new Set(staleOwned);
71
80
  deps.setActiveTools(deps.getActiveTools().filter((name) => !staleSet.has(name)));
72
81
  }
73
82
  function activationFor(name) {
@@ -81,10 +90,14 @@ export function createAutoToolsRuntime(deps) {
81
90
  };
82
91
  }
83
92
  function registerRuntimeTool(cfg) {
93
+ const blocker = getToolNameBlocker(cfg.name);
94
+ if (blocker)
95
+ throw new Error(blocker);
84
96
  const fingerprint = getToolFingerprint(cfg);
85
97
  if (runtimeToolFingerprints.get(cfg.name) !== fingerprint) {
86
98
  deps.registerTool(ToolsLocal.createRuntimeToolDefinition(cfg, deps.exec));
87
99
  runtimeTools.add(cfg.name);
100
+ runtimeToolSources.set(cfg.name, deps.getAllTools?.().find((tool) => tool.name === cfg.name)?.sourceInfo?.path);
88
101
  runtimeToolFingerprints.set(cfg.name, fingerprint);
89
102
  }
90
103
  if (deps.getActiveTools && deps.setActiveTools) {
@@ -129,7 +142,7 @@ export function createAutoToolsRuntime(deps) {
129
142
  lines.push(...other.map((warning) => `• ${warning}`));
130
143
  groups[groups.length] = ["other_diagnostics", start, lines.length];
131
144
  }
132
- lines.push("Next: inspect target=recipes view=doctor");
145
+ lines.push("Next: actors_inspect target=recipes view=doctor");
133
146
  // Preserve the original layout join/coercion and its faults before projecting owned warning slots.
134
147
  void `${lines.join("\n")}\n`;
135
148
  let text = ToolsResponse.formatMarkdownField("report", "pi-actors recipe registry warning");
@@ -141,7 +154,7 @@ export function createAutoToolsRuntime(deps) {
141
154
  : " - warning:\n - result: unavailable\n - reason: malformed_selected_text");
142
155
  }
143
156
  }
144
- return text + "\n" + ToolsResponse.formatMarkdownField("instruction", "Next: inspect target=recipes view=doctor") + "\n";
157
+ return text + "\n" + ToolsResponse.formatMarkdownField("instruction", "Next: actors_inspect target=recipes view=doctor") + "\n";
145
158
  }
146
159
  function loadTools(ctx, resolutionContext) {
147
160
  const warnings = [];
@@ -170,6 +183,7 @@ export function createAutoToolsRuntime(deps) {
170
183
  const blocker = getToolNameBlocker(cfg.name);
171
184
  if (blocker) {
172
185
  warnings.push(blocker);
186
+ tools.delete(cfg.name);
173
187
  continue;
174
188
  }
175
189
  registerRuntimeTool(cfg);
@@ -195,7 +209,7 @@ export function createAutoToolsRuntime(deps) {
195
209
  }),
196
210
  getToolStatus: (name) => {
197
211
  const cfg = tools.get(name);
198
- if (!cfg)
212
+ if (!cfg || getToolNameBlocker(name))
199
213
  return undefined;
200
214
  const usage = cfg.sourcePath
201
215
  ? RecipesUsage.readRecipeUsage(cfg.sourcePath)
@@ -217,8 +231,8 @@ export function createAutoToolsRuntime(deps) {
217
231
  next_actions: activation.callable_now
218
232
  ? [`call tool ${name}`]
219
233
  : [
220
- `register_tool name=${name} update=true`,
221
- `inspect target=tool:${name} view=status`,
234
+ `actors_tool_register name=${name} update=true`,
235
+ `actors_inspect target=tool:${name} view=status`,
222
236
  ],
223
237
  ...(usage?.launch_kind ? { launch_kind: usage.launch_kind } : {}),
224
238
  spawn_calls: Number(usage?.spawn_calls ?? 0),
@@ -271,7 +285,7 @@ export function createRecipeToolReloadWatcher(runtime, deps = {}) {
271
285
  failureNotified = true;
272
286
  setWatchStatus("failed");
273
287
  try {
274
- ctx.ui.notify("- report: Recipe live reload watcher failed\n- instruction: restart the session or use register_tool again to refresh recipe tools.", "warning");
288
+ ctx.ui.notify("- report: Recipe live reload watcher failed\n- instruction: restart the session or use actors_tool_register again to refresh recipe tools.", "warning");
275
289
  }
276
290
  catch (error) {
277
291
  reportCallbackError(error);
@@ -264,7 +264,7 @@ export function applyApprovedToolReviewAtSessionBoundary(options = {}) {
264
264
  ...state,
265
265
  failedStage: "approval_state",
266
266
  lastError: "approved review path is unavailable",
267
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
267
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
268
268
  phase: "processing_failed",
269
269
  updatedAt: (options.now ?? (() => new Date()))().toISOString(),
270
270
  };
@@ -287,7 +287,7 @@ export function applyApprovedToolReviewAtSessionBoundary(options = {}) {
287
287
  ...state,
288
288
  failedStage: "transaction",
289
289
  lastError: `tool review transaction remained ${transaction.phase}`,
290
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
290
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
291
291
  phase: "processing_failed",
292
292
  updatedAt: (options.now ?? (() => new Date()))().toISOString(),
293
293
  };
@@ -316,7 +316,7 @@ export function applyApprovedToolReviewAtSessionBoundary(options = {}) {
316
316
  ...pending,
317
317
  failedStage: "lineage",
318
318
  lastError: error instanceof Error ? error.message : String(error),
319
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
319
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
320
320
  };
321
321
  writeJsonAtomic(statePath, failedPending);
322
322
  return { outcome: "lineage_pending", state: failedPending };
@@ -337,7 +337,7 @@ export function applyApprovedToolReviewAtSessionBoundary(options = {}) {
337
337
  ...state,
338
338
  failedStage: "transaction",
339
339
  lastError: error instanceof Error ? error.message : String(error),
340
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
340
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
341
341
  phase: "processing_failed",
342
342
  updatedAt: (options.now ?? (() => new Date()))().toISOString(),
343
343
  };
@@ -401,7 +401,7 @@ export function createToolReviewScheduler(deps) {
401
401
  ...existing,
402
402
  failedStage: processed.stage ?? "review_result",
403
403
  lastError: processed.error ?? "automatic tool reviewer failed",
404
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
404
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
405
405
  phase: processed.outcome === "processing_failed"
406
406
  ? "processing_failed"
407
407
  : "failed",
@@ -437,7 +437,7 @@ export function createToolReviewScheduler(deps) {
437
437
  attempts: existing.attempts + 1,
438
438
  failedStage: "review_launch",
439
439
  lastError: error instanceof Error ? error.message : String(error),
440
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
440
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
441
441
  phase: "failed",
442
442
  updatedAt: now().toISOString(),
443
443
  });
@@ -471,7 +471,7 @@ export function createToolReviewScheduler(deps) {
471
471
  failedStage: "review_launch",
472
472
  inputPath: batch.inputPath,
473
473
  lastError: error instanceof Error ? error.message : String(error),
474
- nextAction: "message target=runtime action=review.retry input={\"scope\":\"tool\"}",
474
+ nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
475
475
  phase: "failed",
476
476
  reviewerInputPath: batch.reviewerInputPath,
477
477
  reviewId: batch.reviewId,
@@ -33,7 +33,7 @@ export function sessionMismatchError(input) {
33
33
  const actor = input.run ? `run:${input.run}` : (input.target ?? "session");
34
34
  return Object.assign(new Error(`${actor} reason=session_mismatch owner_session=${ownerSession} current_session=${currentSession} hint=inspect_runtime_runs`), {
35
35
  current_session: input.currentSession,
36
- hint: "inspect target=runtime view=runs",
36
+ hint: "actors_inspect target=runtime view=runs",
37
37
  owner_session: input.expectedSession,
38
38
  reason: "session_mismatch",
39
39
  run: input.run,
@@ -145,10 +145,10 @@ function runtimeTriage(ctx, deps) {
145
145
  trace_diagnostic_count: traceDiagnostics.length, trace_diagnostics: traceDiagnostics.slice(0, 40),
146
146
  attention_events: attentionEvents.slice(-40).reverse(),
147
147
  next_actions: [
148
- ...(failed.length ? ["inspect target=runtime view=runs status=failed"] : []),
149
- ...new Set([...staleControls, ...backpressuredRuns].slice(0, 3).map((entry) => `inspect target=run:${entry.run} view=control`)),
148
+ ...(failed.length ? ["actors_inspect target=runtime view=runs status=failed"] : []),
149
+ ...new Set([...staleControls, ...backpressuredRuns].slice(0, 3).map((entry) => `actors_inspect target=run:${entry.run} view=control`)),
150
150
  ...(attentionEvents.length
151
- ? attentionEvents.slice(-3).map((entry) => `inspect target=run:${String(entry.run)} view=trace source=runtime`)
151
+ ? attentionEvents.slice(-3).map((entry) => `actors_inspect target=run:${String(entry.run)} view=trace source=runtime`)
152
152
  : []),
153
153
  ],
154
154
  };
@@ -209,7 +209,7 @@ function formatInspectFailure(fields, next) {
209
209
  return { omittable, full, selected };
210
210
  });
211
211
  // Only producer-known read-only inspect literals become a typed next, never a parsed action.
212
- const typedNext = next ? { next: { tool: "inspect", target: next[0], view: next[1] } } : undefined;
212
+ const typedNext = next ? { next: { tool: "actors_inspect", target: next[0], view: next[1] } } : undefined;
213
213
  const nextFallback = { next_action: omitted };
214
214
  const document = () => ({ ...Object.assign({}, ...parts.map((part) => part.selected)),
215
215
  ...(typedNext ?? nextFallback) });
@@ -336,15 +336,15 @@ function inspectRecipeIdentity(identity, resolutionContext, namespaces, inventor
336
336
  ...(rejectedReason ? { rejected_reason: rejectedReason } : {}),
337
337
  next_actions: resolvable
338
338
  ? [
339
- `spawn recipe=${identity}`,
340
- `register_tool name=<tool-name> from=${identity}`,
339
+ `actors_run_spawn recipe=${identity}`,
340
+ `actors_tool_register name=<tool-name> from=${identity}`,
341
341
  ]
342
342
  : roots.length === 0 && skill
343
343
  ? [
344
344
  `activate Skill ${skill}`,
345
- `inspect target=recipes view=doctor identity=${identity}`,
345
+ `actors_inspect target=recipes view=doctor identity=${identity}`,
346
346
  ]
347
- : ["inspect target=recipes view=imports verbose=true"],
347
+ : ["actors_inspect target=recipes view=imports verbose=true"],
348
348
  };
349
349
  }
350
350
  function inspectRecipes(requestedView, input, deps, context) {
@@ -583,7 +583,7 @@ function compactResult(target, view, details) {
583
583
  ? String(details.next_actions[0])
584
584
  : details.callable_now === true
585
585
  ? `call tool ${name}`
586
- : `register_tool name=${name} update=true`;
586
+ : `actors_tool_register name=${name} update=true`;
587
587
  const hint = ToolsResponse.nextActionMetadata(next, Limits.RUN_DELIVERY_MODEL_MAX_BYTES);
588
588
  const required = compactArguments("required_args", details.required_args);
589
589
  const optional = compactArguments("optional_args", details.optional_args);
@@ -601,7 +601,7 @@ function compactResult(target, view, details) {
601
601
  }
602
602
  export function createInspectToolDefinition(deps = {}) {
603
603
  return {
604
- name: "inspect",
604
+ name: "actors_inspect",
605
605
  label: "Inspect",
606
606
  description: "Inspect a Run's Recipe, Trace, or Control evidence, or runtime/recipe/tool diagnostics.",
607
607
  parameters: Schema.objectSchema({
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Public Control facade under the stable `message` tool name.
2
+ * Public Control facade under the stable `actors_control_apply` tool name.
3
3
  * Zones: Control request validation, Run/runtime dispatch, recipient-scoped receipts
4
4
  * Owns public Control execution; journaling, delivery, and lifecycle mutation stay in Run domains.
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Public Control facade under the stable `message` tool name.
2
+ * Public Control facade under the stable `actors_control_apply` tool name.
3
3
  * Zones: Control request validation, Run/runtime dispatch, recipient-scoped receipts
4
4
  * Owns public Control execution; journaling, delivery, and lifecycle mutation stay in Run domains.
5
5
  */
@@ -40,9 +40,9 @@ function controlDocument(render, order) {
40
40
  }
41
41
  /** Only producer-known read-only inspect literals become typed next; other advice stays opaque content. */
42
42
  function controlNextPart(action) {
43
- const inspect = /^inspect target=([^\s]+) view=([^\s]+)$/u.exec(action);
43
+ const inspect = /^actors_inspect target=([^\s]+) view=([^\s]+)$/u.exec(action);
44
44
  if (inspect) {
45
- return ToolsResponse.selectYamlNode("next", { tool: "inspect", target: inspect[1], view: inspect[2] }, CONTROL_BUDGET);
45
+ return ToolsResponse.selectYamlNode("next", { tool: "actors_inspect", target: inspect[1], view: inspect[2] }, CONTROL_BUDGET);
46
46
  }
47
47
  const wellFormed = action.isWellFormed();
48
48
  const omitted = { next: { instruction: wellFormed ? "[omitted]" : "[unavailable]",
@@ -107,24 +107,24 @@ export function compactControlResult(request, result) {
107
107
  }
108
108
  function runNextActions(run, result) {
109
109
  if (result.stopped === true)
110
- return [`inspect target=run:${run} view=trace`];
110
+ return [`actors_inspect target=run:${run} view=trace`];
111
111
  if (result.stopped === false || result.archived === false || result.pruned === false) {
112
112
  if (result.reason === "ownership changed" || result.reason === "run generation changed") {
113
- return ["inspect target=runtime view=runs"];
113
+ return ["actors_inspect target=runtime view=runs"];
114
114
  }
115
115
  if (result.stopped === false && result.reason !== "not running")
116
- return [`inspect target=run:${run} view=trace`];
116
+ return [`actors_inspect target=run:${run} view=trace`];
117
117
  }
118
118
  if (result.delivery === "queued" || result.delivery === "delivered") {
119
- return [`inspect target=run:${run} view=control`];
119
+ return [`actors_inspect target=run:${run} view=control`];
120
120
  }
121
121
  return [];
122
122
  }
123
123
  export function createControlToolDefinition(deps = {}) {
124
124
  return {
125
- name: "message",
125
+ name: "actors_control_apply",
126
126
  label: "Control",
127
- description: "Apply one Control action to an existing Run or the pi-actors runtime.",
127
+ description: "Apply one Run/runtime Control action. This is not chat or peer messaging.",
128
128
  parameters: Schema.objectSchema({
129
129
  target: Schema.stringSchema("Run target run:<id>, or literal runtime."),
130
130
  action: Schema.stringSchema("Lowercase ASCII Control action, at most 64 characters."),
@@ -214,7 +214,7 @@ export function createControlToolDefinition(deps = {}) {
214
214
  // Admission-era correlation is not current reply-scope proof after an awaited dispatch.
215
215
  // Display one whole caller-owned id or an explicit whole-id omission; the warning stays literal.
216
216
  const identity = controlSelected("run", run);
217
- const next = controlNextPart("inspect target=runtime view=runs");
217
+ const next = controlNextPart("actors_inspect target=runtime view=runs");
218
218
  const render = () => ({ ...identity.selected, action: request.action,
219
219
  result: "request failed", delivery: "unconfirmed", handling: "unconfirmed",
220
220
  warning: "Effects may already exist.\nDo not repeat automatically.", ...next.selected });
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Public register_tool behavior
3
- * Zones: runtime tool registration, tool registry mutation, register_tool schema
4
- * Owns the public register_tool definition that persists local agent capabilities
2
+ * Public actors_tool_register behavior
3
+ * Zones: runtime tool registration, tool registry mutation, actors_tool_register schema
4
+ * Owns the public actors_tool_register definition that persists local agent capabilities
5
5
  */
6
6
  import * as Registry from "./registry.ts";
7
7
  import * as Schema from "./schema.ts";
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Public register_tool behavior
3
- * Zones: runtime tool registration, tool registry mutation, register_tool schema
4
- * Owns the public register_tool definition that persists local agent capabilities
2
+ * Public actors_tool_register behavior
3
+ * Zones: runtime tool registration, tool registry mutation, actors_tool_register schema
4
+ * Owns the public actors_tool_register definition that persists local agent capabilities
5
5
  */
6
6
  import * as Prompts from "./prompts.js";
7
7
  import * as Registry from "./registry.js";
@@ -15,7 +15,7 @@ const objectSchema = Schema.objectSchema;
15
15
  const looseObjectSchema = Schema.looseObjectSchema;
16
16
  export function createRegisterToolDefinition(deps) {
17
17
  return {
18
- name: "register_tool",
18
+ name: "actors_tool_register",
19
19
  label: "Register Tool",
20
20
  description: Prompts.REGISTER_TOOL_DESCRIPTION,
21
21
  promptSnippet: Prompts.REGISTER_TOOL_PROMPT_SNIPPET,
@@ -28,7 +28,7 @@ export declare function formatYamlNotice(value: Record<string, string | number |
28
28
  /** Close one rendered YAML header; a separate opaque body gets exactly one delimiter pair. */
29
29
  export declare function frameMetadataDocument(header: string, body?: string): string;
30
30
  export declare function formatMarkdownField(key: string, value: string, depth?: number): string;
31
- export declare function formatInspectNextAction(target: string, view: string, verbose?: boolean): string;
31
+ export declare function formatInspectNextAction(target: string, view: string, verbose?: boolean, tool?: string): string;
32
32
  export declare function formatLiteralBlock(value: string): string;
33
33
  export type RunSnapshotRendering = {
34
34
  readonly state: "available";
@@ -163,8 +163,8 @@ export function formatMarkdownField(key, value, depth = 0) {
163
163
  ? `${indent}- ${key}:\n${indent} ${value.replaceAll(/\r\n|\r|\n/gu, newline => `${newline}${indent} `)}`
164
164
  : `${indent}- ${key}: ${value}`;
165
165
  }
166
- export function formatInspectNextAction(target, view, verbose) {
167
- return ["- next:", formatMarkdownField("tool", "inspect", 1), formatMarkdownField("target", target, 1),
166
+ export function formatInspectNextAction(target, view, verbose, tool = "inspect") {
167
+ return ["- next:", formatMarkdownField("tool", tool, 1), formatMarkdownField("target", target, 1),
168
168
  formatMarkdownField("view", view, 1), ...(verbose === undefined ? [] : [formatMarkdownField("verbose", String(verbose), 1)])].join("\n");
169
169
  }
170
170
  export function formatLiteralBlock(value) {
@@ -292,19 +292,19 @@ function launchModelPolicy(value) {
292
292
  }
293
293
  return fields;
294
294
  }
295
- const RUN_FILE_INVESTIGATION_COMMAND = "inspect target=runtime view=runs verbose=true";
295
+ const RUN_FILE_INVESTIGATION_COMMAND = "actors_inspect target=runtime view=runs verbose=true";
296
296
  /** Frozen older completion-format text: new launch layout must not change this shared packet helper. */
297
297
  export function formatRunFileNavigationHandoff() {
298
- return `Files (optional; current session):\n\`${RUN_FILE_INVESTIGATION_COMMAND}\``;
298
+ return "Files (optional; current session):\n`inspect target=runtime view=runs verbose=true`";
299
299
  }
300
300
  export function runNextActions(run) {
301
301
  const id = String(run ?? "").trim();
302
302
  if (!id)
303
303
  return [];
304
304
  return [
305
- `inspect target=run:${id} view=recipe`,
306
- `inspect target=run:${id} view=trace`,
307
- `inspect target=run:${id} view=control`,
305
+ `actors_inspect target=run:${id} view=recipe`,
306
+ `actors_inspect target=run:${id} view=trace`,
307
+ `actors_inspect target=run:${id} view=control`,
308
308
  RUN_FILE_INVESTIGATION_COMMAND,
309
309
  ];
310
310
  }
@@ -342,7 +342,7 @@ export function compactAsyncRunStatus(value) {
342
342
  fields.killed = true;
343
343
  if (status.draft_recipe)
344
344
  fields.draft_recipe = String(status.draft_recipe);
345
- const hint = { file_investigation: "optional; current session", next: { tool: "inspect", target: "runtime", view: "runs", verbose: true } };
345
+ const hint = { file_investigation: "optional; current session", next: { tool: "actors_inspect", target: "runtime", view: "runs", verbose: true } };
346
346
  const hasNavigation = Object.hasOwn(status, "file_navigation");
347
347
  let navigation;
348
348
  if (hasNavigation) {
@@ -504,23 +504,23 @@ export function recipeRegistryNextActions(summary, view) {
504
504
  : 0;
505
505
  const topAction = asRecord(summary.top_action);
506
506
  if (view !== "doctor" && (invalid > 0 || diagnostics > 0)) {
507
- actions.push("inspect target=recipes view=doctor");
507
+ actions.push("actors_inspect target=recipes view=doctor");
508
508
  }
509
509
  if (view === "doctor" && typeof topAction.action === "string") {
510
510
  actions.push(String(topAction.action));
511
511
  }
512
512
  if (summary.skill_recipe_catalog_partial === true) {
513
- actions.push("inspect target=recipes view=imports verbose=true");
513
+ actions.push("actors_inspect target=recipes view=imports verbose=true");
514
514
  }
515
515
  if ((Array.isArray(summary.retained) && summary.retained.length > 0) ||
516
516
  (Array.isArray(summary.retained_diagnostics) && summary.retained_diagnostics.length > 0)) {
517
- actions.push("inspect target=recipes view=summary verbose=true");
517
+ actions.push("actors_inspect target=recipes view=summary verbose=true");
518
518
  }
519
519
  if (drafts.length > 0) {
520
- actions.push("inspect target=recipes view=summary verbose=true");
520
+ actions.push("actors_inspect target=recipes view=summary verbose=true");
521
521
  const firstPath = typeof drafts[0]?.path === "string" ? drafts[0].path : undefined;
522
522
  if (firstPath)
523
- actions.push(`spawn file=${firstPath}`);
523
+ actions.push(`actors_run_spawn file=${firstPath}`);
524
524
  }
525
525
  return [...new Set(actions)].slice(0, 4);
526
526
  }
@@ -530,11 +530,11 @@ export function compactYamlDocument(value) {
530
530
  return `\n${text.replace(/\n$/u, "")}`;
531
531
  }
532
532
  function recipeInspectionHint(action) {
533
- if (action === "inspect target=recipes view=doctor")
533
+ if (action === "actors_inspect target=recipes view=doctor")
534
534
  return { view: "doctor" };
535
- if (action === "inspect target=recipes view=imports verbose=true")
535
+ if (action === "actors_inspect target=recipes view=imports verbose=true")
536
536
  return { view: "imports", verbose: true };
537
- if (action === "inspect target=recipes view=summary verbose=true")
537
+ if (action === "actors_inspect target=recipes view=summary verbose=true")
538
538
  return { view: "summary", verbose: true };
539
539
  return undefined;
540
540
  }
@@ -542,7 +542,7 @@ function recipeInspectionHint(action) {
542
542
  export function nextActionMetadata(action, byteBudget) {
543
543
  const hint = recipeInspectionHint(action);
544
544
  if (hint) {
545
- const typed = { next: { tool: "inspect", target: "recipes", ...hint } };
545
+ const typed = { next: { tool: "actors_inspect", target: "recipes", ...hint } };
546
546
  return { full: typed, selected: typed };
547
547
  }
548
548
  const instruction = selectYamlField("instruction", action, byteBudget);
@@ -551,7 +551,7 @@ export function nextActionMetadata(action, byteBudget) {
551
551
  /** Only these existing read-only inspection hints become action nodes; other advice stays content. */
552
552
  export function formatInspectionHint(action) {
553
553
  const hint = recipeInspectionHint(action);
554
- return hint ? formatInspectNextAction("recipes", hint.view, hint.verbose) : formatMarkdownField("instruction", action);
554
+ return hint ? formatInspectNextAction("recipes", hint.view, hint.verbose, "actors_inspect") : formatMarkdownField("instruction", action);
555
555
  }
556
556
  export function compactRecipeRegistry(summary) {
557
557
  const count = (key, source) => Array.isArray(source) ? source.length : 0;
@@ -90,7 +90,7 @@ function runIdFromTarget(target) {
90
90
  }
91
91
  export function createSpawnToolDefinition() {
92
92
  return {
93
- name: "spawn",
93
+ name: "actors_run_spawn",
94
94
  label: "Spawn",
95
95
  description: "Create one controllable Run from a Recipe file or inline command template.",
96
96
  parameters: Schema.objectSchema({
@@ -25,4 +25,11 @@ export interface CoreActorToolDefinitionDeps<TContext extends RuntimeToolContext
25
25
  }
26
26
  export declare function resolveActiveRuntimeTool(name: string, activeTools: Pick<Map<string, unknown>, "has">, getDefinition: (name: string) => unknown): unknown;
27
27
  export declare const RESERVED_TOOL_NAMES: Set<string>;
28
+ export declare const LEGACY_TOOL_NAMES: {
29
+ readonly register_tool: "actors_tool_register";
30
+ readonly spawn: "actors_run_spawn";
31
+ readonly message: "actors_control_apply";
32
+ readonly inspect: "actors_inspect";
33
+ };
34
+ export declare function createLegacyActorToolDefinitions(definitions: ActorToolDefinition[]): ActorToolDefinition[];
28
35
  export declare function createCoreActorToolDefinitions<TContext extends RuntimeToolContext>(deps: CoreActorToolDefinitionDeps<TContext>): ActorToolDefinition[];
package/dist/lib/tools.js CHANGED
@@ -18,11 +18,36 @@ export const RESERVED_TOOL_NAMES = new Set([
18
18
  "find",
19
19
  "grep",
20
20
  "ls",
21
+ "actors_tool_register",
22
+ "actors_run_spawn",
23
+ "actors_control_apply",
24
+ "actors_inspect",
21
25
  "register_tool",
22
26
  "message",
23
27
  "spawn",
24
28
  "inspect",
25
29
  ]);
30
+ // Compatibility dispatch is registered but omitted from model declarations and normal
31
+ // callable listings. Active legacy names still execute saved native tool calls.
32
+ export const LEGACY_TOOL_NAMES = {
33
+ register_tool: "actors_tool_register",
34
+ spawn: "actors_run_spawn",
35
+ message: "actors_control_apply",
36
+ inspect: "actors_inspect",
37
+ };
38
+ export function createLegacyActorToolDefinitions(definitions) {
39
+ return Object.entries(LEGACY_TOOL_NAMES).map(([name, canonical]) => {
40
+ const definition = definitions.find((tool) => tool.name === canonical);
41
+ if (!definition)
42
+ throw new Error(`Missing canonical actor tool: ${canonical}`);
43
+ return {
44
+ ...definition, name, exposure: "deferred",
45
+ description: `Compatibility dispatch for ${canonical}; use the canonical tool for new calls.`,
46
+ promptSnippet: undefined, promptGuidelines: undefined,
47
+ prepareLoadout: () => ({ hiddenDeclarations: Object.keys(LEGACY_TOOL_NAMES) }),
48
+ };
49
+ });
50
+ }
26
51
  export function createCoreActorToolDefinitions(deps) {
27
52
  return [
28
53
  ToolsRegister.createRegisterToolDefinition({