@dexto/tools-builtins 1.6.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/LICENSE +44 -0
- package/dist/builtin-tools-factory.cjs +95 -0
- package/dist/builtin-tools-factory.d.cts +19 -0
- package/dist/builtin-tools-factory.d.ts +17 -0
- package/dist/builtin-tools-factory.d.ts.map +1 -0
- package/dist/builtin-tools-factory.js +69 -0
- package/dist/builtin-tools-factory.test.cjs +26 -0
- package/dist/builtin-tools-factory.test.d.cts +2 -0
- package/dist/builtin-tools-factory.test.d.ts +2 -0
- package/dist/builtin-tools-factory.test.d.ts.map +1 -0
- package/dist/builtin-tools-factory.test.js +25 -0
- package/dist/implementations/ask-user-tool.cjs +65 -0
- package/dist/implementations/ask-user-tool.d.cts +46 -0
- package/dist/implementations/ask-user-tool.d.ts +45 -0
- package/dist/implementations/ask-user-tool.d.ts.map +1 -0
- package/dist/implementations/ask-user-tool.js +41 -0
- package/dist/implementations/delegate-to-url-tool.cjs +183 -0
- package/dist/implementations/delegate-to-url-tool.d.cts +27 -0
- package/dist/implementations/delegate-to-url-tool.d.ts +26 -0
- package/dist/implementations/delegate-to-url-tool.d.ts.map +1 -0
- package/dist/implementations/delegate-to-url-tool.js +159 -0
- package/dist/implementations/delegate-to-url-tool.test.cjs +75 -0
- package/dist/implementations/delegate-to-url-tool.test.d.cts +2 -0
- package/dist/implementations/delegate-to-url-tool.test.d.ts +2 -0
- package/dist/implementations/delegate-to-url-tool.test.d.ts.map +1 -0
- package/dist/implementations/delegate-to-url-tool.test.js +74 -0
- package/dist/implementations/exa-code-search-tool.cjs +57 -0
- package/dist/implementations/exa-code-search-tool.d.cts +21 -0
- package/dist/implementations/exa-code-search-tool.d.ts +20 -0
- package/dist/implementations/exa-code-search-tool.d.ts.map +1 -0
- package/dist/implementations/exa-code-search-tool.js +33 -0
- package/dist/implementations/exa-mcp.cjs +98 -0
- package/dist/implementations/exa-mcp.d.cts +24 -0
- package/dist/implementations/exa-mcp.d.ts +23 -0
- package/dist/implementations/exa-mcp.d.ts.map +1 -0
- package/dist/implementations/exa-mcp.js +74 -0
- package/dist/implementations/exa-tools.test.cjs +91 -0
- package/dist/implementations/exa-tools.test.d.cts +2 -0
- package/dist/implementations/exa-tools.test.d.ts +2 -0
- package/dist/implementations/exa-tools.test.d.ts.map +1 -0
- package/dist/implementations/exa-tools.test.js +90 -0
- package/dist/implementations/exa-web-search-tool.cjs +63 -0
- package/dist/implementations/exa-web-search-tool.d.cts +30 -0
- package/dist/implementations/exa-web-search-tool.d.ts +29 -0
- package/dist/implementations/exa-web-search-tool.d.ts.map +1 -0
- package/dist/implementations/exa-web-search-tool.js +39 -0
- package/dist/implementations/get-resource-tool.cjs +121 -0
- package/dist/implementations/get-resource-tool.d.cts +22 -0
- package/dist/implementations/get-resource-tool.d.ts +21 -0
- package/dist/implementations/get-resource-tool.d.ts.map +1 -0
- package/dist/implementations/get-resource-tool.js +97 -0
- package/dist/implementations/http-request-tool.cjs +327 -0
- package/dist/implementations/http-request-tool.d.cts +31 -0
- package/dist/implementations/http-request-tool.d.ts +30 -0
- package/dist/implementations/http-request-tool.d.ts.map +1 -0
- package/dist/implementations/http-request-tool.js +303 -0
- package/dist/implementations/invoke-skill-tool.cjs +134 -0
- package/dist/implementations/invoke-skill-tool.d.cts +26 -0
- package/dist/implementations/invoke-skill-tool.d.ts +25 -0
- package/dist/implementations/invoke-skill-tool.d.ts.map +1 -0
- package/dist/implementations/invoke-skill-tool.js +110 -0
- package/dist/implementations/list-resources-tool.cjs +99 -0
- package/dist/implementations/list-resources-tool.d.cts +26 -0
- package/dist/implementations/list-resources-tool.d.ts +25 -0
- package/dist/implementations/list-resources-tool.d.ts.map +1 -0
- package/dist/implementations/list-resources-tool.js +75 -0
- package/dist/implementations/sleep-tool.cjs +45 -0
- package/dist/implementations/sleep-tool.d.cts +16 -0
- package/dist/implementations/sleep-tool.d.ts +15 -0
- package/dist/implementations/sleep-tool.d.ts.map +1 -0
- package/dist/implementations/sleep-tool.js +21 -0
- package/dist/index.cjs +33 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/package.json +38 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var delegate_to_url_tool_exports = {};
|
|
20
|
+
__export(delegate_to_url_tool_exports, {
|
|
21
|
+
createDelegateToUrlTool: () => createDelegateToUrlTool
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(delegate_to_url_tool_exports);
|
|
24
|
+
var import_zod = require("zod");
|
|
25
|
+
var import_core = require("@dexto/core");
|
|
26
|
+
const DelegateToUrlInputSchema = import_zod.z.object({
|
|
27
|
+
url: import_zod.z.string().url().describe(
|
|
28
|
+
'The A2A-compliant agent URL (e.g., "http://localhost:3001" or "https://agent.example.com"). The tool will automatically append the correct JSON-RPC endpoint.'
|
|
29
|
+
),
|
|
30
|
+
message: import_zod.z.string().min(1).describe(
|
|
31
|
+
"The message or task to delegate to the agent. This will be sent as natural language input."
|
|
32
|
+
),
|
|
33
|
+
sessionId: import_zod.z.string().optional().describe(
|
|
34
|
+
"Optional session ID for maintaining conversation state across multiple delegations to the same agent"
|
|
35
|
+
),
|
|
36
|
+
timeout: import_zod.z.number().optional().default(3e4).describe("Request timeout in milliseconds (default: 30000)")
|
|
37
|
+
}).strict();
|
|
38
|
+
function isPlainObject(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
class SimpleA2AClient {
|
|
42
|
+
url;
|
|
43
|
+
timeout;
|
|
44
|
+
constructor(url, timeout = 3e4) {
|
|
45
|
+
this.url = url.replace(/\/$/, "");
|
|
46
|
+
this.timeout = timeout;
|
|
47
|
+
}
|
|
48
|
+
async sendMessage(message, sessionId) {
|
|
49
|
+
const messageId = this.generateId();
|
|
50
|
+
const taskId = sessionId || this.generateId();
|
|
51
|
+
const a2aMessage = {
|
|
52
|
+
role: "user",
|
|
53
|
+
parts: [
|
|
54
|
+
{
|
|
55
|
+
kind: "text",
|
|
56
|
+
text: message
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
messageId,
|
|
60
|
+
taskId,
|
|
61
|
+
contextId: taskId,
|
|
62
|
+
kind: "message"
|
|
63
|
+
};
|
|
64
|
+
const rpcRequest = {
|
|
65
|
+
jsonrpc: "2.0",
|
|
66
|
+
id: this.generateId(),
|
|
67
|
+
method: "message/send",
|
|
68
|
+
params: {
|
|
69
|
+
message: a2aMessage,
|
|
70
|
+
configuration: {
|
|
71
|
+
blocking: true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const endpoints = [`${this.url}/v1/jsonrpc`, `${this.url}/jsonrpc`];
|
|
76
|
+
let lastError = null;
|
|
77
|
+
for (const endpoint of endpoints) {
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(endpoint, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"User-Agent": "@dexto/core"
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify(rpcRequest),
|
|
88
|
+
signal: controller.signal
|
|
89
|
+
});
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
lastError = new Error(
|
|
92
|
+
`HTTP ${response.status}: ${response.statusText} (tried ${endpoint})`
|
|
93
|
+
);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
if (isPlainObject(data) && "error" in data && data.error) {
|
|
98
|
+
const errorMessage = isPlainObject(data.error) && typeof data.error.message === "string" ? data.error.message : "Unknown error";
|
|
99
|
+
throw new Error(`Agent returned error: ${errorMessage}`);
|
|
100
|
+
}
|
|
101
|
+
if (isPlainObject(data) && "result" in data) {
|
|
102
|
+
return this.extractTaskResponse(data.result);
|
|
103
|
+
}
|
|
104
|
+
return data;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
107
|
+
throw new import_core.DextoRuntimeError(
|
|
108
|
+
"DELEGATION_TIMEOUT",
|
|
109
|
+
import_core.ErrorScope.TOOLS,
|
|
110
|
+
import_core.ErrorType.TIMEOUT,
|
|
111
|
+
`Delegation timeout after ${this.timeout}ms`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
115
|
+
} finally {
|
|
116
|
+
clearTimeout(timeoutId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
throw new import_core.DextoRuntimeError(
|
|
120
|
+
"DELEGATION_FAILED",
|
|
121
|
+
import_core.ErrorScope.TOOLS,
|
|
122
|
+
import_core.ErrorType.THIRD_PARTY,
|
|
123
|
+
`Failed to connect to agent at ${this.url}. Tried endpoints: ${endpoints.join(", ")}. Last error: ${lastError?.message || "Unknown error"}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
extractTaskResponse(task) {
|
|
127
|
+
if (isPlainObject(task) && Array.isArray(task.history)) {
|
|
128
|
+
const agentMessages = task.history.filter(
|
|
129
|
+
(message) => isPlainObject(message) && message.role === "agent"
|
|
130
|
+
);
|
|
131
|
+
if (agentMessages.length > 0) {
|
|
132
|
+
const lastMessage = agentMessages[agentMessages.length - 1];
|
|
133
|
+
if (lastMessage && Array.isArray(lastMessage.parts)) {
|
|
134
|
+
const textParts = lastMessage.parts.filter(
|
|
135
|
+
(part) => isPlainObject(part) && part.kind === "text"
|
|
136
|
+
).map((part) => part.text).filter((text) => typeof text === "string");
|
|
137
|
+
return textParts.join("\n");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return JSON.stringify(task, null, 2);
|
|
142
|
+
}
|
|
143
|
+
generateId() {
|
|
144
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function createDelegateToUrlTool() {
|
|
148
|
+
return (0, import_core.defineTool)({
|
|
149
|
+
id: "delegate_to_url",
|
|
150
|
+
displayName: "Delegate",
|
|
151
|
+
description: 'Delegate a task to another A2A-compliant agent at a specific URL. Supports STATEFUL multi-turn conversations via sessionId parameter. USAGE: (1) First delegation: provide url + message. Tool returns a response AND a sessionId. (2) Follow-up: use the SAME sessionId to continue the conversation with that agent. The agent remembers previous context. EXAMPLE: First call {url: "http://agent:3001", message: "Analyze data X"} returns {sessionId: "xyz", response: "..."}. Second call {url: "http://agent:3001", message: "What was the top insight?", sessionId: "xyz"}. The agent will remember the first analysis and can answer specifically.',
|
|
152
|
+
inputSchema: DelegateToUrlInputSchema,
|
|
153
|
+
async execute(input, _context) {
|
|
154
|
+
const { url, message, sessionId, timeout } = input;
|
|
155
|
+
try {
|
|
156
|
+
const client = new SimpleA2AClient(url, timeout);
|
|
157
|
+
const effectiveSessionId = sessionId || `delegation-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
158
|
+
const response = await client.sendMessage(message, effectiveSessionId);
|
|
159
|
+
return {
|
|
160
|
+
success: true,
|
|
161
|
+
agentUrl: url,
|
|
162
|
+
sessionId: effectiveSessionId,
|
|
163
|
+
response,
|
|
164
|
+
_hint: sessionId ? "Continued existing conversation" : "Started new conversation - use this sessionId for follow-ups"
|
|
165
|
+
};
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof import_core.DextoRuntimeError) {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
throw new import_core.DextoRuntimeError(
|
|
171
|
+
"DELEGATION_ERROR",
|
|
172
|
+
import_core.ErrorScope.TOOLS,
|
|
173
|
+
import_core.ErrorType.SYSTEM,
|
|
174
|
+
`Delegation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
181
|
+
0 && (module.exports = {
|
|
182
|
+
createDelegateToUrlTool
|
|
183
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Tool } from '@dexto/core';
|
|
3
|
+
|
|
4
|
+
declare const DelegateToUrlInputSchema: z.ZodObject<{
|
|
5
|
+
url: z.ZodString;
|
|
6
|
+
message: z.ZodString;
|
|
7
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
9
|
+
}, "strict", z.ZodTypeAny, {
|
|
10
|
+
timeout: number;
|
|
11
|
+
message: string;
|
|
12
|
+
url: string;
|
|
13
|
+
sessionId?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
message: string;
|
|
16
|
+
url: string;
|
|
17
|
+
timeout?: number | undefined;
|
|
18
|
+
sessionId?: string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Create the `delegate_to_url` tool.
|
|
22
|
+
*
|
|
23
|
+
* Delegates a message/task to another A2A-compliant agent URL via JSON-RPC and returns its response.
|
|
24
|
+
*/
|
|
25
|
+
declare function createDelegateToUrlTool(): Tool<typeof DelegateToUrlInputSchema>;
|
|
26
|
+
|
|
27
|
+
export { createDelegateToUrlTool };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Tool } from '@dexto/core';
|
|
3
|
+
declare const DelegateToUrlInputSchema: z.ZodObject<{
|
|
4
|
+
url: z.ZodString;
|
|
5
|
+
message: z.ZodString;
|
|
6
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
7
|
+
timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
8
|
+
}, "strict", z.ZodTypeAny, {
|
|
9
|
+
message: string;
|
|
10
|
+
url: string;
|
|
11
|
+
timeout: number;
|
|
12
|
+
sessionId?: string | undefined;
|
|
13
|
+
}, {
|
|
14
|
+
message: string;
|
|
15
|
+
url: string;
|
|
16
|
+
sessionId?: string | undefined;
|
|
17
|
+
timeout?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Create the `delegate_to_url` tool.
|
|
21
|
+
*
|
|
22
|
+
* Delegates a message/task to another A2A-compliant agent URL via JSON-RPC and returns its response.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createDelegateToUrlTool(): Tool<typeof DelegateToUrlInputSchema>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=delegate-to-url-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate-to-url-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/delegate-to-url-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAG9D,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;EA0BjB,CAAC;AAsJd;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAAC,OAAO,wBAAwB,CAAC,CAyC/E"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DextoRuntimeError, ErrorScope, ErrorType, defineTool } from "@dexto/core";
|
|
3
|
+
const DelegateToUrlInputSchema = z.object({
|
|
4
|
+
url: z.string().url().describe(
|
|
5
|
+
'The A2A-compliant agent URL (e.g., "http://localhost:3001" or "https://agent.example.com"). The tool will automatically append the correct JSON-RPC endpoint.'
|
|
6
|
+
),
|
|
7
|
+
message: z.string().min(1).describe(
|
|
8
|
+
"The message or task to delegate to the agent. This will be sent as natural language input."
|
|
9
|
+
),
|
|
10
|
+
sessionId: z.string().optional().describe(
|
|
11
|
+
"Optional session ID for maintaining conversation state across multiple delegations to the same agent"
|
|
12
|
+
),
|
|
13
|
+
timeout: z.number().optional().default(3e4).describe("Request timeout in milliseconds (default: 30000)")
|
|
14
|
+
}).strict();
|
|
15
|
+
function isPlainObject(value) {
|
|
16
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
class SimpleA2AClient {
|
|
19
|
+
url;
|
|
20
|
+
timeout;
|
|
21
|
+
constructor(url, timeout = 3e4) {
|
|
22
|
+
this.url = url.replace(/\/$/, "");
|
|
23
|
+
this.timeout = timeout;
|
|
24
|
+
}
|
|
25
|
+
async sendMessage(message, sessionId) {
|
|
26
|
+
const messageId = this.generateId();
|
|
27
|
+
const taskId = sessionId || this.generateId();
|
|
28
|
+
const a2aMessage = {
|
|
29
|
+
role: "user",
|
|
30
|
+
parts: [
|
|
31
|
+
{
|
|
32
|
+
kind: "text",
|
|
33
|
+
text: message
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
messageId,
|
|
37
|
+
taskId,
|
|
38
|
+
contextId: taskId,
|
|
39
|
+
kind: "message"
|
|
40
|
+
};
|
|
41
|
+
const rpcRequest = {
|
|
42
|
+
jsonrpc: "2.0",
|
|
43
|
+
id: this.generateId(),
|
|
44
|
+
method: "message/send",
|
|
45
|
+
params: {
|
|
46
|
+
message: a2aMessage,
|
|
47
|
+
configuration: {
|
|
48
|
+
blocking: true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const endpoints = [`${this.url}/v1/jsonrpc`, `${this.url}/jsonrpc`];
|
|
53
|
+
let lastError = null;
|
|
54
|
+
for (const endpoint of endpoints) {
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetch(endpoint, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: {
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
"User-Agent": "@dexto/core"
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify(rpcRequest),
|
|
65
|
+
signal: controller.signal
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
lastError = new Error(
|
|
69
|
+
`HTTP ${response.status}: ${response.statusText} (tried ${endpoint})`
|
|
70
|
+
);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const data = await response.json();
|
|
74
|
+
if (isPlainObject(data) && "error" in data && data.error) {
|
|
75
|
+
const errorMessage = isPlainObject(data.error) && typeof data.error.message === "string" ? data.error.message : "Unknown error";
|
|
76
|
+
throw new Error(`Agent returned error: ${errorMessage}`);
|
|
77
|
+
}
|
|
78
|
+
if (isPlainObject(data) && "result" in data) {
|
|
79
|
+
return this.extractTaskResponse(data.result);
|
|
80
|
+
}
|
|
81
|
+
return data;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
84
|
+
throw new DextoRuntimeError(
|
|
85
|
+
"DELEGATION_TIMEOUT",
|
|
86
|
+
ErrorScope.TOOLS,
|
|
87
|
+
ErrorType.TIMEOUT,
|
|
88
|
+
`Delegation timeout after ${this.timeout}ms`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
92
|
+
} finally {
|
|
93
|
+
clearTimeout(timeoutId);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
throw new DextoRuntimeError(
|
|
97
|
+
"DELEGATION_FAILED",
|
|
98
|
+
ErrorScope.TOOLS,
|
|
99
|
+
ErrorType.THIRD_PARTY,
|
|
100
|
+
`Failed to connect to agent at ${this.url}. Tried endpoints: ${endpoints.join(", ")}. Last error: ${lastError?.message || "Unknown error"}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
extractTaskResponse(task) {
|
|
104
|
+
if (isPlainObject(task) && Array.isArray(task.history)) {
|
|
105
|
+
const agentMessages = task.history.filter(
|
|
106
|
+
(message) => isPlainObject(message) && message.role === "agent"
|
|
107
|
+
);
|
|
108
|
+
if (agentMessages.length > 0) {
|
|
109
|
+
const lastMessage = agentMessages[agentMessages.length - 1];
|
|
110
|
+
if (lastMessage && Array.isArray(lastMessage.parts)) {
|
|
111
|
+
const textParts = lastMessage.parts.filter(
|
|
112
|
+
(part) => isPlainObject(part) && part.kind === "text"
|
|
113
|
+
).map((part) => part.text).filter((text) => typeof text === "string");
|
|
114
|
+
return textParts.join("\n");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return JSON.stringify(task, null, 2);
|
|
119
|
+
}
|
|
120
|
+
generateId() {
|
|
121
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function createDelegateToUrlTool() {
|
|
125
|
+
return defineTool({
|
|
126
|
+
id: "delegate_to_url",
|
|
127
|
+
displayName: "Delegate",
|
|
128
|
+
description: 'Delegate a task to another A2A-compliant agent at a specific URL. Supports STATEFUL multi-turn conversations via sessionId parameter. USAGE: (1) First delegation: provide url + message. Tool returns a response AND a sessionId. (2) Follow-up: use the SAME sessionId to continue the conversation with that agent. The agent remembers previous context. EXAMPLE: First call {url: "http://agent:3001", message: "Analyze data X"} returns {sessionId: "xyz", response: "..."}. Second call {url: "http://agent:3001", message: "What was the top insight?", sessionId: "xyz"}. The agent will remember the first analysis and can answer specifically.',
|
|
129
|
+
inputSchema: DelegateToUrlInputSchema,
|
|
130
|
+
async execute(input, _context) {
|
|
131
|
+
const { url, message, sessionId, timeout } = input;
|
|
132
|
+
try {
|
|
133
|
+
const client = new SimpleA2AClient(url, timeout);
|
|
134
|
+
const effectiveSessionId = sessionId || `delegation-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
135
|
+
const response = await client.sendMessage(message, effectiveSessionId);
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
agentUrl: url,
|
|
139
|
+
sessionId: effectiveSessionId,
|
|
140
|
+
response,
|
|
141
|
+
_hint: sessionId ? "Continued existing conversation" : "Started new conversation - use this sessionId for follow-ups"
|
|
142
|
+
};
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error instanceof DextoRuntimeError) {
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
throw new DextoRuntimeError(
|
|
148
|
+
"DELEGATION_ERROR",
|
|
149
|
+
ErrorScope.TOOLS,
|
|
150
|
+
ErrorType.SYSTEM,
|
|
151
|
+
`Delegation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
createDelegateToUrlTool
|
|
159
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_vitest = require("vitest");
|
|
3
|
+
var import_delegate_to_url_tool = require("./delegate-to-url-tool.js");
|
|
4
|
+
var import_core = require("@dexto/core");
|
|
5
|
+
function createMockLogger() {
|
|
6
|
+
const logger = {
|
|
7
|
+
debug: import_vitest.vi.fn(),
|
|
8
|
+
silly: import_vitest.vi.fn(),
|
|
9
|
+
info: import_vitest.vi.fn(),
|
|
10
|
+
warn: import_vitest.vi.fn(),
|
|
11
|
+
error: import_vitest.vi.fn(),
|
|
12
|
+
trackException: import_vitest.vi.fn(),
|
|
13
|
+
createChild: import_vitest.vi.fn(() => logger),
|
|
14
|
+
setLevel: import_vitest.vi.fn(),
|
|
15
|
+
getLevel: import_vitest.vi.fn(() => "debug"),
|
|
16
|
+
getLogFilePath: import_vitest.vi.fn(() => null),
|
|
17
|
+
destroy: import_vitest.vi.fn(async () => void 0)
|
|
18
|
+
};
|
|
19
|
+
return logger;
|
|
20
|
+
}
|
|
21
|
+
function createToolContext(logger) {
|
|
22
|
+
return { logger };
|
|
23
|
+
}
|
|
24
|
+
(0, import_vitest.describe)("delegate_to_url tool", () => {
|
|
25
|
+
(0, import_vitest.afterEach)(() => {
|
|
26
|
+
import_vitest.vi.unstubAllGlobals();
|
|
27
|
+
});
|
|
28
|
+
(0, import_vitest.it)("throws DELEGATION_FAILED when it cannot reach the remote agent", async () => {
|
|
29
|
+
const logger = createMockLogger();
|
|
30
|
+
const context = createToolContext(logger);
|
|
31
|
+
const fetchMock = import_vitest.vi.fn(async () => {
|
|
32
|
+
throw new Error("boom");
|
|
33
|
+
});
|
|
34
|
+
import_vitest.vi.stubGlobal("fetch", fetchMock);
|
|
35
|
+
const tool = (0, import_delegate_to_url_tool.createDelegateToUrlTool)();
|
|
36
|
+
const input = tool.inputSchema.parse({
|
|
37
|
+
url: "http://example.com",
|
|
38
|
+
message: "hi",
|
|
39
|
+
timeout: 1
|
|
40
|
+
});
|
|
41
|
+
await (0, import_vitest.expect)(tool.execute(input, context)).rejects.toMatchObject({
|
|
42
|
+
name: "DextoRuntimeError",
|
|
43
|
+
code: "DELEGATION_FAILED",
|
|
44
|
+
scope: import_core.ErrorScope.TOOLS,
|
|
45
|
+
type: import_core.ErrorType.THIRD_PARTY
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
(0, import_vitest.it)("throws DELEGATION_TIMEOUT when the request times out", async () => {
|
|
49
|
+
const logger = createMockLogger();
|
|
50
|
+
const context = createToolContext(logger);
|
|
51
|
+
const abortError = new Error("aborted");
|
|
52
|
+
abortError.name = "AbortError";
|
|
53
|
+
const fetchMock = import_vitest.vi.fn(async () => {
|
|
54
|
+
throw abortError;
|
|
55
|
+
});
|
|
56
|
+
import_vitest.vi.stubGlobal("fetch", fetchMock);
|
|
57
|
+
const tool = (0, import_delegate_to_url_tool.createDelegateToUrlTool)();
|
|
58
|
+
const input = tool.inputSchema.parse({
|
|
59
|
+
url: "http://example.com",
|
|
60
|
+
message: "hi",
|
|
61
|
+
timeout: 1
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
await tool.execute(input, context);
|
|
65
|
+
import_vitest.expect.fail("Expected tool.execute() to throw");
|
|
66
|
+
} catch (error) {
|
|
67
|
+
(0, import_vitest.expect)(error).toBeInstanceOf(import_core.DextoRuntimeError);
|
|
68
|
+
(0, import_vitest.expect)(error).toMatchObject({
|
|
69
|
+
code: "DELEGATION_TIMEOUT",
|
|
70
|
+
scope: import_core.ErrorScope.TOOLS,
|
|
71
|
+
type: import_core.ErrorType.TIMEOUT
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate-to-url-tool.test.d.ts","sourceRoot":"","sources":["../../src/implementations/delegate-to-url-tool.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { createDelegateToUrlTool } from "./delegate-to-url-tool.js";
|
|
3
|
+
import { DextoRuntimeError, ErrorScope, ErrorType } from "@dexto/core";
|
|
4
|
+
function createMockLogger() {
|
|
5
|
+
const logger = {
|
|
6
|
+
debug: vi.fn(),
|
|
7
|
+
silly: vi.fn(),
|
|
8
|
+
info: vi.fn(),
|
|
9
|
+
warn: vi.fn(),
|
|
10
|
+
error: vi.fn(),
|
|
11
|
+
trackException: vi.fn(),
|
|
12
|
+
createChild: vi.fn(() => logger),
|
|
13
|
+
setLevel: vi.fn(),
|
|
14
|
+
getLevel: vi.fn(() => "debug"),
|
|
15
|
+
getLogFilePath: vi.fn(() => null),
|
|
16
|
+
destroy: vi.fn(async () => void 0)
|
|
17
|
+
};
|
|
18
|
+
return logger;
|
|
19
|
+
}
|
|
20
|
+
function createToolContext(logger) {
|
|
21
|
+
return { logger };
|
|
22
|
+
}
|
|
23
|
+
describe("delegate_to_url tool", () => {
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
vi.unstubAllGlobals();
|
|
26
|
+
});
|
|
27
|
+
it("throws DELEGATION_FAILED when it cannot reach the remote agent", async () => {
|
|
28
|
+
const logger = createMockLogger();
|
|
29
|
+
const context = createToolContext(logger);
|
|
30
|
+
const fetchMock = vi.fn(async () => {
|
|
31
|
+
throw new Error("boom");
|
|
32
|
+
});
|
|
33
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
34
|
+
const tool = createDelegateToUrlTool();
|
|
35
|
+
const input = tool.inputSchema.parse({
|
|
36
|
+
url: "http://example.com",
|
|
37
|
+
message: "hi",
|
|
38
|
+
timeout: 1
|
|
39
|
+
});
|
|
40
|
+
await expect(tool.execute(input, context)).rejects.toMatchObject({
|
|
41
|
+
name: "DextoRuntimeError",
|
|
42
|
+
code: "DELEGATION_FAILED",
|
|
43
|
+
scope: ErrorScope.TOOLS,
|
|
44
|
+
type: ErrorType.THIRD_PARTY
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it("throws DELEGATION_TIMEOUT when the request times out", async () => {
|
|
48
|
+
const logger = createMockLogger();
|
|
49
|
+
const context = createToolContext(logger);
|
|
50
|
+
const abortError = new Error("aborted");
|
|
51
|
+
abortError.name = "AbortError";
|
|
52
|
+
const fetchMock = vi.fn(async () => {
|
|
53
|
+
throw abortError;
|
|
54
|
+
});
|
|
55
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
56
|
+
const tool = createDelegateToUrlTool();
|
|
57
|
+
const input = tool.inputSchema.parse({
|
|
58
|
+
url: "http://example.com",
|
|
59
|
+
message: "hi",
|
|
60
|
+
timeout: 1
|
|
61
|
+
});
|
|
62
|
+
try {
|
|
63
|
+
await tool.execute(input, context);
|
|
64
|
+
expect.fail("Expected tool.execute() to throw");
|
|
65
|
+
} catch (error) {
|
|
66
|
+
expect(error).toBeInstanceOf(DextoRuntimeError);
|
|
67
|
+
expect(error).toMatchObject({
|
|
68
|
+
code: "DELEGATION_TIMEOUT",
|
|
69
|
+
scope: ErrorScope.TOOLS,
|
|
70
|
+
type: ErrorType.TIMEOUT
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var exa_code_search_tool_exports = {};
|
|
20
|
+
__export(exa_code_search_tool_exports, {
|
|
21
|
+
createCodeSearchTool: () => createCodeSearchTool
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(exa_code_search_tool_exports);
|
|
24
|
+
var import_zod = require("zod");
|
|
25
|
+
var import_core = require("@dexto/core");
|
|
26
|
+
var import_exa_mcp = require("./exa-mcp.js");
|
|
27
|
+
const CodeSearchInputSchema = import_zod.z.object({
|
|
28
|
+
query: import_zod.z.string().min(1).describe(
|
|
29
|
+
"Search query for code examples and documentation (e.g., 'React useState examples', 'Express middleware', 'Python pandas dataframe filtering')"
|
|
30
|
+
),
|
|
31
|
+
tokensNum: import_zod.z.number().int().min(1e3).max(5e4).optional().default(5e3).describe("Approximate token budget to return (1000\u201350000, default: 5000)")
|
|
32
|
+
}).strict();
|
|
33
|
+
function createCodeSearchTool() {
|
|
34
|
+
return (0, import_core.defineTool)({
|
|
35
|
+
id: "code_search",
|
|
36
|
+
displayName: "Code Search",
|
|
37
|
+
description: "Search for code examples and documentation across sources like official docs, GitHub, and Stack Overflow. Returns formatted text context.",
|
|
38
|
+
inputSchema: CodeSearchInputSchema,
|
|
39
|
+
async execute(input, context) {
|
|
40
|
+
const { query, tokensNum } = input;
|
|
41
|
+
return await (0, import_exa_mcp.callExaTool)({
|
|
42
|
+
logger: context.logger,
|
|
43
|
+
toolId: "code_search",
|
|
44
|
+
toolName: "get_code_context_exa",
|
|
45
|
+
args: {
|
|
46
|
+
query,
|
|
47
|
+
tokensNum
|
|
48
|
+
},
|
|
49
|
+
timeoutMs: 3e4
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
55
|
+
0 && (module.exports = {
|
|
56
|
+
createCodeSearchTool
|
|
57
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Tool } from '@dexto/core';
|
|
3
|
+
|
|
4
|
+
declare const CodeSearchInputSchema: z.ZodObject<{
|
|
5
|
+
query: z.ZodString;
|
|
6
|
+
tokensNum: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
7
|
+
}, "strict", z.ZodTypeAny, {
|
|
8
|
+
query: string;
|
|
9
|
+
tokensNum: number;
|
|
10
|
+
}, {
|
|
11
|
+
query: string;
|
|
12
|
+
tokensNum?: number | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Create the `code_search` tool.
|
|
16
|
+
*
|
|
17
|
+
* Finds relevant code snippets and documentation by calling Exa's MCP endpoint via the MCP SDK.
|
|
18
|
+
*/
|
|
19
|
+
declare function createCodeSearchTool(): Tool<typeof CodeSearchInputSchema>;
|
|
20
|
+
|
|
21
|
+
export { createCodeSearchTool };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Tool } from '@dexto/core';
|
|
3
|
+
declare const CodeSearchInputSchema: z.ZodObject<{
|
|
4
|
+
query: z.ZodString;
|
|
5
|
+
tokensNum: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
query: string;
|
|
8
|
+
tokensNum: number;
|
|
9
|
+
}, {
|
|
10
|
+
query: string;
|
|
11
|
+
tokensNum?: number | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Create the `code_search` tool.
|
|
15
|
+
*
|
|
16
|
+
* Finds relevant code snippets and documentation by calling Exa's MCP endpoint via the MCP SDK.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createCodeSearchTool(): Tool<typeof CodeSearchInputSchema>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=exa-code-search-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exa-code-search-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/exa-code-search-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAG9D,QAAA,MAAM,qBAAqB;;;;;;;;;EAiBd,CAAC;AACd;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAAC,OAAO,qBAAqB,CAAC,CAsBzE"}
|