@databricks/appkit 0.66.1 → 0.67.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/dist/appkit/package.js +1 -1
- package/dist/core/agent/frontmatter.js +23 -0
- package/dist/core/agent/frontmatter.js.map +1 -0
- package/dist/core/agent/load-agents.d.ts.map +1 -1
- package/dist/core/agent/load-agents.js +36 -6
- package/dist/core/agent/load-agents.js.map +1 -1
- package/dist/core/agent/skills/index.js +7 -0
- package/dist/core/agent/skills/load-skills.js +78 -0
- package/dist/core/agent/skills/load-skills.js.map +1 -0
- package/dist/core/agent/skills/parse-skill.js +69 -0
- package/dist/core/agent/skills/parse-skill.js.map +1 -0
- package/dist/core/agent/skills/read-resource.js +31 -0
- package/dist/core/agent/skills/read-resource.js.map +1 -0
- package/dist/core/agent/skills/render.js +33 -0
- package/dist/core/agent/skills/render.js.map +1 -0
- package/dist/core/agent/skills/resolve-catalog.js +78 -0
- package/dist/core/agent/skills/resolve-catalog.js.map +1 -0
- package/dist/core/agent/skills/types.d.ts +50 -0
- package/dist/core/agent/skills/types.d.ts.map +1 -0
- package/dist/core/agent/types.d.ts +47 -0
- package/dist/core/agent/types.d.ts.map +1 -1
- package/dist/core/agent/types.js.map +1 -1
- package/dist/plugins/agents/agents.d.ts +50 -0
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +225 -13
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/agents/manifest.js +40 -21
- package/dist/plugins/agents/schemas.js +2 -1
- package/dist/plugins/agents/schemas.js.map +1 -1
- package/dist/plugins/server/index.js +2 -2
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js +3 -3
- package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
- package/dist/plugins/server/static-server.js +3 -3
- package/dist/plugins/server/static-server.js.map +1 -1
- package/dist/plugins/server/utils.js +3 -3
- package/dist/plugins/server/utils.js.map +1 -1
- package/dist/plugins/server/vite-dev-server.js +4 -4
- package/dist/plugins/server/vite-dev-server.js.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +2 -2
- package/dist/type-generator/database/generate.js +3 -3
- package/dist/type-generator/database/generate.js.map +1 -1
- package/dist/type-generator/migration.js +2 -2
- package/dist/type-generator/migration.js.map +1 -1
- package/dist/type-generator/serving/server-file-extractor.js +3 -3
- package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
- package/docs/api/appkit/Interface.AgentDefinition.md +11 -0
- package/docs/api/appkit/Interface.AgentsPluginConfig.md +35 -0
- package/docs/api/appkit/Interface.RegisteredAgent.md +11 -0
- package/docs/api/appkit/TypeAlias.ResolvedToolEntry.md +46 -0
- package/docs/plugins/agents.md +68 -1
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { createLogger } from "../../logging/logger.js";
|
|
2
|
+
import { getWorkspaceClient } from "../../context/execution-context.js";
|
|
3
|
+
import "../../context/index.js";
|
|
2
4
|
import { Plugin } from "../../plugin/plugin.js";
|
|
3
5
|
import { toPlugin } from "../../plugin/to-plugin.js";
|
|
4
6
|
import "../../plugin/index.js";
|
|
5
7
|
import { defineManifest } from "../../registry/manifest-loader.js";
|
|
6
8
|
import "../../registry/index.js";
|
|
9
|
+
import { FilesConnector } from "../../connectors/files/client.js";
|
|
10
|
+
import "../../connectors/files/index.js";
|
|
7
11
|
import { buildMcpHostPolicy } from "../../connectors/mcp/host-policy.js";
|
|
8
12
|
import { AppKitMcpClient } from "../../connectors/mcp/client.js";
|
|
9
13
|
import "../../connectors/mcp/index.js";
|
|
@@ -18,6 +22,12 @@ import "../../core/agent/tools/index.js";
|
|
|
18
22
|
import { loadAgentsFromDir } from "../../core/agent/load-agents.js";
|
|
19
23
|
import { CODE_AGENTS_SOURCE_DIR, loadCodeAgentsFromDir, resolveCodeAgentsDir } from "../../core/agent/load-code-agents.js";
|
|
20
24
|
import { normalizeToolResult } from "../../core/agent/normalize-result.js";
|
|
25
|
+
import { parseSkill } from "../../core/agent/skills/parse-skill.js";
|
|
26
|
+
import { loadSkillsFromDir } from "../../core/agent/skills/load-skills.js";
|
|
27
|
+
import { readSkillResource } from "../../core/agent/skills/read-resource.js";
|
|
28
|
+
import { renderLoadedSkill, renderSkillCatalog } from "../../core/agent/skills/render.js";
|
|
29
|
+
import { resolveSkill, resolveSkillCatalog } from "../../core/agent/skills/resolve-catalog.js";
|
|
30
|
+
import "../../core/agent/skills/index.js";
|
|
21
31
|
import { buildBaseSystemPrompt, composeSystemPrompt } from "../../core/agent/system-prompt.js";
|
|
22
32
|
import { agentStreamDefaults } from "./defaults.js";
|
|
23
33
|
import { EventChannel } from "./event-channel.js";
|
|
@@ -99,6 +109,12 @@ var AgentsPlugin = class extends Plugin {
|
|
|
99
109
|
* negative, or `NaN`) can't degrade into immediate auto-denial of every
|
|
100
110
|
* mutating tool call.
|
|
101
111
|
*/
|
|
112
|
+
/**
|
|
113
|
+
* Shared global skill pool (bundle `skills/` + catalog volume), loaded once
|
|
114
|
+
* per registry build. Read by {@link buildRegisteredAgent} to resolve each
|
|
115
|
+
* agent's visible catalog and by the live `register` path.
|
|
116
|
+
*/
|
|
117
|
+
globalSkills = [];
|
|
102
118
|
cachedApprovalPolicy = null;
|
|
103
119
|
get resolvedApprovalPolicy() {
|
|
104
120
|
if (this.cachedApprovalPolicy) return this.cachedApprovalPolicy;
|
|
@@ -183,6 +199,8 @@ var AgentsPlugin = class extends Plugin {
|
|
|
183
199
|
async buildAgentRegistry() {
|
|
184
200
|
const discovered = await this.loadCodeAgents();
|
|
185
201
|
const deprecatedMapRaw = this.config.agents ?? {};
|
|
202
|
+
const [bundleSkills, volumeSkills] = await Promise.all([this.loadGlobalSkills(), this.loadVolumeSkills()]);
|
|
203
|
+
this.globalSkills = [...bundleSkills, ...volumeSkills];
|
|
186
204
|
if (Object.keys(deprecatedMapRaw).length > 0) this.warnAgentsMapDeprecated();
|
|
187
205
|
const deprecatedMap = {};
|
|
188
206
|
for (const [id, def] of Object.entries(deprecatedMapRaw)) {
|
|
@@ -352,7 +370,8 @@ var AgentsPlugin = class extends Plugin {
|
|
|
352
370
|
}
|
|
353
371
|
async buildRegisteredAgent(name, def, src) {
|
|
354
372
|
const adapter = await this.resolveAdapter(def, name);
|
|
355
|
-
const
|
|
373
|
+
const skills = await this.resolveAgentSkills(name, def, src);
|
|
374
|
+
const toolIndex = await this.buildToolIndex(name, def, src, skills);
|
|
356
375
|
warnOnCapabilityMismatch(name, adapter, toolIndex);
|
|
357
376
|
return {
|
|
358
377
|
name,
|
|
@@ -363,9 +382,107 @@ var AgentsPlugin = class extends Plugin {
|
|
|
363
382
|
maxSteps: def.maxSteps,
|
|
364
383
|
maxTokens: def.maxTokens,
|
|
365
384
|
generationParams: def.generationParams,
|
|
366
|
-
ephemeral: def.ephemeral
|
|
385
|
+
ephemeral: def.ephemeral,
|
|
386
|
+
skills
|
|
367
387
|
};
|
|
368
388
|
}
|
|
389
|
+
/** Loads the shared global skill pool from `<agentsDir>/skills/`. */
|
|
390
|
+
async loadGlobalSkills() {
|
|
391
|
+
const dir = this.resolvedAgentsDir();
|
|
392
|
+
if (!dir) return [];
|
|
393
|
+
return loadSkillsFromDir(path.join(dir, "skills"), "bundle-global");
|
|
394
|
+
}
|
|
395
|
+
/** Configured catalog-skills volume path, or null when none is set. */
|
|
396
|
+
resolveSkillsVolume() {
|
|
397
|
+
const configured = this.config.skillsVolume ?? process.env.DATABRICKS_VOLUME_AGENT_SKILLS;
|
|
398
|
+
return configured && configured.trim() !== "" ? configured.trim() : null;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Workspace client used to read catalog skills. v1 reads as the app service
|
|
402
|
+
* principal; `getWorkspaceClient()` resolves to SP outside a user scope
|
|
403
|
+
* (boot and skill-tool dispatch are both unscoped). This is the single
|
|
404
|
+
* switch point for a future OBO mode.
|
|
405
|
+
*/
|
|
406
|
+
skillWorkspaceClient() {
|
|
407
|
+
return getWorkspaceClient();
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Discovers catalog skills from the configured UC Volume, read as the
|
|
411
|
+
* service principal at boot (and on `reload()`). Each `<volume>/<name>/`
|
|
412
|
+
* folder with a `SKILL.md` becomes a `source: "volume"` skill. Best-effort:
|
|
413
|
+
* a missing volume, unavailable workspace client, or a malformed individual
|
|
414
|
+
* skill is logged and skipped rather than failing the whole registry build.
|
|
415
|
+
*/
|
|
416
|
+
async loadVolumeSkills() {
|
|
417
|
+
const volume = this.resolveSkillsVolume();
|
|
418
|
+
if (!volume) return [];
|
|
419
|
+
if ((this.config.skillCredentialMode ?? "sp") === "obo") logger.warn("skillCredentialMode 'obo' is not wired yet; reading catalog skills as the service principal.");
|
|
420
|
+
let client;
|
|
421
|
+
try {
|
|
422
|
+
client = this.skillWorkspaceClient();
|
|
423
|
+
} catch (err) {
|
|
424
|
+
logger.warn("Skipping catalog skills at '%s': no workspace client available (%s).", volume, err instanceof Error ? err.message : String(err));
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
const connector = new FilesConnector({ defaultVolume: volume });
|
|
428
|
+
let entries;
|
|
429
|
+
try {
|
|
430
|
+
entries = await connector.list(client, volume);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
logger.warn("Failed to list catalog skills volume '%s': %s", volume, err instanceof Error ? err.message : String(err));
|
|
433
|
+
return [];
|
|
434
|
+
}
|
|
435
|
+
return (await Promise.all(entries.map(async (entry) => {
|
|
436
|
+
if (!entry.is_directory || !entry.name || !entry.path) return null;
|
|
437
|
+
const skillDir = entry.path;
|
|
438
|
+
const skillFile = `${skillDir}/SKILL.md`;
|
|
439
|
+
try {
|
|
440
|
+
const parsed = parseSkill(await connector.read(client, skillFile), skillFile);
|
|
441
|
+
const files = await this.listVolumeSkillFiles(connector, client, skillDir);
|
|
442
|
+
return {
|
|
443
|
+
name: parsed.name,
|
|
444
|
+
description: parsed.description,
|
|
445
|
+
body: parsed.body,
|
|
446
|
+
source: "volume",
|
|
447
|
+
dir: skillDir,
|
|
448
|
+
files,
|
|
449
|
+
allowedTools: parsed.allowedTools
|
|
450
|
+
};
|
|
451
|
+
} catch (err) {
|
|
452
|
+
logger.warn("Skipping catalog skill '%s': %s", skillDir, err instanceof Error ? err.message : String(err));
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
}))).filter((s) => s !== null);
|
|
456
|
+
}
|
|
457
|
+
/** Lists a volume skill's resource files (one level, excluding SKILL.md). */
|
|
458
|
+
async listVolumeSkillFiles(connector, client, skillDir) {
|
|
459
|
+
try {
|
|
460
|
+
return (await connector.list(client, skillDir)).filter((e) => !e.is_directory && e.name && e.name !== "SKILL.md").map((e) => e.name).sort();
|
|
461
|
+
} catch {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Resolves the per-agent skill catalog: loads this agent's private skills
|
|
467
|
+
* (`<agentsDir>/<id>/skills/`, file-origin only), then applies visibility
|
|
468
|
+
* (opt-in via `def.skills` or `autoInheritSkills`) and collision rules
|
|
469
|
+
* against the shared global pool. Returns `undefined` when nothing is
|
|
470
|
+
* visible so the prompt catalog and dispatch can cheaply skip skills.
|
|
471
|
+
*/
|
|
472
|
+
async resolveAgentSkills(name, def, src) {
|
|
473
|
+
const dir = this.resolvedAgentsDir();
|
|
474
|
+
const perAgentSkills = src.origin === "file" && dir ? await loadSkillsFromDir(path.join(dir, name, "skills"), "bundle-agent") : [];
|
|
475
|
+
const inherit = normalizeAutoInherit(this.config.autoInheritSkills);
|
|
476
|
+
const autoInherit = src.origin === "file" ? inherit.file : inherit.code;
|
|
477
|
+
const catalog = resolveSkillCatalog({
|
|
478
|
+
agentName: name,
|
|
479
|
+
agentSkillNames: def.skills,
|
|
480
|
+
perAgentSkills,
|
|
481
|
+
globalSkills: this.globalSkills,
|
|
482
|
+
autoInherit
|
|
483
|
+
});
|
|
484
|
+
return catalog.byAddress.size > 0 ? catalog : void 0;
|
|
485
|
+
}
|
|
369
486
|
async resolveAdapter(def, name) {
|
|
370
487
|
const source = def.model ?? this.config.defaultModel;
|
|
371
488
|
const adapterOptions = {};
|
|
@@ -391,7 +508,7 @@ var AgentsPlugin = class extends Plugin {
|
|
|
391
508
|
* hosted tools via MCP client. Applies `autoInheritTools` defaults when the
|
|
392
509
|
* definition has no declared tools/agents.
|
|
393
510
|
*/
|
|
394
|
-
async buildToolIndex(agentName, def, src) {
|
|
511
|
+
async buildToolIndex(agentName, def, src, skills) {
|
|
395
512
|
const index = /* @__PURE__ */ new Map();
|
|
396
513
|
const hasDeclaredTools = def.tools !== void 0;
|
|
397
514
|
const toolsRecord = this.resolveDefTools(agentName, def);
|
|
@@ -464,6 +581,20 @@ var AgentsPlugin = class extends Plugin {
|
|
|
464
581
|
throw new Error(`Agent '${agentName}' tool '${key}' has an unrecognized shape`);
|
|
465
582
|
}
|
|
466
583
|
if (hostedToCollect.length > 0) await this.connectHostedTools(hostedToCollect, index);
|
|
584
|
+
if (skills && skills.byAddress.size > 0) {
|
|
585
|
+
index.set("load_skill", {
|
|
586
|
+
source: "skill",
|
|
587
|
+
builtin: "load_skill",
|
|
588
|
+
catalog: skills,
|
|
589
|
+
def: LOAD_SKILL_TOOL_DEF
|
|
590
|
+
});
|
|
591
|
+
index.set("read_skill_file", {
|
|
592
|
+
source: "skill",
|
|
593
|
+
builtin: "read_skill_file",
|
|
594
|
+
catalog: skills,
|
|
595
|
+
def: READ_SKILL_FILE_TOOL_DEF
|
|
596
|
+
});
|
|
597
|
+
}
|
|
467
598
|
return index;
|
|
468
599
|
}
|
|
469
600
|
/**
|
|
@@ -644,9 +775,12 @@ var AgentsPlugin = class extends Plugin {
|
|
|
644
775
|
});
|
|
645
776
|
}
|
|
646
777
|
clientConfig() {
|
|
778
|
+
const skills = {};
|
|
779
|
+
for (const [name, agent] of this.agents) if (agent.skills) skills[name] = agent.skills.catalog;
|
|
647
780
|
return {
|
|
648
781
|
agents: Array.from(this.agents.keys()),
|
|
649
|
-
defaultAgent: this.defaultAgentName
|
|
782
|
+
defaultAgent: this.defaultAgentName,
|
|
783
|
+
skills
|
|
650
784
|
};
|
|
651
785
|
}
|
|
652
786
|
async _handleChat(req, res) {
|
|
@@ -658,7 +792,7 @@ var AgentsPlugin = class extends Plugin {
|
|
|
658
792
|
});
|
|
659
793
|
return;
|
|
660
794
|
}
|
|
661
|
-
const { message, threadId, agent: agentName, mlflowRunId } = parsed.data;
|
|
795
|
+
const { message, threadId, agent: agentName, mlflowRunId, skill } = parsed.data;
|
|
662
796
|
const registered = this.resolveAgent(agentName);
|
|
663
797
|
if (!registered) {
|
|
664
798
|
res.status(400).json({ error: agentName ? `Agent "${agentName}" not found` : "No agent registered" });
|
|
@@ -691,7 +825,7 @@ var AgentsPlugin = class extends Plugin {
|
|
|
691
825
|
res.status(500).json({ error: "Thread operation failed" });
|
|
692
826
|
return;
|
|
693
827
|
}
|
|
694
|
-
return this._streamAgent(req, res, registered, thread, userId, mlflowRunId);
|
|
828
|
+
return this._streamAgent(req, res, registered, thread, userId, mlflowRunId, skill);
|
|
695
829
|
}
|
|
696
830
|
/**
|
|
697
831
|
* Returns the names of tools in `registered.toolIndex` whose annotations
|
|
@@ -774,7 +908,7 @@ var AgentsPlugin = class extends Plugin {
|
|
|
774
908
|
}
|
|
775
909
|
return this._runAgentNonStreaming(req, res, registered, thread, userId, mlflowRunId);
|
|
776
910
|
}
|
|
777
|
-
async _streamAgent(req, res, registered, thread, userId, mlflowRunId) {
|
|
911
|
+
async _streamAgent(req, res, registered, thread, userId, mlflowRunId, forcedSkill) {
|
|
778
912
|
const abortController = new AbortController();
|
|
779
913
|
const signal = abortController.signal;
|
|
780
914
|
const requestId = randomUUID();
|
|
@@ -809,14 +943,19 @@ var AgentsPlugin = class extends Plugin {
|
|
|
809
943
|
})) }, async (span) => {
|
|
810
944
|
if (mlflowRunId) linkTraceToRun(mlflowRunId);
|
|
811
945
|
const pluginNames = this.context ? this.context.getPluginNames().filter((n) => n !== this.name && n !== "server") : [];
|
|
946
|
+
let fullPrompt = composePromptForAgent(registered, this.config.baseSystemPrompt, {
|
|
947
|
+
agentName: registered.name,
|
|
948
|
+
pluginNames,
|
|
949
|
+
toolNames: tools.map((t) => t.name)
|
|
950
|
+
});
|
|
951
|
+
if (forcedSkill) {
|
|
952
|
+
const addendum = this.renderForcedSkill(registered, forcedSkill);
|
|
953
|
+
if (addendum) fullPrompt = `${fullPrompt}\n\n${addendum}`;
|
|
954
|
+
}
|
|
812
955
|
const messagesWithSystem = [{
|
|
813
956
|
id: "system",
|
|
814
957
|
role: "system",
|
|
815
|
-
content:
|
|
816
|
-
agentName: registered.name,
|
|
817
|
-
pluginNames,
|
|
818
|
-
toolNames: tools.map((t) => t.name)
|
|
819
|
-
}),
|
|
958
|
+
content: fullPrompt,
|
|
820
959
|
createdAt: /* @__PURE__ */ new Date()
|
|
821
960
|
}, ...thread.messages];
|
|
822
961
|
const fullContent = await consumeAdapterStream(registered.adapter.run({
|
|
@@ -1067,10 +1206,46 @@ var AgentsPlugin = class extends Plugin {
|
|
|
1067
1206
|
if (!childAgent) throw new Error(`Sub-agent not found: ${entry.agentName}`);
|
|
1068
1207
|
result = await this.runSubAgent(runState, childAgent, args, depth + 1);
|
|
1069
1208
|
} else if (entry.source === "hosted-supervisor") throw new Error(`Tool '${name}' is a hosted-supervisor tool and cannot be invoked from the Node process. It is executed server-side by the Databricks AI Gateway and is only reachable when the agent's model is a Supervisor API adapter.`);
|
|
1209
|
+
else if (entry.source === "skill") result = await this.dispatchSkillTool(entry, args);
|
|
1070
1210
|
return result;
|
|
1071
1211
|
}));
|
|
1072
1212
|
}
|
|
1073
1213
|
/**
|
|
1214
|
+
* Executes the built-in `load_skill` / `read_skill_file` tools against the
|
|
1215
|
+
* agent's resolved skill catalog. `load_skill` returns a skill's body plus a
|
|
1216
|
+
* manifest of bundled files; `read_skill_file` returns the contents of one
|
|
1217
|
+
* of those files (bundle skills only in v1 — catalog-volume resource reads
|
|
1218
|
+
* arrive with the volume source).
|
|
1219
|
+
*/
|
|
1220
|
+
async dispatchSkillTool(entry, args) {
|
|
1221
|
+
const obj = typeof args === "object" && args !== null ? args : {};
|
|
1222
|
+
const skillName = typeof obj.skill === "string" ? obj.skill.trim() : "";
|
|
1223
|
+
if (!skillName) throw new Error(`'${entry.builtin}' requires a 'skill' argument naming the skill to use.`);
|
|
1224
|
+
const skill = resolveSkill(entry.catalog, skillName);
|
|
1225
|
+
if (entry.builtin === "load_skill") return renderLoadedSkill(skill);
|
|
1226
|
+
const filePath = typeof obj.path === "string" ? obj.path.trim() : "";
|
|
1227
|
+
if (!filePath) throw new Error("'read_skill_file' requires a 'path' argument.");
|
|
1228
|
+
if (!skill.files.includes(filePath)) throw new Error(`Skill '${skill.name}' has no bundled file '${filePath}'. Available: ${skill.files.join(", ") || "<none>"}.`);
|
|
1229
|
+
if (skill.source === "volume") return new FilesConnector({ defaultVolume: skill.dir }).read(this.skillWorkspaceClient(), `${skill.dir}/${filePath}`);
|
|
1230
|
+
return readSkillResource(skill.dir, filePath);
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Renders the prompt addendum for a force-loaded skill (`/skill-name`).
|
|
1234
|
+
* Returns null when the agent has no catalog or the name doesn't resolve —
|
|
1235
|
+
* an unusable request shouldn't fail the whole turn, so it's logged and the
|
|
1236
|
+
* model proceeds with the catalog + `load_skill` still available.
|
|
1237
|
+
*/
|
|
1238
|
+
renderForcedSkill(registered, name) {
|
|
1239
|
+
if (!registered.skills) return null;
|
|
1240
|
+
try {
|
|
1241
|
+
const skill = resolveSkill(registered.skills, name);
|
|
1242
|
+
return `The user explicitly requested the "${skill.name}" skill for this turn. Its instructions:\n\n${renderLoadedSkill(skill)}`;
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
logger.warn("Ignoring forced skill '%s': %s", name, err instanceof Error ? err.message : String(err));
|
|
1245
|
+
return null;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1074
1249
|
* Runs a sub-agent in response to an `agent-<key>` tool call. Returns the
|
|
1075
1250
|
* concatenated text output to hand back to the parent adapter as the tool
|
|
1076
1251
|
* result.
|
|
@@ -1261,6 +1436,40 @@ function normalizeAutoInherit(value) {
|
|
|
1261
1436
|
code: value.code ?? false
|
|
1262
1437
|
};
|
|
1263
1438
|
}
|
|
1439
|
+
/** Built-in tool the model calls to load a skill's full instructions on demand. */
|
|
1440
|
+
const LOAD_SKILL_TOOL_DEF = {
|
|
1441
|
+
name: "load_skill",
|
|
1442
|
+
description: "Load the full instructions for one of the available skills by name. Call this before acting on a task that matches a skill's description. Returns the skill's instructions plus a list of any bundled files you can read with read_skill_file.",
|
|
1443
|
+
parameters: {
|
|
1444
|
+
type: "object",
|
|
1445
|
+
properties: { skill: {
|
|
1446
|
+
type: "string",
|
|
1447
|
+
description: "The exact skill name to load, as shown in the available-skills list."
|
|
1448
|
+
} },
|
|
1449
|
+
required: ["skill"]
|
|
1450
|
+
},
|
|
1451
|
+
annotations: { effect: "read" }
|
|
1452
|
+
};
|
|
1453
|
+
/** Built-in tool for reading a resource file that a loaded skill references. */
|
|
1454
|
+
const READ_SKILL_FILE_TOOL_DEF = {
|
|
1455
|
+
name: "read_skill_file",
|
|
1456
|
+
description: "Read a bundled resource file that a loaded skill references (e.g. a reference doc). Only files listed by load_skill for that skill are readable.",
|
|
1457
|
+
parameters: {
|
|
1458
|
+
type: "object",
|
|
1459
|
+
properties: {
|
|
1460
|
+
skill: {
|
|
1461
|
+
type: "string",
|
|
1462
|
+
description: "The skill that owns the file."
|
|
1463
|
+
},
|
|
1464
|
+
path: {
|
|
1465
|
+
type: "string",
|
|
1466
|
+
description: "Relative path of the file within the skill, as listed by load_skill."
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
required: ["skill", "path"]
|
|
1470
|
+
},
|
|
1471
|
+
annotations: { effect: "read" }
|
|
1472
|
+
};
|
|
1264
1473
|
function composePromptForAgent(registered, pluginLevel, ctx) {
|
|
1265
1474
|
const perAgent = registered.baseSystemPrompt;
|
|
1266
1475
|
const resolved = perAgent !== void 0 ? perAgent : pluginLevel;
|
|
@@ -1269,7 +1478,10 @@ function composePromptForAgent(registered, pluginLevel, ctx) {
|
|
|
1269
1478
|
else if (typeof resolved === "string") base = resolved;
|
|
1270
1479
|
else if (typeof resolved === "function") base = resolved(ctx);
|
|
1271
1480
|
else base = buildBaseSystemPrompt(ctx);
|
|
1272
|
-
|
|
1481
|
+
const composed = composeSystemPrompt(base, registered.instructions);
|
|
1482
|
+
const catalog = registered.skills?.catalog;
|
|
1483
|
+
if (!catalog || catalog.length === 0) return composed;
|
|
1484
|
+
return `${composed}\n\n${renderSkillCatalog(catalog)}`;
|
|
1273
1485
|
}
|
|
1274
1486
|
/**
|
|
1275
1487
|
* Pulls the LLM-readable description off any {@link SupervisorTool} kind.
|