veryfront 0.1.144 → 0.1.145
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/esm/deno.js +1 -1
- package/esm/src/agent/ag-ui-handler.d.ts +77 -0
- package/esm/src/agent/ag-ui-handler.d.ts.map +1 -0
- package/esm/src/agent/ag-ui-handler.js +331 -0
- package/esm/src/agent/index.d.ts +2 -1
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +2 -1
- package/esm/src/agent/runtime/index.d.ts +2 -0
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +15 -7
- package/esm/src/agent/runtime/resume-session.d.ts +61 -0
- package/esm/src/agent/runtime/resume-session.d.ts.map +1 -0
- package/esm/src/agent/runtime/resume-session.js +232 -0
- package/esm/src/agent/runtime/tool-helpers.d.ts +4 -2
- package/esm/src/agent/runtime/tool-helpers.d.ts.map +1 -1
- package/esm/src/agent/runtime/tool-helpers.js +57 -4
- package/esm/src/agent/types.d.ts +2 -1
- package/esm/src/agent/types.d.ts.map +1 -1
- package/esm/src/internal-agents/session-manager.d.ts +8 -24
- package/esm/src/internal-agents/session-manager.d.ts.map +1 -1
- package/esm/src/internal-agents/session-manager.js +55 -178
- package/esm/src/mcp/index.d.ts +2 -0
- package/esm/src/mcp/index.d.ts.map +1 -1
- package/esm/src/mcp/index.js +2 -0
- package/esm/src/mcp/server.d.ts +1 -0
- package/esm/src/mcp/server.d.ts.map +1 -1
- package/esm/src/mcp/server.js +47 -8
- package/esm/src/mcp/session.d.ts +9 -0
- package/esm/src/mcp/session.d.ts.map +1 -0
- package/esm/src/mcp/session.js +25 -0
- package/esm/src/mcp/sse.d.ts +8 -0
- package/esm/src/mcp/sse.d.ts.map +1 -0
- package/esm/src/mcp/sse.js +17 -0
- package/esm/src/platform/cloud/resolver.d.ts.map +1 -1
- package/esm/src/platform/cloud/resolver.js +13 -3
- package/esm/src/provider/index.d.ts +2 -0
- package/esm/src/provider/index.d.ts.map +1 -1
- package/esm/src/provider/index.js +1 -0
- package/esm/src/provider/veryfront-cloud/context.d.ts +10 -0
- package/esm/src/provider/veryfront-cloud/context.d.ts.map +1 -0
- package/esm/src/provider/veryfront-cloud/context.js +11 -0
- package/esm/src/tool/index.d.ts +3 -1
- package/esm/src/tool/index.d.ts.map +1 -1
- package/esm/src/tool/index.js +1 -0
- package/esm/src/tool/remote-mcp.d.ts +14 -0
- package/esm/src/tool/remote-mcp.d.ts.map +1 -0
- package/esm/src/tool/remote-mcp.js +176 -0
- package/esm/src/tool/types.d.ts +12 -0
- package/esm/src/tool/types.d.ts.map +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/agent/ag-ui-handler.ts +451 -0
- package/src/src/agent/index.ts +21 -1
- package/src/src/agent/runtime/index.ts +28 -3
- package/src/src/agent/runtime/resume-session.ts +319 -0
- package/src/src/agent/runtime/tool-helpers.ts +89 -6
- package/src/src/agent/types.ts +2 -1
- package/src/src/internal-agents/session-manager.ts +69 -243
- package/src/src/mcp/index.ts +3 -0
- package/src/src/mcp/server.ts +54 -9
- package/src/src/mcp/session.ts +31 -0
- package/src/src/mcp/sse.ts +19 -0
- package/src/src/platform/cloud/resolver.ts +15 -3
- package/src/src/provider/index.ts +5 -0
- package/src/src/provider/veryfront-cloud/context.ts +28 -0
- package/src/src/tool/index.ts +9 -1
- package/src/src/tool/remote-mcp.ts +261 -0
- package/src/src/tool/types.ts +17 -0
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
2
|
+
export type RunSessionStatus = "running" | "waiting" | "completed" | "cancelled" | "failed";
|
|
3
|
+
|
|
4
|
+
export class RunCancelledError extends Error {
|
|
5
|
+
constructor(message = "Run cancelled") {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "RunCancelledError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class RunAlreadyExistsError extends Error {
|
|
12
|
+
constructor(runId: string) {
|
|
13
|
+
super(`Run "${runId}" is already active`);
|
|
14
|
+
this.name = "RunAlreadyExistsError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class RunNotActiveError extends Error {
|
|
19
|
+
constructor(runId: string) {
|
|
20
|
+
super(`Run "${runId}" is not active`);
|
|
21
|
+
this.name = "RunNotActiveError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class WaitNotPendingError extends Error {
|
|
26
|
+
constructor(runId: string, waitKey: string) {
|
|
27
|
+
super(`Run "${runId}" is not waiting for "${waitKey}"`);
|
|
28
|
+
this.name = "WaitNotPendingError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class WaitConflictError extends Error {
|
|
33
|
+
constructor(runId: string, waitKey: string) {
|
|
34
|
+
super(`Conflicting resume value for run "${runId}" and wait key "${waitKey}"`);
|
|
35
|
+
this.name = "WaitConflictError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SubmitResumeValueOutcome {
|
|
40
|
+
accepted: true;
|
|
41
|
+
duplicate?: true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type SubmittedValue<T> = {
|
|
45
|
+
value: T;
|
|
46
|
+
key: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type WaitingState<T> = {
|
|
50
|
+
waitKey: string;
|
|
51
|
+
resolve: (value: SubmittedValue<T>) => void;
|
|
52
|
+
reject: (reason?: unknown) => void;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type RunSession<T> = {
|
|
56
|
+
runId: string;
|
|
57
|
+
status: RunSessionStatus;
|
|
58
|
+
abortController: AbortController;
|
|
59
|
+
waitingState: WaitingState<T> | null;
|
|
60
|
+
submittedValues: Map<string, SubmittedValue<T>>;
|
|
61
|
+
waitingTimeoutId: number | null;
|
|
62
|
+
sessionTimeoutId: number | null;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const DEFAULT_WAITING_TTL_MS = 5 * 60 * 1000;
|
|
66
|
+
const DEFAULT_MAX_CONCURRENT_SESSIONS = 100;
|
|
67
|
+
|
|
68
|
+
export interface RunResumeSessionManagerOptions<T> {
|
|
69
|
+
waitingTtlMs?: number;
|
|
70
|
+
sessionTtlMs?: number | null;
|
|
71
|
+
maxConcurrentSessions?: number;
|
|
72
|
+
setTimeoutFn?: typeof dntShim.setTimeout;
|
|
73
|
+
clearTimeoutFn?: typeof clearTimeout;
|
|
74
|
+
getConflictKey?: (value: T) => string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function defaultConflictKey(value: unknown): string {
|
|
78
|
+
return JSON.stringify(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class RunResumeSessionManager<T> {
|
|
82
|
+
private readonly sessions = new Map<string, RunSession<T>>();
|
|
83
|
+
|
|
84
|
+
constructor(
|
|
85
|
+
private readonly options: RunResumeSessionManagerOptions<T> = {},
|
|
86
|
+
) {}
|
|
87
|
+
|
|
88
|
+
private get waitingTtlMs(): number {
|
|
89
|
+
return this.options.waitingTtlMs ?? DEFAULT_WAITING_TTL_MS;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private get sessionTtlMs(): number | null {
|
|
93
|
+
return this.options.sessionTtlMs ?? null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private get maxConcurrentSessions(): number {
|
|
97
|
+
return this.options.maxConcurrentSessions ?? DEFAULT_MAX_CONCURRENT_SESSIONS;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private get setTimeoutFn(): typeof dntShim.setTimeout {
|
|
101
|
+
return this.options.setTimeoutFn ?? dntShim.dntGlobalThis.setTimeout.bind(dntShim.dntGlobalThis);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private get clearTimeoutFn(): typeof clearTimeout {
|
|
105
|
+
return this.options.clearTimeoutFn ?? globalThis.clearTimeout.bind(dntShim.dntGlobalThis);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private getConflictKey(value: T): string {
|
|
109
|
+
const createConflictKey = this.options.getConflictKey ?? defaultConflictKey;
|
|
110
|
+
return createConflictKey(value);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private clearWaitingTimeout(session: RunSession<T>): void {
|
|
114
|
+
if (session.waitingTimeoutId === null) return;
|
|
115
|
+
this.clearTimeoutFn(session.waitingTimeoutId);
|
|
116
|
+
session.waitingTimeoutId = null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private clearSessionTimeout(session: RunSession<T>): void {
|
|
120
|
+
if (session.sessionTimeoutId === null) return;
|
|
121
|
+
this.clearTimeoutFn(session.sessionTimeoutId);
|
|
122
|
+
session.sessionTimeoutId = null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private scheduleSessionTimeout(session: RunSession<T>): void {
|
|
126
|
+
if (this.sessionTtlMs === null) return;
|
|
127
|
+
this.clearSessionTimeout(session);
|
|
128
|
+
session.sessionTimeoutId = this.setTimeoutFn(() => {
|
|
129
|
+
this.cancelRun(session.runId);
|
|
130
|
+
}, this.sessionTtlMs) as unknown as number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private scheduleWaitingTimeout(session: RunSession<T>): void {
|
|
134
|
+
this.clearWaitingTimeout(session);
|
|
135
|
+
session.waitingTimeoutId = this.setTimeoutFn(() => {
|
|
136
|
+
this.cancelRun(session.runId);
|
|
137
|
+
}, this.waitingTtlMs) as unknown as number;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private touchSession(session: RunSession<T>): void {
|
|
141
|
+
if (session.status === "running" || session.status === "waiting") {
|
|
142
|
+
this.scheduleSessionTimeout(session);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private finalizeSession(
|
|
147
|
+
session: RunSession<T>,
|
|
148
|
+
status: Exclude<RunSessionStatus, "running" | "waiting">,
|
|
149
|
+
): void {
|
|
150
|
+
session.status = status;
|
|
151
|
+
this.clearWaitingTimeout(session);
|
|
152
|
+
this.clearSessionTimeout(session);
|
|
153
|
+
session.waitingState = null;
|
|
154
|
+
this.sessions.delete(session.runId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
startRun(input: { runId: string; threadId: string }): AbortSignal {
|
|
158
|
+
const existing = this.sessions.get(input.runId);
|
|
159
|
+
if (existing && (existing.status === "running" || existing.status === "waiting")) {
|
|
160
|
+
throw new RunAlreadyExistsError(input.runId);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (this.sessions.size >= this.maxConcurrentSessions) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Maximum concurrent sessions (${this.maxConcurrentSessions}) reached`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const session: RunSession<T> = {
|
|
170
|
+
runId: input.runId,
|
|
171
|
+
status: "running",
|
|
172
|
+
abortController: new AbortController(),
|
|
173
|
+
waitingState: null,
|
|
174
|
+
submittedValues: new Map(),
|
|
175
|
+
waitingTimeoutId: null,
|
|
176
|
+
sessionTimeoutId: null,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
this.sessions.set(input.runId, session);
|
|
180
|
+
this.touchSession(session);
|
|
181
|
+
return session.abortController.signal;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async waitForSignal(runId: string, waitKey: string): Promise<T> {
|
|
185
|
+
const session = this.sessions.get(runId);
|
|
186
|
+
if (!session || session.status === "completed" || session.status === "failed") {
|
|
187
|
+
throw new RunNotActiveError(runId);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (session.abortController.signal.aborted || session.status === "cancelled") {
|
|
191
|
+
throw new RunCancelledError();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const existingValue = session.submittedValues.get(waitKey);
|
|
195
|
+
if (existingValue) {
|
|
196
|
+
session.status = "running";
|
|
197
|
+
this.touchSession(session);
|
|
198
|
+
return existingValue.value;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (session.waitingState && session.waitingState.waitKey !== waitKey) {
|
|
202
|
+
throw new WaitNotPendingError(runId, waitKey);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
session.status = "waiting";
|
|
206
|
+
this.scheduleWaitingTimeout(session);
|
|
207
|
+
this.touchSession(session);
|
|
208
|
+
|
|
209
|
+
return await new Promise<T>((resolve, reject) => {
|
|
210
|
+
const abortHandler = () => {
|
|
211
|
+
this.clearWaitingTimeout(session);
|
|
212
|
+
session.waitingState = null;
|
|
213
|
+
session.status = "cancelled";
|
|
214
|
+
reject(new RunCancelledError());
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
session.abortController.signal.addEventListener("abort", abortHandler, { once: true });
|
|
218
|
+
session.waitingState = {
|
|
219
|
+
waitKey,
|
|
220
|
+
resolve: (value) => {
|
|
221
|
+
session.abortController.signal.removeEventListener("abort", abortHandler);
|
|
222
|
+
this.clearWaitingTimeout(session);
|
|
223
|
+
session.waitingState = null;
|
|
224
|
+
session.status = "running";
|
|
225
|
+
this.touchSession(session);
|
|
226
|
+
resolve(value.value);
|
|
227
|
+
},
|
|
228
|
+
reject: (reason) => {
|
|
229
|
+
session.abortController.signal.removeEventListener("abort", abortHandler);
|
|
230
|
+
this.clearWaitingTimeout(session);
|
|
231
|
+
session.waitingState = null;
|
|
232
|
+
reject(reason);
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
submitSignal(
|
|
239
|
+
runId: string,
|
|
240
|
+
input: { waitKey: string; value: T },
|
|
241
|
+
): SubmitResumeValueOutcome {
|
|
242
|
+
const session = this.sessions.get(runId);
|
|
243
|
+
if (!session) {
|
|
244
|
+
throw new RunNotActiveError(runId);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const normalized: SubmittedValue<T> = {
|
|
248
|
+
value: input.value,
|
|
249
|
+
key: this.getConflictKey(input.value),
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const existingValue = session.submittedValues.get(input.waitKey);
|
|
253
|
+
if (existingValue) {
|
|
254
|
+
if (existingValue.key === normalized.key) {
|
|
255
|
+
return { accepted: true, duplicate: true };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
throw new WaitConflictError(runId, input.waitKey);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (
|
|
262
|
+
session.status === "completed" || session.status === "failed" ||
|
|
263
|
+
session.status === "cancelled"
|
|
264
|
+
) {
|
|
265
|
+
throw new RunNotActiveError(runId);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!session.waitingState || session.waitingState.waitKey !== input.waitKey) {
|
|
269
|
+
throw new WaitNotPendingError(runId, input.waitKey);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
session.submittedValues.set(input.waitKey, normalized);
|
|
273
|
+
this.touchSession(session);
|
|
274
|
+
session.waitingState.resolve(normalized);
|
|
275
|
+
return { accepted: true };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
cancelRun(runId: string): boolean {
|
|
279
|
+
const session = this.sessions.get(runId);
|
|
280
|
+
if (!session) return false;
|
|
281
|
+
|
|
282
|
+
if (
|
|
283
|
+
session.status === "completed" || session.status === "failed" ||
|
|
284
|
+
session.status === "cancelled"
|
|
285
|
+
) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const waitingState = session.waitingState;
|
|
290
|
+
session.abortController.abort(new RunCancelledError());
|
|
291
|
+
waitingState?.reject(new RunCancelledError());
|
|
292
|
+
this.finalizeSession(session, "cancelled");
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
completeRun(runId: string): void {
|
|
297
|
+
const session = this.sessions.get(runId);
|
|
298
|
+
if (!session) return;
|
|
299
|
+
this.finalizeSession(session, "completed");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
failRun(runId: string): void {
|
|
303
|
+
const session = this.sessions.get(runId);
|
|
304
|
+
if (!session) return;
|
|
305
|
+
this.finalizeSession(session, "failed");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
getRunStatus(runId: string): RunSessionStatus | null {
|
|
309
|
+
return this.sessions.get(runId)?.status ?? null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
reset(): void {
|
|
313
|
+
for (const session of this.sessions.values()) {
|
|
314
|
+
this.clearWaitingTimeout(session);
|
|
315
|
+
this.clearSessionTimeout(session);
|
|
316
|
+
}
|
|
317
|
+
this.sessions.clear();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module ai/agent/runtime/tool-helpers
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { Tool, ToolDefinition, ToolExecutionContext } from "../../tool/index.js";
|
|
9
|
+
import type { RemoteToolSource, Tool, ToolDefinition, ToolExecutionContext } from "../../tool/index.js";
|
|
10
10
|
import { executeTool, toolRegistry } from "../../tool/index.js";
|
|
11
11
|
import { toolToProviderDefinition } from "../../tool/registry.js";
|
|
12
12
|
import { SKILL_TOOL_IDS } from "../../skill/types.js";
|
|
@@ -101,21 +101,90 @@ function throwUnknownConfiguredToolsError(
|
|
|
101
101
|
async function getRemoteToolDefinitions(options?: {
|
|
102
102
|
includeIntegrationTools?: boolean;
|
|
103
103
|
allowedRemoteToolNames?: string[];
|
|
104
|
+
remoteToolSources?: RemoteToolSource[];
|
|
105
|
+
remoteToolContext?: ToolExecutionContext;
|
|
104
106
|
}): Promise<ToolDefinition[]> {
|
|
107
|
+
const remoteToolContext = options?.remoteToolContext;
|
|
108
|
+
const definitions: ToolDefinition[] = [];
|
|
109
|
+
const seenToolNames = new Set<string>();
|
|
110
|
+
|
|
111
|
+
const addDefinition = (definition: ToolDefinition): void => {
|
|
112
|
+
if (seenToolNames.has(definition.name)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (
|
|
116
|
+
options?.allowedRemoteToolNames &&
|
|
117
|
+
!options.allowedRemoteToolNames.includes(definition.name)
|
|
118
|
+
) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
seenToolNames.add(definition.name);
|
|
122
|
+
definitions.push(definition);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
for (const source of options?.remoteToolSources ?? []) {
|
|
126
|
+
try {
|
|
127
|
+
const sourceDefs = await source.listTools(remoteToolContext);
|
|
128
|
+
for (const def of sourceDefs) {
|
|
129
|
+
addDefinition(def);
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.warn("Failed to fetch remote tool definitions from source", {
|
|
133
|
+
sourceId: source.id,
|
|
134
|
+
error: error instanceof Error ? error.message : String(error),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
105
139
|
if (options?.includeIntegrationTools === false) {
|
|
106
|
-
return
|
|
140
|
+
return definitions;
|
|
107
141
|
}
|
|
108
142
|
|
|
109
143
|
try {
|
|
110
144
|
const { getRemoteIntegrationToolDefinitions } = await import(
|
|
111
145
|
"../../integrations/remote-tools.js"
|
|
112
146
|
);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
147
|
+
for (const def of await getRemoteIntegrationToolDefinitions()) {
|
|
148
|
+
addDefinition(def);
|
|
149
|
+
}
|
|
116
150
|
} catch {
|
|
117
|
-
return
|
|
151
|
+
return definitions;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return definitions;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function sourceHasTool(
|
|
158
|
+
source: RemoteToolSource,
|
|
159
|
+
toolName: string,
|
|
160
|
+
context?: ToolExecutionContext,
|
|
161
|
+
): Promise<boolean> {
|
|
162
|
+
return (await source.listTools(context)).some((definition) => definition.name === toolName);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function executeRemoteToolFromSources(
|
|
166
|
+
toolName: string,
|
|
167
|
+
input: Record<string, unknown>,
|
|
168
|
+
context: ToolExecutionContext | undefined,
|
|
169
|
+
allowedRemoteToolNames: string[] | undefined,
|
|
170
|
+
remoteToolSources: RemoteToolSource[] | undefined,
|
|
171
|
+
): Promise<{ handled: boolean; result?: unknown }> {
|
|
172
|
+
for (const source of remoteToolSources ?? []) {
|
|
173
|
+
if (!(await sourceHasTool(source, toolName, context))) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (allowedRemoteToolNames && !allowedRemoteToolNames.includes(toolName)) {
|
|
178
|
+
throw new Error(`Tool "${toolName}" is not allowed for this run`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
handled: true,
|
|
183
|
+
result: await source.executeTool(toolName, input, context),
|
|
184
|
+
};
|
|
118
185
|
}
|
|
186
|
+
|
|
187
|
+
return { handled: false };
|
|
119
188
|
}
|
|
120
189
|
|
|
121
190
|
export function resolveConfiguredTool(
|
|
@@ -148,6 +217,7 @@ export async function executeConfiguredTool(
|
|
|
148
217
|
toolsConfig: true | Record<string, ToolConfigEntry> | undefined,
|
|
149
218
|
context?: ToolExecutionContext,
|
|
150
219
|
allowedRemoteToolNames?: string[],
|
|
220
|
+
remoteToolSources?: RemoteToolSource[],
|
|
151
221
|
): Promise<unknown> {
|
|
152
222
|
const configuredTool = resolveConfiguredTool(toolsConfig, toolName);
|
|
153
223
|
if (configuredTool) {
|
|
@@ -160,6 +230,17 @@ export async function executeConfiguredTool(
|
|
|
160
230
|
return await registryTool.execute(input, context);
|
|
161
231
|
}
|
|
162
232
|
|
|
233
|
+
const remoteSourceResult = await executeRemoteToolFromSources(
|
|
234
|
+
toolName,
|
|
235
|
+
input,
|
|
236
|
+
context,
|
|
237
|
+
allowedRemoteToolNames,
|
|
238
|
+
remoteToolSources,
|
|
239
|
+
);
|
|
240
|
+
if (remoteSourceResult.handled) {
|
|
241
|
+
return remoteSourceResult.result;
|
|
242
|
+
}
|
|
243
|
+
|
|
163
244
|
// Fall back to remote execution for integration tools (e.g., github:list-repos)
|
|
164
245
|
if (isRemoteIntegrationTool(toolName)) {
|
|
165
246
|
if (allowedRemoteToolNames && !allowedRemoteToolNames.includes(toolName)) {
|
|
@@ -207,6 +288,8 @@ export async function getAvailableTools(
|
|
|
207
288
|
includeSkillTools?: boolean;
|
|
208
289
|
includeIntegrationTools?: boolean;
|
|
209
290
|
allowedRemoteToolNames?: string[];
|
|
291
|
+
remoteToolSources?: RemoteToolSource[];
|
|
292
|
+
remoteToolContext?: ToolExecutionContext;
|
|
210
293
|
},
|
|
211
294
|
): Promise<ToolDefinition[]> {
|
|
212
295
|
if (!toolsConfig) return [];
|
package/src/src/agent/types.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import * as dntShim from "../../_dnt.shims.js";
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
import type { Tool } from "../tool/index.js";
|
|
7
|
+
import type { RemoteToolSource, Tool } from "../tool/index.js";
|
|
8
8
|
import { INVALID_ARGUMENT } from "../errors/error-registry.js";
|
|
9
9
|
import type { Memory } from "./memory/memory-interface.js";
|
|
10
10
|
|
|
@@ -79,6 +79,7 @@ export interface AgentConfig {
|
|
|
79
79
|
model?: ModelString;
|
|
80
80
|
system: string | (() => string) | (() => Promise<string>);
|
|
81
81
|
tools?: true | Record<string, Tool | boolean>;
|
|
82
|
+
remoteTools?: RemoteToolSource[];
|
|
82
83
|
maxSteps?: number;
|
|
83
84
|
streaming?: boolean;
|
|
84
85
|
memory?: MemoryConfig;
|