@dogpile/sdk 0.2.2 → 0.3.1
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/CHANGELOG.md +15 -0
- package/dist/browser/index.js +1044 -507
- package/dist/browser/index.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/broadcast.d.ts +1 -0
- package/dist/runtime/broadcast.d.ts.map +1 -1
- package/dist/runtime/broadcast.js +28 -19
- package/dist/runtime/broadcast.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +1 -0
- package/dist/runtime/coordinator.d.ts.map +1 -1
- package/dist/runtime/coordinator.js +46 -21
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +5 -0
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/ids.d.ts +19 -0
- package/dist/runtime/ids.d.ts.map +1 -0
- package/dist/runtime/ids.js +36 -0
- package/dist/runtime/ids.js.map +1 -0
- package/dist/runtime/logger.d.ts +61 -0
- package/dist/runtime/logger.d.ts.map +1 -0
- package/dist/runtime/logger.js +114 -0
- package/dist/runtime/logger.js.map +1 -0
- package/dist/runtime/retry.d.ts +99 -0
- package/dist/runtime/retry.d.ts.map +1 -0
- package/dist/runtime/retry.js +181 -0
- package/dist/runtime/retry.js.map +1 -0
- package/dist/runtime/sequential.d.ts +1 -0
- package/dist/runtime/sequential.d.ts.map +1 -1
- package/dist/runtime/sequential.js +25 -16
- package/dist/runtime/sequential.js.map +1 -1
- package/dist/runtime/shared.d.ts +1 -0
- package/dist/runtime/shared.d.ts.map +1 -1
- package/dist/runtime/shared.js +25 -19
- package/dist/runtime/shared.js.map +1 -1
- package/dist/runtime/termination.d.ts +6 -1
- package/dist/runtime/termination.d.ts.map +1 -1
- package/dist/runtime/termination.js +75 -0
- package/dist/runtime/termination.js.map +1 -1
- package/dist/runtime/tools/built-in.d.ts +99 -0
- package/dist/runtime/tools/built-in.d.ts.map +1 -0
- package/dist/runtime/tools/built-in.js +577 -0
- package/dist/runtime/tools/built-in.js.map +1 -0
- package/dist/runtime/tools/vercel-ai.d.ts +67 -0
- package/dist/runtime/tools/vercel-ai.d.ts.map +1 -0
- package/dist/runtime/tools/vercel-ai.js +148 -0
- package/dist/runtime/tools/vercel-ai.js.map +1 -0
- package/dist/runtime/tools.d.ts +5 -268
- package/dist/runtime/tools.d.ts.map +1 -1
- package/dist/runtime/tools.js +7 -770
- package/dist/runtime/tools.js.map +1 -1
- package/dist/runtime/validation.d.ts.map +1 -1
- package/dist/runtime/validation.js +22 -0
- package/dist/runtime/validation.js.map +1 -1
- package/dist/runtime/wrap-up.d.ts +26 -0
- package/dist/runtime/wrap-up.d.ts.map +1 -0
- package/dist/runtime/wrap-up.js +178 -0
- package/dist/runtime/wrap-up.js.map +1 -0
- package/dist/types/benchmark.d.ts +276 -0
- package/dist/types/benchmark.d.ts.map +1 -0
- package/dist/types/benchmark.js +2 -0
- package/dist/types/benchmark.js.map +1 -0
- package/dist/types/events.d.ts +495 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/replay.d.ts +169 -0
- package/dist/types/replay.d.ts.map +1 -0
- package/dist/types/replay.js +2 -0
- package/dist/types/replay.js.map +1 -0
- package/dist/types.d.ts +74 -935
- package/dist/types.d.ts.map +1 -1
- package/package.json +28 -1
- package/src/index.ts +7 -1
- package/src/runtime/broadcast.ts +50 -35
- package/src/runtime/coordinator.ts +84 -43
- package/src/runtime/engine.ts +6 -0
- package/src/runtime/ids.ts +41 -0
- package/src/runtime/logger.ts +152 -0
- package/src/runtime/retry.ts +270 -0
- package/src/runtime/sequential.ts +46 -31
- package/src/runtime/shared.ts +46 -35
- package/src/runtime/termination.ts +100 -0
- package/src/runtime/tools/built-in.ts +875 -0
- package/src/runtime/tools/vercel-ai.ts +269 -0
- package/src/runtime/tools.ts +60 -1255
- package/src/runtime/validation.ts +25 -0
- package/src/runtime/wrap-up.ts +257 -0
- package/src/types/benchmark.ts +300 -0
- package/src/types/events.ts +544 -0
- package/src/types/replay.ts +201 -0
- package/src/types.ts +174 -994
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
JsonObject,
|
|
3
|
+
JsonValue,
|
|
4
|
+
RuntimeToolAdapterContract,
|
|
5
|
+
RuntimeToolAdapterError,
|
|
6
|
+
RuntimeToolExecutionContext,
|
|
7
|
+
RuntimeToolIdentity,
|
|
8
|
+
RuntimeToolInputSchema,
|
|
9
|
+
RuntimeToolPermission,
|
|
10
|
+
RuntimeToolResult,
|
|
11
|
+
RuntimeToolValidationIssue,
|
|
12
|
+
RuntimeToolValidationResult
|
|
13
|
+
} from "../../types.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Built-in Dogpile tool names with stable protocol-facing semantics.
|
|
17
|
+
*/
|
|
18
|
+
export type DogpileBuiltInToolName = "webSearch" | "codeExec";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Web search types
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
export interface WebSearchToolInput extends JsonObject {
|
|
25
|
+
readonly query: string;
|
|
26
|
+
readonly maxResults?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WebSearchToolResult extends JsonObject {
|
|
30
|
+
readonly title: string;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
readonly snippet?: string;
|
|
33
|
+
readonly metadata?: JsonObject;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface WebSearchToolOutput extends JsonObject {
|
|
37
|
+
readonly results: WebSearchToolResult[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type WebSearchFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
41
|
+
|
|
42
|
+
export interface WebSearchFetchRequest {
|
|
43
|
+
readonly url: string | URL;
|
|
44
|
+
readonly init?: RequestInit;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type WebSearchFetchRequestBuilder = (
|
|
48
|
+
input: Readonly<WebSearchToolInput>,
|
|
49
|
+
context: RuntimeToolExecutionContext
|
|
50
|
+
) => WebSearchFetchRequest;
|
|
51
|
+
|
|
52
|
+
export type WebSearchFetchResponseParser = (
|
|
53
|
+
response: Response,
|
|
54
|
+
input: Readonly<WebSearchToolInput>,
|
|
55
|
+
context: RuntimeToolExecutionContext
|
|
56
|
+
) => WebSearchToolOutput | Promise<WebSearchToolOutput>;
|
|
57
|
+
|
|
58
|
+
export interface WebSearchToolAdapterOptions {
|
|
59
|
+
readonly endpoint: string | URL;
|
|
60
|
+
readonly fetch?: WebSearchFetch;
|
|
61
|
+
readonly headers?: HeadersInit;
|
|
62
|
+
readonly defaultMaxResults?: number;
|
|
63
|
+
readonly identity?: BuiltInDogpileToolIdentityOptions;
|
|
64
|
+
readonly permissions?: readonly RuntimeToolPermission[];
|
|
65
|
+
readonly buildRequest?: WebSearchFetchRequestBuilder;
|
|
66
|
+
readonly parseResponse?: WebSearchFetchResponseParser;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type WebSearchToolExecutor = (
|
|
70
|
+
input: Readonly<WebSearchToolInput>,
|
|
71
|
+
context: RuntimeToolExecutionContext
|
|
72
|
+
) => RuntimeToolResult<WebSearchToolOutput> | Promise<RuntimeToolResult<WebSearchToolOutput>>;
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Code exec types
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
export interface CodeExecToolAdapterOptions {
|
|
79
|
+
readonly execute: CodeExecSandboxExecutor;
|
|
80
|
+
readonly defaultTimeoutMs?: number;
|
|
81
|
+
readonly maxTimeoutMs?: number;
|
|
82
|
+
readonly languages?: readonly CodeExecToolLanguage[];
|
|
83
|
+
readonly allowNetwork?: boolean;
|
|
84
|
+
readonly identity?: BuiltInDogpileToolIdentityOptions;
|
|
85
|
+
readonly permissions?: readonly RuntimeToolPermission[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface CodeExecToolInput extends JsonObject {
|
|
89
|
+
readonly language: "javascript" | "typescript" | "python" | "bash" | "shell";
|
|
90
|
+
readonly code: string;
|
|
91
|
+
readonly timeoutMs?: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CodeExecToolOutput extends JsonObject {
|
|
95
|
+
readonly stdout: string;
|
|
96
|
+
readonly stderr: string;
|
|
97
|
+
readonly exitCode: number;
|
|
98
|
+
readonly metadata?: JsonObject;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type CodeExecToolLanguage = CodeExecToolInput["language"];
|
|
102
|
+
|
|
103
|
+
export type CodeExecToolExecutor = (
|
|
104
|
+
input: Readonly<CodeExecToolInput>,
|
|
105
|
+
context: RuntimeToolExecutionContext
|
|
106
|
+
) => RuntimeToolResult<CodeExecToolOutput> | Promise<RuntimeToolResult<CodeExecToolOutput>>;
|
|
107
|
+
|
|
108
|
+
export type CodeExecSandboxExecutor = (
|
|
109
|
+
input: Readonly<CodeExecToolInput>,
|
|
110
|
+
context: RuntimeToolExecutionContext
|
|
111
|
+
) => CodeExecToolOutput | Promise<CodeExecToolOutput>;
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Built-in identity / definition shapes
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
export interface BuiltInDogpileToolIdentityOptions {
|
|
118
|
+
readonly namespace?: string;
|
|
119
|
+
readonly version?: string;
|
|
120
|
+
readonly description?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface WebSearchDogpileToolDefinition {
|
|
124
|
+
readonly name: "webSearch";
|
|
125
|
+
readonly execute: WebSearchToolExecutor;
|
|
126
|
+
readonly identity?: BuiltInDogpileToolIdentityOptions;
|
|
127
|
+
readonly inputSchema?: RuntimeToolInputSchema;
|
|
128
|
+
readonly permissions?: readonly RuntimeToolPermission[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface CodeExecDogpileToolDefinition {
|
|
132
|
+
readonly name: "codeExec";
|
|
133
|
+
readonly execute: CodeExecToolExecutor;
|
|
134
|
+
readonly identity?: BuiltInDogpileToolIdentityOptions;
|
|
135
|
+
readonly inputSchema?: RuntimeToolInputSchema;
|
|
136
|
+
readonly permissions?: readonly RuntimeToolPermission[];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type BuiltInDogpileToolDefinition = WebSearchDogpileToolDefinition | CodeExecDogpileToolDefinition;
|
|
140
|
+
|
|
141
|
+
export interface BuiltInDogpileToolExecutors {
|
|
142
|
+
readonly webSearch?: WebSearchToolExecutor | WebSearchDogpileToolDefinition;
|
|
143
|
+
readonly codeExec?: CodeExecToolExecutor | CodeExecDogpileToolDefinition;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type BuiltInDogpileRuntimeTool =
|
|
147
|
+
| RuntimeToolAdapterContract<WebSearchToolInput, WebSearchToolOutput>
|
|
148
|
+
| RuntimeToolAdapterContract<CodeExecToolInput, CodeExecToolOutput>;
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Built-in tool identity / schema / permission constants
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
const webSearchIdentity: RuntimeToolIdentity = {
|
|
155
|
+
id: "dogpile.tools.webSearch",
|
|
156
|
+
namespace: "dogpile",
|
|
157
|
+
name: "webSearch",
|
|
158
|
+
version: "1.0.0",
|
|
159
|
+
description: "Search the web through a caller-provided fetch-compatible search adapter."
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const codeExecIdentity: RuntimeToolIdentity = {
|
|
163
|
+
id: "dogpile.tools.codeExec",
|
|
164
|
+
namespace: "dogpile",
|
|
165
|
+
name: "codeExec",
|
|
166
|
+
version: "1.0.0",
|
|
167
|
+
description: "Execute code through a caller-provided sandbox adapter."
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const webSearchInputSchema: RuntimeToolInputSchema = {
|
|
171
|
+
kind: "json-schema",
|
|
172
|
+
description: "Web search query and optional result cap.",
|
|
173
|
+
schema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
query: { type: "string" },
|
|
177
|
+
maxResults: { type: "number", minimum: 1 }
|
|
178
|
+
},
|
|
179
|
+
required: ["query"],
|
|
180
|
+
additionalProperties: false
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const codeExecInputSchema: RuntimeToolInputSchema = {
|
|
185
|
+
kind: "json-schema",
|
|
186
|
+
description: "Code snippet plus language and optional timeout.",
|
|
187
|
+
schema: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
language: {
|
|
191
|
+
type: "string",
|
|
192
|
+
enum: ["javascript", "typescript", "python", "bash", "shell"]
|
|
193
|
+
},
|
|
194
|
+
code: { type: "string" },
|
|
195
|
+
timeoutMs: { type: "number", minimum: 1 }
|
|
196
|
+
},
|
|
197
|
+
required: ["language", "code"],
|
|
198
|
+
additionalProperties: false
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const webSearchPermissions: readonly RuntimeToolPermission[] = [
|
|
203
|
+
{ kind: "network", allowPrivateNetwork: false }
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
const codeExecPermissions: readonly RuntimeToolPermission[] = [
|
|
207
|
+
{
|
|
208
|
+
kind: "code-execution",
|
|
209
|
+
sandbox: "caller-provided",
|
|
210
|
+
languages: ["javascript", "typescript", "python", "bash", "shell"],
|
|
211
|
+
allowNetwork: false
|
|
212
|
+
}
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
const codeExecLanguages: readonly CodeExecToolLanguage[] = [
|
|
216
|
+
"javascript",
|
|
217
|
+
"typescript",
|
|
218
|
+
"python",
|
|
219
|
+
"bash",
|
|
220
|
+
"shell"
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// Public dispatch helpers (per-built-in identity / schema / permissions / validation)
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
export function builtInDogpileToolIdentity(name: "webSearch"): RuntimeToolIdentity;
|
|
228
|
+
export function builtInDogpileToolIdentity(name: "codeExec"): RuntimeToolIdentity;
|
|
229
|
+
export function builtInDogpileToolIdentity(name: DogpileBuiltInToolName): RuntimeToolIdentity {
|
|
230
|
+
return name === "webSearch" ? webSearchIdentity : codeExecIdentity;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function builtInDogpileToolInputSchema(name: "webSearch"): RuntimeToolInputSchema;
|
|
234
|
+
export function builtInDogpileToolInputSchema(name: "codeExec"): RuntimeToolInputSchema;
|
|
235
|
+
export function builtInDogpileToolInputSchema(name: DogpileBuiltInToolName): RuntimeToolInputSchema {
|
|
236
|
+
return name === "webSearch" ? webSearchInputSchema : codeExecInputSchema;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function builtInDogpileToolPermissions(name: DogpileBuiltInToolName): readonly RuntimeToolPermission[] {
|
|
240
|
+
return name === "webSearch" ? webSearchPermissions : codeExecPermissions;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function validateBuiltInDogpileToolInput(
|
|
244
|
+
name: "webSearch",
|
|
245
|
+
input: Readonly<Partial<WebSearchToolInput>>
|
|
246
|
+
): RuntimeToolValidationResult;
|
|
247
|
+
export function validateBuiltInDogpileToolInput(
|
|
248
|
+
name: "codeExec",
|
|
249
|
+
input: Readonly<Partial<CodeExecToolInput>>
|
|
250
|
+
): RuntimeToolValidationResult;
|
|
251
|
+
export function validateBuiltInDogpileToolInput(
|
|
252
|
+
name: DogpileBuiltInToolName,
|
|
253
|
+
input: Readonly<Partial<WebSearchToolInput> | Partial<CodeExecToolInput>>
|
|
254
|
+
): RuntimeToolValidationResult;
|
|
255
|
+
export function validateBuiltInDogpileToolInput(
|
|
256
|
+
name: DogpileBuiltInToolName,
|
|
257
|
+
input: Readonly<Partial<WebSearchToolInput> | Partial<CodeExecToolInput>>
|
|
258
|
+
): RuntimeToolValidationResult {
|
|
259
|
+
const issues =
|
|
260
|
+
name === "webSearch"
|
|
261
|
+
? validateWebSearchInput(input as Readonly<Partial<WebSearchToolInput>>)
|
|
262
|
+
: validateCodeExecInput(input as Readonly<Partial<CodeExecToolInput>>);
|
|
263
|
+
return issues.length === 0 ? { type: "valid" } : { type: "invalid", issues };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// Web search adapter
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
export function createWebSearchToolAdapter(
|
|
271
|
+
options: WebSearchToolAdapterOptions
|
|
272
|
+
): RuntimeToolAdapterContract<WebSearchToolInput, WebSearchToolOutput> {
|
|
273
|
+
const identity = mergeIdentity(webSearchIdentity, options.identity);
|
|
274
|
+
|
|
275
|
+
return normalizeBuiltInDogpileTool({
|
|
276
|
+
name: "webSearch",
|
|
277
|
+
...(options.identity ? { identity: options.identity } : {}),
|
|
278
|
+
...(options.permissions ? { permissions: options.permissions } : {}),
|
|
279
|
+
async execute(input, context): Promise<RuntimeToolResult<WebSearchToolOutput>> {
|
|
280
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
281
|
+
|
|
282
|
+
if (!fetchImplementation) {
|
|
283
|
+
return {
|
|
284
|
+
type: "error",
|
|
285
|
+
toolCallId: context.toolCallId,
|
|
286
|
+
tool: identity,
|
|
287
|
+
error: {
|
|
288
|
+
code: "unavailable",
|
|
289
|
+
message: "No fetch implementation is available for webSearch.",
|
|
290
|
+
retryable: false
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const request = options.buildRequest
|
|
296
|
+
? options.buildRequest(input, context)
|
|
297
|
+
: defaultWebSearchRequest(options, input, context);
|
|
298
|
+
const response = await fetchImplementation(request.url, {
|
|
299
|
+
...request.init,
|
|
300
|
+
...(context.abortSignal ? { signal: context.abortSignal } : {})
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (!response.ok) {
|
|
304
|
+
throw {
|
|
305
|
+
code: response.status >= 500 ? "unavailable" : "backend-error",
|
|
306
|
+
message: `Web search backend returned HTTP ${response.status}.`,
|
|
307
|
+
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
308
|
+
detail: {
|
|
309
|
+
status: response.status,
|
|
310
|
+
statusText: response.statusText
|
|
311
|
+
}
|
|
312
|
+
} satisfies RuntimeToolAdapterError;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const output = options.parseResponse
|
|
316
|
+
? await options.parseResponse(response, input, context)
|
|
317
|
+
: await defaultWebSearchResponseParser(response);
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
type: "success",
|
|
321
|
+
toolCallId: context.toolCallId,
|
|
322
|
+
tool: identity,
|
|
323
|
+
output
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// Code exec adapter
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
export function createCodeExecToolAdapter(
|
|
334
|
+
options: CodeExecToolAdapterOptions
|
|
335
|
+
): RuntimeToolAdapterContract<CodeExecToolInput, CodeExecToolOutput> {
|
|
336
|
+
const identity = mergeIdentity(codeExecIdentity, options.identity);
|
|
337
|
+
const permissions =
|
|
338
|
+
options.permissions ??
|
|
339
|
+
codeExecPermissionsFor(options.languages ?? codeExecLanguages, options.allowNetwork ?? false);
|
|
340
|
+
const inputSchema = codeExecInputSchemaFor(options.languages ?? codeExecLanguages);
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
identity,
|
|
344
|
+
inputSchema,
|
|
345
|
+
permissions,
|
|
346
|
+
validateInput: (input: Readonly<CodeExecToolInput>) => validateCodeExecAdapterInput(input, options),
|
|
347
|
+
async execute(input, context): Promise<RuntimeToolResult<CodeExecToolOutput>> {
|
|
348
|
+
const validation = validateCodeExecAdapterInput(input, options);
|
|
349
|
+
|
|
350
|
+
if (validation.type === "invalid") {
|
|
351
|
+
return {
|
|
352
|
+
type: "error",
|
|
353
|
+
toolCallId: context.toolCallId,
|
|
354
|
+
tool: identity,
|
|
355
|
+
error: {
|
|
356
|
+
code: "invalid-input",
|
|
357
|
+
message: "Invalid codeExec tool input.",
|
|
358
|
+
retryable: false,
|
|
359
|
+
detail: { issues: serializeValidationIssues(validation.issues) }
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const timeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
365
|
+
const executionInput: CodeExecToolInput =
|
|
366
|
+
timeoutMs === undefined ? input : { ...input, timeoutMs };
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const output = await executeSandboxWithPolicy(options.execute, executionInput, context, timeoutMs);
|
|
370
|
+
return {
|
|
371
|
+
type: "success",
|
|
372
|
+
toolCallId: context.toolCallId,
|
|
373
|
+
tool: identity,
|
|
374
|
+
output
|
|
375
|
+
};
|
|
376
|
+
} catch (error) {
|
|
377
|
+
return {
|
|
378
|
+
type: "error",
|
|
379
|
+
toolCallId: context.toolCallId,
|
|
380
|
+
tool: identity,
|
|
381
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
// Built-in normalizers
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
|
|
392
|
+
export function normalizeBuiltInDogpileTool(
|
|
393
|
+
definition: WebSearchDogpileToolDefinition
|
|
394
|
+
): RuntimeToolAdapterContract<WebSearchToolInput, WebSearchToolOutput>;
|
|
395
|
+
export function normalizeBuiltInDogpileTool(
|
|
396
|
+
definition: CodeExecDogpileToolDefinition
|
|
397
|
+
): RuntimeToolAdapterContract<CodeExecToolInput, CodeExecToolOutput>;
|
|
398
|
+
export function normalizeBuiltInDogpileTool(definition: BuiltInDogpileToolDefinition): BuiltInDogpileRuntimeTool {
|
|
399
|
+
switch (definition.name) {
|
|
400
|
+
case "webSearch": {
|
|
401
|
+
const identity = mergeIdentity(webSearchIdentity, definition.identity);
|
|
402
|
+
const permissions = definition.permissions ?? webSearchPermissions;
|
|
403
|
+
const tool: RuntimeToolAdapterContract<WebSearchToolInput, WebSearchToolOutput> = {
|
|
404
|
+
identity,
|
|
405
|
+
inputSchema: definition.inputSchema ?? webSearchInputSchema,
|
|
406
|
+
permissions,
|
|
407
|
+
validateInput: (input: Readonly<WebSearchToolInput>) => validateBuiltInDogpileToolInput("webSearch", input),
|
|
408
|
+
execute: (input: Readonly<WebSearchToolInput>, context: RuntimeToolExecutionContext) =>
|
|
409
|
+
executeBuiltInTool(identity, definition.execute, input, context, "webSearch")
|
|
410
|
+
};
|
|
411
|
+
return tool;
|
|
412
|
+
}
|
|
413
|
+
case "codeExec": {
|
|
414
|
+
const identity = mergeIdentity(codeExecIdentity, definition.identity);
|
|
415
|
+
const permissions = definition.permissions ?? codeExecPermissions;
|
|
416
|
+
const tool: RuntimeToolAdapterContract<CodeExecToolInput, CodeExecToolOutput> = {
|
|
417
|
+
identity,
|
|
418
|
+
inputSchema: definition.inputSchema ?? codeExecInputSchema,
|
|
419
|
+
permissions,
|
|
420
|
+
validateInput: (input: Readonly<CodeExecToolInput>) => validateBuiltInDogpileToolInput("codeExec", input),
|
|
421
|
+
execute: (input: Readonly<CodeExecToolInput>, context: RuntimeToolExecutionContext) =>
|
|
422
|
+
executeBuiltInTool(identity, definition.execute, input, context, "codeExec")
|
|
423
|
+
};
|
|
424
|
+
return tool;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function normalizeBuiltInDogpileTools(tools: BuiltInDogpileToolExecutors): readonly BuiltInDogpileRuntimeTool[] {
|
|
430
|
+
const normalized: BuiltInDogpileRuntimeTool[] = [];
|
|
431
|
+
if (tools.webSearch) {
|
|
432
|
+
normalized.push(normalizeBuiltInDogpileTool(asWebSearchDefinition(tools.webSearch)));
|
|
433
|
+
}
|
|
434
|
+
if (tools.codeExec) {
|
|
435
|
+
normalized.push(normalizeBuiltInDogpileTool(asCodeExecDefinition(tools.codeExec)));
|
|
436
|
+
}
|
|
437
|
+
return normalized;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
// Internal helpers
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
|
|
444
|
+
async function executeBuiltInTool<Input extends object, Output>(
|
|
445
|
+
identity: RuntimeToolIdentity,
|
|
446
|
+
execute: (
|
|
447
|
+
input: Readonly<Input>,
|
|
448
|
+
context: RuntimeToolExecutionContext
|
|
449
|
+
) => RuntimeToolResult<Output> | Promise<RuntimeToolResult<Output>>,
|
|
450
|
+
input: Readonly<Input>,
|
|
451
|
+
context: RuntimeToolExecutionContext,
|
|
452
|
+
name: DogpileBuiltInToolName
|
|
453
|
+
): Promise<RuntimeToolResult<Output>> {
|
|
454
|
+
const validation = validateBuiltInDogpileToolInput(
|
|
455
|
+
name,
|
|
456
|
+
input as Readonly<Partial<WebSearchToolInput> | Partial<CodeExecToolInput>>
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
if (validation.type === "invalid") {
|
|
460
|
+
return {
|
|
461
|
+
type: "error",
|
|
462
|
+
toolCallId: context.toolCallId,
|
|
463
|
+
tool: identity,
|
|
464
|
+
error: {
|
|
465
|
+
code: "invalid-input",
|
|
466
|
+
message: `Invalid ${name} tool input.`,
|
|
467
|
+
retryable: false,
|
|
468
|
+
detail: { issues: serializeValidationIssues(validation.issues) }
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
return await execute(input, context);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
return {
|
|
477
|
+
type: "error",
|
|
478
|
+
toolCallId: context.toolCallId,
|
|
479
|
+
tool: identity,
|
|
480
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function asWebSearchDefinition(
|
|
486
|
+
tool: WebSearchToolExecutor | WebSearchDogpileToolDefinition
|
|
487
|
+
): WebSearchDogpileToolDefinition {
|
|
488
|
+
return typeof tool === "function" ? { name: "webSearch", execute: tool } : tool;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function asCodeExecDefinition(tool: CodeExecToolExecutor | CodeExecDogpileToolDefinition): CodeExecDogpileToolDefinition {
|
|
492
|
+
return typeof tool === "function" ? { name: "codeExec", execute: tool } : tool;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function mergeIdentity(
|
|
496
|
+
defaultIdentity: RuntimeToolIdentity,
|
|
497
|
+
options: BuiltInDogpileToolIdentityOptions | undefined
|
|
498
|
+
): RuntimeToolIdentity {
|
|
499
|
+
if (!options) return defaultIdentity;
|
|
500
|
+
return {
|
|
501
|
+
...defaultIdentity,
|
|
502
|
+
...(options.namespace !== undefined ? { namespace: options.namespace } : {}),
|
|
503
|
+
...(options.version !== undefined ? { version: options.version } : {}),
|
|
504
|
+
...(options.description !== undefined ? { description: options.description } : {})
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function validateCodeExecAdapterInput(
|
|
509
|
+
input: Readonly<Partial<CodeExecToolInput>>,
|
|
510
|
+
options: Pick<CodeExecToolAdapterOptions, "languages" | "maxTimeoutMs" | "defaultTimeoutMs">
|
|
511
|
+
): RuntimeToolValidationResult {
|
|
512
|
+
const issues = [...validateCodeExecInput(input)];
|
|
513
|
+
const languages = options.languages ?? codeExecLanguages;
|
|
514
|
+
|
|
515
|
+
if (typeof input.language === "string" && isCodeExecLanguage(input.language) && !languages.includes(input.language)) {
|
|
516
|
+
issues.push({
|
|
517
|
+
code: "invalid-value",
|
|
518
|
+
path: "language",
|
|
519
|
+
message: "codeExec.language is not enabled for this adapter.",
|
|
520
|
+
detail: { allowed: Array.from(languages) }
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const effectiveTimeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
525
|
+
if (
|
|
526
|
+
effectiveTimeoutMs !== undefined &&
|
|
527
|
+
options.maxTimeoutMs !== undefined &&
|
|
528
|
+
Number.isFinite(effectiveTimeoutMs) &&
|
|
529
|
+
Number.isFinite(options.maxTimeoutMs) &&
|
|
530
|
+
effectiveTimeoutMs > options.maxTimeoutMs
|
|
531
|
+
) {
|
|
532
|
+
issues.push({
|
|
533
|
+
code: "out-of-range",
|
|
534
|
+
path: input.timeoutMs === undefined ? "defaultTimeoutMs" : "timeoutMs",
|
|
535
|
+
message: `codeExec.timeoutMs must be less than or equal to ${options.maxTimeoutMs}.`,
|
|
536
|
+
detail: { maximum: options.maxTimeoutMs }
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return issues.length === 0 ? { type: "valid" } : { type: "invalid", issues };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function defaultWebSearchRequest(
|
|
544
|
+
options: WebSearchToolAdapterOptions,
|
|
545
|
+
input: Readonly<WebSearchToolInput>,
|
|
546
|
+
_context: RuntimeToolExecutionContext
|
|
547
|
+
): WebSearchFetchRequest {
|
|
548
|
+
const url = new URL(String(options.endpoint));
|
|
549
|
+
url.searchParams.set("q", input.query);
|
|
550
|
+
url.searchParams.set("limit", String(input.maxResults ?? options.defaultMaxResults ?? 10));
|
|
551
|
+
return {
|
|
552
|
+
url,
|
|
553
|
+
init: {
|
|
554
|
+
method: "GET",
|
|
555
|
+
...(options.headers ? { headers: options.headers } : {})
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async function defaultWebSearchResponseParser(response: Response): Promise<WebSearchToolOutput> {
|
|
561
|
+
const payload: unknown = await response.json();
|
|
562
|
+
const resultValues = Array.isArray(payload)
|
|
563
|
+
? payload
|
|
564
|
+
: isJsonObject(payload) && Array.isArray(payload.results)
|
|
565
|
+
? payload.results
|
|
566
|
+
: undefined;
|
|
567
|
+
|
|
568
|
+
if (!resultValues) {
|
|
569
|
+
throw {
|
|
570
|
+
code: "backend-error",
|
|
571
|
+
message: "Web search backend response must contain a results array.",
|
|
572
|
+
retryable: false
|
|
573
|
+
} satisfies RuntimeToolAdapterError;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return { results: resultValues.map(normalizeWebSearchResult) };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function codeExecPermissionsFor(
|
|
580
|
+
languages: readonly CodeExecToolLanguage[],
|
|
581
|
+
allowNetwork: boolean
|
|
582
|
+
): readonly RuntimeToolPermission[] {
|
|
583
|
+
return [
|
|
584
|
+
{ kind: "code-execution", sandbox: "caller-provided", languages, allowNetwork }
|
|
585
|
+
];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function codeExecInputSchemaFor(languages: readonly CodeExecToolLanguage[]): RuntimeToolInputSchema {
|
|
589
|
+
return {
|
|
590
|
+
kind: "json-schema",
|
|
591
|
+
...(codeExecInputSchema.description ? { description: codeExecInputSchema.description } : {}),
|
|
592
|
+
schema: {
|
|
593
|
+
type: "object",
|
|
594
|
+
properties: {
|
|
595
|
+
language: { type: "string", enum: Array.from(languages) },
|
|
596
|
+
code: { type: "string" },
|
|
597
|
+
timeoutMs: { type: "number", minimum: 1 }
|
|
598
|
+
},
|
|
599
|
+
required: ["language", "code"],
|
|
600
|
+
additionalProperties: false
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async function executeSandboxWithPolicy(
|
|
606
|
+
execute: CodeExecSandboxExecutor,
|
|
607
|
+
input: Readonly<CodeExecToolInput>,
|
|
608
|
+
context: RuntimeToolExecutionContext,
|
|
609
|
+
timeoutMs: number | undefined
|
|
610
|
+
): Promise<CodeExecToolOutput> {
|
|
611
|
+
if (context.abortSignal?.aborted) {
|
|
612
|
+
throw {
|
|
613
|
+
code: "aborted",
|
|
614
|
+
message: "Code execution was aborted before the sandbox started.",
|
|
615
|
+
retryable: true
|
|
616
|
+
} satisfies RuntimeToolAdapterError;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const execution = Promise.resolve().then(() => execute(input, context));
|
|
620
|
+
|
|
621
|
+
if (timeoutMs === undefined && context.abortSignal === undefined) {
|
|
622
|
+
return await execution;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return await new Promise<CodeExecToolOutput>((resolve, reject) => {
|
|
626
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
627
|
+
|
|
628
|
+
const cleanup = (): void => {
|
|
629
|
+
if (timeoutId !== undefined) clearTimeout(timeoutId);
|
|
630
|
+
context.abortSignal?.removeEventListener("abort", abortHandler);
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const abortHandler = (): void => {
|
|
634
|
+
cleanup();
|
|
635
|
+
reject({
|
|
636
|
+
code: "aborted",
|
|
637
|
+
message: "Code execution was aborted.",
|
|
638
|
+
retryable: true
|
|
639
|
+
} satisfies RuntimeToolAdapterError);
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
if (context.abortSignal) {
|
|
643
|
+
context.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (timeoutMs !== undefined) {
|
|
647
|
+
timeoutId = setTimeout(() => {
|
|
648
|
+
cleanup();
|
|
649
|
+
reject({
|
|
650
|
+
code: "timeout",
|
|
651
|
+
message: `Code execution exceeded timeout of ${timeoutMs}ms.`,
|
|
652
|
+
retryable: true,
|
|
653
|
+
detail: { timeoutMs }
|
|
654
|
+
} satisfies RuntimeToolAdapterError);
|
|
655
|
+
}, timeoutMs);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
execution.then(
|
|
659
|
+
(output) => { cleanup(); resolve(output); },
|
|
660
|
+
(error: unknown) => { cleanup(); reject(error); }
|
|
661
|
+
);
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function normalizeWebSearchResult(value: unknown): WebSearchToolResult {
|
|
666
|
+
if (!isJsonObject(value)) {
|
|
667
|
+
throw {
|
|
668
|
+
code: "backend-error",
|
|
669
|
+
message: "Web search result must be a JSON object.",
|
|
670
|
+
retryable: false
|
|
671
|
+
} satisfies RuntimeToolAdapterError;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const title = jsonString(value.title, "title");
|
|
675
|
+
const url = jsonString(value.url, "url");
|
|
676
|
+
const snippet = optionalJsonString(value.snippet, "snippet");
|
|
677
|
+
const metadata = optionalJsonObject(value.metadata, "metadata");
|
|
678
|
+
|
|
679
|
+
return {
|
|
680
|
+
title,
|
|
681
|
+
url,
|
|
682
|
+
...(snippet !== undefined ? { snippet } : {}),
|
|
683
|
+
...(metadata !== undefined ? { metadata } : {})
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function jsonString(value: JsonValue | undefined, fieldName: string): string {
|
|
688
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
689
|
+
throw {
|
|
690
|
+
code: "backend-error",
|
|
691
|
+
message: `Web search result ${fieldName} must be a non-empty string.`,
|
|
692
|
+
retryable: false
|
|
693
|
+
} satisfies RuntimeToolAdapterError;
|
|
694
|
+
}
|
|
695
|
+
return value;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function optionalJsonString(value: JsonValue | undefined, fieldName: string): string | undefined {
|
|
699
|
+
if (value === undefined) return undefined;
|
|
700
|
+
if (typeof value !== "string") {
|
|
701
|
+
throw {
|
|
702
|
+
code: "backend-error",
|
|
703
|
+
message: `Web search result ${fieldName} must be a string when present.`,
|
|
704
|
+
retryable: false
|
|
705
|
+
} satisfies RuntimeToolAdapterError;
|
|
706
|
+
}
|
|
707
|
+
return value;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function optionalJsonObject(value: JsonValue | undefined, fieldName: string): JsonObject | undefined {
|
|
711
|
+
if (value === undefined) return undefined;
|
|
712
|
+
if (!isJsonObject(value)) {
|
|
713
|
+
throw {
|
|
714
|
+
code: "backend-error",
|
|
715
|
+
message: `Web search result ${fieldName} must be a JSON object when present.`,
|
|
716
|
+
retryable: false
|
|
717
|
+
} satisfies RuntimeToolAdapterError;
|
|
718
|
+
}
|
|
719
|
+
return value;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function isJsonObject(value: unknown): value is JsonObject {
|
|
723
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function validateWebSearchInput(input: Readonly<Partial<WebSearchToolInput>>): readonly RuntimeToolValidationIssue[] {
|
|
727
|
+
const issues: RuntimeToolValidationIssue[] = [];
|
|
728
|
+
|
|
729
|
+
if (typeof input.query !== "string") {
|
|
730
|
+
issues.push({
|
|
731
|
+
code: input.query === undefined ? "missing-field" : "invalid-type",
|
|
732
|
+
path: "query",
|
|
733
|
+
message: "webSearch.query must be a string."
|
|
734
|
+
});
|
|
735
|
+
} else if (input.query.trim().length === 0) {
|
|
736
|
+
issues.push({
|
|
737
|
+
code: "invalid-value",
|
|
738
|
+
path: "query",
|
|
739
|
+
message: "webSearch.query must not be empty."
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (input.maxResults !== undefined) {
|
|
744
|
+
if (typeof input.maxResults !== "number" || !Number.isFinite(input.maxResults)) {
|
|
745
|
+
issues.push({
|
|
746
|
+
code: "invalid-type",
|
|
747
|
+
path: "maxResults",
|
|
748
|
+
message: "webSearch.maxResults must be a finite number."
|
|
749
|
+
});
|
|
750
|
+
} else if (input.maxResults < 1) {
|
|
751
|
+
issues.push({
|
|
752
|
+
code: "out-of-range",
|
|
753
|
+
path: "maxResults",
|
|
754
|
+
message: "webSearch.maxResults must be greater than or equal to 1.",
|
|
755
|
+
detail: { minimum: 1 }
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return issues;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function validateCodeExecInput(input: Readonly<Partial<CodeExecToolInput>>): readonly RuntimeToolValidationIssue[] {
|
|
764
|
+
const issues: RuntimeToolValidationIssue[] = [];
|
|
765
|
+
|
|
766
|
+
if (typeof input.language !== "string") {
|
|
767
|
+
issues.push({
|
|
768
|
+
code: input.language === undefined ? "missing-field" : "invalid-type",
|
|
769
|
+
path: "language",
|
|
770
|
+
message: "codeExec.language must be a string."
|
|
771
|
+
});
|
|
772
|
+
} else if (!isCodeExecLanguage(input.language)) {
|
|
773
|
+
issues.push({
|
|
774
|
+
code: "invalid-value",
|
|
775
|
+
path: "language",
|
|
776
|
+
message: "codeExec.language must be one of javascript, typescript, python, bash, or shell.",
|
|
777
|
+
detail: { allowed: ["javascript", "typescript", "python", "bash", "shell"] }
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (typeof input.code !== "string") {
|
|
782
|
+
issues.push({
|
|
783
|
+
code: input.code === undefined ? "missing-field" : "invalid-type",
|
|
784
|
+
path: "code",
|
|
785
|
+
message: "codeExec.code must be a string."
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (input.timeoutMs !== undefined) {
|
|
790
|
+
if (typeof input.timeoutMs !== "number" || !Number.isFinite(input.timeoutMs)) {
|
|
791
|
+
issues.push({
|
|
792
|
+
code: "invalid-type",
|
|
793
|
+
path: "timeoutMs",
|
|
794
|
+
message: "codeExec.timeoutMs must be a finite number."
|
|
795
|
+
});
|
|
796
|
+
} else if (input.timeoutMs < 1) {
|
|
797
|
+
issues.push({
|
|
798
|
+
code: "out-of-range",
|
|
799
|
+
path: "timeoutMs",
|
|
800
|
+
message: "codeExec.timeoutMs must be greater than or equal to 1.",
|
|
801
|
+
detail: { minimum: 1 }
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return issues;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function isCodeExecLanguage(value: string): value is CodeExecToolInput["language"] {
|
|
810
|
+
return value === "javascript" || value === "typescript" || value === "python" || value === "bash" || value === "shell";
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// ---------------------------------------------------------------------------
|
|
814
|
+
// Adapter error normalization (shared with the executor in tools.ts)
|
|
815
|
+
// ---------------------------------------------------------------------------
|
|
816
|
+
|
|
817
|
+
export function normalizeRuntimeToolAdapterError(error: unknown): RuntimeToolAdapterError {
|
|
818
|
+
if (isRuntimeToolAdapterError(error)) return error;
|
|
819
|
+
|
|
820
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
821
|
+
return {
|
|
822
|
+
code: "aborted",
|
|
823
|
+
message: error.message || "Tool execution was aborted.",
|
|
824
|
+
retryable: true,
|
|
825
|
+
detail: { name: error.name }
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
if (error instanceof Error) {
|
|
830
|
+
return {
|
|
831
|
+
code: "backend-error",
|
|
832
|
+
message: error.message,
|
|
833
|
+
retryable: false,
|
|
834
|
+
detail: { name: error.name }
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return {
|
|
839
|
+
code: "unknown",
|
|
840
|
+
message: "Tool execution failed with a non-Error value.",
|
|
841
|
+
retryable: false,
|
|
842
|
+
detail: { valueType: typeof error }
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function isRuntimeToolAdapterError(error: unknown): error is RuntimeToolAdapterError {
|
|
847
|
+
if (typeof error !== "object" || error === null || !("code" in error) || !("message" in error)) {
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
const candidate = error as { readonly code: unknown; readonly message: unknown };
|
|
851
|
+
return isRuntimeToolAdapterErrorCode(candidate.code) && typeof candidate.message === "string";
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function isRuntimeToolAdapterErrorCode(value: unknown): value is RuntimeToolAdapterError["code"] {
|
|
855
|
+
return (
|
|
856
|
+
value === "invalid-input" ||
|
|
857
|
+
value === "permission-denied" ||
|
|
858
|
+
value === "timeout" ||
|
|
859
|
+
value === "aborted" ||
|
|
860
|
+
value === "unavailable" ||
|
|
861
|
+
value === "backend-error" ||
|
|
862
|
+
value === "unknown"
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
function serializeValidationIssues(
|
|
867
|
+
issues: readonly RuntimeToolValidationIssue[]
|
|
868
|
+
): JsonValue {
|
|
869
|
+
return issues.map((issue): JsonObject => ({
|
|
870
|
+
code: issue.code,
|
|
871
|
+
path: issue.path,
|
|
872
|
+
message: issue.message,
|
|
873
|
+
...(issue.detail !== undefined ? { detail: issue.detail } : {})
|
|
874
|
+
}));
|
|
875
|
+
}
|