@dbx-tools/appkit-mastra 0.9.74 → 0.9.93

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.
@@ -0,0 +1,171 @@
1
+ /**
2
+ * On-demand skill discovery backed by a materialized workspace catalogue.
3
+ *
4
+ * @module
5
+ */
6
+ import { errorUtils } from "@dbx-tools/shared-core";
7
+ import { MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
8
+ import { createTool } from "@mastra/core/tools";
9
+ import Fuse from "fuse.js";
10
+ import { z } from "zod";
11
+ const DEFAULT_STATE_TTL_MS = 60 * 60 * 1000;
12
+ /** Mastra-compatible search, load, and lazy-read tools over one cached catalogue. */
13
+ export class CatalogueSkillSearchProcessor {
14
+ id = "skill-catalogue-search";
15
+ name = "Skill Catalogue Search";
16
+ providesSkillDiscovery = "on-demand";
17
+ minScore;
18
+ resolve;
19
+ states = new Map();
20
+ topK;
21
+ ttlMs;
22
+ constructor(options) {
23
+ this.resolve = options.resolve;
24
+ this.topK = options.topK ?? 5;
25
+ this.minScore = options.minScore ?? 0.1;
26
+ this.ttlMs = options.ttlMs ?? DEFAULT_STATE_TTL_MS;
27
+ }
28
+ async processInputStep(args) {
29
+ const owner = await this.resolve(args);
30
+ const catalogue = await owner.get(args.abortSignal);
31
+ const state = this.threadState(args);
32
+ args.messageList.addSystem("To discover available skills, call search_skills with a keyword query. " +
33
+ "To load a skill's instructions, call load_skill with the exact skill name. " +
34
+ "Use skill_read only after loading a skill when its instructions require a reference, script, or asset.");
35
+ for (const [skillName, instructions] of state.skills) {
36
+ args.messageList.addSystem(`[Skill: ${skillName}]\n\n${instructions}`);
37
+ }
38
+ const search = new Fuse(catalogue.skills, {
39
+ includeScore: true,
40
+ ignoreLocation: true,
41
+ threshold: 1,
42
+ keys: [
43
+ { name: "name", weight: 0.5 },
44
+ { name: "description", weight: 0.35 },
45
+ { name: "instructions", weight: 0.15 },
46
+ ],
47
+ });
48
+ return {
49
+ tools: {
50
+ ...(args.tools ?? {}),
51
+ search_skills: createTool({
52
+ id: "search_skills",
53
+ description: "Search for available skills by keyword. Load a useful result with load_skill.",
54
+ inputSchema: z.object({
55
+ query: z.string().trim().min(1, "Query is required").describe("Search keywords."),
56
+ }),
57
+ outputSchema: z.object({
58
+ results: z.array(z.object({
59
+ name: z.string(),
60
+ description: z.string(),
61
+ score: z.number(),
62
+ })),
63
+ message: z.string(),
64
+ }),
65
+ execute: async ({ query }) => {
66
+ const results = search
67
+ .search(query)
68
+ .map((result) => ({
69
+ name: result.item.name,
70
+ description: result.item.description.length > 150
71
+ ? `${result.item.description.slice(0, 147)}...`
72
+ : result.item.description,
73
+ score: Math.round((1 - (result.score ?? 1)) * 100) / 100,
74
+ }))
75
+ .filter(({ score }) => score >= this.minScore)
76
+ .slice(0, this.topK);
77
+ return {
78
+ results,
79
+ message: results.length === 0
80
+ ? `No skills found matching "${query}". Try different keywords.`
81
+ : `Found ${results.length} skill(s). Use load_skill with the exact skill name to load its instructions.`,
82
+ };
83
+ },
84
+ }),
85
+ load_skill: createTool({
86
+ id: "load_skill",
87
+ description: "Load a skill's full instructions into the conversation.",
88
+ inputSchema: z.object({
89
+ skillName: z.string().describe("Exact skill name returned by search_skills."),
90
+ }),
91
+ outputSchema: z.object({
92
+ success: z.boolean(),
93
+ message: z.string(),
94
+ skillName: z.string().optional(),
95
+ }),
96
+ execute: async ({ skillName }) => {
97
+ if (state.skills.has(skillName)) {
98
+ return {
99
+ success: true,
100
+ message: `Skill "${skillName}" is already loaded.`,
101
+ skillName,
102
+ };
103
+ }
104
+ const skill = catalogue.skills.find(({ name }) => name === skillName);
105
+ if (!skill) {
106
+ return {
107
+ success: false,
108
+ message: `Skill "${skillName}" not found. Use search_skills to find available skills.`,
109
+ };
110
+ }
111
+ state.skills.set(skillName, skill.instructions);
112
+ return {
113
+ success: true,
114
+ message: `Skill "${skillName}" loaded. Its instructions are now available as context.`,
115
+ skillName,
116
+ };
117
+ },
118
+ }),
119
+ skill_read: createTool({
120
+ id: "skill_read",
121
+ description: "Read a reference, script, template, or asset from a loaded skill. The path is relative to the skill root.",
122
+ inputSchema: z.object({
123
+ skillName: z.string().describe("Exact loaded skill name."),
124
+ path: z.string().describe("File path relative to the skill root."),
125
+ startLine: z.number().int().positive().optional(),
126
+ endLine: z.number().int().positive().optional(),
127
+ }),
128
+ execute: async ({ skillName, path, startLine, endLine }) => {
129
+ if (!state.skills.has(skillName)) {
130
+ return `Skill "${skillName}" is not loaded. Call load_skill first.`;
131
+ }
132
+ try {
133
+ const source = await owner.readSkillFile(skillName, path, args.abortSignal);
134
+ return lineRange(source, path, startLine, endLine);
135
+ }
136
+ catch (error) {
137
+ return `Could not read "${path}" from skill "${skillName}": ${errorUtils.errorMessage(error)}`;
138
+ }
139
+ },
140
+ }),
141
+ },
142
+ };
143
+ }
144
+ threadState(args) {
145
+ const now = Date.now();
146
+ for (const [key, state] of this.states) {
147
+ if (now - state.lastAccessed > this.ttlMs)
148
+ this.states.delete(key);
149
+ }
150
+ const key = args.requestContext?.get(MASTRA_THREAD_ID_KEY) ?? "default";
151
+ const current = this.states.get(String(key)) ?? {
152
+ lastAccessed: now,
153
+ skills: new Map(),
154
+ };
155
+ current.lastAccessed = now;
156
+ this.states.set(String(key), current);
157
+ return current;
158
+ }
159
+ }
160
+ function lineRange(source, path, startLine, endLine) {
161
+ if (startLine === undefined && endLine === undefined)
162
+ return source;
163
+ const lines = source.split(/\r?\n/);
164
+ const start = Math.max(1, startLine ?? 1);
165
+ const end = Math.min(lines.length, endLine ?? lines.length);
166
+ if (start > end || start > lines.length) {
167
+ return `File "${path}" has ${lines.length} lines; requested range ${start}-${end} is empty.`;
168
+ }
169
+ return `${path} (lines ${start}-${end} of ${lines.length})\n${lines.slice(start - 1, end).join("\n")}`;
170
+ }
171
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tpbGwtc2VhcmNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NraWxsLXNlYXJjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRXBELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNoRCxPQUFPLElBQUksTUFBTSxTQUFTLENBQUM7QUFDM0IsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUd4QixNQUFNLG9CQUFvQixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBZTVDLHFGQUFxRjtBQUNyRixNQUFNLE9BQU8sNkJBQTZCO0lBQy9CLEVBQUUsR0FBRyx3QkFBd0IsQ0FBQztJQUM5QixJQUFJLEdBQUcsd0JBQXdCLENBQUM7SUFDaEMsc0JBQXNCLEdBQUcsV0FBb0IsQ0FBQztJQUV0QyxRQUFRLENBQVM7SUFDakIsT0FBTyxDQUF5QjtJQUNoQyxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFDeEMsSUFBSSxDQUFTO0lBQ2IsS0FBSyxDQUFTO0lBRS9CLFlBQVksT0FBNkM7UUFDdkQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDO1FBQy9CLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxJQUFJLEdBQUcsQ0FBQztRQUN4QyxJQUFJLENBQUMsS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLElBQUksb0JBQW9CLENBQUM7SUFDckQsQ0FBQztJQUVELEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxJQUEwQjtRQUMvQyxNQUFNLEtBQUss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Y292ZXJ5IGJhY2tlZCBieSBhIG1hdGVyaWFsaXplZCB3b3Jrc3BhY2UgY2F0YWxvZ3VlLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBlcnJvclV0aWxzIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB0eXBlIHsgUHJvY2Vzc0lucHV0U3RlcEFyZ3MsIFByb2Nlc3NvciB9IGZyb20gXCJAbWFzdHJhL2NvcmUvcHJvY2Vzc29yc1wiO1xuaW1wb3J0IHsgTUFTVFJBX1RIUkVBRF9JRF9LRVkgfSBmcm9tIFwiQG1hc3RyYS9jb3JlL3JlcXVlc3QtY29udGV4dFwiO1xuaW1wb3J0IHsgY3JlYXRlVG9vbCB9IGZyb20gXCJAbWFzdHJhL2NvcmUvdG9vbHNcIjtcbmltcG9ydCBGdXNlIGZyb20gXCJmdXNlLmpzXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuaW1wb3J0IHR5cGUgeyBTa2lsbENhdGFsb2d1ZVJlc29sdmVyIH0gZnJvbSBcIi4vc2tpbGwtY2FjaGUudHNcIjtcblxuY29uc3QgREVGQVVMVF9TVEFURV9UVExfTVMgPSA2MCAqIDYwICogMTAwMDtcblxuaW50ZXJmYWNlIFRocmVhZFN0YXRlIHtcbiAgbGFzdEFjY2Vzc2VkOiBudW1iZXI7XG4gIHNraWxsczogTWFwPHN0cmluZywgc3RyaW5nPjtcbn1cblxuLyoqIENvbmZpZ3VyYXRpb24gZm9yIHtAbGluayBDYXRhbG9ndWVTa2lsbFNlYXJjaFByb2Nlc3Nvcn0uICovXG5leHBvcnQgaW50ZXJmYWNlIENhdGFsb2d1ZVNraWxsU2VhcmNoUHJvY2Vzc29yT3B0aW9ucyB7XG4gIG1pblNjb3JlPzogbnVtYmVyO1xuICByZXNvbHZlOiBTa2lsbENhdGFsb2d1ZVJlc29sdmVyO1xuICB0b3BLPzogbnVtYmVyO1xuICB0dGxNcz86IG51bWJlcjtcbn1cblxuLyoqIE1hc3RyYS1jb21wYXRpYmxlIHNlYXJjaCwgbG9hZCwgYW5kIGxhenktcmVhZCB0b29scyBvdmVyIG9uZSBjYWNoZWQgY2F0YWxvZ3VlLiAqL1xuZXhwb3J0IGNsYXNzIENhdGFsb2d1ZVNraWxsU2VhcmNoUHJvY2Vzc29yIGltcGxlbWVudHMgUHJvY2Vzc29yPFwic2tpbGwtY2F0YWxvZ3VlLXNlYXJjaFwiPiB7XG4gIHJlYWRvbmx5IGlkID0gXCJza2lsbC1jYXRhbG9ndWUtc2VhcmNoXCI7XG4gIHJlYWRvbmx5IG5hbWUgPSBcIlNraWxsIENhdGFsb2d1ZSBTZWFyY2hcIjtcbiAgcmVhZG9ubHkgcHJvdmlkZXNTa2lsbERpc2NvdmVyeSA9IFwib24tZGVtYW5kXCIgYXMgY29uc3Q7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBtaW5TY29yZTogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IHJlc29sdmU6IFNraWxsQ2F0YWxvZ3VlUmVzb2x2ZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgc3RhdGVzID0gbmV3IE1hcDxzdHJpbmcsIFRocmVhZFN0YXRlPigpO1xuICBwcml2YXRlIHJlYWRvbmx5IHRvcEs6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSB0dGxNczogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IENhdGFsb2d1ZVNraWxsU2VhcmNoUHJvY2Vzc29yT3B0aW9ucykge1xuICAgIHRoaXMucmVzb2x2ZSA9IG9wdGlvbnMucmVzb2x2ZTtcbiAgICB0aGlzLnRvcEsgPSBvcHRpb25zLnRvcEsgPz8gNTtcbiAgICB0aGlzLm1pblNjb3JlID0gb3B0aW9ucy5taW5TY29yZSA/PyAwLjE7XG4gICAgdGhpcy50dGxNcyA9IG9wdGlvbnMudHRsTXMgPz8gREVGQVVMVF9TVEFURV9UVExfTVM7XG4gIH1cblxuICBhc3luYyBwcm9jZXNzSW5wdXRTdGVwKGFyZ3M6IFByb2Nlc3NJbnB1dFN0ZXBBcmdzKTogUHJvbWlzZTx7IHRvb2xzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PiB7XG4gICAgY29uc3Qgb3duZXIgPSBhd2FpdCB0aGlzLnJlc29sdmUoYXJncyk7XG4gICAgY29uc3QgY2F0YWxvZ3VlID0gYXdhaXQgb3duZXIuZ2V0KGFyZ3MuYWJvcnRTaWduYWwpO1xuICAgIGNvbnN0IHN0YXRlID0gdGhpcy50aHJlYWRTdGF0ZShhcmdzKTtcbiAgICBhcmdzLm1lc3NhZ2VMaXN0LmFkZFN5c3RlbShcbiAgICAgIFwiVG8gZGlzY292ZXIgYXZhaWxhYmxlIHNraWxscywgY2FsbCBzZWFyY2hfc2tpbGxzIHdpdGggYSBrZXl3b3JkIHF1ZXJ5LiBcIiArXG4gICAgICAgIFwiVG8gbG9hZCBhIHNraWxsJ3MgaW5zdHJ1Y3Rpb25zLCBjYWxsIGxvYWRfc2tpbGwgd2l0aCB0aGUgZXhhY3Qgc2tpbGwgbmFtZS4gXCIgK1xuICAgICAgICBcIlVzZSBza2lsbF9yZWFkIG9ubHkgYWZ0ZXIgbG9hZGluZyBhIHNraWxsIHdoZW4gaXRzIGluc3RydWN0aW9ucyByZXF1aXJlIGEgcmVmZXJlbmNlLCBzY3JpcHQsIG9yIGFzc2V0LlwiLFxuICAgICk7XG4gICAgZm9yIChjb25zdCBbc2tpbGxOYW1lLCBpbnN0cnVjdGlvbnNdIG9mIHN0YXRlLnNraWxscykge1xuICAgICAgYXJncy5tZXNzYWdlTGlzdC5hZGRTeXN0ZW0oYFtTa2lsbDogJHtza2lsbE5hbWV9XVxcblxcbiR7aW5zdHJ1Y3Rpb25zfWApO1xuICAgIH1cbiAgICBjb25zdCBzZWFyY2ggPSBuZXcgRnVzZShjYXRhbG9ndWUuc2tpbGxzLCB7XG4gICAgICBpbmNsdWRlU2NvcmU6IHRydWUsXG4gICAgICBpZ25vcmVMb2NhdGlvbjogdHJ1ZSxcbiAgICAgIHRocmVzaG9sZDogMSxcbiAgICAgIGtleXM6IFtcbiAgICAgICAgeyBuYW1lOiBcIm5hbWVcIiwgd2VpZ2h0OiAwLjUgfSxcbiAgICAgICAgeyBuYW1lOiBcImRlc2NyaXB0aW9uXCIsIHdlaWdodDogMC4zNSB9LFxuICAgICAgICB7IG5hbWU6IFwiaW5zdHJ1Y3Rpb25zXCIsIHdlaWdodDogMC4xNSB9LFxuICAgICAgXSxcbiAgICB9KTtcbiAgICByZXR1cm4ge1xuICAgICAgdG9vbHM6IHtcbiAgICAgICAgLi4uKGFyZ3MudG9vbHMgPz8ge30pLFxuICAgICAgICBzZWFyY2hfc2tpbGxzOiBjcmVhdGVUb29sKHtcbiAgICAgICAgICBpZDogXCJzZWFyY2hfc2tpbGxzXCIsXG4gICAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgICBcIlNlYXJjaCBmb3IgYXZhaWxhYmxlIHNraWxscyBieSBrZXl3b3JkLiBMb2FkIGEgdXNlZnVsIHJlc3VsdCB3aXRoIGxvYWRfc2tpbGwuXCIsXG4gICAgICAgICAgaW5wdXRTY2hlbWE6IHoub2JqZWN0KHtcbiAgICAgICAgICAgIHF1ZXJ5OiB6LnN0cmluZygpLnRyaW0oKS5taW4oMSwgXCJRdWVyeSBpcyByZXF1aXJlZFwiKS5kZXNjcmliZShcIlNlYXJjaCBrZXl3b3Jkcy5cIiksXG4gICAgICAgICAgfSksXG4gICAgICAgICAgb3V0cHV0U2NoZW1hOiB6Lm9iamVjdCh7XG4gICAgICAgICAgICByZXN1bHRzOiB6LmFycmF5KFxuICAgICAgICAgICAgICB6Lm9iamVjdCh7XG4gICAgICAgICAgICAgICAgbmFtZTogei5zdHJpbmcoKSxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogei5zdHJpbmcoKSxcbiAgICAgICAgICAgICAgICBzY29yZTogei5udW1iZXIoKSxcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICApLFxuICAgICAgICAgICAgbWVzc2FnZTogei5zdHJpbmcoKSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgICBleGVjdXRlOiBhc3luYyAoeyBxdWVyeSB9KSA9PiB7XG4gICAgICAgICAgICBjb25zdCByZXN1bHRzID0gc2VhcmNoXG4gICAgICAgICAgICAgIC5zZWFyY2gocXVlcnkpXG4gICAgICAgICAgICAgIC5tYXAoKHJlc3VsdCkgPT4gKHtcbiAgICAgICAgICAgICAgICBuYW1lOiByZXN1bHQuaXRlbS5uYW1lLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICAgICAgICAgcmVzdWx0Lml0ZW0uZGVzY3JpcHRpb24ubGVuZ3RoID4gMTUwXG4gICAgICAgICAgICAgICAgICAgID8gYCR7cmVzdWx0Lml0ZW0uZGVzY3JpcHRpb24uc2xpY2UoMCwgMTQ3KX0uLi5gXG4gICAgICAgICAgICAgICAgICAgIDogcmVzdWx0Lml0ZW0uZGVzY3JpcHRpb24sXG4gICAgICAgICAgICAgICAgc2NvcmU6IE1hdGgucm91bmQoKDEgLSAocmVzdWx0LnNjb3JlID8/IDEpKSAqIDEwMCkgLyAxMDAsXG4gICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgICAuZmlsdGVyKCh7IHNjb3JlIH0pID0+IHNjb3JlID49IHRoaXMubWluU2NvcmUpXG4gICAgICAgICAgICAgIC5zbGljZSgwLCB0aGlzLnRvcEspO1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgcmVzdWx0cyxcbiAgICAgICAgICAgICAgbWVzc2FnZTpcbiAgICAgICAgICAgICAgICByZXN1bHRzLmxlbmd0aCA9PT0gMFxuICAgICAgICAgICAgICAgICAgPyBgTm8gc2tpbGxzIGZvdW5kIG1hdGNoaW5nIFwiJHtxdWVyeX1cIi4gVHJ5IGRpZmZlcmVudCBrZXl3b3Jkcy5gXG4gICAgICAgICAgICAgICAgICA6IGBGb3VuZCAke3Jlc3VsdHMubGVuZ3RofSBza2lsbChzKS4gVXNlIGxvYWRfc2tpbGwgd2l0aCB0aGUgZXhhY3Qgc2tpbGwgbmFtZSB0byBsb2FkIGl0cyBpbnN0cnVjdGlvbnMuYCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfSxcbiAgICAgICAgfSksXG4gICAgICAgIGxvYWRfc2tpbGw6IGNyZWF0ZVRvb2woe1xuICAgICAgICAgIGlkOiBcImxvYWRfc2tpbGxcIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogXCJMb2FkIGEgc2tpbGwncyBmdWxsIGluc3RydWN0aW9ucyBpbnRvIHRoZSBjb252ZXJzYXRpb24uXCIsXG4gICAgICAgICAgaW5wdXRTY2hlbWE6IHoub2JqZWN0KHtcbiAgICAgICAgICAgIHNraWxsTmFtZTogei5zdHJpbmcoKS5kZXNjcmliZShcIkV4YWN0IHNraWxsIG5hbWUgcmV0dXJuZWQgYnkgc2VhcmNoX3NraWxscy5cIiksXG4gICAgICAgICAgfSksXG4gICAgICAgICAgb3V0cHV0U2NoZW1hOiB6Lm9iamVjdCh7XG4gICAgICAgICAgICBzdWNjZXNzOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICAgIG1lc3NhZ2U6IHouc3RyaW5nKCksXG4gICAgICAgICAgICBza2lsbE5hbWU6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgICBleGVjdXRlOiBhc3luYyAoeyBza2lsbE5hbWUgfSkgPT4ge1xuICAgICAgICAgICAgaWYgKHN0YXRlLnNraWxscy5oYXMoc2tpbGxOYW1lKSkge1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYFNraWxsIFwiJHtza2lsbE5hbWV9XCIgaXMgYWxyZWFkeSBsb2FkZWQuYCxcbiAgICAgICAgICAgICAgICBza2lsbE5hbWUsXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBza2lsbCA9IGNhdGFsb2d1ZS5za2lsbHMuZmluZCgoeyBuYW1lIH0pID0+IG5hbWUgPT09IHNraWxsTmFtZSk7XG4gICAgICAgICAgICBpZiAoIXNraWxsKSB7XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYFNraWxsIFwiJHtza2lsbE5hbWV9XCIgbm90IGZvdW5kLiBVc2Ugc2VhcmNoX3NraWxscyB0byBmaW5kIGF2YWlsYWJsZSBza2lsbHMuYCxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHN0YXRlLnNraWxscy5zZXQoc2tpbGxOYW1lLCBza2lsbC5pbnN0cnVjdGlvbnMpO1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogYFNraWxsIFwiJHtza2lsbE5hbWV9XCIgbG9hZGVkLiBJdHMgaW5zdHJ1Y3Rpb25zIGFyZSBub3cgYXZhaWxhYmxlIGFzIGNvbnRleHQuYCxcbiAgICAgICAgICAgICAgc2tpbGxOYW1lLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9LFxuICAgICAgICB9KSxcbiAgICAgICAgc2tpbGxfcmVhZDogY3JlYXRlVG9vbCh7XG4gICAgICAgICAgaWQ6IFwic2tpbGxfcmVhZFwiLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICAgXCJSZWFkIGEgcmVmZXJlbmNlLCBzY3JpcHQsIHRlbXBsYXRlLCBvciBhc3NldCBmcm9tIGEgbG9hZGVkIHNraWxsLiBUaGUgcGF0aCBpcyByZWxhdGl2ZSB0byB0aGUgc2tpbGwgcm9vdC5cIixcbiAgICAgICAgICBpbnB1dFNjaGVtYTogei5vYmplY3Qoe1xuICAgICAgICAgICAgc2tpbGxOYW1lOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiRXhhY3QgbG9hZGVkIHNraWxsIG5hbWUuXCIpLFxuICAgICAgICAgICAgcGF0aDogei5zdHJpbmcoKS5kZXNjcmliZShcIkZpbGUgcGF0aCByZWxhdGl2ZSB0byB0aGUgc2tpbGwgcm9vdC5cIiksXG4gICAgICAgICAgICBzdGFydExpbmU6IHoubnVtYmVyKCkuaW50KCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLFxuICAgICAgICAgICAgZW5kTGluZTogei5udW1iZXIoKS5pbnQoKS5wb3NpdGl2ZSgpLm9wdGlvbmFsKCksXG4gICAgICAgICAgfSksXG4gICAgICAgICAgZXhlY3V0ZTogYXN5bmMgKHsgc2tpbGxOYW1lLCBwYXRoLCBzdGFydExpbmUsIGVuZExpbmUgfSkgPT4ge1xuICAgICAgICAgICAgaWYgKCFzdGF0ZS5za2lsbHMuaGFzKHNraWxsTmFtZSkpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGBTa2lsbCBcIiR7c2tpbGxOYW1lfVwiIGlzIG5vdCBsb2FkZWQuIENhbGwgbG9hZF9za2lsbCBmaXJzdC5gO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3Qgc291cmNlID0gYXdhaXQgb3duZXIucmVhZFNraWxsRmlsZShza2lsbE5hbWUsIHBhdGgsIGFyZ3MuYWJvcnRTaWduYWwpO1xuICAgICAgICAgICAgICByZXR1cm4gbGluZVJhbmdlKHNvdXJjZSwgcGF0aCwgc3RhcnRMaW5lLCBlbmRMaW5lKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICAgIHJldHVybiBgQ291bGQgbm90IHJlYWQgXCIke3BhdGh9XCIgZnJvbSBza2lsbCBcIiR7c2tpbGxOYW1lfVwiOiAke2Vycm9yVXRpbHMuZXJyb3JNZXNzYWdlKGVycm9yKX1gO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSB0aHJlYWRTdGF0ZShhcmdzOiBQcm9jZXNzSW5wdXRTdGVwQXJncyk6IFRocmVhZFN0YXRlIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIGZvciAoY29uc3QgW2tleSwgc3RhdGVdIG9mIHRoaXMuc3RhdGVzKSB7XG4gICAgICBpZiAobm93IC0gc3RhdGUubGFzdEFjY2Vzc2VkID4gdGhpcy50dGxNcykgdGhpcy5zdGF0ZXMuZGVsZXRlKGtleSk7XG4gICAgfVxuICAgIGNvbnN0IGtleSA9IGFyZ3MucmVxdWVzdENvbnRleHQ/LmdldChNQVNUUkFfVEhSRUFEX0lEX0tFWSkgPz8gXCJkZWZhdWx0XCI7XG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuc3RhdGVzLmdldChTdHJpbmcoa2V5KSkgPz8ge1xuICAgICAgbGFzdEFjY2Vzc2VkOiBub3csXG4gICAgICBza2lsbHM6IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCksXG4gICAgfTtcbiAgICBjdXJyZW50Lmxhc3RBY2Nlc3NlZCA9IG5vdztcbiAgICB0aGlzLnN0YXRlcy5zZXQoU3RyaW5nKGtleSksIGN1cnJlbnQpO1xuICAgIHJldHVybiBjdXJyZW50O1xuICB9XG59XG5cbmZ1bmN0aW9uIGxpbmVSYW5nZShcbiAgc291cmNlOiBzdHJpbmcsXG4gIHBhdGg6IHN0cmluZyxcbiAgc3RhcnRMaW5lOiBudW1iZXIgfCB1bmRlZmluZWQsXG4gIGVuZExpbmU6IG51bWJlciB8IHVuZGVmaW5lZCxcbik6IHN0cmluZyB7XG4gIGlmIChzdGFydExpbmUgPT09IHVuZGVmaW5lZCAmJiBlbmRMaW5lID09PSB1bmRlZmluZWQpIHJldHVybiBzb3VyY2U7XG4gIGNvbnN0IGxpbmVzID0gc291cmNlLnNwbGl0KC9cXHI/XFxuLyk7XG4gIGNvbnN0IHN0YXJ0ID0gTWF0aC5tYXgoMSwgc3RhcnRMaW5lID8/IDEpO1xuICBjb25zdCBlbmQgPSBNYXRoLm1pbihsaW5lcy5sZW5ndGgsIGVuZExpbmUgPz8gbGluZXMubGVuZ3RoKTtcbiAgaWYgKHN0YXJ0ID4gZW5kIHx8IHN0YXJ0ID4gbGluZXMubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGBGaWxlIFwiJHtwYXRofVwiIGhhcyAke2xpbmVzLmxlbmd0aH0gbGluZXM7IHJlcXVlc3RlZCByYW5nZSAke3N0YXJ0fS0ke2VuZH0gaXMgZW1wdHkuYDtcbiAgfVxuICByZXR1cm4gYCR7cGF0aH0gKGxpbmVzICR7c3RhcnR9LSR7ZW5kfSBvZiAke2xpbmVzLmxlbmd0aH0pXFxuJHtsaW5lcy5zbGljZShzdGFydCAtIDEsIGVuZCkuam9pbihcIlxcblwiKX1gO1xufVxuIl19
@@ -20,6 +20,7 @@
20
20
  import type { RequestContext } from "@mastra/core/request-context";
21
21
  import { Workspace, type SkillsResolver, type WorkspaceFilesystem, type WorkspaceSandbox, type WorkspaceSandboxResolver } from "@mastra/core/workspace";
22
22
  import { type DatabricksWorkspaceSandboxOptions } from "./sandbox.ts";
23
+ import { type SkillCatalogueResolver, type SkillCatalogueSource } from "./skill-cache.ts";
23
24
  /** Per-request context for mount and skill-folder resolvers. */
24
25
  export interface WorkspaceMountContext {
25
26
  requestContext?: RequestContext;
@@ -58,6 +59,8 @@ export interface SkillFolderOptions {
58
59
  /** Mount map plus optional Mastra skill scan roots for one resolver. */
59
60
  export interface WorkspaceMountContribution {
60
61
  mounts: Record<string, WorkspaceFilesystem>;
62
+ /** Ordered source roots included in catalogue discovery. */
63
+ skillSources?: SkillCatalogueSource[];
61
64
  /** Paths within the composite namespace where `SKILL.md` files are scanned. */
62
65
  skillPaths?: string[];
63
66
  /**
@@ -100,16 +103,16 @@ export interface CreateWorkspaceOptions {
100
103
  /** Forwarded to Mastra when skill discovery is enabled. */
101
104
  checkSkillFileMtime?: boolean;
102
105
  /**
103
- * AppKit cache TTL for Databricks skill metadata and content.
104
- * Defaults to five minutes.
106
+ * Refresh TTL for the complete parsed skill catalogue. Auxiliary reads are
107
+ * always fresh. Defaults to five minutes.
105
108
  */
106
109
  workspaceSkillRefreshTtlMs?: number;
107
110
  /** Enable BM25 keyword search over indexed workspace content. */
108
111
  bm25?: boolean;
109
112
  /**
110
- * Command sandbox. Defaults to Databricks Sandbox. Pass `false` to disable
111
- * command execution, or an explicit Mastra sandbox/provider resolver to
112
- * replace Databricks for this workspace.
113
+ * Command sandbox. Defaults to Monty. Pass `"databricks"` or an options
114
+ * object for Databricks Sandbox, `false` to disable command execution, or an
115
+ * explicit Mastra sandbox/provider resolver.
113
116
  */
114
117
  sandbox?: WorkspaceSandboxSelection;
115
118
  /**
@@ -164,6 +167,8 @@ export declare const DEFAULT_SKILL_FOLDERS: Readonly<Record<DefaultSkillFolderNa
164
167
  * ```
165
168
  */
166
169
  export declare function createWorkspace(options?: CreateWorkspaceOptions): Workspace;
170
+ /** Return the dbx-tools catalogue resolver attached to one workspace. */
171
+ export declare function workspaceSkillCatalogueResolver(workspace: Workspace | undefined): SkillCatalogueResolver | undefined;
167
172
  /**
168
173
  * Merge the configured skill folders over {@link DEFAULT_SKILL_FOLDERS}.
169
174
  *