@llblab/pi-actors 0.46.1 → 0.48.0
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 +7 -5
- package/BACKLOG.md +0 -582
- package/CHANGELOG.md +16 -0
- package/README.md +8 -1
- package/banner.jpg +0 -0
- package/dist/lib/prompts.d.ts +6 -5
- package/dist/lib/prompts.js +22 -23
- package/dist/lib/recipes-discovery.d.ts +4 -0
- package/dist/lib/recipes-discovery.js +13 -1
- package/dist/lib/recipes-references.js +10 -6
- package/dist/lib/registry.d.ts +15 -11
- package/dist/lib/registry.js +195 -25
- package/dist/lib/runtime.js +37 -2
- package/dist/lib/tools-inspect.js +156 -12
- package/dist/lib/tools-register.js +2 -1
- package/dist/lib/tools-response.js +5 -1
- package/dist/scripts/conformance.mjs +1 -0
- package/dist/skills/actors/SKILL.md +87 -65
- package/dist/skills/actors/references/diagnostics.md +44 -0
- package/dist/skills/actors/references/persistent-tools.md +74 -0
- package/dist/skills/actors/references/recipes.md +51 -0
- package/dist/skills/actors/references/runs.md +39 -0
- package/dist/skills/artifacts/SKILL.md +24 -7
- package/dist/skills/media/SKILL.md +35 -7
- package/dist/skills/project-work/SKILL.md +28 -7
- package/dist/skills/recipe-memory/SKILL.md +27 -7
- package/dist/skills/swarm/SKILL.md +56 -437
- package/dist/skills/swarm/references/development-swarm.md +118 -525
- package/dist/skills/swarm/references/review-swarms.md +115 -0
- package/docs/README.md +5 -5
- package/docs/recipe-library.md +15 -10
- package/docs/tool-registry.md +10 -4
- package/lib/prompts.ts +24 -24
- package/lib/recipes-discovery.ts +22 -1
- package/lib/recipes-references.ts +14 -6
- package/lib/registry.ts +288 -51
- package/lib/runtime.ts +41 -2
- package/lib/tools-inspect.ts +202 -10
- package/lib/tools-register.ts +4 -3
- package/lib/tools-response.ts +5 -1
- package/package.json +1 -1
- package/scripts/conformance.mjs +1 -0
- package/skills/actors/SKILL.md +87 -65
- package/skills/actors/references/diagnostics.md +44 -0
- package/skills/actors/references/persistent-tools.md +74 -0
- package/skills/actors/references/recipes.md +51 -0
- package/skills/actors/references/runs.md +39 -0
- package/skills/artifacts/SKILL.md +24 -7
- package/skills/media/SKILL.md +35 -7
- package/skills/project-work/SKILL.md +28 -7
- package/skills/recipe-memory/SKILL.md +27 -7
- package/skills/swarm/SKILL.md +56 -437
- package/skills/swarm/references/development-swarm.md +118 -525
- package/skills/swarm/references/review-swarms.md +115 -0
package/lib/registry.ts
CHANGED
|
@@ -27,28 +27,30 @@ export interface RegisterToolInput {
|
|
|
27
27
|
description?: string;
|
|
28
28
|
async?: boolean;
|
|
29
29
|
template?: CommandTemplates.CommandTemplateValue | null;
|
|
30
|
+
from?: string;
|
|
31
|
+
defaults?: Record<string, unknown>;
|
|
30
32
|
draft?: string;
|
|
31
33
|
args?: string;
|
|
32
34
|
update?: boolean;
|
|
33
|
-
values?: Record<string, unknown>;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export interface RegisterToolResultDetails {
|
|
37
38
|
active_tool?: boolean;
|
|
38
39
|
activation?: "current_session" | "unverified";
|
|
40
|
+
activation_boundary?: string;
|
|
39
41
|
args?: string[];
|
|
40
42
|
async?: boolean;
|
|
41
43
|
callable_now?: boolean;
|
|
42
|
-
config?: string;
|
|
43
44
|
defaults?: Record<string, string>;
|
|
44
|
-
draft?: string;
|
|
45
45
|
host_registered?: boolean;
|
|
46
|
+
next_actions?: string[];
|
|
47
|
+
optional_args?: string[];
|
|
46
48
|
persisted?: boolean;
|
|
47
49
|
promoted?: boolean;
|
|
48
50
|
registry_active?: boolean;
|
|
51
|
+
required_args?: string[];
|
|
49
52
|
resolved?: boolean;
|
|
50
|
-
|
|
51
|
-
template?: CommandTemplates.CommandTemplateValue;
|
|
53
|
+
source?: string;
|
|
52
54
|
templateWarnings?: string[];
|
|
53
55
|
tool: string;
|
|
54
56
|
validated?: boolean;
|
|
@@ -59,6 +61,13 @@ export interface RegisterToolResult {
|
|
|
59
61
|
details: RegisterToolResultDetails;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
interface RuntimeActivation {
|
|
65
|
+
active_tool: boolean;
|
|
66
|
+
activation: "current_session" | "unverified";
|
|
67
|
+
callable_now: boolean;
|
|
68
|
+
host_registered: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
export interface RegisterToolRuntimeDeps<TContext> {
|
|
63
72
|
configPath: string;
|
|
64
73
|
recipeRoot?: string;
|
|
@@ -70,14 +79,7 @@ export interface RegisterToolRuntimeDeps<TContext> {
|
|
|
70
79
|
message: string,
|
|
71
80
|
type: "info" | "warning" | "error",
|
|
72
81
|
) => void;
|
|
73
|
-
registerRuntimeTool: (cfg: Config.RegisteredTool) =>
|
|
74
|
-
| {
|
|
75
|
-
active_tool: boolean;
|
|
76
|
-
activation: "current_session" | "unverified";
|
|
77
|
-
callable_now: boolean;
|
|
78
|
-
host_registered: boolean;
|
|
79
|
-
}
|
|
80
|
-
| void;
|
|
82
|
+
registerRuntimeTool: (cfg: Config.RegisteredTool) => RuntimeActivation | void;
|
|
81
83
|
reservedToolNames: Set<string>;
|
|
82
84
|
setActiveTools: (toolNames: string[]) => void;
|
|
83
85
|
}
|
|
@@ -117,6 +119,54 @@ function getToolRecipePath<TContext>(
|
|
|
117
119
|
return join(getRecipeRoot(deps), `${name}.json`);
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
function activationBoundary(
|
|
123
|
+
activation: RuntimeActivation | undefined,
|
|
124
|
+
): string {
|
|
125
|
+
if (activation?.callable_now) return "current_session";
|
|
126
|
+
if (!activation) return "session_activation_unverified";
|
|
127
|
+
if (!activation.host_registered) return "host_registration";
|
|
128
|
+
if (!activation.active_tool) return "active_tool_set";
|
|
129
|
+
return "callability_verification";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function recipeDoctorAction(source: string): string {
|
|
133
|
+
return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(source)
|
|
134
|
+
? `inspect target=recipes view=doctor identity=${source}`
|
|
135
|
+
: "inspect target=recipes view=doctor";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function registrationNextActions(
|
|
139
|
+
tool: string,
|
|
140
|
+
source: string,
|
|
141
|
+
callableNow: boolean,
|
|
142
|
+
): string[] {
|
|
143
|
+
if (callableNow) {
|
|
144
|
+
return [
|
|
145
|
+
`call tool ${tool}`,
|
|
146
|
+
`inspect target=tool:${tool} view=status`,
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
return [
|
|
150
|
+
`inspect target=tool:${tool} view=status`,
|
|
151
|
+
...(source === "template" || source === "draft"
|
|
152
|
+
? []
|
|
153
|
+
: [recipeDoctorAction(source)]),
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function activeSkillSummary(
|
|
158
|
+
resolutionContext: RecipesContext.RecipeResolutionContext,
|
|
159
|
+
): string {
|
|
160
|
+
const names = Object.keys(
|
|
161
|
+
RecipesReferences.getActiveSkillRecipeNamespaces(
|
|
162
|
+
resolutionContext.activeSkills,
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
.sort()
|
|
166
|
+
.slice(0, 20);
|
|
167
|
+
return names.length > 0 ? names.join(", ") : "<none>";
|
|
168
|
+
}
|
|
169
|
+
|
|
120
170
|
function assertDraftPath<TContext>(
|
|
121
171
|
deps: RegisterToolRuntimeDeps<TContext>,
|
|
122
172
|
draft: string,
|
|
@@ -199,23 +249,38 @@ function promoteDraftRecipe<TContext>(
|
|
|
199
249
|
resolutionContext,
|
|
200
250
|
).tool!;
|
|
201
251
|
tools.set(name, promoted);
|
|
202
|
-
deps.registerRuntimeTool(promoted);
|
|
252
|
+
const activation = deps.registerRuntimeTool(promoted) ?? undefined;
|
|
253
|
+
const argSummary = RecipesDiscovery.summarizeRegisteredToolArgs(promoted);
|
|
254
|
+
const callableNow = activation?.callable_now ?? false;
|
|
255
|
+
const nextActions = registrationNextActions(name, "draft", callableNow);
|
|
203
256
|
deps.notify(ctx, `Promoted draft recipe: ${name}`, "info");
|
|
204
257
|
return {
|
|
205
258
|
content: [
|
|
206
259
|
textContent(
|
|
207
260
|
ExecutionOutput.formatToolText(
|
|
208
|
-
`${existing ? "Updated" : "Registered"} tool "${name}" from draft
|
|
261
|
+
`${existing ? "Updated" : "Registered"} tool "${name}" from draft; callable_now=${callableNow}; next=${nextActions[0]}.`,
|
|
209
262
|
),
|
|
210
263
|
),
|
|
211
264
|
],
|
|
212
265
|
details: {
|
|
266
|
+
active_tool: activation?.active_tool ?? false,
|
|
267
|
+
activation: activation?.activation ?? "unverified",
|
|
268
|
+
activation_boundary: activationBoundary(activation),
|
|
213
269
|
args: promoted.args,
|
|
214
|
-
|
|
215
|
-
|
|
270
|
+
callable_now: callableNow,
|
|
271
|
+
defaults: promoted.defaults,
|
|
272
|
+
host_registered: activation?.host_registered ?? false,
|
|
273
|
+
next_actions: nextActions,
|
|
274
|
+
optional_args: argSummary.optional,
|
|
275
|
+
persisted: true,
|
|
216
276
|
promoted: true,
|
|
277
|
+
registry_active: tools.get(name) === promoted,
|
|
278
|
+
required_args: argSummary.required,
|
|
279
|
+
resolved: true,
|
|
280
|
+
source: "draft",
|
|
217
281
|
tool: name,
|
|
218
|
-
|
|
282
|
+
validated: true,
|
|
283
|
+
},
|
|
219
284
|
};
|
|
220
285
|
}
|
|
221
286
|
|
|
@@ -261,7 +326,7 @@ function deleteTool<TContext>(
|
|
|
261
326
|
),
|
|
262
327
|
),
|
|
263
328
|
],
|
|
264
|
-
details: {
|
|
329
|
+
details: { tool: name },
|
|
265
330
|
};
|
|
266
331
|
}
|
|
267
332
|
|
|
@@ -296,9 +361,38 @@ function readAuthoritativeStoredTool<TContext>(
|
|
|
296
361
|
}
|
|
297
362
|
}
|
|
298
363
|
|
|
364
|
+
function looksLikeRecipeReference(value: string): boolean {
|
|
365
|
+
const trimmed = value.trim();
|
|
366
|
+
return (
|
|
367
|
+
trimmed.endsWith(".json") ||
|
|
368
|
+
trimmed.endsWith(".md") ||
|
|
369
|
+
/^[^/:\\]+\/[^/:\\]+$/.test(trimmed)
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function assertTemplateIsNotNestedRecipe(value: unknown): void {
|
|
374
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
375
|
+
const record = value as Record<string, unknown>;
|
|
376
|
+
if (
|
|
377
|
+
typeof record.template === "string" &&
|
|
378
|
+
looksLikeRecipeReference(record.template) &&
|
|
379
|
+
(Object.keys(record).length === 1 ||
|
|
380
|
+
["artifacts", "async", "control", "description", "imports", "values"].some(
|
|
381
|
+
(key) => Object.hasOwn(record, key),
|
|
382
|
+
))
|
|
383
|
+
) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
ExecutionOutput.formatToolText(
|
|
386
|
+
"Nested Recipe-shaped templates are not a register_tool source mode. Next: retry with from=<skill>/<recipe> and defaults={...}.",
|
|
387
|
+
),
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
299
392
|
function getInputTemplate(
|
|
300
393
|
value: CommandTemplates.CommandTemplateValue | null | undefined,
|
|
301
394
|
): CommandTemplates.CommandTemplateValue | null | undefined {
|
|
395
|
+
assertTemplateIsNotNestedRecipe(value);
|
|
302
396
|
if (typeof value === "string") return value.trim();
|
|
303
397
|
if (value === null || value === undefined) return value;
|
|
304
398
|
if (Array.isArray(value)) {
|
|
@@ -344,6 +438,55 @@ function getRegistrationResolutionContext<TContext>(
|
|
|
344
438
|
);
|
|
345
439
|
}
|
|
346
440
|
|
|
441
|
+
function normalizeDefaults(value: unknown): Record<string, unknown> | undefined {
|
|
442
|
+
if (value === undefined) return undefined;
|
|
443
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
ExecutionOutput.formatToolText("Tool defaults must be an object."),
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
return { ...(value as Record<string, unknown>) };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function normalizeFromReference(
|
|
452
|
+
value: string,
|
|
453
|
+
resolutionContext: RecipesContext.RecipeResolutionContext,
|
|
454
|
+
): string {
|
|
455
|
+
const trimmed = value.trim();
|
|
456
|
+
if (!trimmed) {
|
|
457
|
+
throw new Error(
|
|
458
|
+
ExecutionOutput.formatToolText(
|
|
459
|
+
"Tool from must be a non-empty Recipe reference. Next: retry register_tool with from=<skill>/<recipe> or an explicit .json/.md path.",
|
|
460
|
+
),
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
if (trimmed.startsWith("std:")) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
ExecutionOutput.formatToolText(
|
|
466
|
+
"std: Recipe references were removed; use <skill>/<recipe> or an explicit .json/.md file path.",
|
|
467
|
+
),
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
if (trimmed.startsWith("skill:")) {
|
|
471
|
+
throw new Error(
|
|
472
|
+
ExecutionOutput.formatToolText(
|
|
473
|
+
`skill: Recipe references were removed; use ${trimmed.slice("skill:".length)} without a prefix.`,
|
|
474
|
+
),
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if (trimmed.endsWith(".json") || trimmed.endsWith(".md")) {
|
|
478
|
+
return resolve(resolutionContext.cwd, trimmed);
|
|
479
|
+
}
|
|
480
|
+
if (!looksLikeRecipeReference(trimmed)) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
ExecutionOutput.formatToolText(
|
|
483
|
+
"Tool from must use <skill>/<recipe> or an explicit .json/.md file path.",
|
|
484
|
+
),
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
return trimmed;
|
|
488
|
+
}
|
|
489
|
+
|
|
347
490
|
function buildAuthoredRecipe(
|
|
348
491
|
input: RegisterToolInput,
|
|
349
492
|
existing: Config.RegisteredTool | undefined,
|
|
@@ -355,14 +498,40 @@ function buildAuthoredRecipe(
|
|
|
355
498
|
: Schema.parseToolArgDeclarations(input.args);
|
|
356
499
|
if (explicitArgs?.error)
|
|
357
500
|
throw new Error(ExecutionOutput.formatToolText(explicitArgs.error));
|
|
501
|
+
const from = typeof input.from === "string" ? input.from.trim() : "";
|
|
358
502
|
const description = (input.description ?? existing?.description ?? "").trim();
|
|
359
|
-
if (!description) {
|
|
503
|
+
if (!description && !from) {
|
|
360
504
|
throw new Error(
|
|
361
505
|
ExecutionOutput.formatToolText(
|
|
362
|
-
"Tool description is required
|
|
506
|
+
"Tool description is required for command-template registration.",
|
|
363
507
|
),
|
|
364
508
|
);
|
|
365
509
|
}
|
|
510
|
+
const inputDefaults = normalizeDefaults(input.defaults);
|
|
511
|
+
if (from) {
|
|
512
|
+
const runtimeOwnedDefault = Object.keys(inputDefaults ?? {}).find((key) =>
|
|
513
|
+
RecipesReferences.isRuntimeOwnedRecipeInput(key),
|
|
514
|
+
);
|
|
515
|
+
if (runtimeOwnedDefault) {
|
|
516
|
+
throw new Error(
|
|
517
|
+
ExecutionOutput.formatToolText(
|
|
518
|
+
`Tool defaults cannot set runtime-owned input: ${runtimeOwnedDefault}. Next: remove that default and retry register_tool; the runtime supplies it.`,
|
|
519
|
+
),
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
const authored: Record<string, unknown> = {
|
|
523
|
+
...(description ? { description } : {}),
|
|
524
|
+
template: from,
|
|
525
|
+
};
|
|
526
|
+
const retainedDefaults =
|
|
527
|
+
inputDefaults ??
|
|
528
|
+
(existingRecipe?.defaults && typeof existingRecipe.defaults === "object"
|
|
529
|
+
? (existingRecipe.defaults as Record<string, unknown>)
|
|
530
|
+
: undefined);
|
|
531
|
+
if (retainedDefaults && Object.keys(retainedDefaults).length > 0)
|
|
532
|
+
authored.defaults = retainedDefaults;
|
|
533
|
+
return authored;
|
|
534
|
+
}
|
|
366
535
|
const template = getInputTemplate(input.template);
|
|
367
536
|
if (template === null) {
|
|
368
537
|
throw new Error(
|
|
@@ -386,21 +555,71 @@ function buildAuthoredRecipe(
|
|
|
386
555
|
if (typeof input.async === "boolean") authored.async = input.async;
|
|
387
556
|
if (explicitArgs) {
|
|
388
557
|
authored.args = explicitArgs.declarations;
|
|
389
|
-
|
|
390
|
-
|
|
558
|
+
const defaults = { ...explicitArgs.defaults, ...(inputDefaults ?? {}) };
|
|
559
|
+
if (Object.keys(defaults).length > 0) authored.defaults = defaults;
|
|
560
|
+
else delete authored.defaults;
|
|
561
|
+
} else if (inputDefaults !== undefined) {
|
|
562
|
+
if (Object.keys(inputDefaults).length > 0) authored.defaults = inputDefaults;
|
|
391
563
|
else delete authored.defaults;
|
|
392
564
|
}
|
|
393
|
-
if (input.values && typeof input.values === "object")
|
|
394
|
-
authored.values = input.values;
|
|
395
565
|
return authored;
|
|
396
566
|
}
|
|
397
567
|
|
|
568
|
+
function assertRegisterToolInputModes(input: RegisterToolInput): void {
|
|
569
|
+
const record = input as RegisterToolInput & Record<string, unknown>;
|
|
570
|
+
if (Object.hasOwn(record, "values")) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
ExecutionOutput.formatToolText(
|
|
573
|
+
"register_tool values was removed. Next: retry register_tool with defaults for caller inputs, or use from=<skill>/<recipe> for maintained composition.",
|
|
574
|
+
),
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
const fromProvided = Object.hasOwn(record, "from");
|
|
578
|
+
if (
|
|
579
|
+
fromProvided &&
|
|
580
|
+
(typeof input.from !== "string" || input.from.trim().length === 0)
|
|
581
|
+
) {
|
|
582
|
+
throw new Error(
|
|
583
|
+
ExecutionOutput.formatToolText(
|
|
584
|
+
"Tool from must be a non-empty Recipe reference. Next: retry register_tool with from=<skill>/<recipe> or an explicit .json/.md path.",
|
|
585
|
+
),
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const draftProvided =
|
|
589
|
+
typeof input.draft === "string" && input.draft.trim().length > 0;
|
|
590
|
+
const templateProvided = Object.hasOwn(record, "template");
|
|
591
|
+
const modeCount =
|
|
592
|
+
Number(fromProvided) + Number(draftProvided) + Number(templateProvided);
|
|
593
|
+
if (modeCount > 1) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
ExecutionOutput.formatToolText(
|
|
596
|
+
"Use exactly one register_tool source mode: from, template, or draft. Next: remove the extra source fields and retry register_tool.",
|
|
597
|
+
),
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
if (
|
|
601
|
+
fromProvided &&
|
|
602
|
+
(Object.hasOwn(record, "args") || Object.hasOwn(record, "async"))
|
|
603
|
+
) {
|
|
604
|
+
throw new Error(
|
|
605
|
+
ExecutionOutput.formatToolText(
|
|
606
|
+
"register_tool from inherits args and async behavior. Next: retry register_tool with from and optional defaults only; omit args and async.",
|
|
607
|
+
),
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
398
612
|
function executeRegisterToolUnlocked<TContext>(
|
|
399
613
|
params: unknown,
|
|
400
614
|
ctx: TContext,
|
|
401
615
|
deps: RegisterToolRuntimeDeps<TContext>,
|
|
402
616
|
): RegisterToolResult {
|
|
403
|
-
const
|
|
617
|
+
const rawInput =
|
|
618
|
+
params && typeof params === "object"
|
|
619
|
+
? (params as RegisterToolInput)
|
|
620
|
+
: ({} as RegisterToolInput);
|
|
621
|
+
assertRegisterToolInputModes(rawInput);
|
|
622
|
+
let input = rawInput;
|
|
404
623
|
if (!input.name) return listTools(deps);
|
|
405
624
|
const name = Identity.normalizeToolName(input.name);
|
|
406
625
|
if (!name)
|
|
@@ -413,6 +632,27 @@ function executeRegisterToolUnlocked<TContext>(
|
|
|
413
632
|
if (typeof input.draft === "string" && input.draft.trim()) {
|
|
414
633
|
return promoteDraftRecipe(name, input, ctx, deps);
|
|
415
634
|
}
|
|
635
|
+
const resolutionContext = getRegistrationResolutionContext(ctx, deps);
|
|
636
|
+
const requestedSource =
|
|
637
|
+
typeof rawInput.from === "string" ? rawInput.from.trim() : undefined;
|
|
638
|
+
if (typeof input.from === "string") {
|
|
639
|
+
try {
|
|
640
|
+
input = {
|
|
641
|
+
...input,
|
|
642
|
+
from: normalizeFromReference(input.from, resolutionContext),
|
|
643
|
+
};
|
|
644
|
+
} catch (error) {
|
|
645
|
+
const reason = error instanceof Error ? error.message.trim() : String(error);
|
|
646
|
+
const diagnosticSource = requestedSource?.startsWith("skill:")
|
|
647
|
+
? requestedSource.slice("skill:".length)
|
|
648
|
+
: requestedSource ?? "";
|
|
649
|
+
throw new Error(
|
|
650
|
+
ExecutionOutput.formatToolText(
|
|
651
|
+
`Recipe source "${requestedSource}" is invalid: ${reason}. Active Skills: ${activeSkillSummary(resolutionContext)}. Next: ${recipeDoctorAction(diagnosticSource)}`,
|
|
652
|
+
),
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
416
656
|
const templateProvided = Object.hasOwn(input, "template");
|
|
417
657
|
const template = getInputTemplate(input.template);
|
|
418
658
|
if (templateProvided && (template === null || template === ""))
|
|
@@ -429,10 +669,10 @@ function executeRegisterToolUnlocked<TContext>(
|
|
|
429
669
|
),
|
|
430
670
|
);
|
|
431
671
|
}
|
|
432
|
-
if (template === undefined && !existing) {
|
|
672
|
+
if (template === undefined && !input.from && !existing) {
|
|
433
673
|
throw new Error(
|
|
434
674
|
ExecutionOutput.formatToolText(
|
|
435
|
-
"
|
|
675
|
+
"New registration requires exactly one source mode: from, template, or draft. Next: retry register_tool with one source field.",
|
|
436
676
|
),
|
|
437
677
|
);
|
|
438
678
|
}
|
|
@@ -442,29 +682,20 @@ function executeRegisterToolUnlocked<TContext>(
|
|
|
442
682
|
existing,
|
|
443
683
|
authoritative.recipe,
|
|
444
684
|
);
|
|
445
|
-
const resolutionContext = getRegistrationResolutionContext(ctx, deps);
|
|
446
685
|
const candidate = RecipesDiscovery.admitUserRecipe(
|
|
447
686
|
recipePath,
|
|
448
687
|
resolutionContext,
|
|
449
688
|
authoredRecipe,
|
|
450
689
|
);
|
|
451
690
|
if (!candidate.validated || !candidate.tool) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
);
|
|
691
|
+
const sourceDiagnostic = requestedSource
|
|
692
|
+
? `Recipe source "${requestedSource}" validation failed: ${candidate.diagnostics.join("; ")}. Active Skills: ${activeSkillSummary(resolutionContext)}. Next: ${recipeDoctorAction(requestedSource)}`
|
|
693
|
+
: `Tool recipe validation failed: ${candidate.diagnostics.join("; ")}`;
|
|
694
|
+
throw new Error(ExecutionOutput.formatToolText(sourceDiagnostic));
|
|
457
695
|
}
|
|
458
696
|
const activeBefore = deps.getActiveTools();
|
|
459
697
|
let persisted = false;
|
|
460
|
-
let activation:
|
|
461
|
-
| {
|
|
462
|
-
active_tool: boolean;
|
|
463
|
-
activation: "current_session" | "unverified";
|
|
464
|
-
callable_now: boolean;
|
|
465
|
-
host_registered: boolean;
|
|
466
|
-
}
|
|
467
|
-
| undefined;
|
|
698
|
+
let activation: RuntimeActivation | undefined;
|
|
468
699
|
let cfg: Config.RegisteredTool;
|
|
469
700
|
try {
|
|
470
701
|
persistToolRecipe(deps, name, authoredRecipe);
|
|
@@ -488,7 +719,7 @@ function executeRegisterToolUnlocked<TContext>(
|
|
|
488
719
|
!activation.callable_now)
|
|
489
720
|
) {
|
|
490
721
|
throw new Error(
|
|
491
|
-
`
|
|
722
|
+
`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.`,
|
|
492
723
|
);
|
|
493
724
|
}
|
|
494
725
|
} catch (error) {
|
|
@@ -520,31 +751,37 @@ function executeRegisterToolUnlocked<TContext>(
|
|
|
520
751
|
templateWarnings.length > 0
|
|
521
752
|
? `\nWarnings:\n${templateWarnings.map((warning) => `- ${warning}`).join("\n")}`
|
|
522
753
|
: "";
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
754
|
+
const callableNow = activation?.callable_now ?? false;
|
|
755
|
+
const source = requestedSource ?? "template";
|
|
756
|
+
const argSummary = RecipesDiscovery.summarizeRegisteredToolArgs(cfg);
|
|
757
|
+
const nextActions = registrationNextActions(name, source, callableNow);
|
|
758
|
+
const outcomeText = callableNow
|
|
759
|
+
? `${existing ? "Updated" : "Registered"} tool "${name}" from ${source}; callable_now=true`
|
|
760
|
+
: `Persisted tool "${name}" from ${source}; callable_now=false; activation_boundary=${activationBoundary(activation)}`;
|
|
526
761
|
return {
|
|
527
762
|
content: [
|
|
528
763
|
textContent(
|
|
529
764
|
ExecutionOutput.formatToolText(
|
|
530
|
-
`${outcomeText} (
|
|
765
|
+
`${outcomeText}; required=${Schema.formatToolArgs(argSummary.required)}; optional=${Schema.formatToolArgs(argSummary.optional)}; next=${nextActions[0]}.${warningText}`,
|
|
531
766
|
),
|
|
532
767
|
),
|
|
533
768
|
],
|
|
534
769
|
details: {
|
|
535
770
|
active_tool: activation?.active_tool ?? false,
|
|
536
771
|
activation: activation?.activation ?? "unverified",
|
|
772
|
+
activation_boundary: activationBoundary(activation),
|
|
537
773
|
args: cfg.args,
|
|
538
|
-
callable_now:
|
|
539
|
-
config: recipePath,
|
|
774
|
+
callable_now: callableNow,
|
|
540
775
|
defaults: cfg.defaults,
|
|
541
776
|
host_registered: activation?.host_registered ?? false,
|
|
777
|
+
next_actions: nextActions,
|
|
778
|
+
optional_args: argSummary.optional,
|
|
542
779
|
persisted: true,
|
|
543
780
|
registry_active: tools.get(name) === cfg,
|
|
781
|
+
required_args: argSummary.required,
|
|
544
782
|
resolved: true,
|
|
783
|
+
source,
|
|
545
784
|
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
546
|
-
...(cfg.recipe?.name ? { recipeName: cfg.recipe.name } : {}),
|
|
547
|
-
...(cfg.template ? { template: cfg.template } : {}),
|
|
548
785
|
...(templateWarnings.length > 0 ? { templateWarnings } : {}),
|
|
549
786
|
tool: name,
|
|
550
787
|
validated: true,
|
package/lib/runtime.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { RegisteredToolExec } from "./execution.ts";
|
|
|
12
12
|
import * as Paths from "./paths.ts";
|
|
13
13
|
import type { RecipeResolutionContext } from "./recipes-context.ts";
|
|
14
14
|
import * as RecipesDiscovery from "./recipes-discovery.ts";
|
|
15
|
+
import * as RecipesReferences from "./recipes-references.ts";
|
|
15
16
|
import * as RecipesUsage from "./recipes-usage.ts";
|
|
16
17
|
import * as ToolsLocal from "./tools-local.ts";
|
|
17
18
|
|
|
@@ -111,6 +112,25 @@ export function createAutoToolsRuntime(
|
|
|
111
112
|
template: cfg.template,
|
|
112
113
|
});
|
|
113
114
|
}
|
|
115
|
+
function registeredToolSource(cfg: Config.RegisteredTool): string {
|
|
116
|
+
const raw = cfg.sourcePath
|
|
117
|
+
? RecipesReferences.readRawRecipeConfig(cfg.sourcePath)
|
|
118
|
+
: undefined;
|
|
119
|
+
const template = raw?.template;
|
|
120
|
+
if (
|
|
121
|
+
typeof template === "string" &&
|
|
122
|
+
/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(template)
|
|
123
|
+
) {
|
|
124
|
+
return template;
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
127
|
+
typeof template === "string" &&
|
|
128
|
+
(template.endsWith(".json") || template.endsWith(".md"))
|
|
129
|
+
) {
|
|
130
|
+
return `<explicit-file:${basename(template)}>`;
|
|
131
|
+
}
|
|
132
|
+
return "template";
|
|
133
|
+
}
|
|
114
134
|
function deactivateMissingRuntimeTools(activeNames: Set<string>): void {
|
|
115
135
|
const stale = [...runtimeTools].filter((name) => !activeNames.has(name));
|
|
116
136
|
if (stale.length === 0) return;
|
|
@@ -187,6 +207,7 @@ export function createAutoToolsRuntime(
|
|
|
187
207
|
lines.push("Other registry diagnostics:");
|
|
188
208
|
lines.push(...other.map((warning) => `• ${warning}`));
|
|
189
209
|
}
|
|
210
|
+
lines.push("Next: inspect target=recipes view=doctor");
|
|
190
211
|
return `${lines.join("\n")}\n`;
|
|
191
212
|
}
|
|
192
213
|
function loadTools(
|
|
@@ -248,9 +269,27 @@ export function createAutoToolsRuntime(
|
|
|
248
269
|
const usage = cfg.sourcePath
|
|
249
270
|
? RecipesUsage.readRecipeUsage(cfg.sourcePath)
|
|
250
271
|
: undefined;
|
|
272
|
+
const activation = activationFor(name);
|
|
273
|
+
const args = RecipesDiscovery.summarizeRegisteredToolArgs(cfg);
|
|
251
274
|
return {
|
|
252
|
-
...
|
|
253
|
-
|
|
275
|
+
...activation,
|
|
276
|
+
activation_boundary: activation.callable_now
|
|
277
|
+
? "current_session"
|
|
278
|
+
: !activation.host_registered
|
|
279
|
+
? "host_registration"
|
|
280
|
+
: "active_tool_set",
|
|
281
|
+
persisted: Boolean(cfg.sourcePath),
|
|
282
|
+
registry_active: true,
|
|
283
|
+
source: registeredToolSource(cfg),
|
|
284
|
+
required_args: args.required,
|
|
285
|
+
optional_args: args.optional,
|
|
286
|
+
next_actions: activation.callable_now
|
|
287
|
+
? [`call tool ${name}`]
|
|
288
|
+
: [
|
|
289
|
+
`register_tool name=${name} update=true`,
|
|
290
|
+
`inspect target=tool:${name} view=status`,
|
|
291
|
+
],
|
|
292
|
+
...(usage?.launch_kind ? { launch_kind: usage.launch_kind } : {}),
|
|
254
293
|
spawn_calls: Number(usage?.spawn_calls ?? 0),
|
|
255
294
|
tool_calls: Number(usage?.tool_calls ?? 0),
|
|
256
295
|
};
|