@dexto/tools-builtins 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/implementations/ask-user-tool.cjs +40 -9
- package/dist/implementations/ask-user-tool.d.ts +5 -5
- package/dist/implementations/ask-user-tool.d.ts.map +1 -1
- package/dist/implementations/ask-user-tool.js +41 -10
- package/dist/implementations/delegate-to-url-tool.cjs +6 -1
- package/dist/implementations/delegate-to-url-tool.d.ts.map +1 -1
- package/dist/implementations/delegate-to-url-tool.js +14 -2
- package/dist/implementations/delegate-to-url-tool.test.cjs +1 -0
- package/dist/implementations/delegate-to-url-tool.test.js +1 -0
- package/dist/implementations/exa-code-search-tool.cjs +6 -1
- package/dist/implementations/exa-code-search-tool.d.ts.map +1 -1
- package/dist/implementations/exa-code-search-tool.js +7 -2
- package/dist/implementations/exa-tools.test.cjs +1 -0
- package/dist/implementations/exa-tools.test.js +1 -0
- package/dist/implementations/exa-web-search-tool.cjs +6 -1
- package/dist/implementations/exa-web-search-tool.d.ts.map +1 -1
- package/dist/implementations/exa-web-search-tool.js +7 -2
- package/dist/implementations/get-resource-tool.cjs +9 -1
- package/dist/implementations/get-resource-tool.d.ts.map +1 -1
- package/dist/implementations/get-resource-tool.js +10 -2
- package/dist/implementations/http-request-tool.cjs +90 -40
- package/dist/implementations/http-request-tool.d.ts +5 -0
- package/dist/implementations/http-request-tool.d.ts.map +1 -1
- package/dist/implementations/http-request-tool.js +96 -40
- package/dist/implementations/http-request-tool.test.cjs +50 -0
- package/dist/implementations/http-request-tool.test.d.ts +2 -0
- package/dist/implementations/http-request-tool.test.d.ts.map +1 -0
- package/dist/implementations/http-request-tool.test.js +49 -0
- package/dist/implementations/invoke-skill-tool.cjs +45 -1
- package/dist/implementations/invoke-skill-tool.d.ts.map +1 -1
- package/dist/implementations/invoke-skill-tool.js +46 -2
- package/dist/implementations/list-resources-tool.cjs +12 -1
- package/dist/implementations/list-resources-tool.d.ts.map +1 -1
- package/dist/implementations/list-resources-tool.js +13 -2
- package/dist/implementations/sleep-tool.cjs +6 -1
- package/dist/implementations/sleep-tool.d.ts.map +1 -1
- package/dist/implementations/sleep-tool.js +7 -2
- package/dist/index.d.cts +19 -3
- package/package.json +4 -4
- package/dist/builtin-tools-factory.d.cts +0 -19
- package/dist/builtin-tools-factory.test.d.cts +0 -2
- package/dist/implementations/ask-user-tool.d.cts +0 -46
- package/dist/implementations/delegate-to-url-tool.d.cts +0 -27
- package/dist/implementations/delegate-to-url-tool.test.d.cts +0 -2
- package/dist/implementations/exa-code-search-tool.d.cts +0 -21
- package/dist/implementations/exa-mcp.d.cts +0 -24
- package/dist/implementations/exa-tools.test.d.cts +0 -2
- package/dist/implementations/exa-web-search-tool.d.cts +0 -30
- package/dist/implementations/get-resource-tool.d.cts +0 -22
- package/dist/implementations/http-request-tool.d.cts +0 -31
- package/dist/implementations/invoke-skill-tool.d.cts +0 -26
- package/dist/implementations/list-resources-tool.d.cts +0 -26
- package/dist/implementations/sleep-tool.d.cts +0 -16
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DextoRuntimeError,
|
|
4
|
+
ErrorScope,
|
|
5
|
+
ErrorType,
|
|
6
|
+
createLocalToolCallHeader,
|
|
7
|
+
defineTool,
|
|
8
|
+
truncateForHeader
|
|
9
|
+
} from "@dexto/core";
|
|
3
10
|
import { promises as dns } from "node:dns";
|
|
4
11
|
import { isIP } from "node:net";
|
|
5
12
|
import { TextDecoder } from "node:util";
|
|
@@ -73,61 +80,101 @@ function isPrivateAddress(ip) {
|
|
|
73
80
|
if (version === 6) return isPrivateIpv6(ip);
|
|
74
81
|
return false;
|
|
75
82
|
}
|
|
76
|
-
function
|
|
77
|
-
const
|
|
83
|
+
function createSafeLookup(config) {
|
|
84
|
+
const dnsLookup = config?.dnsLookup ?? dns.lookup;
|
|
85
|
+
return (hostname, options, callback) => {
|
|
78
86
|
void (async () => {
|
|
87
|
+
const toErrnoException = (err) => err;
|
|
88
|
+
const emitError = (err) => {
|
|
89
|
+
try {
|
|
90
|
+
if (options.all) {
|
|
91
|
+
callback(err, []);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
callback(err, "", 0);
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
};
|
|
79
98
|
try {
|
|
80
99
|
if (BLOCKED_HOSTNAMES.has(hostname) || hostname.endsWith(".localhost")) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
100
|
+
emitError(
|
|
101
|
+
toErrnoException(
|
|
102
|
+
new DextoRuntimeError(
|
|
103
|
+
"HTTP_REQUEST_UNSAFE_TARGET",
|
|
104
|
+
ErrorScope.TOOLS,
|
|
105
|
+
ErrorType.FORBIDDEN,
|
|
106
|
+
`Blocked request to local hostname: ${hostname}`
|
|
107
|
+
)
|
|
108
|
+
)
|
|
90
109
|
);
|
|
91
110
|
return;
|
|
92
111
|
}
|
|
93
|
-
const records = await
|
|
112
|
+
const records = await dnsLookup(hostname, {
|
|
94
113
|
all: true,
|
|
95
|
-
verbatim: true
|
|
114
|
+
verbatim: true,
|
|
115
|
+
family: options.family,
|
|
116
|
+
hints: options.hints
|
|
96
117
|
});
|
|
97
118
|
if (!records.length) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
119
|
+
emitError(
|
|
120
|
+
toErrnoException(
|
|
121
|
+
new DextoRuntimeError(
|
|
122
|
+
"HTTP_REQUEST_DNS_FAILED",
|
|
123
|
+
ErrorScope.TOOLS,
|
|
124
|
+
ErrorType.THIRD_PARTY,
|
|
125
|
+
`Failed to resolve hostname: ${hostname}`
|
|
126
|
+
)
|
|
127
|
+
)
|
|
107
128
|
);
|
|
108
129
|
return;
|
|
109
130
|
}
|
|
110
131
|
for (const record of records) {
|
|
111
132
|
if (isPrivateAddress(record.address)) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
133
|
+
emitError(
|
|
134
|
+
toErrnoException(
|
|
135
|
+
new DextoRuntimeError(
|
|
136
|
+
"HTTP_REQUEST_UNSAFE_TARGET",
|
|
137
|
+
ErrorScope.TOOLS,
|
|
138
|
+
ErrorType.FORBIDDEN,
|
|
139
|
+
`Blocked request to private address: ${record.address}`
|
|
140
|
+
)
|
|
141
|
+
)
|
|
121
142
|
);
|
|
122
143
|
return;
|
|
123
144
|
}
|
|
124
145
|
}
|
|
146
|
+
if (options.all) {
|
|
147
|
+
try {
|
|
148
|
+
callback(
|
|
149
|
+
null,
|
|
150
|
+
records
|
|
151
|
+
);
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
125
156
|
const selected = records[0];
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
157
|
+
if (!selected) {
|
|
158
|
+
emitError(
|
|
159
|
+
toErrnoException(
|
|
160
|
+
new DextoRuntimeError(
|
|
161
|
+
"HTTP_REQUEST_DNS_FAILED",
|
|
162
|
+
ErrorScope.TOOLS,
|
|
163
|
+
ErrorType.THIRD_PARTY,
|
|
164
|
+
`Failed to resolve hostname: ${hostname}`
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
callback(
|
|
172
|
+
null,
|
|
173
|
+
selected.address,
|
|
174
|
+
selected.family ?? (isIP(selected.address) === 6 ? 6 : 4)
|
|
175
|
+
);
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
131
178
|
} catch (error) {
|
|
132
179
|
const err = error instanceof DextoRuntimeError ? error : new DextoRuntimeError(
|
|
133
180
|
"HTTP_REQUEST_DNS_FAILED",
|
|
@@ -135,10 +182,13 @@ function createSafeDispatcher() {
|
|
|
135
182
|
ErrorType.THIRD_PARTY,
|
|
136
183
|
`Failed to resolve hostname: ${hostname}`
|
|
137
184
|
);
|
|
138
|
-
|
|
185
|
+
emitError(toErrnoException(err));
|
|
139
186
|
}
|
|
140
187
|
})();
|
|
141
188
|
};
|
|
189
|
+
}
|
|
190
|
+
function createSafeDispatcher() {
|
|
191
|
+
const lookup = createSafeLookup();
|
|
142
192
|
return new Agent({ connect: { lookup } });
|
|
143
193
|
}
|
|
144
194
|
async function assertSafeUrl(requestUrl) {
|
|
@@ -218,9 +268,14 @@ async function readResponseTextWithLimit(response) {
|
|
|
218
268
|
function createHttpRequestTool() {
|
|
219
269
|
return defineTool({
|
|
220
270
|
id: "http_request",
|
|
221
|
-
displayName: "Fetch",
|
|
222
271
|
description: "Make a direct HTTP request using fetch. Supports method, headers, query params, JSON bodies, and timeouts. Returns status, headers, raw body text, and parsed JSON when available.",
|
|
223
272
|
inputSchema: HttpRequestInputSchema,
|
|
273
|
+
presentation: {
|
|
274
|
+
describeHeader: (input) => createLocalToolCallHeader({
|
|
275
|
+
title: "Fetch",
|
|
276
|
+
argsText: truncateForHeader(input.url, 140)
|
|
277
|
+
})
|
|
278
|
+
},
|
|
224
279
|
async execute(input, _context) {
|
|
225
280
|
const { url, method, headers, query, body, timeoutMs } = input;
|
|
226
281
|
const requestUrl = new URL(url);
|
|
@@ -299,5 +354,6 @@ function createHttpRequestTool() {
|
|
|
299
354
|
});
|
|
300
355
|
}
|
|
301
356
|
export {
|
|
302
|
-
createHttpRequestTool
|
|
357
|
+
createHttpRequestTool,
|
|
358
|
+
createSafeLookup
|
|
303
359
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_vitest = require("vitest");
|
|
3
|
+
var import_http_request_tool = require("./http-request-tool.js");
|
|
4
|
+
(0, import_vitest.describe)("http_request tool", () => {
|
|
5
|
+
(0, import_vitest.it)("supports undici lookup({ all: true }) callback signature", async () => {
|
|
6
|
+
const records = [
|
|
7
|
+
{ address: "93.184.216.34", family: 4 },
|
|
8
|
+
{ address: "2606:2800:220:1:248:1893:25c8:1946", family: 6 }
|
|
9
|
+
];
|
|
10
|
+
const dnsLookup = import_vitest.vi.fn().mockResolvedValue(records);
|
|
11
|
+
const safeLookup = (0, import_http_request_tool.createSafeLookup)({ dnsLookup });
|
|
12
|
+
const result = await new Promise((resolve, reject) => {
|
|
13
|
+
safeLookup(
|
|
14
|
+
"example.com",
|
|
15
|
+
{ all: true },
|
|
16
|
+
((err, addresses) => {
|
|
17
|
+
if (err) {
|
|
18
|
+
reject(err);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
resolve(addresses);
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
(0, import_vitest.expect)(dnsLookup).toHaveBeenCalledWith(
|
|
26
|
+
"example.com",
|
|
27
|
+
import_vitest.expect.objectContaining({
|
|
28
|
+
all: true,
|
|
29
|
+
verbatim: true
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
(0, import_vitest.expect)(result).toEqual(records);
|
|
33
|
+
});
|
|
34
|
+
(0, import_vitest.it)("blocks localhost hostnames", async () => {
|
|
35
|
+
const tool = (0, import_http_request_tool.createHttpRequestTool)();
|
|
36
|
+
await (0, import_vitest.expect)(
|
|
37
|
+
tool.execute(
|
|
38
|
+
{
|
|
39
|
+
url: "http://localhost:1234",
|
|
40
|
+
method: "GET",
|
|
41
|
+
timeoutMs: 1e3
|
|
42
|
+
},
|
|
43
|
+
{}
|
|
44
|
+
)
|
|
45
|
+
).rejects.toMatchObject({
|
|
46
|
+
name: "DextoRuntimeError",
|
|
47
|
+
code: "HTTP_REQUEST_UNSAFE_TARGET"
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-request-tool.test.d.ts","sourceRoot":"","sources":["../../src/implementations/http-request-tool.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { createHttpRequestTool, createSafeLookup } from "./http-request-tool.js";
|
|
3
|
+
describe("http_request tool", () => {
|
|
4
|
+
it("supports undici lookup({ all: true }) callback signature", async () => {
|
|
5
|
+
const records = [
|
|
6
|
+
{ address: "93.184.216.34", family: 4 },
|
|
7
|
+
{ address: "2606:2800:220:1:248:1893:25c8:1946", family: 6 }
|
|
8
|
+
];
|
|
9
|
+
const dnsLookup = vi.fn().mockResolvedValue(records);
|
|
10
|
+
const safeLookup = createSafeLookup({ dnsLookup });
|
|
11
|
+
const result = await new Promise((resolve, reject) => {
|
|
12
|
+
safeLookup(
|
|
13
|
+
"example.com",
|
|
14
|
+
{ all: true },
|
|
15
|
+
((err, addresses) => {
|
|
16
|
+
if (err) {
|
|
17
|
+
reject(err);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
resolve(addresses);
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
expect(dnsLookup).toHaveBeenCalledWith(
|
|
25
|
+
"example.com",
|
|
26
|
+
expect.objectContaining({
|
|
27
|
+
all: true,
|
|
28
|
+
verbatim: true
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
expect(result).toEqual(records);
|
|
32
|
+
});
|
|
33
|
+
it("blocks localhost hostnames", async () => {
|
|
34
|
+
const tool = createHttpRequestTool();
|
|
35
|
+
await expect(
|
|
36
|
+
tool.execute(
|
|
37
|
+
{
|
|
38
|
+
url: "http://localhost:1234",
|
|
39
|
+
method: "GET",
|
|
40
|
+
timeoutMs: 1e3
|
|
41
|
+
},
|
|
42
|
+
{}
|
|
43
|
+
)
|
|
44
|
+
).rejects.toMatchObject({
|
|
45
|
+
name: "DextoRuntimeError",
|
|
46
|
+
code: "HTTP_REQUEST_UNSAFE_TARGET"
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -35,9 +35,19 @@ const InvokeSkillInputSchema = import_zod.z.object({
|
|
|
35
35
|
function createInvokeSkillTool() {
|
|
36
36
|
return (0, import_core.defineTool)({
|
|
37
37
|
id: "invoke_skill",
|
|
38
|
-
displayName: "Skill",
|
|
39
38
|
description: buildToolDescription(),
|
|
40
39
|
inputSchema: InvokeSkillInputSchema,
|
|
40
|
+
presentation: {
|
|
41
|
+
describeHeader: (input) => {
|
|
42
|
+
const skillName = input.skill;
|
|
43
|
+
const colonIndex = skillName.indexOf(":");
|
|
44
|
+
const displaySkillName = colonIndex >= 0 ? skillName.slice(colonIndex + 1) : skillName;
|
|
45
|
+
return (0, import_core.createLocalToolCallHeader)({
|
|
46
|
+
title: "Skill",
|
|
47
|
+
argsText: `/${displaySkillName}`
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
},
|
|
41
51
|
async execute(input, context) {
|
|
42
52
|
const { skill, args, taskContext } = input;
|
|
43
53
|
const promptManager = context.services?.prompts;
|
|
@@ -48,11 +58,13 @@ function createInvokeSkillTool() {
|
|
|
48
58
|
}
|
|
49
59
|
const autoInvocable = await promptManager.listAutoInvocablePrompts();
|
|
50
60
|
let skillKey;
|
|
61
|
+
let matchedInfo;
|
|
51
62
|
for (const key of Object.keys(autoInvocable)) {
|
|
52
63
|
const info = autoInvocable[key];
|
|
53
64
|
if (!info) continue;
|
|
54
65
|
if (key === skill || info.displayName === skill || info.commandName === skill || info.name === skill) {
|
|
55
66
|
skillKey = key;
|
|
67
|
+
matchedInfo = info;
|
|
56
68
|
break;
|
|
57
69
|
}
|
|
58
70
|
}
|
|
@@ -62,7 +74,39 @@ function createInvokeSkillTool() {
|
|
|
62
74
|
availableSkills: Object.keys(autoInvocable)
|
|
63
75
|
};
|
|
64
76
|
}
|
|
77
|
+
const toolkits = Array.isArray(matchedInfo?.metadata?.toolkits) ? (matchedInfo?.metadata?.toolkits).filter((toolkit) => typeof toolkit === "string").map((toolkit) => toolkit.trim()).filter((toolkit) => toolkit.length > 0) : [];
|
|
78
|
+
if (toolkits.length > 0) {
|
|
79
|
+
if (!context.agent?.loadToolkits) {
|
|
80
|
+
return {
|
|
81
|
+
error: `Skill '${skill}' requires toolkits (${toolkits.join(
|
|
82
|
+
", "
|
|
83
|
+
)}), but this agent does not support dynamic tool loading.`,
|
|
84
|
+
toolkits
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
await context.agent.loadToolkits(toolkits);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
error: error instanceof Error ? error.message : "Failed to load required toolkits",
|
|
92
|
+
toolkits
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
65
96
|
const promptDef = await promptManager.getPromptDefinition(skillKey);
|
|
97
|
+
if (promptDef?.context !== "fork" && promptDef?.allowedTools && promptDef.allowedTools.length > 0 && context.sessionId && context.agent?.toolManager) {
|
|
98
|
+
try {
|
|
99
|
+
context.agent.toolManager.addSessionAutoApproveTools(
|
|
100
|
+
context.sessionId,
|
|
101
|
+
promptDef.allowedTools
|
|
102
|
+
);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
context.logger?.warn("Failed to add auto-approve tools for skill", {
|
|
105
|
+
tools: promptDef.allowedTools,
|
|
106
|
+
error: error instanceof Error ? error.message : String(error)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
66
110
|
const promptResult = await promptManager.getPrompt(skillKey, args);
|
|
67
111
|
const flattened = (0, import_core.flattenPromptResult)(promptResult);
|
|
68
112
|
const content = flattened.text;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke-skill-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/invoke-skill-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"invoke-skill-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/invoke-skill-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAA+B,IAAI,EAAwB,MAAM,aAAa,CAAC;AAE3F,QAAA,MAAM,sBAAsB;;;;;;;;;;;;EAgBf,CAAC;AAEd;;;;;;GAMG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAAC,OAAO,sBAAsB,CAAC,CA0J3E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ToolError, defineTool, flattenPromptResult } from "@dexto/core";
|
|
2
|
+
import { ToolError, createLocalToolCallHeader, defineTool, flattenPromptResult } from "@dexto/core";
|
|
3
3
|
const InvokeSkillInputSchema = z.object({
|
|
4
4
|
skill: z.string().min(1, "Skill name is required").describe(
|
|
5
5
|
'The name of the skill to invoke (e.g., "plugin-name:skill-name" or "skill-name")'
|
|
@@ -12,9 +12,19 @@ const InvokeSkillInputSchema = z.object({
|
|
|
12
12
|
function createInvokeSkillTool() {
|
|
13
13
|
return defineTool({
|
|
14
14
|
id: "invoke_skill",
|
|
15
|
-
displayName: "Skill",
|
|
16
15
|
description: buildToolDescription(),
|
|
17
16
|
inputSchema: InvokeSkillInputSchema,
|
|
17
|
+
presentation: {
|
|
18
|
+
describeHeader: (input) => {
|
|
19
|
+
const skillName = input.skill;
|
|
20
|
+
const colonIndex = skillName.indexOf(":");
|
|
21
|
+
const displaySkillName = colonIndex >= 0 ? skillName.slice(colonIndex + 1) : skillName;
|
|
22
|
+
return createLocalToolCallHeader({
|
|
23
|
+
title: "Skill",
|
|
24
|
+
argsText: `/${displaySkillName}`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
},
|
|
18
28
|
async execute(input, context) {
|
|
19
29
|
const { skill, args, taskContext } = input;
|
|
20
30
|
const promptManager = context.services?.prompts;
|
|
@@ -25,11 +35,13 @@ function createInvokeSkillTool() {
|
|
|
25
35
|
}
|
|
26
36
|
const autoInvocable = await promptManager.listAutoInvocablePrompts();
|
|
27
37
|
let skillKey;
|
|
38
|
+
let matchedInfo;
|
|
28
39
|
for (const key of Object.keys(autoInvocable)) {
|
|
29
40
|
const info = autoInvocable[key];
|
|
30
41
|
if (!info) continue;
|
|
31
42
|
if (key === skill || info.displayName === skill || info.commandName === skill || info.name === skill) {
|
|
32
43
|
skillKey = key;
|
|
44
|
+
matchedInfo = info;
|
|
33
45
|
break;
|
|
34
46
|
}
|
|
35
47
|
}
|
|
@@ -39,7 +51,39 @@ function createInvokeSkillTool() {
|
|
|
39
51
|
availableSkills: Object.keys(autoInvocable)
|
|
40
52
|
};
|
|
41
53
|
}
|
|
54
|
+
const toolkits = Array.isArray(matchedInfo?.metadata?.toolkits) ? (matchedInfo?.metadata?.toolkits).filter((toolkit) => typeof toolkit === "string").map((toolkit) => toolkit.trim()).filter((toolkit) => toolkit.length > 0) : [];
|
|
55
|
+
if (toolkits.length > 0) {
|
|
56
|
+
if (!context.agent?.loadToolkits) {
|
|
57
|
+
return {
|
|
58
|
+
error: `Skill '${skill}' requires toolkits (${toolkits.join(
|
|
59
|
+
", "
|
|
60
|
+
)}), but this agent does not support dynamic tool loading.`,
|
|
61
|
+
toolkits
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await context.agent.loadToolkits(toolkits);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
error: error instanceof Error ? error.message : "Failed to load required toolkits",
|
|
69
|
+
toolkits
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
42
73
|
const promptDef = await promptManager.getPromptDefinition(skillKey);
|
|
74
|
+
if (promptDef?.context !== "fork" && promptDef?.allowedTools && promptDef.allowedTools.length > 0 && context.sessionId && context.agent?.toolManager) {
|
|
75
|
+
try {
|
|
76
|
+
context.agent.toolManager.addSessionAutoApproveTools(
|
|
77
|
+
context.sessionId,
|
|
78
|
+
promptDef.allowedTools
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
context.logger?.warn("Failed to add auto-approve tools for skill", {
|
|
82
|
+
tools: promptDef.allowedTools,
|
|
83
|
+
error: error instanceof Error ? error.message : String(error)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
43
87
|
const promptResult = await promptManager.getPrompt(skillKey, args);
|
|
44
88
|
const flattened = flattenPromptResult(promptResult);
|
|
45
89
|
const content = flattened.text;
|
|
@@ -33,9 +33,20 @@ const ListResourcesInputSchema = import_zod.z.object({
|
|
|
33
33
|
function createListResourcesTool() {
|
|
34
34
|
return (0, import_core.defineTool)({
|
|
35
35
|
id: "list_resources",
|
|
36
|
-
displayName: "List Resources",
|
|
37
36
|
description: "List available resources (images, files, etc.). Returns resource references that can be used with get_resource to obtain shareable URLs or metadata. Filter by source (tool/user) or kind (image/audio/video/binary).",
|
|
38
37
|
inputSchema: ListResourcesInputSchema,
|
|
38
|
+
presentation: {
|
|
39
|
+
describeHeader: (input) => {
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (input.source && input.source !== "all") parts.push(`source=${input.source}`);
|
|
42
|
+
if (input.kind && input.kind !== "all") parts.push(`kind=${input.kind}`);
|
|
43
|
+
if (typeof input.limit === "number") parts.push(`limit=${input.limit}`);
|
|
44
|
+
return (0, import_core.createLocalToolCallHeader)({
|
|
45
|
+
title: "List Resources",
|
|
46
|
+
...parts.length > 0 ? { argsText: parts.join(", ") } : {}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
},
|
|
39
50
|
async execute(input, context) {
|
|
40
51
|
const { source, kind, limit } = input;
|
|
41
52
|
const resourceManager = context.services?.resources;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-resources-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/list-resources-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAE9D,QAAA,MAAM,wBAAwB;;;;;;;;;;;;EAoBjB,CAAC;AAYd;;;;;;GAMG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAAC,OAAO,wBAAwB,CAAC,
|
|
1
|
+
{"version":3,"file":"list-resources-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/list-resources-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAE9D,QAAA,MAAM,wBAAwB;;;;;;;;;;;;EAoBjB,CAAC;AAYd;;;;;;GAMG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAAC,OAAO,wBAAwB,CAAC,CA0F/E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ToolError, defineTool } from "@dexto/core";
|
|
2
|
+
import { ToolError, createLocalToolCallHeader, defineTool } from "@dexto/core";
|
|
3
3
|
const ListResourcesInputSchema = z.object({
|
|
4
4
|
source: z.enum(["all", "tool", "user"]).optional().default("all").describe(
|
|
5
5
|
'Filter by source: "tool" for tool-generated resources, "user" for user-uploaded, "all" for both'
|
|
@@ -10,9 +10,20 @@ const ListResourcesInputSchema = z.object({
|
|
|
10
10
|
function createListResourcesTool() {
|
|
11
11
|
return defineTool({
|
|
12
12
|
id: "list_resources",
|
|
13
|
-
displayName: "List Resources",
|
|
14
13
|
description: "List available resources (images, files, etc.). Returns resource references that can be used with get_resource to obtain shareable URLs or metadata. Filter by source (tool/user) or kind (image/audio/video/binary).",
|
|
15
14
|
inputSchema: ListResourcesInputSchema,
|
|
15
|
+
presentation: {
|
|
16
|
+
describeHeader: (input) => {
|
|
17
|
+
const parts = [];
|
|
18
|
+
if (input.source && input.source !== "all") parts.push(`source=${input.source}`);
|
|
19
|
+
if (input.kind && input.kind !== "all") parts.push(`kind=${input.kind}`);
|
|
20
|
+
if (typeof input.limit === "number") parts.push(`limit=${input.limit}`);
|
|
21
|
+
return createLocalToolCallHeader({
|
|
22
|
+
title: "List Resources",
|
|
23
|
+
...parts.length > 0 ? { argsText: parts.join(", ") } : {}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
},
|
|
16
27
|
async execute(input, context) {
|
|
17
28
|
const { source, kind, limit } = input;
|
|
18
29
|
const resourceManager = context.services?.resources;
|
|
@@ -29,9 +29,14 @@ const SleepInputSchema = import_zod.z.object({
|
|
|
29
29
|
function createSleepTool() {
|
|
30
30
|
return (0, import_core.defineTool)({
|
|
31
31
|
id: "sleep",
|
|
32
|
-
displayName: "Sleep",
|
|
33
32
|
description: "Pause execution for a specified number of milliseconds.",
|
|
34
33
|
inputSchema: SleepInputSchema,
|
|
34
|
+
presentation: {
|
|
35
|
+
describeHeader: (input) => (0, import_core.createLocalToolCallHeader)({
|
|
36
|
+
title: "Sleep",
|
|
37
|
+
argsText: `${input.ms}ms`
|
|
38
|
+
})
|
|
39
|
+
},
|
|
35
40
|
async execute(input, _context) {
|
|
36
41
|
const { ms } = input;
|
|
37
42
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sleep-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/sleep-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAE9D,QAAA,MAAM,gBAAgB;;;;;;EAST,CAAC;AAEd;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAAC,OAAO,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"sleep-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/sleep-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAE9D,QAAA,MAAM,gBAAgB;;;;;;EAST,CAAC;AAEd;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAAC,OAAO,gBAAgB,CAAC,CAkB/D"}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { defineTool } from "@dexto/core";
|
|
2
|
+
import { createLocalToolCallHeader, defineTool } from "@dexto/core";
|
|
3
3
|
const SleepInputSchema = z.object({
|
|
4
4
|
ms: z.number().int().positive().max(6e5).describe("Milliseconds to sleep (max 10 minutes)")
|
|
5
5
|
}).strict();
|
|
6
6
|
function createSleepTool() {
|
|
7
7
|
return defineTool({
|
|
8
8
|
id: "sleep",
|
|
9
|
-
displayName: "Sleep",
|
|
10
9
|
description: "Pause execution for a specified number of milliseconds.",
|
|
11
10
|
inputSchema: SleepInputSchema,
|
|
11
|
+
presentation: {
|
|
12
|
+
describeHeader: (input) => createLocalToolCallHeader({
|
|
13
|
+
title: "Sleep",
|
|
14
|
+
argsText: `${input.ms}ms`
|
|
15
|
+
})
|
|
16
|
+
},
|
|
12
17
|
async execute(input, _context) {
|
|
13
18
|
const { ms } = input;
|
|
14
19
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
3
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ToolFactory } from '@dexto/agent-config';
|
|
3
|
+
|
|
4
|
+
declare const BUILTIN_TOOL_NAMES: readonly ["ask_user", "delegate_to_url", "list_resources", "get_resource", "invoke_skill", "http_request", "sleep", "web_search", "code_search"];
|
|
5
|
+
type BuiltinToolName = (typeof BUILTIN_TOOL_NAMES)[number];
|
|
6
|
+
declare const BuiltinToolsConfigSchema: z.ZodObject<{
|
|
7
|
+
type: z.ZodLiteral<"builtin-tools">;
|
|
8
|
+
enabledTools: z.ZodOptional<z.ZodArray<z.ZodEnum<["ask_user", "delegate_to_url", "list_resources", "get_resource", "invoke_skill", "http_request", "sleep", "web_search", "code_search"]>, "many">>;
|
|
9
|
+
}, "strict", z.ZodTypeAny, {
|
|
10
|
+
type: "builtin-tools";
|
|
11
|
+
enabledTools?: ("ask_user" | "delegate_to_url" | "get_resource" | "invoke_skill" | "list_resources" | "http_request" | "sleep" | "web_search" | "code_search")[] | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
type: "builtin-tools";
|
|
14
|
+
enabledTools?: ("ask_user" | "delegate_to_url" | "get_resource" | "invoke_skill" | "list_resources" | "http_request" | "sleep" | "web_search" | "code_search")[] | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
type BuiltinToolsConfig = z.output<typeof BuiltinToolsConfigSchema>;
|
|
17
|
+
declare const builtinToolsFactory: ToolFactory<BuiltinToolsConfig>;
|
|
18
|
+
|
|
19
|
+
export { BUILTIN_TOOL_NAMES, type BuiltinToolName, type BuiltinToolsConfig, BuiltinToolsConfigSchema, builtinToolsFactory };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexto/tools-builtins",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Built-in tools factory for Dexto agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"undici": "^7.22.0",
|
|
21
21
|
"zod": "^3.25.0",
|
|
22
|
-
"@dexto/agent-config": "1.6.
|
|
23
|
-
"@dexto/core": "1.6.
|
|
22
|
+
"@dexto/agent-config": "1.6.2",
|
|
23
|
+
"@dexto/core": "1.6.2"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"tsup": "^8.0.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"README.md"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "tsup",
|
|
34
|
+
"build": "tsup && node ../../scripts/clean-tsbuildinfo.mjs && tsc -b tsconfig.json --emitDeclarationOnly",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"clean": "rm -rf dist"
|
|
37
37
|
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { ToolFactory } from '@dexto/agent-config';
|
|
3
|
-
|
|
4
|
-
declare const BUILTIN_TOOL_NAMES: readonly ["ask_user", "delegate_to_url", "list_resources", "get_resource", "invoke_skill", "http_request", "sleep", "web_search", "code_search"];
|
|
5
|
-
type BuiltinToolName = (typeof BUILTIN_TOOL_NAMES)[number];
|
|
6
|
-
declare const BuiltinToolsConfigSchema: z.ZodObject<{
|
|
7
|
-
type: z.ZodLiteral<"builtin-tools">;
|
|
8
|
-
enabledTools: z.ZodOptional<z.ZodArray<z.ZodEnum<["ask_user", "delegate_to_url", "list_resources", "get_resource", "invoke_skill", "http_request", "sleep", "web_search", "code_search"]>, "many">>;
|
|
9
|
-
}, "strict", z.ZodTypeAny, {
|
|
10
|
-
type: "builtin-tools";
|
|
11
|
-
enabledTools?: ("ask_user" | "delegate_to_url" | "list_resources" | "get_resource" | "invoke_skill" | "http_request" | "sleep" | "web_search" | "code_search")[] | undefined;
|
|
12
|
-
}, {
|
|
13
|
-
type: "builtin-tools";
|
|
14
|
-
enabledTools?: ("ask_user" | "delegate_to_url" | "list_resources" | "get_resource" | "invoke_skill" | "http_request" | "sleep" | "web_search" | "code_search")[] | undefined;
|
|
15
|
-
}>;
|
|
16
|
-
type BuiltinToolsConfig = z.output<typeof BuiltinToolsConfigSchema>;
|
|
17
|
-
declare const builtinToolsFactory: ToolFactory<BuiltinToolsConfig>;
|
|
18
|
-
|
|
19
|
-
export { BUILTIN_TOOL_NAMES, type BuiltinToolName, type BuiltinToolsConfig, BuiltinToolsConfigSchema, builtinToolsFactory };
|