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.
Files changed (120) hide show
  1. package/README.md +4 -1
  2. package/dist/browser/socket-io.d.ts +25 -7
  3. package/dist/browser/socket-io.d.ts.map +1 -1
  4. package/dist/cli.js +14 -9
  5. package/dist/contract/errors.d.ts +7 -0
  6. package/dist/contract/errors.d.ts.map +1 -1
  7. package/dist/contract/file-ref.d.ts +13 -0
  8. package/dist/contract/file-ref.d.ts.map +1 -0
  9. package/dist/contract/index.d.ts +1 -0
  10. package/dist/contract/index.d.ts.map +1 -1
  11. package/dist/contract/index.js +21 -17
  12. package/dist/files/boundary.d.ts +55 -0
  13. package/dist/files/boundary.d.ts.map +1 -0
  14. package/dist/files.d.ts +3 -0
  15. package/dist/files.d.ts.map +1 -0
  16. package/dist/files.js +16 -0
  17. package/dist/index-1bx83sw4.js +4 -0
  18. package/dist/{index-pfqjb5xy.js → index-28cqssm3.js} +30 -5
  19. package/dist/index-557by2db.js +34 -0
  20. package/dist/index-5r13htq1.js +283 -0
  21. package/dist/index-6k1937bx.js +20 -0
  22. package/dist/index-6y759j86.js +1031 -0
  23. package/dist/{index-bmnhqtya.js → index-7eq9va6e.js} +1 -4
  24. package/dist/index-bfcpjw20.js +363 -0
  25. package/dist/{index-yxpe3phd.js → index-cby4ar3v.js} +1 -18
  26. package/dist/{index-7vb44hy8.js → index-escqg10p.js} +193 -751
  27. package/dist/{index-173tvqcp.js → index-j3dem06f.js} +9 -114
  28. package/dist/{index-gvha32jh.js → index-jcc611vh.js} +2 -2
  29. package/dist/{index-z7761jk2.js → index-n4nfa7gh.js} +16 -22
  30. package/dist/{index-e1es808r.js → index-qh4xevf5.js} +1 -1
  31. package/dist/index-sbdmyz75.js +18 -0
  32. package/dist/{index-b15rtha1.js → index-sm2tjx06.js} +98 -3
  33. package/dist/{index-yez419px.js → index-xy8fmh6w.js} +99 -7
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +188 -40
  37. package/dist/node.js +31 -27
  38. package/dist/observability/index.js +28 -25
  39. package/dist/react.js +2 -2
  40. package/dist/realtime/contract.d.ts +9 -0
  41. package/dist/realtime/contract.d.ts.map +1 -1
  42. package/dist/realtime/index.d.ts +2 -1
  43. package/dist/realtime/index.d.ts.map +1 -1
  44. package/dist/realtime/request.d.ts +21 -0
  45. package/dist/realtime/request.d.ts.map +1 -0
  46. package/dist/realtime/socket.d.ts +3 -0
  47. package/dist/realtime/socket.d.ts.map +1 -1
  48. package/dist/remote.d.ts +9 -0
  49. package/dist/remote.d.ts.map +1 -0
  50. package/dist/remote.js +79 -0
  51. package/dist/server/context-contribution.d.ts +18 -0
  52. package/dist/server/context-contribution.d.ts.map +1 -0
  53. package/dist/server/context.d.ts.map +1 -1
  54. package/dist/server/index.d.ts +2 -1
  55. package/dist/server/index.d.ts.map +1 -1
  56. package/dist/server/index.js +136 -88
  57. package/dist/server/lifecycle.d.ts +4 -0
  58. package/dist/server/lifecycle.d.ts.map +1 -0
  59. package/dist/server/middleware/auth.d.ts +64 -9
  60. package/dist/server/middleware/auth.d.ts.map +1 -1
  61. package/dist/server/types.d.ts +1 -1
  62. package/dist/server/types.d.ts.map +1 -1
  63. package/dist/testing/surface-conformance.d.ts +180 -0
  64. package/dist/testing/surface-conformance.d.ts.map +1 -0
  65. package/dist/testing/surface-manifest.d.ts +255 -0
  66. package/dist/testing/surface-manifest.d.ts.map +1 -0
  67. package/dist/testing.d.ts +2 -0
  68. package/dist/testing.d.ts.map +1 -1
  69. package/dist/testing.js +714 -3
  70. package/dist/tools/agent.d.ts.map +1 -1
  71. package/dist/tools/async-operation.d.ts +360 -0
  72. package/dist/tools/async-operation.d.ts.map +1 -0
  73. package/dist/tools/cli-args.d.ts +1 -1
  74. package/dist/tools/cli-args.d.ts.map +1 -1
  75. package/dist/tools/cli-format.d.ts +2 -2
  76. package/dist/tools/cli-format.d.ts.map +1 -1
  77. package/dist/tools/cli-wait.d.ts +11 -2
  78. package/dist/tools/cli-wait.d.ts.map +1 -1
  79. package/dist/tools/cli.d.ts +2 -0
  80. package/dist/tools/cli.d.ts.map +1 -1
  81. package/dist/tools/define-download-tool.d.ts +6 -5
  82. package/dist/tools/define-download-tool.d.ts.map +1 -1
  83. package/dist/tools/define-upload-tool.d.ts +3 -1
  84. package/dist/tools/define-upload-tool.d.ts.map +1 -1
  85. package/dist/tools/define-wait-tool.d.ts +1 -1
  86. package/dist/tools/define-wait-tool.d.ts.map +1 -1
  87. package/dist/tools/download-core.d.ts +5 -10
  88. package/dist/tools/download-core.d.ts.map +1 -1
  89. package/dist/tools/internal/surface-projector.d.ts +101 -0
  90. package/dist/tools/internal/surface-projector.d.ts.map +1 -0
  91. package/dist/tools/lifecycle.d.ts +4 -0
  92. package/dist/tools/lifecycle.d.ts.map +1 -0
  93. package/dist/tools/managed-file-error.d.ts +6 -0
  94. package/dist/tools/managed-file-error.d.ts.map +1 -0
  95. package/dist/tools/mcp-prepare.d.ts +3 -21
  96. package/dist/tools/mcp-prepare.d.ts.map +1 -1
  97. package/dist/tools/mcp-round-policy.d.ts +9 -0
  98. package/dist/tools/mcp-round-policy.d.ts.map +1 -0
  99. package/dist/tools/mcp-round.d.ts +0 -4
  100. package/dist/tools/mcp-round.d.ts.map +1 -1
  101. package/dist/tools/mount-download.d.ts +5 -4
  102. package/dist/tools/mount-download.d.ts.map +1 -1
  103. package/dist/tools/mount-upload.d.ts +4 -2
  104. package/dist/tools/mount-upload.d.ts.map +1 -1
  105. package/dist/tools/mount.d.ts +5 -9
  106. package/dist/tools/mount.d.ts.map +1 -1
  107. package/dist/tools/runtime-tool.d.ts.map +1 -1
  108. package/dist/tools/surface.d.ts +7 -0
  109. package/dist/tools/surface.d.ts.map +1 -1
  110. package/dist/tools/upload-core.d.ts +3 -2
  111. package/dist/tools/upload-core.d.ts.map +1 -1
  112. package/dist/tools/view-file.d.ts +4 -3
  113. package/dist/tools/view-file.d.ts.map +1 -1
  114. package/dist/tools/wait-core.d.ts +6 -1
  115. package/dist/tools/wait-core.d.ts.map +1 -1
  116. package/dist/tools.d.ts +3 -2
  117. package/dist/tools.d.ts.map +1 -1
  118. package/dist/tools.js +705 -599
  119. package/llms-full.txt +727 -34
  120. package/package.json +10 -2
@@ -1,23 +1,36 @@
1
1
  import {
2
- isWithinDir,
3
- jsonSchemaFields,
4
- toJsonSchema
5
- } from "./index-yxpe3phd.js";
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-173tvqcp.js";
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-bmnhqtya.js";
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 z4.ZodObject) {
955
- const conflicts = Object.keys(extra).filter((key) => (key in base.shape));
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 z4.intersection(z4.object(extra), base);
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 [methodName, method] of Object.entries(service.methods)) {
967
- if (transport === "CLI") {
968
- if (!method.expose?.includes("CLI"))
969
- continue;
970
- } else if (method.expose && !method.expose.includes(transport)) {
971
- continue;
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
- const name = method.toolName ?? toToolName(service.name, methodName);
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: z4.object(config.extend.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
- if (assertName) {
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: definition.name,
474
+ name: projected.name,
1113
475
  argumentSchema: definition.input,
1114
- presentationSchema: buildToolPresentationSchema({
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 z5 } from "zod";
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 z5.ZodOptional || field instanceof z5.ZodNullable || field instanceof z5.ZodDefault) {
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 z5.ZodBoolean)
498
+ if (field instanceof z3.ZodBoolean)
1140
499
  return "boolean";
1141
- if (field instanceof z5.ZodNumber)
500
+ if (field instanceof z3.ZodNumber)
1142
501
  return "number";
1143
- if (field instanceof z5.ZodBigInt)
502
+ if (field instanceof z3.ZodBigInt)
1144
503
  return "bigint";
1145
- if (field instanceof z5.ZodDate)
504
+ if (field instanceof z3.ZodDate)
1146
505
  return "date";
1147
- if (field instanceof z5.ZodEnum)
506
+ if (field instanceof z3.ZodEnum)
1148
507
  return "enum";
1149
- if (field instanceof z5.ZodArray)
508
+ if (field instanceof z3.ZodArray)
1150
509
  return "array";
1151
- if (field instanceof z5.ZodObject)
510
+ if (field instanceof z3.ZodObject)
1152
511
  return "object";
1153
- if (field instanceof z5.ZodString || field instanceof z5.ZodLiteral)
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 z5.ZodObject) {
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 z5.ZodArray) {
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 z5.ZodUnion) {
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 z5.ZodIntersection) {
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 text = opts.json ? JSON.stringify(result.data) : JSON.stringify(result.data, null, 2);
1504
- writers.stdout(`${text}
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
- writers.stderr(`${JSON.stringify(error, null, 2)}
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 startedAt = Date.now();
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 state = await params.poll();
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 elapsedSec = (Date.now() - startedAt) / 1000;
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, params.signal),
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) => !result.ok || wait.done(result.data),
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 service of surface.services ?? []) {
1939
- for (const mountable of collectTools(service, transport, collectConfig)) {
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: service.name,
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 raw JSON on stdout (for piping / scripts)"],
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 width = Math.max(...fields.map((f) => f.name.length));
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
- lines.push(` --${padRight(f.name, width)} <${typeLabel(f.schema)}>${req}${desc}`);
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, assertToolName, assertUniqueToolName, flattenToolJsonSchema, buildToolPresentationSchema, isObjectPresentationSchema, presentationMetadata, collectTools, createToolRunner, formatToolError, defineRuntimeTool, createRuntimeToolFactory, collectToolSurface, fetchGuarded, fetchPinnedDocument, readCapped, writeDownload, parseCliArgs, defineCliCommand, DEFAULT_EXIT_CODES, emitResult, runWaitOperation, pollUntilDone, createCli };
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 };