stitchkit 0.54.0 → 0.56.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/README.md +4 -1
- package/dist/browser/socket-io.d.ts +25 -7
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +14 -9
- package/dist/contract/errors.d.ts +7 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/file-ref.d.ts +13 -0
- package/dist/contract/file-ref.d.ts.map +1 -0
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +21 -17
- package/dist/files/boundary.d.ts +55 -0
- package/dist/files/boundary.d.ts.map +1 -0
- package/dist/files.d.ts +3 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +16 -0
- package/dist/index-1bx83sw4.js +4 -0
- package/dist/{index-pfqjb5xy.js → index-28cqssm3.js} +30 -5
- package/dist/index-557by2db.js +34 -0
- package/dist/index-5r13htq1.js +283 -0
- package/dist/index-6k1937bx.js +20 -0
- package/dist/index-6y759j86.js +1031 -0
- package/dist/{index-bmnhqtya.js → index-7eq9va6e.js} +1 -4
- package/dist/index-bfcpjw20.js +363 -0
- package/dist/{index-yxpe3phd.js → index-cby4ar3v.js} +1 -18
- package/dist/{index-7vb44hy8.js → index-escqg10p.js} +193 -751
- package/dist/{index-173tvqcp.js → index-j3dem06f.js} +9 -114
- package/dist/{index-gvha32jh.js → index-jcc611vh.js} +2 -2
- package/dist/{index-z7761jk2.js → index-n4nfa7gh.js} +16 -22
- package/dist/{index-e1es808r.js → index-qh4xevf5.js} +1 -1
- package/dist/index-sbdmyz75.js +18 -0
- package/dist/{index-b15rtha1.js → index-sm2tjx06.js} +98 -3
- package/dist/{index-yez419px.js → index-xy8fmh6w.js} +99 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +188 -40
- package/dist/node.js +31 -27
- package/dist/observability/index.js +28 -25
- package/dist/react.js +2 -2
- package/dist/realtime/contract.d.ts +9 -0
- package/dist/realtime/contract.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +2 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/request.d.ts +21 -0
- package/dist/realtime/request.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +3 -0
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/remote.d.ts +9 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +79 -0
- package/dist/server/context-contribution.d.ts +18 -0
- package/dist/server/context-contribution.d.ts.map +1 -0
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +136 -88
- package/dist/server/lifecycle.d.ts +4 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/middleware/auth.d.ts +64 -9
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +180 -0
- package/dist/testing/surface-conformance.d.ts.map +1 -0
- package/dist/testing/surface-manifest.d.ts +255 -0
- package/dist/testing/surface-manifest.d.ts.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +714 -3
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/async-operation.d.ts +360 -0
- package/dist/tools/async-operation.d.ts.map +1 -0
- package/dist/tools/cli-args.d.ts +1 -1
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-format.d.ts +2 -2
- package/dist/tools/cli-format.d.ts.map +1 -1
- package/dist/tools/cli-wait.d.ts +11 -2
- package/dist/tools/cli-wait.d.ts.map +1 -1
- package/dist/tools/cli.d.ts +2 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/define-download-tool.d.ts +6 -5
- package/dist/tools/define-download-tool.d.ts.map +1 -1
- package/dist/tools/define-upload-tool.d.ts +3 -1
- package/dist/tools/define-upload-tool.d.ts.map +1 -1
- package/dist/tools/define-wait-tool.d.ts +1 -1
- package/dist/tools/define-wait-tool.d.ts.map +1 -1
- package/dist/tools/download-core.d.ts +5 -10
- package/dist/tools/download-core.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +101 -0
- package/dist/tools/internal/surface-projector.d.ts.map +1 -0
- package/dist/tools/lifecycle.d.ts +4 -0
- package/dist/tools/lifecycle.d.ts.map +1 -0
- package/dist/tools/managed-file-error.d.ts +6 -0
- package/dist/tools/managed-file-error.d.ts.map +1 -0
- package/dist/tools/mcp-prepare.d.ts +3 -21
- package/dist/tools/mcp-prepare.d.ts.map +1 -1
- package/dist/tools/mcp-round-policy.d.ts +9 -0
- package/dist/tools/mcp-round-policy.d.ts.map +1 -0
- package/dist/tools/mcp-round.d.ts +0 -4
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools/mount-download.d.ts +5 -4
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/mount-upload.d.ts +4 -2
- package/dist/tools/mount-upload.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +5 -9
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/surface.d.ts +7 -0
- package/dist/tools/surface.d.ts.map +1 -1
- package/dist/tools/upload-core.d.ts +3 -2
- package/dist/tools/upload-core.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +4 -3
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools/wait-core.d.ts +6 -1
- package/dist/tools/wait-core.d.ts.map +1 -1
- package/dist/tools.d.ts +3 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +705 -599
- package/llms-full.txt +727 -34
- package/package.json +10 -2
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import {
|
|
2
|
-
isWithinDir
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
isWithinDir
|
|
3
|
+
} from "./index-sbdmyz75.js";
|
|
4
|
+
import {
|
|
5
|
+
assertToolExtensionCompatible,
|
|
6
|
+
assertUniqueToolName,
|
|
7
|
+
buildToolPresentationSchema,
|
|
8
|
+
mergeSchemas,
|
|
9
|
+
objectShapeKeys,
|
|
10
|
+
projectRuntimeTool,
|
|
11
|
+
projectToolSurface,
|
|
12
|
+
rebuildObject
|
|
13
|
+
} from "./index-6y759j86.js";
|
|
14
|
+
import {
|
|
15
|
+
jsonSchemaFields
|
|
16
|
+
} from "./index-cby4ar3v.js";
|
|
6
17
|
import {
|
|
7
|
-
AppError,
|
|
8
|
-
STITCH_ERROR_STATUS,
|
|
9
18
|
formatZodError,
|
|
10
19
|
getRequestContext,
|
|
11
|
-
isStitchErrorCode,
|
|
12
|
-
isUnsafeKey,
|
|
13
20
|
normalizeError,
|
|
14
21
|
runWithRequestContext,
|
|
15
|
-
safeJsonParse,
|
|
16
22
|
validateDeclaredOutput
|
|
17
|
-
} from "./index-
|
|
23
|
+
} from "./index-j3dem06f.js";
|
|
24
|
+
import {
|
|
25
|
+
AppError,
|
|
26
|
+
STITCH_ERROR_STATUS,
|
|
27
|
+
isStitchErrorCode,
|
|
28
|
+
isUnsafeKey,
|
|
29
|
+
safeJsonParse
|
|
30
|
+
} from "./index-5r13htq1.js";
|
|
18
31
|
import {
|
|
19
32
|
isRecord
|
|
20
|
-
} from "./index-
|
|
33
|
+
} from "./index-7eq9va6e.js";
|
|
21
34
|
|
|
22
35
|
// src/tools/coerce.ts
|
|
23
36
|
import { z } from "zod";
|
|
@@ -100,454 +113,8 @@ function coerceJsonArgs(args, schema) {
|
|
|
100
113
|
return isRecord(coerced) ? coerced : args;
|
|
101
114
|
}
|
|
102
115
|
|
|
103
|
-
// src/tools/flatten-join.ts
|
|
104
|
-
var ANNOTATION_KEYS = new Set(["$schema", "default", "description", "examples", "title"]);
|
|
105
|
-
var TYPE_KEYWORDS = {
|
|
106
|
-
array: [
|
|
107
|
-
"contains",
|
|
108
|
-
"items",
|
|
109
|
-
"maxContains",
|
|
110
|
-
"maxItems",
|
|
111
|
-
"minContains",
|
|
112
|
-
"minItems",
|
|
113
|
-
"prefixItems",
|
|
114
|
-
"uniqueItems"
|
|
115
|
-
],
|
|
116
|
-
integer: ["exclusiveMaximum", "exclusiveMinimum", "maximum", "minimum", "multipleOf"],
|
|
117
|
-
number: ["exclusiveMaximum", "exclusiveMinimum", "maximum", "minimum", "multipleOf"],
|
|
118
|
-
object: [
|
|
119
|
-
"additionalProperties",
|
|
120
|
-
"dependentRequired",
|
|
121
|
-
"dependentSchemas",
|
|
122
|
-
"maxProperties",
|
|
123
|
-
"minProperties",
|
|
124
|
-
"patternProperties",
|
|
125
|
-
"properties",
|
|
126
|
-
"propertyNames",
|
|
127
|
-
"required"
|
|
128
|
-
],
|
|
129
|
-
string: [
|
|
130
|
-
"contentEncoding",
|
|
131
|
-
"contentMediaType",
|
|
132
|
-
"format",
|
|
133
|
-
"maxLength",
|
|
134
|
-
"minLength",
|
|
135
|
-
"pattern"
|
|
136
|
-
]
|
|
137
|
-
};
|
|
138
|
-
function nullableProjection(schema) {
|
|
139
|
-
if (!Array.isArray(schema.anyOf) || schema.anyOf.length !== 2)
|
|
140
|
-
return schema;
|
|
141
|
-
const branches = schema.anyOf.filter(isRecord);
|
|
142
|
-
if (branches.length !== 2)
|
|
143
|
-
return schema;
|
|
144
|
-
const nullBranch = branches.find((branch) => branch.type === "null");
|
|
145
|
-
const value = branches.find((branch) => typeof branch.type === "string" && branch.type !== "null");
|
|
146
|
-
if (!nullBranch || !value || Object.keys(nullBranch).some((key) => key !== "type")) {
|
|
147
|
-
return schema;
|
|
148
|
-
}
|
|
149
|
-
const type = value.type;
|
|
150
|
-
if (typeof type !== "string")
|
|
151
|
-
return schema;
|
|
152
|
-
const projected = { type: [type, "null"] };
|
|
153
|
-
for (const key of [...ANNOTATION_KEYS, ...TYPE_KEYWORDS[type] ?? []]) {
|
|
154
|
-
if (value[key] !== undefined)
|
|
155
|
-
projected[key] = value[key];
|
|
156
|
-
else if (schema[key] !== undefined)
|
|
157
|
-
projected[key] = schema[key];
|
|
158
|
-
}
|
|
159
|
-
if (value.const !== undefined)
|
|
160
|
-
projected.enum = [value.const, null];
|
|
161
|
-
else if (Array.isArray(value.enum))
|
|
162
|
-
projected.enum = [...value.enum, null];
|
|
163
|
-
return projected;
|
|
164
|
-
}
|
|
165
|
-
function withoutAnnotations(schema) {
|
|
166
|
-
const result = {};
|
|
167
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
168
|
-
if (!ANNOTATION_KEYS.has(key))
|
|
169
|
-
result[key] = value;
|
|
170
|
-
}
|
|
171
|
-
return result;
|
|
172
|
-
}
|
|
173
|
-
function stableJson(value) {
|
|
174
|
-
if (Array.isArray(value))
|
|
175
|
-
return `[${value.map(stableJson).join(",")}]`;
|
|
176
|
-
if (!isRecord(value))
|
|
177
|
-
return JSON.stringify(value) ?? "undefined";
|
|
178
|
-
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
179
|
-
}
|
|
180
|
-
function stringValues(schema) {
|
|
181
|
-
if (typeof schema.const === "string")
|
|
182
|
-
return [schema.const];
|
|
183
|
-
if (!Array.isArray(schema.enum))
|
|
184
|
-
return null;
|
|
185
|
-
const values = schema.enum.filter((value) => typeof value === "string");
|
|
186
|
-
return values.length === schema.enum.length ? values : null;
|
|
187
|
-
}
|
|
188
|
-
var JSON_SCHEMA_TYPE_ORDER = [
|
|
189
|
-
"string",
|
|
190
|
-
"number",
|
|
191
|
-
"integer",
|
|
192
|
-
"boolean",
|
|
193
|
-
"object",
|
|
194
|
-
"array",
|
|
195
|
-
"null"
|
|
196
|
-
];
|
|
197
|
-
function jsonSchemaType(value) {
|
|
198
|
-
switch (value) {
|
|
199
|
-
case "array":
|
|
200
|
-
case "boolean":
|
|
201
|
-
case "integer":
|
|
202
|
-
case "null":
|
|
203
|
-
case "number":
|
|
204
|
-
case "object":
|
|
205
|
-
case "string":
|
|
206
|
-
return value;
|
|
207
|
-
default:
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function valueType(value) {
|
|
212
|
-
if (value === null)
|
|
213
|
-
return "null";
|
|
214
|
-
if (Array.isArray(value))
|
|
215
|
-
return "array";
|
|
216
|
-
if (isRecord(value))
|
|
217
|
-
return "object";
|
|
218
|
-
if (typeof value === "string")
|
|
219
|
-
return "string";
|
|
220
|
-
if (typeof value === "number")
|
|
221
|
-
return "number";
|
|
222
|
-
if (typeof value === "boolean")
|
|
223
|
-
return "boolean";
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
function normalizeTypes(types) {
|
|
227
|
-
const normalized = new Set(types);
|
|
228
|
-
if (normalized.has("number"))
|
|
229
|
-
normalized.delete("integer");
|
|
230
|
-
return JSON_SCHEMA_TYPE_ORDER.filter((type) => normalized.has(type));
|
|
231
|
-
}
|
|
232
|
-
function combinedBranchTypes(schema) {
|
|
233
|
-
for (const keyword of ["oneOf", "anyOf"]) {
|
|
234
|
-
const value = schema[keyword];
|
|
235
|
-
if (!Array.isArray(value) || value.length === 0)
|
|
236
|
-
continue;
|
|
237
|
-
const collected = [];
|
|
238
|
-
for (const branch of value) {
|
|
239
|
-
if (!isRecord(branch))
|
|
240
|
-
return null;
|
|
241
|
-
const types = typeNames(branch);
|
|
242
|
-
if (!types)
|
|
243
|
-
return null;
|
|
244
|
-
collected.push(...types);
|
|
245
|
-
}
|
|
246
|
-
return normalizeTypes(collected);
|
|
247
|
-
}
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
250
|
-
function typeNames(schema) {
|
|
251
|
-
const direct = jsonSchemaType(schema.type);
|
|
252
|
-
if (direct)
|
|
253
|
-
return [direct];
|
|
254
|
-
if (Array.isArray(schema.type)) {
|
|
255
|
-
const values = [];
|
|
256
|
-
for (const value of schema.type) {
|
|
257
|
-
const type = jsonSchemaType(value);
|
|
258
|
-
if (!type)
|
|
259
|
-
return null;
|
|
260
|
-
values.push(type);
|
|
261
|
-
}
|
|
262
|
-
return values.length > 0 ? normalizeTypes(values) : null;
|
|
263
|
-
}
|
|
264
|
-
if (schema.const !== undefined) {
|
|
265
|
-
const type = valueType(schema.const);
|
|
266
|
-
return type ? [type] : null;
|
|
267
|
-
}
|
|
268
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
269
|
-
const values = [];
|
|
270
|
-
for (const value of schema.enum) {
|
|
271
|
-
const type = valueType(value);
|
|
272
|
-
if (!type)
|
|
273
|
-
return null;
|
|
274
|
-
values.push(type);
|
|
275
|
-
}
|
|
276
|
-
return normalizeTypes(values);
|
|
277
|
-
}
|
|
278
|
-
return combinedBranchTypes(schema);
|
|
279
|
-
}
|
|
280
|
-
function typeSchema(type, nullable) {
|
|
281
|
-
const schema = { type: nullable ? [type, "null"] : type };
|
|
282
|
-
if (type === "array")
|
|
283
|
-
schema.items = {};
|
|
284
|
-
if (type === "object")
|
|
285
|
-
schema.additionalProperties = {};
|
|
286
|
-
return schema;
|
|
287
|
-
}
|
|
288
|
-
function commonBaseType(schemas) {
|
|
289
|
-
const names = schemas.map(typeNames);
|
|
290
|
-
if (names.some((value) => value === null))
|
|
291
|
-
return {};
|
|
292
|
-
let nullable = false;
|
|
293
|
-
const bases = new Set;
|
|
294
|
-
for (const branch of names) {
|
|
295
|
-
if (!branch)
|
|
296
|
-
return {};
|
|
297
|
-
for (const name of branch) {
|
|
298
|
-
if (name === "null")
|
|
299
|
-
nullable = true;
|
|
300
|
-
else
|
|
301
|
-
bases.add(name);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (bases.size === 0)
|
|
305
|
-
return nullable ? { type: "null" } : {};
|
|
306
|
-
if (bases.size === 1) {
|
|
307
|
-
const [only] = bases;
|
|
308
|
-
return only ? typeSchema(only, nullable) : {};
|
|
309
|
-
}
|
|
310
|
-
if ([...bases].every((name) => name === "integer" || name === "number")) {
|
|
311
|
-
return typeSchema("number", nullable);
|
|
312
|
-
}
|
|
313
|
-
const types = normalizeTypes(nullable ? [...bases, "null"] : bases);
|
|
314
|
-
return types.length > 0 ? { type: types } : {};
|
|
315
|
-
}
|
|
316
|
-
function mergePropertySchemas(properties) {
|
|
317
|
-
const schemas = properties.map((property) => nullableProjection(property.schema));
|
|
318
|
-
const structural = schemas.map(withoutAnnotations);
|
|
319
|
-
const [first] = structural;
|
|
320
|
-
let merged;
|
|
321
|
-
if (first && structural.every((schema) => stableJson(schema) === stableJson(first))) {
|
|
322
|
-
merged = structuredClone(first);
|
|
323
|
-
} else {
|
|
324
|
-
const values = schemas.map(stringValues);
|
|
325
|
-
const allStrings = values.every((value) => value !== null);
|
|
326
|
-
merged = allStrings ? { type: "string", enum: [...new Set(values.flatMap((value) => value ?? []))] } : commonBaseType(schemas);
|
|
327
|
-
}
|
|
328
|
-
const descriptions = [
|
|
329
|
-
...new Set(schemas.map((schema) => schema.description).filter((value) => typeof value === "string"))
|
|
330
|
-
];
|
|
331
|
-
if (descriptions.length === 1)
|
|
332
|
-
merged.description = descriptions[0];
|
|
333
|
-
return merged;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// src/tools/flatten.ts
|
|
337
|
-
function rebuild(value) {
|
|
338
|
-
if (Array.isArray(value))
|
|
339
|
-
return value.map(rebuild);
|
|
340
|
-
if (!isRecord(value))
|
|
341
|
-
return value;
|
|
342
|
-
const node = {};
|
|
343
|
-
for (const [key, child] of Object.entries(value))
|
|
344
|
-
node[key] = rebuild(child);
|
|
345
|
-
return flattenNode(node);
|
|
346
|
-
}
|
|
347
|
-
function stringValues2(schema) {
|
|
348
|
-
if (typeof schema.const === "string")
|
|
349
|
-
return [schema.const];
|
|
350
|
-
if (!Array.isArray(schema.enum))
|
|
351
|
-
return null;
|
|
352
|
-
const values = schema.enum.filter((value) => typeof value === "string");
|
|
353
|
-
return values.length === schema.enum.length ? values : null;
|
|
354
|
-
}
|
|
355
|
-
function objectVariants(node) {
|
|
356
|
-
if (!Array.isArray(node.oneOf) || node.oneOf.length < 2)
|
|
357
|
-
return null;
|
|
358
|
-
const variants = node.oneOf.filter(isRecord);
|
|
359
|
-
if (variants.length !== node.oneOf.length)
|
|
360
|
-
return null;
|
|
361
|
-
if (variants.some((variant) => variant.type !== "object" || !isRecord(variant.properties))) {
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
return variants;
|
|
365
|
-
}
|
|
366
|
-
function findDiscriminator(variants) {
|
|
367
|
-
const [first] = variants;
|
|
368
|
-
if (!first || !isRecord(first.properties))
|
|
369
|
-
return null;
|
|
370
|
-
for (const key of Object.keys(first.properties).sort()) {
|
|
371
|
-
const values = [];
|
|
372
|
-
const seen = new Set;
|
|
373
|
-
let valid = true;
|
|
374
|
-
for (const variant of variants) {
|
|
375
|
-
if (!requiredKeys(variant).has(key)) {
|
|
376
|
-
valid = false;
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
const properties = variant.properties;
|
|
380
|
-
const field = isRecord(properties) ? properties[key] : undefined;
|
|
381
|
-
const branchValues = isRecord(field) ? stringValues2(field) : null;
|
|
382
|
-
if (!branchValues || branchValues.length === 0) {
|
|
383
|
-
valid = false;
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
for (const value of branchValues) {
|
|
387
|
-
if (seen.has(value)) {
|
|
388
|
-
valid = false;
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
seen.add(value);
|
|
392
|
-
}
|
|
393
|
-
if (!valid)
|
|
394
|
-
break;
|
|
395
|
-
values.push(branchValues);
|
|
396
|
-
}
|
|
397
|
-
if (valid)
|
|
398
|
-
return { key, values };
|
|
399
|
-
}
|
|
400
|
-
return null;
|
|
401
|
-
}
|
|
402
|
-
function requiredKeys(variant) {
|
|
403
|
-
if (!Array.isArray(variant.required))
|
|
404
|
-
return new Set;
|
|
405
|
-
return new Set(variant.required.filter((key) => typeof key === "string"));
|
|
406
|
-
}
|
|
407
|
-
function appendHint(schema, hint) {
|
|
408
|
-
const description = typeof schema.description === "string" ? schema.description : undefined;
|
|
409
|
-
return { ...schema, description: description ? `${description} ${hint}` : hint };
|
|
410
|
-
}
|
|
411
|
-
function flattenNode(node) {
|
|
412
|
-
const variants = objectVariants(node);
|
|
413
|
-
if (!variants)
|
|
414
|
-
return node;
|
|
415
|
-
const discriminator = findDiscriminator(variants);
|
|
416
|
-
if (!discriminator)
|
|
417
|
-
return node;
|
|
418
|
-
const perKey = new Map;
|
|
419
|
-
for (let index = 0;index < variants.length; index++) {
|
|
420
|
-
const variant = variants[index];
|
|
421
|
-
if (!variant)
|
|
422
|
-
continue;
|
|
423
|
-
const labels = discriminator.values[index] ?? [];
|
|
424
|
-
const required2 = requiredKeys(variant);
|
|
425
|
-
if (!isRecord(variant.properties))
|
|
426
|
-
continue;
|
|
427
|
-
for (const [key, rawSchema] of Object.entries(variant.properties)) {
|
|
428
|
-
if (key === discriminator.key || !isRecord(rawSchema))
|
|
429
|
-
continue;
|
|
430
|
-
const entries = perKey.get(key) ?? [];
|
|
431
|
-
entries.push({ schema: rawSchema, labels, required: required2.has(key) });
|
|
432
|
-
perKey.set(key, entries);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
const discriminatorProperties = [];
|
|
436
|
-
for (let index = 0;index < variants.length; index++) {
|
|
437
|
-
const variant = variants[index];
|
|
438
|
-
if (!variant || !isRecord(variant.properties))
|
|
439
|
-
continue;
|
|
440
|
-
const schema = variant.properties[discriminator.key];
|
|
441
|
-
if (!isRecord(schema))
|
|
442
|
-
continue;
|
|
443
|
-
discriminatorProperties.push({
|
|
444
|
-
schema,
|
|
445
|
-
labels: discriminator.values[index] ?? [],
|
|
446
|
-
required: true
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
const properties = {
|
|
450
|
-
[discriminator.key]: mergePropertySchemas(discriminatorProperties)
|
|
451
|
-
};
|
|
452
|
-
const required = [discriminator.key];
|
|
453
|
-
for (const [key, entries] of perKey) {
|
|
454
|
-
let merged = mergePropertySchemas(entries);
|
|
455
|
-
const requiredEverywhere = entries.length === variants.length && entries.every((entry) => entry.required);
|
|
456
|
-
if (requiredEverywhere) {
|
|
457
|
-
required.push(key);
|
|
458
|
-
} else {
|
|
459
|
-
const requiredLabels = entries.filter((entry) => entry.required).flatMap((entry) => entry.labels);
|
|
460
|
-
const presentLabels = entries.flatMap((entry) => entry.labels);
|
|
461
|
-
const labels = requiredLabels.length > 0 ? requiredLabels : presentLabels;
|
|
462
|
-
const verb = requiredLabels.length > 0 ? "Required" : "Available";
|
|
463
|
-
merged = appendHint(merged, `${verb} if ${discriminator.key} = ${[...new Set(labels)].join(" | ")}`);
|
|
464
|
-
}
|
|
465
|
-
properties[key] = merged;
|
|
466
|
-
}
|
|
467
|
-
const result = {};
|
|
468
|
-
for (const [key, value] of Object.entries(node)) {
|
|
469
|
-
if (key !== "oneOf" && key !== "anyOf" && key !== "properties" && key !== "required") {
|
|
470
|
-
result[key] = value;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
result.type = "object";
|
|
474
|
-
result.properties = properties;
|
|
475
|
-
result.required = required;
|
|
476
|
-
result.additionalProperties = variants.every((variant) => variant.additionalProperties === false) ? false : {};
|
|
477
|
-
return result;
|
|
478
|
-
}
|
|
479
|
-
function flattenToolJsonSchema(schema) {
|
|
480
|
-
const result = rebuild(schema);
|
|
481
|
-
return isRecord(result) ? result : {};
|
|
482
|
-
}
|
|
483
|
-
function freezeToolJsonSchema(schema) {
|
|
484
|
-
for (const value of Object.values(schema))
|
|
485
|
-
freezeJsonValue(value);
|
|
486
|
-
return Object.freeze(schema);
|
|
487
|
-
}
|
|
488
|
-
function freezeJsonValue(value) {
|
|
489
|
-
if (Array.isArray(value)) {
|
|
490
|
-
for (const item of value)
|
|
491
|
-
freezeJsonValue(item);
|
|
492
|
-
Object.freeze(value);
|
|
493
|
-
} else if (isRecord(value)) {
|
|
494
|
-
freezeToolJsonSchema(value);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
116
|
// src/tools/mount.ts
|
|
499
|
-
import { z as z4 } from "zod";
|
|
500
|
-
|
|
501
|
-
// src/tools/schema.ts
|
|
502
117
|
import { z as z2 } from "zod";
|
|
503
|
-
function objectShapeKeys(schema) {
|
|
504
|
-
return schema instanceof z2.ZodObject ? Object.keys(schema.shape) : [];
|
|
505
|
-
}
|
|
506
|
-
function keyPolicyOf(schema) {
|
|
507
|
-
return schema.def.catchall;
|
|
508
|
-
}
|
|
509
|
-
function rebuildObject(source, shape) {
|
|
510
|
-
return withKeyPolicy(z2.object(shape), keyPolicyOf(source));
|
|
511
|
-
}
|
|
512
|
-
function withKeyPolicy(object, policy) {
|
|
513
|
-
if (policy === undefined)
|
|
514
|
-
return object;
|
|
515
|
-
return object.catchall(representable(policy));
|
|
516
|
-
}
|
|
517
|
-
function representable(policy) {
|
|
518
|
-
if (policy instanceof z2.ZodNever || policy instanceof z2.ZodUnknown)
|
|
519
|
-
return policy;
|
|
520
|
-
if (!(policy instanceof z2.ZodType))
|
|
521
|
-
return z2.unknown();
|
|
522
|
-
try {
|
|
523
|
-
toJsonSchema(policy, "input");
|
|
524
|
-
return policy;
|
|
525
|
-
} catch {
|
|
526
|
-
return z2.unknown();
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
function mergeSchemas(paramsSchema, inputSchema) {
|
|
530
|
-
if (paramsSchema && !(paramsSchema instanceof z2.ZodObject)) {
|
|
531
|
-
throw new Error("Tool params schema must be a z.object()");
|
|
532
|
-
}
|
|
533
|
-
const paramsObject = paramsSchema instanceof z2.ZodObject ? paramsSchema : undefined;
|
|
534
|
-
if (!inputSchema) {
|
|
535
|
-
return paramsObject ?? z2.object({});
|
|
536
|
-
}
|
|
537
|
-
if (inputSchema instanceof z2.ZodObject) {
|
|
538
|
-
if (paramsObject) {
|
|
539
|
-
const conflicts = Object.keys(paramsObject.shape).filter((key) => (key in inputSchema.shape));
|
|
540
|
-
if (conflicts.length > 0) {
|
|
541
|
-
throw new Error(`Schema merge conflict: ${conflicts.join(", ")} appear in both params and input`);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return rebuildObject(inputSchema, {
|
|
545
|
-
...paramsObject?.shape ?? {},
|
|
546
|
-
...inputSchema.shape
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
return paramsObject ? z2.intersection(paramsObject, inputSchema) : inputSchema;
|
|
550
|
-
}
|
|
551
118
|
|
|
552
119
|
// src/tools/execute.ts
|
|
553
120
|
var normalizedToolErrors = new WeakMap;
|
|
@@ -769,239 +336,44 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
769
336
|
}
|
|
770
337
|
}
|
|
771
338
|
|
|
772
|
-
// src/tools/names.ts
|
|
773
|
-
var SINGULAR_EXCEPTIONS = new Set([
|
|
774
|
-
"analytics",
|
|
775
|
-
"status",
|
|
776
|
-
"stats",
|
|
777
|
-
"settings",
|
|
778
|
-
"media",
|
|
779
|
-
"progress",
|
|
780
|
-
"news"
|
|
781
|
-
]);
|
|
782
|
-
var TOOL_NAME_RE = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
783
|
-
function normalizeService(value) {
|
|
784
|
-
return value.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
785
|
-
}
|
|
786
|
-
function normalizeMethod(value) {
|
|
787
|
-
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
788
|
-
}
|
|
789
|
-
function singularize(name) {
|
|
790
|
-
if (SINGULAR_EXCEPTIONS.has(name))
|
|
791
|
-
return name;
|
|
792
|
-
if (name.endsWith("ies"))
|
|
793
|
-
return `${name.slice(0, -3)}y`;
|
|
794
|
-
if (name.endsWith("s") && !name.endsWith("ss"))
|
|
795
|
-
return name.slice(0, -1);
|
|
796
|
-
return name;
|
|
797
|
-
}
|
|
798
|
-
function singularizeTail(name) {
|
|
799
|
-
const cut = name.lastIndexOf("_");
|
|
800
|
-
if (cut === -1)
|
|
801
|
-
return singularize(name);
|
|
802
|
-
return `${name.slice(0, cut + 1)}${singularize(name.slice(cut + 1))}`;
|
|
803
|
-
}
|
|
804
|
-
function hasUsableChars(value) {
|
|
805
|
-
return /[a-zA-Z0-9]/.test(value);
|
|
806
|
-
}
|
|
807
|
-
function assertToolName(name, serviceName, key) {
|
|
808
|
-
const where = `service "${serviceName}", method "${key}"`;
|
|
809
|
-
if (!TOOL_NAME_RE.test(name)) {
|
|
810
|
-
const why = name.length > 64 ? `is ${name.length} characters (max 64) — set an explicit \`toolName\`` : "must match [a-zA-Z0-9_-]";
|
|
811
|
-
throw new Error(`Tool name "${name}" (${where}) ${why}`);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
function toToolName(serviceName, methodName) {
|
|
815
|
-
const normalized = normalizeService(serviceName);
|
|
816
|
-
const singular = singularizeTail(normalized);
|
|
817
|
-
const method = normalizeMethod(methodName);
|
|
818
|
-
if (method === "list")
|
|
819
|
-
return `list_${normalized}`;
|
|
820
|
-
if (method === "get")
|
|
821
|
-
return `get_${singular}`;
|
|
822
|
-
if (method === "create")
|
|
823
|
-
return `create_${singular}`;
|
|
824
|
-
if (method === "update")
|
|
825
|
-
return `update_${singular}`;
|
|
826
|
-
if (method === "delete")
|
|
827
|
-
return `delete_${singular}`;
|
|
828
|
-
const snake = method.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
829
|
-
return `${snake}_${singular}`.replace(/^_/, "");
|
|
830
|
-
}
|
|
831
|
-
function assertUniqueToolName(name, taken, surface) {
|
|
832
|
-
if (taken) {
|
|
833
|
-
throw new Error(`Duplicate ${surface} "${name}" across mounted operations`);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
// src/tools/presentation.ts
|
|
838
|
-
import { z as z3 } from "zod";
|
|
839
|
-
function withoutDialect(schema) {
|
|
840
|
-
const result = {};
|
|
841
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
842
|
-
if (key !== "$schema")
|
|
843
|
-
result[key] = value;
|
|
844
|
-
}
|
|
845
|
-
return result;
|
|
846
|
-
}
|
|
847
|
-
function hasLocalReference(value) {
|
|
848
|
-
if (Array.isArray(value))
|
|
849
|
-
return value.some(hasLocalReference);
|
|
850
|
-
if (!isRecord(value))
|
|
851
|
-
return false;
|
|
852
|
-
if (typeof value.$ref === "string" && value.$ref.startsWith("#"))
|
|
853
|
-
return true;
|
|
854
|
-
return Object.values(value).some(hasLocalReference);
|
|
855
|
-
}
|
|
856
|
-
function rewriteLocalReferences(value, namespace) {
|
|
857
|
-
if (Array.isArray(value)) {
|
|
858
|
-
return value.map((child) => rewriteLocalReferences(child, namespace));
|
|
859
|
-
}
|
|
860
|
-
if (!isRecord(value))
|
|
861
|
-
return value;
|
|
862
|
-
const rewritten = {};
|
|
863
|
-
for (const [key, child] of Object.entries(value)) {
|
|
864
|
-
rewritten[key] = key === "$ref" && typeof child === "string" && child.startsWith("#") ? `#/definitions/${namespace}${child.slice(1)}` : rewriteLocalReferences(child, namespace);
|
|
865
|
-
}
|
|
866
|
-
return rewritten;
|
|
867
|
-
}
|
|
868
|
-
function namespaceLocalReferences(schema, namespace) {
|
|
869
|
-
if (!schema || !hasLocalReference(schema))
|
|
870
|
-
return schema;
|
|
871
|
-
const rewritten = rewriteLocalReferences(schema, namespace);
|
|
872
|
-
if (!isRecord(rewritten))
|
|
873
|
-
return schema;
|
|
874
|
-
return { ...rewritten, definitions: { [namespace]: rewritten } };
|
|
875
|
-
}
|
|
876
|
-
function objectProperties(schema) {
|
|
877
|
-
return schema.type === "object" && isRecord(schema.properties) ? schema.properties : null;
|
|
878
|
-
}
|
|
879
|
-
function requiredKeys2(schema) {
|
|
880
|
-
return Array.isArray(schema.required) ? schema.required.filter((key) => typeof key === "string") : [];
|
|
881
|
-
}
|
|
882
|
-
function mergeObjectSchemas(left, right, rightOwnsUnknownKeys) {
|
|
883
|
-
const leftProperties = objectProperties(left);
|
|
884
|
-
const rightProperties = objectProperties(right);
|
|
885
|
-
if (!leftProperties || !rightProperties)
|
|
886
|
-
return { allOf: [left, right] };
|
|
887
|
-
const conflicts = Object.keys(leftProperties).filter((key) => (key in rightProperties));
|
|
888
|
-
if (conflicts.length > 0) {
|
|
889
|
-
throw new Error(`Schema merge conflict: ${conflicts.join(", ")} appear in both params and input`);
|
|
890
|
-
}
|
|
891
|
-
const merged = {
|
|
892
|
-
type: "object",
|
|
893
|
-
properties: { ...leftProperties, ...rightProperties },
|
|
894
|
-
required: [...new Set([...requiredKeys2(left), ...requiredKeys2(right)])]
|
|
895
|
-
};
|
|
896
|
-
const leftDefinitions = isRecord(left.definitions) ? left.definitions : {};
|
|
897
|
-
const rightDefinitions = isRecord(right.definitions) ? right.definitions : {};
|
|
898
|
-
const definitions = { ...leftDefinitions, ...rightDefinitions };
|
|
899
|
-
if (Object.keys(definitions).length > 0)
|
|
900
|
-
merged.definitions = definitions;
|
|
901
|
-
const unknownKeys = rightOwnsUnknownKeys ? right.additionalProperties : left.additionalProperties;
|
|
902
|
-
if (unknownKeys !== undefined)
|
|
903
|
-
merged.additionalProperties = unknownKeys;
|
|
904
|
-
return merged;
|
|
905
|
-
}
|
|
906
|
-
function schemaFromZod(schema, unrepresentable, flatten) {
|
|
907
|
-
if (!schema)
|
|
908
|
-
return;
|
|
909
|
-
const json = toJsonSchema(schema, "input", unrepresentable, "draft-07");
|
|
910
|
-
return flatten ? flattenToolJsonSchema(json) : json;
|
|
911
|
-
}
|
|
912
|
-
function buildToolPresentationSchema(config) {
|
|
913
|
-
const unrepresentable = config.unrepresentable ?? "any";
|
|
914
|
-
const flatten = config.flattenUnionInput ?? false;
|
|
915
|
-
const params = namespaceLocalReferences(schemaFromZod(config.paramsSchema, unrepresentable, flatten), "params");
|
|
916
|
-
const input = namespaceLocalReferences(schemaFromZod(config.inputSchema, unrepresentable, flatten), "input");
|
|
917
|
-
let presentation;
|
|
918
|
-
if (params && input) {
|
|
919
|
-
presentation = mergeObjectSchemas(withoutDialect(params), withoutDialect(input), true);
|
|
920
|
-
} else if (input) {
|
|
921
|
-
presentation = input;
|
|
922
|
-
} else if (params) {
|
|
923
|
-
presentation = params;
|
|
924
|
-
} else {
|
|
925
|
-
presentation = {
|
|
926
|
-
$schema: "http://json-schema.org/draft-07/schema#",
|
|
927
|
-
type: "object",
|
|
928
|
-
properties: {}
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
if (config.extendSchema) {
|
|
932
|
-
const extra = namespaceLocalReferences(schemaFromZod(z3.object(config.extendSchema), unrepresentable, flatten), "extend");
|
|
933
|
-
if (extra) {
|
|
934
|
-
presentation = mergeObjectSchemas(withoutDialect(extra), withoutDialect(presentation), true);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (presentation.$schema === undefined) {
|
|
938
|
-
presentation = {
|
|
939
|
-
$schema: "http://json-schema.org/draft-07/schema#",
|
|
940
|
-
...presentation
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
return freezeToolJsonSchema(presentation);
|
|
944
|
-
}
|
|
945
|
-
function isObjectPresentationSchema(schema) {
|
|
946
|
-
return schema.type === "object" && isRecord(schema.properties);
|
|
947
|
-
}
|
|
948
|
-
function presentationMetadata(schema) {
|
|
949
|
-
return withoutDialect(schema);
|
|
950
|
-
}
|
|
951
|
-
|
|
952
339
|
// src/tools/mount.ts
|
|
953
340
|
function applyExtend(base, extra) {
|
|
954
|
-
if (base instanceof
|
|
955
|
-
|
|
956
|
-
if (conflicts.length > 0) {
|
|
957
|
-
throw new Error(`Tool extend conflict: ${conflicts.join(", ")} already declared by the contract`);
|
|
958
|
-
}
|
|
341
|
+
if (base instanceof z2.ZodObject) {
|
|
342
|
+
assertToolExtensionCompatible(base, extra);
|
|
959
343
|
return rebuildObject(base, { ...extra, ...base.shape });
|
|
960
344
|
}
|
|
961
|
-
return
|
|
345
|
+
return z2.intersection(z2.object(extra), base);
|
|
346
|
+
}
|
|
347
|
+
function contractToolMountable(projected, extend) {
|
|
348
|
+
const method = projected.source;
|
|
349
|
+
const baseArgumentSchema = mergeSchemas(method.paramsSchema, method.inputSchema);
|
|
350
|
+
const argumentSchema = projected.shouldExtend && extend ? applyExtend(baseArgumentSchema, extend.schema) : baseArgumentSchema;
|
|
351
|
+
return {
|
|
352
|
+
method,
|
|
353
|
+
name: projected.name,
|
|
354
|
+
argumentSchema,
|
|
355
|
+
presentationSchema: projected.presentationSchema,
|
|
356
|
+
shouldExtend: projected.shouldExtend
|
|
357
|
+
};
|
|
962
358
|
}
|
|
963
359
|
function collectTools(service, transport, config = {}) {
|
|
964
360
|
const { extend, flattenUnionInput = false, assertNames = true } = config;
|
|
965
361
|
const tools = [];
|
|
966
|
-
for (const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
if (method.multipart)
|
|
974
|
-
continue;
|
|
975
|
-
if (method.rawBody)
|
|
976
|
-
continue;
|
|
977
|
-
if (method.responseMeta)
|
|
978
|
-
continue;
|
|
979
|
-
if (method.rawResponse)
|
|
362
|
+
for (const projected of projectToolSurface({ services: [service] }, transport === "HTTP" ? "AGENT" : transport, {
|
|
363
|
+
extend,
|
|
364
|
+
flattenUnionInput,
|
|
365
|
+
assertNames,
|
|
366
|
+
assertUniqueNames: false
|
|
367
|
+
})) {
|
|
368
|
+
if (projected.kind !== "contract")
|
|
980
369
|
continue;
|
|
981
|
-
|
|
982
|
-
if (assertNames && transport !== "CLI") {
|
|
983
|
-
if (!method.toolName && !hasUsableChars(service.name)) {
|
|
984
|
-
throw new Error(`Service prefix "${service.name}" (method "${methodName}") has no characters usable in a tool name — set an explicit \`toolName\` or rename the prefix`);
|
|
985
|
-
}
|
|
986
|
-
assertToolName(name, service.name, methodName);
|
|
987
|
-
}
|
|
988
|
-
const shouldExtend = !!extend && (!extend.filter || extend.filter(service, method));
|
|
989
|
-
const baseArgumentSchema = mergeSchemas(method.paramsSchema, method.inputSchema);
|
|
990
|
-
const argumentSchema = shouldExtend && extend ? applyExtend(baseArgumentSchema, extend.schema) : baseArgumentSchema;
|
|
991
|
-
const presentationSchema = buildToolPresentationSchema({
|
|
992
|
-
paramsSchema: method.paramsSchema,
|
|
993
|
-
inputSchema: method.inputSchema,
|
|
994
|
-
extendSchema: shouldExtend ? extend?.schema : undefined,
|
|
995
|
-
flattenUnionInput,
|
|
996
|
-
unrepresentable: "any"
|
|
997
|
-
});
|
|
998
|
-
tools.push({ method, name, argumentSchema, presentationSchema, shouldExtend });
|
|
370
|
+
tools.push(contractToolMountable(projected, extend));
|
|
999
371
|
}
|
|
1000
372
|
return tools;
|
|
1001
373
|
}
|
|
1002
374
|
function createToolRunner(config) {
|
|
1003
375
|
const extension = config.extend ? {
|
|
1004
|
-
schema:
|
|
376
|
+
schema: z2.object(config.extend.schema),
|
|
1005
377
|
resolve: config.extend.resolve
|
|
1006
378
|
} : undefined;
|
|
1007
379
|
return async function runOneToolCall(tool, rawArgs, context) {
|
|
@@ -1075,14 +447,6 @@ function createRuntimeToolFactory(config) {
|
|
|
1075
447
|
}
|
|
1076
448
|
return { define };
|
|
1077
449
|
}
|
|
1078
|
-
function runtimeToolSupports(definition, transport) {
|
|
1079
|
-
if (definition.transports?.length === 0) {
|
|
1080
|
-
throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
|
|
1081
|
-
}
|
|
1082
|
-
if (!definition.transports)
|
|
1083
|
-
return transport !== "CLI";
|
|
1084
|
-
return definition.transports.includes(transport);
|
|
1085
|
-
}
|
|
1086
450
|
function runtimeToolIdentity(definition) {
|
|
1087
451
|
return {
|
|
1088
452
|
method: definition.identity.method,
|
|
@@ -1098,9 +462,7 @@ function runtimeToolIdentity(definition) {
|
|
|
1098
462
|
};
|
|
1099
463
|
}
|
|
1100
464
|
function runtimeToolMountable(definition, assertName = true) {
|
|
1101
|
-
|
|
1102
|
-
assertToolName(definition.name, definition.identity.serviceName, definition.identity.action);
|
|
1103
|
-
}
|
|
465
|
+
const projected = projectRuntimeTool(definition, assertName);
|
|
1104
466
|
const method = {
|
|
1105
467
|
...runtimeToolIdentity(definition),
|
|
1106
468
|
inputSchema: definition.input,
|
|
@@ -1109,18 +471,15 @@ function runtimeToolMountable(definition, assertName = true) {
|
|
|
1109
471
|
};
|
|
1110
472
|
return {
|
|
1111
473
|
method,
|
|
1112
|
-
name:
|
|
474
|
+
name: projected.name,
|
|
1113
475
|
argumentSchema: definition.input,
|
|
1114
|
-
presentationSchema:
|
|
1115
|
-
inputSchema: definition.input,
|
|
1116
|
-
unrepresentable: "any"
|
|
1117
|
-
}),
|
|
476
|
+
presentationSchema: projected.presentationSchema,
|
|
1118
477
|
shouldExtend: false
|
|
1119
478
|
};
|
|
1120
479
|
}
|
|
1121
480
|
|
|
1122
481
|
// src/tools/cli-args.ts
|
|
1123
|
-
import { z as
|
|
482
|
+
import { z as z3 } from "zod";
|
|
1124
483
|
var NUMERIC_VALUE = /^-(\d+\.?\d*|\.\d+)$/;
|
|
1125
484
|
var BOOL_OPTIONS = new Set(["json", "wait", "quiet", "dry-run", "help"]);
|
|
1126
485
|
var VALUE_OPTIONS = new Set(["wait-timeout", "output-dir"]);
|
|
@@ -1130,27 +489,27 @@ class CliArgumentError extends Error {
|
|
|
1130
489
|
name = "CliArgumentError";
|
|
1131
490
|
}
|
|
1132
491
|
function unwrap(field) {
|
|
1133
|
-
if (field instanceof
|
|
492
|
+
if (field instanceof z3.ZodOptional || field instanceof z3.ZodNullable || field instanceof z3.ZodDefault) {
|
|
1134
493
|
return unwrap(field.unwrap());
|
|
1135
494
|
}
|
|
1136
495
|
return field;
|
|
1137
496
|
}
|
|
1138
497
|
function classify(field) {
|
|
1139
|
-
if (field instanceof
|
|
498
|
+
if (field instanceof z3.ZodBoolean)
|
|
1140
499
|
return "boolean";
|
|
1141
|
-
if (field instanceof
|
|
500
|
+
if (field instanceof z3.ZodNumber)
|
|
1142
501
|
return "number";
|
|
1143
|
-
if (field instanceof
|
|
502
|
+
if (field instanceof z3.ZodBigInt)
|
|
1144
503
|
return "bigint";
|
|
1145
|
-
if (field instanceof
|
|
504
|
+
if (field instanceof z3.ZodDate)
|
|
1146
505
|
return "date";
|
|
1147
|
-
if (field instanceof
|
|
506
|
+
if (field instanceof z3.ZodEnum)
|
|
1148
507
|
return "enum";
|
|
1149
|
-
if (field instanceof
|
|
508
|
+
if (field instanceof z3.ZodArray)
|
|
1150
509
|
return "array";
|
|
1151
|
-
if (field instanceof
|
|
510
|
+
if (field instanceof z3.ZodObject)
|
|
1152
511
|
return "object";
|
|
1153
|
-
if (field instanceof
|
|
512
|
+
if (field instanceof z3.ZodString || field instanceof z3.ZodLiteral)
|
|
1154
513
|
return "string";
|
|
1155
514
|
return "other";
|
|
1156
515
|
}
|
|
@@ -1170,11 +529,11 @@ function mergeField(fields, name, info) {
|
|
|
1170
529
|
}
|
|
1171
530
|
function collectSchemaFields(schema, fields) {
|
|
1172
531
|
const base = unwrap(schema);
|
|
1173
|
-
if (base instanceof
|
|
532
|
+
if (base instanceof z3.ZodObject) {
|
|
1174
533
|
for (const [name, raw] of Object.entries(base.shape)) {
|
|
1175
534
|
const fieldBase = unwrap(raw);
|
|
1176
535
|
const kind = classify(fieldBase);
|
|
1177
|
-
if (kind === "array" && fieldBase instanceof
|
|
536
|
+
if (kind === "array" && fieldBase instanceof z3.ZodArray) {
|
|
1178
537
|
mergeField(fields, name, { kind, elementKind: classify(unwrap(fieldBase.element)) });
|
|
1179
538
|
} else {
|
|
1180
539
|
mergeField(fields, name, { kind });
|
|
@@ -1182,12 +541,12 @@ function collectSchemaFields(schema, fields) {
|
|
|
1182
541
|
}
|
|
1183
542
|
return;
|
|
1184
543
|
}
|
|
1185
|
-
if (base instanceof
|
|
544
|
+
if (base instanceof z3.ZodUnion) {
|
|
1186
545
|
for (const option of base.def.options)
|
|
1187
546
|
collectSchemaFields(option, fields);
|
|
1188
547
|
return;
|
|
1189
548
|
}
|
|
1190
|
-
if (base instanceof
|
|
549
|
+
if (base instanceof z3.ZodIntersection) {
|
|
1191
550
|
collectSchemaFields(base.def.left, fields);
|
|
1192
551
|
collectSchemaFields(base.def.right, fields);
|
|
1193
552
|
return;
|
|
@@ -1494,19 +853,21 @@ var DEFAULT_EXIT_CODES = {
|
|
|
1494
853
|
CONFLICT: 5,
|
|
1495
854
|
RATE_LIMITED: 6,
|
|
1496
855
|
TIMEOUT: 7,
|
|
856
|
+
WAIT_FAILED: 1,
|
|
1497
857
|
INTERNAL_SERVER_ERROR: 1
|
|
1498
858
|
};
|
|
1499
859
|
function emitResult(result, writers, opts) {
|
|
1500
860
|
if (result.ok) {
|
|
1501
861
|
if (result.data === undefined)
|
|
1502
862
|
return 0;
|
|
1503
|
-
const
|
|
1504
|
-
writers.stdout(`${
|
|
863
|
+
const text2 = opts.json ? JSON.stringify(result.data) : JSON.stringify(result.data, null, 2);
|
|
864
|
+
writers.stdout(`${text2}
|
|
1505
865
|
`);
|
|
1506
866
|
return 0;
|
|
1507
867
|
}
|
|
1508
868
|
const error = formatToolError(result, opts.toolName, opts.errorHint);
|
|
1509
|
-
|
|
869
|
+
const text = opts.json ? JSON.stringify(error) : JSON.stringify(error, null, 2);
|
|
870
|
+
writers.stderr(`${text}
|
|
1510
871
|
`);
|
|
1511
872
|
const codes = opts.exitCodes ?? DEFAULT_EXIT_CODES;
|
|
1512
873
|
return codes[result.code] ?? 1;
|
|
@@ -1515,6 +876,13 @@ function emitResult(result, writers, opts) {
|
|
|
1515
876
|
// src/tools/wait-core.ts
|
|
1516
877
|
var DEFAULT_BACKOFF = [2, 3, 5, 5, 8, 10];
|
|
1517
878
|
var DEFAULT_TIMEOUT = 600;
|
|
879
|
+
|
|
880
|
+
class WaitTimeoutError extends Error {
|
|
881
|
+
constructor() {
|
|
882
|
+
super("wait deadline elapsed before the first snapshot");
|
|
883
|
+
this.name = "WaitTimeoutError";
|
|
884
|
+
}
|
|
885
|
+
}
|
|
1518
886
|
var defaultSleep = (ms, signal) => {
|
|
1519
887
|
signal?.throwIfAborted();
|
|
1520
888
|
return new Promise((resolve, reject) => {
|
|
@@ -1544,52 +912,104 @@ async function pollUntil(params) {
|
|
|
1544
912
|
const lastBackoff = backoff[backoff.length - 1] ?? 5;
|
|
1545
913
|
const timeoutSec = params.timeoutSec ?? DEFAULT_TIMEOUT;
|
|
1546
914
|
const sleep = params.sleepFn ?? defaultSleep;
|
|
1547
|
-
const
|
|
915
|
+
const now = params.nowFn ?? (() => performance.now());
|
|
916
|
+
const startedAt = now();
|
|
917
|
+
const deadline = startedAt + timeoutSec * 1000;
|
|
918
|
+
let last;
|
|
1548
919
|
for (let attempt = 0;; attempt++) {
|
|
1549
920
|
params.signal?.throwIfAborted();
|
|
1550
|
-
const
|
|
921
|
+
const remainingBeforePoll = Math.max(0, deadline - now());
|
|
922
|
+
if (remainingBeforePoll === 0 && last) {
|
|
923
|
+
return { state: last.state, timedOut: true };
|
|
924
|
+
}
|
|
925
|
+
const deadlineController = new AbortController;
|
|
926
|
+
const onCallerAbort = () => deadlineController.abort(params.signal?.reason);
|
|
927
|
+
params.signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
928
|
+
const deadlineTimer = setTimeout(() => deadlineController.abort(new WaitTimeoutError), remainingBeforePoll);
|
|
929
|
+
deadlineTimer.unref?.();
|
|
930
|
+
let state;
|
|
931
|
+
try {
|
|
932
|
+
state = await params.poll(deadlineController.signal);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
if (params.signal?.aborted)
|
|
935
|
+
throw params.signal.reason ?? error;
|
|
936
|
+
if (deadlineController.signal.aborted && now() >= deadline) {
|
|
937
|
+
if (last)
|
|
938
|
+
return { state: last.state, timedOut: true };
|
|
939
|
+
throw new WaitTimeoutError;
|
|
940
|
+
}
|
|
941
|
+
throw error;
|
|
942
|
+
} finally {
|
|
943
|
+
clearTimeout(deadlineTimer);
|
|
944
|
+
params.signal?.removeEventListener("abort", onCallerAbort);
|
|
945
|
+
}
|
|
1551
946
|
params.signal?.throwIfAborted();
|
|
947
|
+
last = { state };
|
|
1552
948
|
if (params.done(state))
|
|
1553
949
|
return { state, timedOut: false };
|
|
1554
|
-
const
|
|
950
|
+
const elapsedMs = now() - startedAt;
|
|
951
|
+
const elapsedSec = elapsedMs / 1000;
|
|
1555
952
|
params.onTick?.(attempt + 1, elapsedSec);
|
|
1556
953
|
if (elapsedSec >= timeoutSec)
|
|
1557
954
|
return { state, timedOut: true };
|
|
1558
955
|
const waitSec = backoff[Math.min(attempt, backoff.length - 1)] ?? lastBackoff;
|
|
1559
|
-
await sleep(waitSec * 1000, params.signal);
|
|
956
|
+
await sleep(Math.min(waitSec * 1000, Math.max(0, deadline - now())), params.signal);
|
|
1560
957
|
}
|
|
1561
958
|
}
|
|
1562
959
|
function runWaitOperation(params) {
|
|
1563
960
|
return pollUntil({
|
|
1564
|
-
poll: () => params.poll(params.input,
|
|
961
|
+
poll: (signal) => params.poll(params.input, signal),
|
|
1565
962
|
done: params.done,
|
|
1566
963
|
backoff: params.backoff,
|
|
1567
964
|
timeoutSec: params.timeoutSec,
|
|
1568
965
|
sleepFn: params.sleepFn,
|
|
1569
966
|
onTick: params.onTick,
|
|
1570
|
-
signal: params.signal
|
|
967
|
+
signal: params.signal,
|
|
968
|
+
nowFn: params.nowFn
|
|
1571
969
|
});
|
|
1572
970
|
}
|
|
1573
971
|
|
|
1574
972
|
// src/tools/cli-wait.ts
|
|
1575
973
|
var DEFAULT_TIMEOUT2 = 600;
|
|
974
|
+
function waitFailure(tool, result) {
|
|
975
|
+
return {
|
|
976
|
+
ok: false,
|
|
977
|
+
code: "WAIT_FAILED",
|
|
978
|
+
details: {
|
|
979
|
+
message: `"${tool}" reached a terminal failed state`,
|
|
980
|
+
result
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
1576
984
|
async function pollUntilDone(params) {
|
|
1577
985
|
const { initial, wait, call } = params;
|
|
1578
986
|
if (!initial.ok)
|
|
1579
987
|
return initial;
|
|
988
|
+
if (wait.failed?.(initial.data))
|
|
989
|
+
return waitFailure(wait.tool, initial.data);
|
|
1580
990
|
if (wait.done(initial.data))
|
|
1581
991
|
return initial;
|
|
1582
992
|
const pollArgs = wait.poll(initial.data);
|
|
1583
993
|
if (!pollArgs)
|
|
1584
994
|
return initial;
|
|
1585
995
|
const timeoutSec = params.timeoutSec ?? wait.timeout ?? DEFAULT_TIMEOUT2;
|
|
996
|
+
let failedState;
|
|
1586
997
|
const { state, timedOut } = await pollUntil({
|
|
1587
998
|
poll: () => call(wait.tool, pollArgs),
|
|
1588
|
-
done: (result) =>
|
|
999
|
+
done: (result) => {
|
|
1000
|
+
if (!result.ok)
|
|
1001
|
+
return true;
|
|
1002
|
+
if (wait.failed?.(result.data)) {
|
|
1003
|
+
failedState = result;
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
return wait.done(result.data);
|
|
1007
|
+
},
|
|
1589
1008
|
backoff: wait.backoff,
|
|
1590
1009
|
timeoutSec,
|
|
1591
1010
|
sleepFn: params.sleepFn,
|
|
1592
|
-
onTick: params.onTick
|
|
1011
|
+
onTick: params.onTick,
|
|
1012
|
+
signal: params.signal
|
|
1593
1013
|
});
|
|
1594
1014
|
if (timedOut) {
|
|
1595
1015
|
return {
|
|
@@ -1598,6 +1018,8 @@ async function pollUntilDone(params) {
|
|
|
1598
1018
|
details: { message: `Timed out after ${timeoutSec}s waiting for "${wait.tool}"` }
|
|
1599
1019
|
};
|
|
1600
1020
|
}
|
|
1021
|
+
if (state === failedState && state.ok)
|
|
1022
|
+
return waitFailure(wait.tool, state.data);
|
|
1601
1023
|
return state;
|
|
1602
1024
|
}
|
|
1603
1025
|
|
|
@@ -1913,13 +1335,6 @@ async function writeDownload(root, target, data) {
|
|
|
1913
1335
|
}
|
|
1914
1336
|
|
|
1915
1337
|
// src/tools/surface.ts
|
|
1916
|
-
function duplicateLabel(transport) {
|
|
1917
|
-
if (transport === "MCP")
|
|
1918
|
-
return "MCP tool name";
|
|
1919
|
-
if (transport === "AGENT")
|
|
1920
|
-
return "agent tool name";
|
|
1921
|
-
return "CLI command";
|
|
1922
|
-
}
|
|
1923
1338
|
function collectToolSurface({
|
|
1924
1339
|
surface,
|
|
1925
1340
|
transport,
|
|
@@ -1927,41 +1342,41 @@ function collectToolSurface({
|
|
|
1927
1342
|
...collectConfig
|
|
1928
1343
|
}) {
|
|
1929
1344
|
const entries = [];
|
|
1930
|
-
const names = new Set;
|
|
1931
1345
|
const append = (entry) => {
|
|
1932
|
-
if (assertUniqueNames) {
|
|
1933
|
-
assertUniqueToolName(entry.mountable.name, names.has(entry.mountable.name), duplicateLabel(transport));
|
|
1934
|
-
}
|
|
1935
|
-
names.add(entry.mountable.name);
|
|
1936
1346
|
entries.push(entry);
|
|
1937
1347
|
};
|
|
1938
|
-
for (const
|
|
1939
|
-
|
|
1348
|
+
for (const projected of projectToolSurface(surface, transport, {
|
|
1349
|
+
extend: collectConfig.extend,
|
|
1350
|
+
flattenUnionInput: collectConfig.flattenUnionInput,
|
|
1351
|
+
assertNames: collectConfig.assertNames,
|
|
1352
|
+
assertUniqueNames
|
|
1353
|
+
})) {
|
|
1354
|
+
if (projected.kind === "contract") {
|
|
1355
|
+
const mountable = contractToolMountable(projected, collectConfig.extend);
|
|
1940
1356
|
append({
|
|
1941
1357
|
kind: "contract",
|
|
1942
|
-
service:
|
|
1358
|
+
service: projected.serviceName,
|
|
1943
1359
|
action: mountable.method.key,
|
|
1944
|
-
mountable
|
|
1360
|
+
mountable,
|
|
1361
|
+
projection: projected
|
|
1362
|
+
});
|
|
1363
|
+
} else {
|
|
1364
|
+
append({
|
|
1365
|
+
kind: "runtime",
|
|
1366
|
+
service: projected.serviceName,
|
|
1367
|
+
action: projected.action,
|
|
1368
|
+
mountable: runtimeToolMountable(projected.source, false),
|
|
1369
|
+
definition: projected.source,
|
|
1370
|
+
projection: projected
|
|
1945
1371
|
});
|
|
1946
1372
|
}
|
|
1947
1373
|
}
|
|
1948
|
-
for (const definition of surface.runtimeTools ?? []) {
|
|
1949
|
-
if (!runtimeToolSupports(definition, transport))
|
|
1950
|
-
continue;
|
|
1951
|
-
append({
|
|
1952
|
-
kind: "runtime",
|
|
1953
|
-
service: definition.identity.serviceName,
|
|
1954
|
-
action: definition.identity.action,
|
|
1955
|
-
mountable: runtimeToolMountable(definition, collectConfig.assertNames),
|
|
1956
|
-
definition
|
|
1957
|
-
});
|
|
1958
|
-
}
|
|
1959
1374
|
return entries;
|
|
1960
1375
|
}
|
|
1961
1376
|
|
|
1962
1377
|
// src/tools/cli.ts
|
|
1963
1378
|
var GLOBAL_OPTIONS = [
|
|
1964
|
-
["--json", "Emit
|
|
1379
|
+
["--json", "Emit compact success/error JSON records (for scripts)"],
|
|
1965
1380
|
["--wait", "Block-poll an async result to a terminal state"],
|
|
1966
1381
|
["--wait-timeout <s>", "Override the --wait timeout in seconds"],
|
|
1967
1382
|
["--output-dir <dir>", "Download result media into a directory"],
|
|
@@ -2058,15 +1473,41 @@ function renderTopHelp(name, version, commands) {
|
|
|
2058
1473
|
`;
|
|
2059
1474
|
}
|
|
2060
1475
|
function renderCommandHelp(name, command, descriptor) {
|
|
2061
|
-
const lines = [descriptor.description, "", `Usage: ${name} ${command} [args] [--flags]`, ""];
|
|
2062
1476
|
const fields = jsonSchemaFields(descriptor.presentationSchema);
|
|
1477
|
+
const fieldsByName = new Map(fields.map((field) => [field.name, field]));
|
|
1478
|
+
const positionals = [];
|
|
1479
|
+
for (const [fieldName, info] of describeSchemaFields(descriptor.argumentSchema)) {
|
|
1480
|
+
if (info.kind === "boolean")
|
|
1481
|
+
continue;
|
|
1482
|
+
const field = fieldsByName.get(fieldName);
|
|
1483
|
+
if (field)
|
|
1484
|
+
positionals.push(field);
|
|
1485
|
+
}
|
|
1486
|
+
const positionalSyntax = new Map(positionals.map((field) => [
|
|
1487
|
+
field.name,
|
|
1488
|
+
field.required ? `<${field.name}>` : `[${field.name}]`
|
|
1489
|
+
]));
|
|
1490
|
+
const usage = [
|
|
1491
|
+
`Usage: ${name} ${command}`,
|
|
1492
|
+
...positionals.map((field) => positionalSyntax.get(field.name) ?? field.name),
|
|
1493
|
+
"[--flags]"
|
|
1494
|
+
].join(" ");
|
|
1495
|
+
const lines = [descriptor.description, "", usage, ""];
|
|
2063
1496
|
if (fields.length > 0) {
|
|
2064
1497
|
lines.push("Arguments:");
|
|
2065
|
-
const
|
|
1498
|
+
const labels = new Map(fields.map((field) => {
|
|
1499
|
+
const positional = positionalSyntax.get(field.name);
|
|
1500
|
+
return [
|
|
1501
|
+
field.name,
|
|
1502
|
+
positional ? `${positional} | --${field.name}` : `--${field.name}`
|
|
1503
|
+
];
|
|
1504
|
+
}));
|
|
1505
|
+
const width = Math.max(...fields.map((field) => labels.get(field.name)?.length ?? 0));
|
|
2066
1506
|
for (const f of fields) {
|
|
2067
1507
|
const req = f.required ? " (required)" : "";
|
|
2068
1508
|
const desc = f.description ? ` — ${f.description}` : "";
|
|
2069
|
-
|
|
1509
|
+
const label = labels.get(f.name) ?? `--${f.name}`;
|
|
1510
|
+
lines.push(` ${padRight(label, width)} <${typeLabel(f.schema)}>${req}${desc}`);
|
|
2070
1511
|
}
|
|
2071
1512
|
lines.push("");
|
|
2072
1513
|
}
|
|
@@ -2302,7 +1743,7 @@ async function createCli(config) {
|
|
|
2302
1743
|
}
|
|
2303
1744
|
const runTool = createToolRunner({
|
|
2304
1745
|
source: "cli",
|
|
2305
|
-
context: config.context?.(managed.auth),
|
|
1746
|
+
context: { ...config.context?.(managed.auth), signal: config.signal },
|
|
2306
1747
|
hooks: config.hooks,
|
|
2307
1748
|
lifecycle: config.lifecycle,
|
|
2308
1749
|
errorHint: config.errorHint,
|
|
@@ -2332,7 +1773,8 @@ async function createCli(config) {
|
|
|
2332
1773
|
},
|
|
2333
1774
|
timeoutSec: options.waitTimeout,
|
|
2334
1775
|
onTick: options.quiet ? undefined : (attempt) => stderr(`waiting… (poll ${attempt})
|
|
2335
|
-
`)
|
|
1776
|
+
`),
|
|
1777
|
+
signal: config.signal
|
|
2336
1778
|
});
|
|
2337
1779
|
}
|
|
2338
1780
|
let downloadsOk = true;
|
|
@@ -2348,4 +1790,4 @@ async function createCli(config) {
|
|
|
2348
1790
|
return exit(downloadsOk ? exitCode : 1);
|
|
2349
1791
|
}
|
|
2350
1792
|
|
|
2351
|
-
export { coerceJsonArgs, toolResultFromError, toolErrorFromResult, executeToolMethod,
|
|
1793
|
+
export { coerceJsonArgs, toolResultFromError, toolErrorFromResult, executeToolMethod, collectTools, createToolRunner, formatToolError, defineRuntimeTool, createRuntimeToolFactory, collectToolSurface, WaitTimeoutError, runWaitOperation, fetchGuarded, fetchPinnedDocument, readCapped, parseCliArgs, defineCliCommand, DEFAULT_EXIT_CODES, emitResult, pollUntilDone, createCli };
|