@zapier/kitcore 0.8.0 → 0.9.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/dist/index.mjs CHANGED
@@ -1,6 +1,3 @@
1
- // src/registry.ts
2
- import { z } from "zod";
3
-
4
1
  // src/utils/string-utils.ts
5
2
  function toTitleCase(input) {
6
3
  return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
@@ -24,6 +21,65 @@ function pluralizeLastWord(title) {
24
21
  return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
25
22
  }
26
23
 
24
+ // src/utils/schema-utils.ts
25
+ import { z } from "zod";
26
+ function canonicalInputSchema(schema) {
27
+ if (schema instanceof z.ZodUnion) {
28
+ return schema.options[0];
29
+ }
30
+ return schema;
31
+ }
32
+ function getOutputSchema(inputSchema) {
33
+ return inputSchema._zod.def.outputSchema;
34
+ }
35
+ function withOutputSchema(inputSchema, outputSchema) {
36
+ Object.assign(inputSchema._zod.def, {
37
+ outputSchema
38
+ });
39
+ return inputSchema;
40
+ }
41
+ function withResolver(schema, config) {
42
+ schema._zod.def.resolverMeta = config;
43
+ return schema;
44
+ }
45
+ function getSchemaDescription(schema) {
46
+ return schema.description;
47
+ }
48
+ function getFieldDescriptions(schema) {
49
+ const descriptions = {};
50
+ const shape = schema.shape;
51
+ for (const [key, fieldSchema] of Object.entries(shape)) {
52
+ if (fieldSchema instanceof z.ZodType && fieldSchema.description) {
53
+ descriptions[key] = fieldSchema.description;
54
+ }
55
+ }
56
+ return descriptions;
57
+ }
58
+ function withPositional(schema) {
59
+ Object.assign(schema._zod.def, {
60
+ positionalMeta: { positional: true }
61
+ });
62
+ return schema;
63
+ }
64
+ function schemaHasPositionalMeta(schema) {
65
+ return "positionalMeta" in schema._zod.def;
66
+ }
67
+ function isPositional(schema) {
68
+ if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
69
+ return true;
70
+ }
71
+ if (schema instanceof z.ZodOptional) {
72
+ return isPositional(schema._zod.def.innerType);
73
+ }
74
+ if (schema instanceof z.ZodDefault) {
75
+ return isPositional(schema._zod.def.innerType);
76
+ }
77
+ return false;
78
+ }
79
+ function openEnum(values, description) {
80
+ return z.union([z.enum(values), z.string()]).describe(description);
81
+ }
82
+
27
83
  // src/registry.ts
28
84
  function resolveCategoryDefinition(ref) {
29
85
  const def = typeof ref === "string" ? { key: ref } : ref;
@@ -34,30 +90,25 @@ function resolveCategoryDefinition(ref) {
34
90
  titlePlural: def.titlePlural ?? pluralizeLastWord(title)
35
91
  };
36
92
  }
37
- function canonicalInputSchema(schema) {
38
- if (schema instanceof z.ZodUnion) {
39
- return schema.options[0];
40
- }
41
- return schema;
42
- }
43
93
  function buildRegistry({
44
94
  sdk,
45
95
  meta,
46
96
  formatters,
47
- boundResolvers,
97
+ resolvers,
48
98
  positional,
99
+ skipInputValidation,
49
100
  packageFilter
50
101
  }) {
51
102
  const definitionsByKey = /* @__PURE__ */ new Map();
52
103
  const objectDeclaredKeys = /* @__PURE__ */ new Set();
53
104
  for (const m of Object.values(meta)) {
54
105
  for (const ref of m.categories ?? []) {
55
- const key2 = typeof ref === "string" ? ref : ref.key;
106
+ const key = typeof ref === "string" ? ref : ref.key;
56
107
  if (typeof ref === "object") {
57
- objectDeclaredKeys.add(key2);
58
- definitionsByKey.set(key2, resolveCategoryDefinition(ref));
59
- } else if (!objectDeclaredKeys.has(key2)) {
60
- definitionsByKey.set(key2, resolveCategoryDefinition(ref));
108
+ objectDeclaredKeys.add(key);
109
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
110
+ } else if (!objectDeclaredKeys.has(key)) {
111
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
61
112
  }
62
113
  }
63
114
  }
@@ -65,30 +116,29 @@ function buildRegistry({
65
116
  definitionsByKey.set("other", resolveCategoryDefinition("other"));
66
117
  }
67
118
  const knownCategories = Array.from(definitionsByKey.keys());
68
- const functions = Object.keys(meta).filter((key2) => {
69
- const property = sdk[key2];
119
+ const functions = Object.keys(meta).filter((key) => {
120
+ const property = sdk[key];
70
121
  if (typeof property === "function") return true;
71
- const [rootKey] = key2.split(".");
122
+ const [rootKey] = key.split(".");
72
123
  const rootProperty = sdk[rootKey];
73
124
  return typeof rootProperty === "object" && rootProperty !== null;
74
- }).map((key2) => {
75
- const m = meta[key2];
125
+ }).map((key) => {
126
+ const m = meta[key];
76
127
  return {
77
- name: key2,
128
+ name: key,
78
129
  description: m.description,
79
130
  type: m.type,
80
131
  itemType: m.itemType,
81
132
  returnType: m.returnType,
82
133
  inputSchema: canonicalInputSchema(m.inputSchema),
83
- inputParameters: m.inputParameters,
84
134
  outputSchema: m.outputSchema,
85
- positional: positional?.[key2],
135
+ positional: positional?.[key],
136
+ skipInputValidation: skipInputValidation?.[key],
86
137
  categories: (m.categories ?? []).map(
87
138
  (c) => typeof c === "string" ? c : c.key
88
139
  ),
89
- resolvers: m.resolvers,
90
- boundResolvers: boundResolvers?.[key2],
91
- formatter: formatters?.[key2],
140
+ resolvers: resolvers?.[key],
141
+ formatter: formatters?.[key],
92
142
  experimental: m.experimental,
93
143
  packages: m.packages,
94
144
  confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
@@ -577,6 +627,52 @@ function runInMethodScope(fn) {
577
627
  var runWithTelemetryContext = runInMethodScope;
578
628
  var isTelemetryNested = isNestedMethodCall;
579
629
 
630
+ // src/utils/call-context.ts
631
+ var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
632
+ function isCallContext(value) {
633
+ return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
634
+ }
635
+ function generateCallId() {
636
+ try {
637
+ const webCrypto = globalThis.crypto;
638
+ if (webCrypto?.randomUUID) {
639
+ return webCrypto.randomUUID();
640
+ }
641
+ if (webCrypto?.getRandomValues) {
642
+ const bytes = webCrypto.getRandomValues(new Uint8Array(16));
643
+ const hex = Array.from(bytes, (byte, i) => {
644
+ const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
645
+ return value.toString(16).padStart(2, "0");
646
+ });
647
+ return [
648
+ hex.slice(0, 4).join(""),
649
+ hex.slice(4, 6).join(""),
650
+ hex.slice(6, 8).join(""),
651
+ hex.slice(8, 10).join(""),
652
+ hex.slice(10, 16).join("")
653
+ ].join("-");
654
+ }
655
+ } catch {
656
+ }
657
+ return null;
658
+ }
659
+ function rootCallContext() {
660
+ return {
661
+ callId: generateCallId(),
662
+ depth: 0,
663
+ annotations: {},
664
+ [CALL_CONTEXT_BRAND]: true
665
+ };
666
+ }
667
+ function childCallContext(parent) {
668
+ return {
669
+ callId: parent.callId,
670
+ depth: parent.depth + 1,
671
+ annotations: {},
672
+ [CALL_CONTEXT_BRAND]: true
673
+ };
674
+ }
675
+
580
676
  // src/utils/core-options.ts
581
677
  function defaultLogDeprecation({
582
678
  methodName,
@@ -595,6 +691,9 @@ function resolveCoreOptions(context) {
595
691
  return context.core;
596
692
  }
597
693
  var INTERNAL_CALL = Symbol("kitcore.internalCall");
694
+ function resolveCallContext(secondArg) {
695
+ return isCallContext(secondArg) ? secondArg : rootCallContext();
696
+ }
598
697
  function signalDeprecation(context, methodName, getDeprecation) {
599
698
  if (isInsideObserver()) return;
600
699
  const deprecation = getDeprecation?.();
@@ -624,14 +723,16 @@ function createFunction(coreFn, options) {
624
723
  const functionName = name || coreFn.name;
625
724
  const namedFunctions = {
626
725
  [functionName]: async function(callOptions) {
627
- if (arguments[1] !== INTERNAL_CALL) {
726
+ const internal = arguments[1];
727
+ const context = resolveCallContext(internal);
728
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
628
729
  signalDeprecation(sdk.context, functionName, getDeprecation);
629
730
  }
630
731
  return runInMethodScope(async () => {
631
732
  const startTime = Date.now();
632
733
  const normalizedOptions = callOptions ?? {};
633
734
  const args = [normalizedOptions];
634
- const depth = getCurrentDepth();
735
+ const depth = Math.max(context.depth, getCurrentDepth());
635
736
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
636
737
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
637
738
  hooks?.onMethodStart?.({
@@ -650,12 +751,15 @@ function createFunction(coreFn, options) {
650
751
  adaptError
651
752
  }
652
753
  );
653
- result = await coreFn({
654
- ...normalizedOptions,
655
- ...validatedOptions
656
- });
754
+ result = await coreFn(
755
+ {
756
+ ...normalizedOptions,
757
+ ...validatedOptions
758
+ },
759
+ context
760
+ );
657
761
  } else {
658
- result = await coreFn(normalizedOptions);
762
+ result = await coreFn(normalizedOptions, context);
659
763
  }
660
764
  hooks?.onMethodEnd?.({
661
765
  methodName: functionName,
@@ -685,17 +789,19 @@ function createFunction(coreFn, options) {
685
789
  function createRawFunction(coreFn, options) {
686
790
  const { sdk, name, schema, positional, getDeprecation } = options;
687
791
  return function(rawInput) {
688
- if (arguments[1] !== INTERNAL_CALL) {
792
+ const internal = arguments[1];
793
+ const context = resolveCallContext(internal);
794
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
689
795
  signalDeprecation(sdk.context, name, getDeprecation);
690
796
  }
691
797
  return runInMethodScope(() => {
692
798
  const startTime = Date.now();
693
- const depth = getCurrentDepth();
799
+ const depth = Math.max(context.depth, getCurrentDepth());
694
800
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
695
801
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
696
802
  const input = schema ? rawInput ?? {} : rawInput;
697
803
  const record = input;
698
- const args = positional ? positional.filter((key2) => record?.[key2] !== void 0).map((key2) => record?.[key2]) : [input];
804
+ const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
699
805
  hooks?.onMethodStart?.({
700
806
  methodName: name,
701
807
  args,
@@ -714,7 +820,7 @@ function createRawFunction(coreFn, options) {
714
820
  };
715
821
  try {
716
822
  const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
717
- const result = coreFn(parsed);
823
+ const result = coreFn(parsed, context);
718
824
  if (result !== null && typeof result === "object" && typeof result.then === "function") {
719
825
  return result.then(
720
826
  (value) => {
@@ -753,9 +859,9 @@ function createPageFunction(coreFn, {
753
859
  }) {
754
860
  const functionName = coreFn.name + "Page";
755
861
  const namedFunctions = {
756
- [functionName]: async function(options) {
862
+ [functionName]: async function(options, callContext) {
757
863
  try {
758
- const response = await coreFn(options);
864
+ const response = await coreFn(options, callContext);
759
865
  const page = adaptPage ? adaptPage(response) : response;
760
866
  if (!isSdkPage(page)) {
761
867
  throw new Error(
@@ -779,14 +885,16 @@ function createPaginatedFunction(coreFn, options) {
779
885
  const functionName = name || coreFn.name;
780
886
  const namedFunctions = {
781
887
  [functionName]: function(callOptions) {
782
- if (arguments[1] !== INTERNAL_CALL) {
888
+ const internal = arguments[1];
889
+ const context = resolveCallContext(internal);
890
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
783
891
  signalDeprecation(sdk.context, functionName, getDeprecation);
784
892
  }
785
893
  return runInMethodScope(() => {
786
894
  const startTime = Date.now();
787
895
  const normalizedOptions = callOptions ?? {};
788
896
  const args = [normalizedOptions];
789
- const depth = getCurrentDepth();
897
+ const depth = Math.max(context.depth, getCurrentDepth());
790
898
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
791
899
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
792
900
  hooks?.onMethodStart?.({
@@ -805,7 +913,11 @@ function createPaginatedFunction(coreFn, options) {
805
913
  ...validatedOptions,
806
914
  pageSize
807
915
  };
808
- const iterator = paginate(pageFunction, optimizedOptions);
916
+ const iterator = paginate(
917
+ (pageOptions) => pageFunction(pageOptions, context),
918
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
919
+ optimizedOptions
920
+ );
809
921
  const firstPagePromise = iterator.next().then((result) => {
810
922
  if (result.done) {
811
923
  throw new Error("Paginate should always iterate at least once");
@@ -956,11 +1068,11 @@ var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
956
1068
  "context",
957
1069
  "getRegistry"
958
1070
  ]);
959
- function hasOwn(obj, key2) {
960
- return Object.prototype.hasOwnProperty.call(obj, key2);
1071
+ function hasOwn(obj, key) {
1072
+ return Object.prototype.hasOwnProperty.call(obj, key);
961
1073
  }
962
- function setOwn(target, key2, value) {
963
- Object.defineProperty(target, key2, {
1074
+ function setOwn(target, key, value) {
1075
+ Object.defineProperty(target, key, {
964
1076
  value,
965
1077
  enumerable: true,
966
1078
  configurable: true,
@@ -972,31 +1084,31 @@ function checkCollisions(target, source, kind, callerLabel, override) {
972
1084
  checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
973
1085
  return;
974
1086
  }
975
- for (const key2 of Object.keys(source)) {
976
- if (!override && hasOwn(target, key2)) {
1087
+ for (const key of Object.keys(source)) {
1088
+ if (!override && hasOwn(target, key)) {
977
1089
  throw new Error(
978
- `${callerLabel}: duplicate ${kind} "${key2}". If the override is intentional, pass { override: true } in the options.`
1090
+ `${callerLabel}: duplicate ${kind} "${key}". If the override is intentional, pass { override: true } in the options.`
979
1091
  );
980
1092
  }
981
1093
  }
982
1094
  }
983
1095
  function checkRootKeyCollisions(target, keys, override, callerLabel) {
984
- for (const key2 of keys) {
985
- if (RESERVED_ROOT_KEYS.has(key2)) {
1096
+ for (const key of keys) {
1097
+ if (RESERVED_ROOT_KEYS.has(key)) {
986
1098
  throw new Error(
987
- `${callerLabel}: plugin attempted to register reserved root key "${key2}". The SDK uses this key for its own accessor; rename the plugin's method.`
1099
+ `${callerLabel}: plugin attempted to register reserved root key "${key}". The SDK uses this key for its own accessor; rename the plugin's method.`
988
1100
  );
989
1101
  }
990
- if (!override && hasOwn(target, key2)) {
1102
+ if (!override && hasOwn(target, key)) {
991
1103
  throw new Error(
992
- `${callerLabel}: duplicate root key "${key2}". If the override is intentional, pass { override: true } in the options.`
1104
+ `${callerLabel}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
993
1105
  );
994
1106
  }
995
1107
  }
996
1108
  }
997
1109
  function applyOwnProperties(target, source) {
998
- for (const key2 of Object.keys(source)) {
999
- setOwn(target, key2, source[key2]);
1110
+ for (const key of Object.keys(source)) {
1111
+ setOwn(target, key, source[key]);
1000
1112
  }
1001
1113
  }
1002
1114
  function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
@@ -1214,7 +1326,6 @@ var LEAF_META_KEYS = [
1214
1326
  "itemType",
1215
1327
  "returnType",
1216
1328
  "outputSchema",
1217
- "inputParameters",
1218
1329
  "packages",
1219
1330
  "experimental",
1220
1331
  "confirm",
@@ -1254,8 +1365,8 @@ function normalizeImports(deps) {
1254
1365
  }
1255
1366
  function collectLeafMeta(config) {
1256
1367
  let meta;
1257
- for (const key2 of LEAF_META_KEYS) {
1258
- if (config[key2] !== void 0) (meta ?? (meta = {}))[key2] = config[key2];
1368
+ for (const key of LEAF_META_KEYS) {
1369
+ if (config[key] !== void 0) (meta ?? (meta = {}))[key] = config[key];
1259
1370
  }
1260
1371
  return meta;
1261
1372
  }
@@ -1338,7 +1449,8 @@ function defineResolver(config) {
1338
1449
  type: "object",
1339
1450
  properties: config.properties,
1340
1451
  definitions: config.definitions,
1341
- getProperties: config.getProperties
1452
+ getProperties: config.getProperties,
1453
+ additionalKeys: config.additionalKeys
1342
1454
  };
1343
1455
  case "array":
1344
1456
  return {
@@ -1650,7 +1762,7 @@ function normalizeFormatter(entry, sdk) {
1650
1762
  const legacy = entry.meta?.formatter;
1651
1763
  return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
1652
1764
  }
1653
- function normalizeBoundResolvers(entry) {
1765
+ function normalizeResolvers(entry) {
1654
1766
  if (entry.pluginType !== "method") return void 0;
1655
1767
  return entry.resolvers;
1656
1768
  }
@@ -1691,17 +1803,20 @@ function collectSurfaceProjection(context, formatterSdk) {
1691
1803
  foldDynamicMembers(entry, surfaceBindings, meta);
1692
1804
  }
1693
1805
  const formatters = {};
1694
- const boundResolvers = {};
1806
+ const resolvers = {};
1695
1807
  const positional = {};
1808
+ const skipInputValidation = {};
1696
1809
  for (const [binding, entry] of Object.entries(entries)) {
1697
1810
  const f = normalizeFormatter(entry, formatterSdk);
1698
1811
  if (f) formatters[binding] = f;
1699
- const r = normalizeBoundResolvers(entry);
1700
- if (r) boundResolvers[binding] = r;
1812
+ const r = normalizeResolvers(entry);
1813
+ if (r) resolvers[binding] = r;
1701
1814
  const p = methodPositional(entry);
1702
1815
  if (p) positional[binding] = p;
1816
+ if (entry.pluginType === "method" && entry.skipInputValidation)
1817
+ skipInputValidation[binding] = true;
1703
1818
  }
1704
- return { meta, formatters, boundResolvers, positional };
1819
+ return { meta, formatters, resolvers, positional, skipInputValidation };
1705
1820
  }
1706
1821
  function buildSurfaceRegistry(context, packageFilter) {
1707
1822
  const surface = {};
@@ -1758,6 +1873,11 @@ function nestedResolvers(resolver) {
1758
1873
  for (const field of Object.values(resolver.properties ?? {})) {
1759
1874
  if (!isResolverRef(field.resolver)) out.push(field.resolver);
1760
1875
  }
1876
+ const ak = resolver.additionalKeys;
1877
+ if (ak) {
1878
+ if (!isResolverRef(ak.values)) out.push(ak.values);
1879
+ if (ak.keys && !isResolverRef(ak.keys)) out.push(ak.keys);
1880
+ }
1761
1881
  out.push(...Object.values(resolver.definitions ?? {}));
1762
1882
  } else if (resolver.type === "array") {
1763
1883
  if (!isResolverRef(resolver.items)) out.push(resolver.items);
@@ -1882,16 +2002,16 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
1882
2002
  }
1883
2003
  return byId;
1884
2004
  }
1885
- function bindValue(target, key2, entry, callType = "surface") {
2005
+ function bindValue(target, key, entry, callType = "surface", ctx) {
1886
2006
  if (entry.pluginType === "property" && entry.getValue) {
1887
- Object.defineProperty(target, key2, {
2007
+ Object.defineProperty(target, key, {
1888
2008
  get: entry.getValue,
1889
2009
  enumerable: true,
1890
2010
  configurable: true
1891
2011
  });
1892
2012
  } else {
1893
- const value = callType === "internal" && entry.pluginType === "method" ? entry.internalValue ?? entry.value : entry.value;
1894
- Object.defineProperty(target, key2, {
2013
+ const value = callType === "internal" && entry.pluginType === "method" ? entry.bindInternal?.(ctx) ?? entry.internalValue ?? entry.value : entry.value;
2014
+ Object.defineProperty(target, key, {
1895
2015
  value,
1896
2016
  writable: true,
1897
2017
  enumerable: true,
@@ -1908,7 +2028,7 @@ function buildSurface(context, ...maps) {
1908
2028
  sdk[CONTEXT] = context;
1909
2029
  return sdk;
1910
2030
  }
1911
- function buildImports(plugins, importBindings) {
2031
+ function buildImports(plugins, importBindings, ctx) {
1912
2032
  const imports = {};
1913
2033
  for (const { binding, id, optional } of importBindings) {
1914
2034
  const entry = plugins[id];
@@ -1921,7 +2041,7 @@ function buildImports(plugins, importBindings) {
1921
2041
  });
1922
2042
  continue;
1923
2043
  }
1924
- bindValue(imports, binding, entry, "internal");
2044
+ bindValue(imports, binding, entry, "internal", ctx);
1925
2045
  }
1926
2046
  return imports;
1927
2047
  }
@@ -2000,6 +2120,19 @@ function bindResolver(resolver, plugins) {
2000
2120
  const { getProperties } = resolver;
2001
2121
  if (getProperties)
2002
2122
  bound.getProperties = ({ input }) => getProperties({ imports, input });
2123
+ if (resolver.additionalKeys) {
2124
+ const ak = resolver.additionalKeys;
2125
+ const boundAk = {
2126
+ values: isResolverRef(ak.values) ? ak.values : bindResolver(ak.values, plugins),
2127
+ minEntries: ak.minEntries,
2128
+ maxEntries: ak.maxEntries,
2129
+ keyValueType: ak.keyValueType,
2130
+ valueValueType: ak.valueValueType
2131
+ };
2132
+ if (ak.keys)
2133
+ boundAk.keys = isResolverRef(ak.keys) ? ak.keys : bindResolver(ak.keys, plugins);
2134
+ bound.additionalKeys = boundAk;
2135
+ }
2003
2136
  return bound;
2004
2137
  }
2005
2138
  case "array": {
@@ -2055,8 +2188,8 @@ function bindResolver(resolver, plugins) {
2055
2188
  }
2056
2189
  function bindFields(fields, plugins) {
2057
2190
  const out = {};
2058
- for (const [key2, field] of Object.entries(fields)) {
2059
- out[key2] = {
2191
+ for (const [key, field] of Object.entries(fields)) {
2192
+ out[key] = {
2060
2193
  ...field,
2061
2194
  resolver: isResolverRef(field.resolver) ? field.resolver : bindResolver(field.resolver, plugins)
2062
2195
  };
@@ -2065,8 +2198,8 @@ function bindFields(fields, plugins) {
2065
2198
  }
2066
2199
  function bindDefinitions(definitions, plugins) {
2067
2200
  const out = {};
2068
- for (const [key2, def] of Object.entries(definitions)) {
2069
- out[key2] = bindResolver(def, plugins);
2201
+ for (const [key, def] of Object.entries(definitions)) {
2202
+ out[key] = bindResolver(def, plugins);
2070
2203
  }
2071
2204
  return out;
2072
2205
  }
@@ -2151,6 +2284,7 @@ function buildMethodEntries(descriptors, context, states) {
2151
2284
  name: descriptor.name,
2152
2285
  chain: [],
2153
2286
  inputSchema: descriptor.inputSchema,
2287
+ skipInputValidation: descriptor.skipInputValidation,
2154
2288
  // Derive the presentation type from the output mode when the author did
2155
2289
  // not set one; an explicit meta.type (e.g. "create") still wins.
2156
2290
  meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
@@ -2158,17 +2292,17 @@ function buildMethodEntries(descriptors, context, states) {
2158
2292
  // Replaced below; never called.
2159
2293
  value: () => void 0
2160
2294
  };
2161
- const callRun = (input) => descriptor.run({
2162
- imports: buildImports(plugins, descriptor.importBindings),
2295
+ const callRun = (input, ctx) => descriptor.run({
2296
+ imports: buildImports(plugins, descriptor.importBindings, ctx),
2163
2297
  state: states.get(id),
2164
2298
  input
2165
2299
  });
2166
- const fold = (coreFn) => (input) => {
2167
- let next = coreFn;
2300
+ const fold = (coreFn) => (input, ctx) => {
2301
+ let next = (i) => coreFn(i, ctx);
2168
2302
  for (const wrap of entry.chain) {
2169
2303
  const inner = next;
2170
2304
  next = (i) => wrap.run({
2171
- imports: buildImports(plugins, wrap.owner.importBindings),
2305
+ imports: buildImports(plugins, wrap.owner.importBindings, ctx),
2172
2306
  next: inner,
2173
2307
  input: i,
2174
2308
  // Overwritten by the chain item's own closure with the owning
@@ -2192,7 +2326,7 @@ function buildMethodEntries(descriptors, context, states) {
2192
2326
  }
2193
2327
  );
2194
2328
  } else if (out.type === "item") {
2195
- const itemCore = async (input) => callRun(input);
2329
+ const itemCore = async (input, ctx) => callRun(input, ctx);
2196
2330
  entry.value = createFunction(
2197
2331
  fold(itemCore),
2198
2332
  {
@@ -2204,7 +2338,7 @@ function buildMethodEntries(descriptors, context, states) {
2204
2338
  );
2205
2339
  } else {
2206
2340
  entry.value = createRawFunction(
2207
- (input) => fold(callRun)(input),
2341
+ (input, ctx) => fold(callRun)(input, ctx),
2208
2342
  {
2209
2343
  sdk,
2210
2344
  name: descriptor.name,
@@ -2227,11 +2361,15 @@ function buildMethodEntries(descriptors, context, states) {
2227
2361
  });
2228
2362
  return packed;
2229
2363
  };
2364
+ const internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2230
2365
  entry.value = (...args) => canonicalValue(pack(args));
2231
- entry.internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2366
+ entry.internalValue = internalValue;
2367
+ entry.bindInternal = (ctx) => ctx ? (...args) => canonicalValue(pack(args), childCallContext(ctx)) : internalValue;
2232
2368
  entry.positional = names;
2233
2369
  } else {
2234
- entry.internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2370
+ const internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2371
+ entry.internalValue = internalValue;
2372
+ entry.bindInternal = (ctx) => ctx ? (input) => canonicalValue(input, childCallContext(ctx)) : internalValue;
2235
2373
  }
2236
2374
  plugins[id] = entry;
2237
2375
  }
@@ -2471,7 +2609,7 @@ function createSdk(root, options) {
2471
2609
  pluginSurface = {};
2472
2610
  bindValue(pluginSurface, plugin.name, plugins2[plugin.id]);
2473
2611
  }
2474
- for (const key2 of Object.keys(legacyExports)) context.surface[key2] = key2;
2612
+ for (const key of Object.keys(legacyExports)) context.surface[key] = key;
2475
2613
  if (plugin.pluginType === "aggregate") {
2476
2614
  recordExportSurface(context, plugin.exports);
2477
2615
  } else {
@@ -2610,6 +2748,7 @@ function valueTypeOf(inner) {
2610
2748
  if (inner instanceof z3.ZodEnum) return "string";
2611
2749
  if (inner instanceof z3.ZodArray) return "array";
2612
2750
  if (inner instanceof z3.ZodObject) return "object";
2751
+ if (inner instanceof z3.ZodRecord) return "object";
2613
2752
  return void 0;
2614
2753
  }
2615
2754
  function staticChoicesOf(inner) {
@@ -2620,7 +2759,8 @@ function staticChoicesOf(inner) {
2620
2759
  return void 0;
2621
2760
  }
2622
2761
  function objectShape(schema) {
2623
- const { inner } = schema ? unwrap(schema) : { inner: void 0 };
2762
+ const canonical = canonicalInputSchema(schema);
2763
+ const { inner } = canonical ? unwrap(canonical) : { inner: void 0 };
2624
2764
  if (inner instanceof z3.ZodObject) {
2625
2765
  return inner.shape;
2626
2766
  }
@@ -2642,7 +2782,7 @@ function topoOrder2(specs) {
2642
2782
  }
2643
2783
  function planParameters(entry) {
2644
2784
  const shape = objectShape(entry.inputSchema);
2645
- const resolvers = entry.boundResolvers ?? {};
2785
+ const resolvers = entry.resolvers ?? {};
2646
2786
  const names = shape ? [
2647
2787
  ...Object.keys(shape),
2648
2788
  ...Object.keys(resolvers).filter(
@@ -2680,24 +2820,48 @@ function getAtPath(root, path) {
2680
2820
  }
2681
2821
  return node;
2682
2822
  }
2823
+ function defineOwn(node, key, value) {
2824
+ Object.defineProperty(node, key, {
2825
+ value,
2826
+ writable: true,
2827
+ enumerable: true,
2828
+ configurable: true
2829
+ });
2830
+ }
2683
2831
  function setAtPath(root, path, value) {
2684
2832
  let node = root;
2685
2833
  for (let i = 0; i < path.length - 1; i++) {
2686
2834
  const seg = path[i];
2687
- if (node[seg] == null || typeof node[seg] !== "object") node[seg] = {};
2688
- node = node[seg];
2835
+ const existing = Object.prototype.hasOwnProperty.call(node, seg) ? node[seg] : void 0;
2836
+ if (existing != null && typeof existing === "object") {
2837
+ node = existing;
2838
+ } else {
2839
+ const child = {};
2840
+ defineOwn(node, seg, child);
2841
+ node = child;
2842
+ }
2689
2843
  }
2690
- node[path[path.length - 1]] = value;
2844
+ defineOwn(node, path[path.length - 1], value);
2691
2845
  }
2692
- var key = (path) => path.join(".");
2846
+ var SAFE_SEGMENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2847
+ var pathToKey = (path) => {
2848
+ let out = "";
2849
+ for (const segment of path) {
2850
+ if (typeof segment === "number") out += `[${segment}]`;
2851
+ else if (SAFE_SEGMENT.test(segment))
2852
+ out += out === "" ? segment : `.${segment}`;
2853
+ else out += `[${JSON.stringify(segment)}]`;
2854
+ }
2855
+ return out;
2856
+ };
2693
2857
  function isSettled(state, path) {
2694
- return state.settled.includes(key(path));
2858
+ return state.settled.includes(pathToKey(path));
2695
2859
  }
2696
2860
  function remember(state, k) {
2697
2861
  if (!state.settled.includes(k)) state.settled.push(k);
2698
2862
  }
2699
2863
  function settle(state, path) {
2700
- remember(state, key(path));
2864
+ remember(state, pathToKey(path));
2701
2865
  }
2702
2866
  function clone(state) {
2703
2867
  return JSON.parse(JSON.stringify(state));
@@ -2712,6 +2876,28 @@ function coerce(leaf, raw) {
2712
2876
  if (raw === "true") return true;
2713
2877
  if (raw === "false") return false;
2714
2878
  }
2879
+ if (leaf.valueType === "object") {
2880
+ const trimmed = raw.trim();
2881
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2882
+ try {
2883
+ return JSON.parse(trimmed);
2884
+ } catch {
2885
+ return raw;
2886
+ }
2887
+ }
2888
+ return raw;
2889
+ }
2890
+ if (leaf.valueType === "array") {
2891
+ const trimmed = raw.trim();
2892
+ if (trimmed.startsWith("[")) {
2893
+ try {
2894
+ return JSON.parse(trimmed);
2895
+ } catch {
2896
+ return raw;
2897
+ }
2898
+ }
2899
+ return raw;
2900
+ }
2715
2901
  return raw;
2716
2902
  }
2717
2903
  async function validationError(leaf, value, state) {
@@ -2769,27 +2955,6 @@ async function objectChildren(resolver, input) {
2769
2955
  return toLeaf(name, field, resolver.definitions);
2770
2956
  });
2771
2957
  }
2772
- function arrayItem(resolver) {
2773
- const items = resolver.items;
2774
- const valueType = resolver.itemValueType;
2775
- if (isRef(items)) {
2776
- return {
2777
- name: "",
2778
- required: true,
2779
- resolver: resolver.definitions?.[items.ref],
2780
- extraInput: items.input,
2781
- valueType,
2782
- requires: []
2783
- };
2784
- }
2785
- return {
2786
- name: "",
2787
- required: true,
2788
- resolver: items,
2789
- valueType,
2790
- requires: []
2791
- };
2792
- }
2793
2958
  function autoSettles(resolver) {
2794
2959
  return resolver.type === "constant" || resolver.type === "info";
2795
2960
  }
@@ -2809,10 +2974,28 @@ async function leafAt(ctx, path, resolved) {
2809
2974
  const seg = path[i];
2810
2975
  if (typeof seg === "number") {
2811
2976
  if (leaf?.resolver?.type !== "array") return void 0;
2812
- leaf = arrayItem(leaf.resolver);
2977
+ leaf = boundLeaf(
2978
+ "",
2979
+ leaf.resolver.items,
2980
+ leaf.resolver.definitions,
2981
+ leaf.resolver.itemValueType
2982
+ );
2813
2983
  } else {
2814
- leaf = children.find((c) => c.name === seg);
2815
- if (!leaf) return void 0;
2984
+ const parent = leaf;
2985
+ const found = children.find((c) => c.name === seg);
2986
+ if (found) {
2987
+ leaf = found;
2988
+ } else if (parent?.resolver?.type === "object" && parent.resolver.additionalKeys) {
2989
+ const ak = parent.resolver.additionalKeys;
2990
+ leaf = boundLeaf(
2991
+ String(seg),
2992
+ ak.values,
2993
+ parent.resolver.definitions,
2994
+ ak.valueValueType
2995
+ );
2996
+ } else {
2997
+ return void 0;
2998
+ }
2816
2999
  }
2817
3000
  if (i < path.length - 1 && typeof path[i + 1] === "string") {
2818
3001
  if (leaf?.resolver?.type !== "object") return void 0;
@@ -2824,16 +3007,81 @@ async function leafAt(ctx, path, resolved) {
2824
3007
  }
2825
3008
  return leaf;
2826
3009
  }
3010
+ function boundLeaf(name, resolverOrRef, definitions, valueType) {
3011
+ if (isRef(resolverOrRef)) {
3012
+ return {
3013
+ name,
3014
+ required: true,
3015
+ resolver: definitions?.[resolverOrRef.ref],
3016
+ extraInput: resolverOrRef.input,
3017
+ valueType,
3018
+ requires: []
3019
+ };
3020
+ }
3021
+ return {
3022
+ name,
3023
+ required: true,
3024
+ resolver: resolverOrRef,
3025
+ valueType,
3026
+ requires: []
3027
+ };
3028
+ }
3029
+ async function recordInfoAt(ctx, path, resolved) {
3030
+ const leaf = await leafAt(ctx, path, resolved);
3031
+ const resolver = leaf?.resolver;
3032
+ if (resolver?.type !== "object" || !resolver.additionalKeys) {
3033
+ throw new Error(
3034
+ `expected an object resolver with additionalKeys at "${pathToKey(path)}"`
3035
+ );
3036
+ }
3037
+ if (resolver.getProperties) {
3038
+ throw new Error(
3039
+ `object resolver at "${pathToKey(path)}" cannot combine getProperties with additionalKeys`
3040
+ );
3041
+ }
3042
+ const ak = resolver.additionalKeys;
3043
+ const defs = resolver.definitions;
3044
+ const keyLeaf = ak.keys ? boundLeaf("key", ak.keys, defs, ak.keyValueType ?? "string") : {
3045
+ name: "key",
3046
+ required: true,
3047
+ resolver: { type: "static", inputType: "text" },
3048
+ valueType: "string",
3049
+ requires: []
3050
+ };
3051
+ const valueLeaf = boundLeaf("value", ak.values, defs, ak.valueValueType);
3052
+ if (keyLeaf.resolver && keyLeaf.resolver.type !== "static") {
3053
+ throw new Error(
3054
+ `record key resolver at "${pathToKey(path)}" must be a static free-text prompt, not "${keyLeaf.resolver.type}"`
3055
+ );
3056
+ }
3057
+ if (valueLeaf.resolver?.type === "object" || valueLeaf.resolver?.type === "array") {
3058
+ throw new Error(
3059
+ `record value resolver at "${pathToKey(path)}" must be a single value, not "${valueLeaf.resolver.type}"`
3060
+ );
3061
+ }
3062
+ return {
3063
+ min: ak.minEntries ?? 0,
3064
+ max: ak.maxEntries ?? Infinity,
3065
+ keyLeaf,
3066
+ valueLeaf,
3067
+ fixedKeys: Object.keys(resolver.properties ?? {})
3068
+ };
3069
+ }
2827
3070
  async function arrayInfoAt(ctx, path, resolved) {
2828
3071
  const leaf = await leafAt(ctx, path, resolved);
2829
3072
  const resolver = leaf?.resolver;
2830
3073
  if (resolver?.type !== "array") {
2831
- throw new Error(`expected an array resolver at "${key(path)}"`);
3074
+ throw new Error(`expected an array resolver at "${pathToKey(path)}"`);
2832
3075
  }
2833
3076
  return {
2834
3077
  min: resolver.minItems ?? 0,
2835
3078
  max: resolver.maxItems ?? Infinity,
2836
- item: { ...arrayItem(resolver), name: String(path[path.length - 1]) }
3079
+ item: boundLeaf(
3080
+ String(path[path.length - 1]),
3081
+ resolver.items,
3082
+ resolver.definitions,
3083
+ resolver.itemValueType
3084
+ )
2837
3085
  };
2838
3086
  }
2839
3087
  async function firstPage(result) {
@@ -2914,6 +3162,9 @@ var AFFORDANCE = {
2914
3162
  retry: { action: "retry", description: "Retry loading the options" },
2915
3163
  cancel: { action: "cancel", description: "Cancel resolution" }
2916
3164
  };
3165
+ function affordance(base, description) {
3166
+ return { ...base, description };
3167
+ }
2917
3168
  function selectActions(leaf, page, multiple) {
2918
3169
  const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
2919
3170
  if (searchMode && page.position.search === void 0 && page.items.length === 0) {
@@ -3028,7 +3279,7 @@ async function buildQuestion(leaf, path, input) {
3028
3279
  }
3029
3280
  };
3030
3281
  }
3031
- function collectionQuestion(t) {
3282
+ function arrayItemsQuestion(t) {
3032
3283
  const actions = [AFFORDANCE.add];
3033
3284
  if (t.count >= t.min) actions.push(AFFORDANCE.done);
3034
3285
  return {
@@ -3044,22 +3295,37 @@ function collectionQuestion(t) {
3044
3295
  actions
3045
3296
  };
3046
3297
  }
3047
- function objectGateQuestion(path) {
3298
+ function recordEntriesQuestion(t) {
3299
+ const actions = [
3300
+ affordance(AFFORDANCE.add, "Add another entry")
3301
+ ];
3302
+ if (t.count >= t.min) {
3303
+ actions.push(affordance(AFFORDANCE.done, "Finish the entries"));
3304
+ }
3305
+ return {
3306
+ type: "collection",
3307
+ path: t.path,
3308
+ message: `Add another ${t.path[t.path.length - 1]} entry? (${t.count} so far)`,
3309
+ container: "record",
3310
+ count: t.count,
3311
+ min: t.min,
3312
+ ...Number.isFinite(t.max) ? { max: t.max } : {},
3313
+ actions
3314
+ };
3315
+ }
3316
+ function objectOptionalQuestion(path) {
3048
3317
  return {
3049
3318
  type: "collection",
3050
3319
  path,
3051
3320
  message: `Add ${path[path.length - 1]}?`,
3052
3321
  container: "object",
3053
3322
  actions: [
3054
- {
3055
- action: "add",
3056
- description: "Provide values for these fields"
3057
- },
3058
- { action: "done", description: "Skip these fields" }
3323
+ affordance(AFFORDANCE.add, "Provide values for these fields"),
3324
+ affordance(AFFORDANCE.done, "Skip these fields")
3059
3325
  ]
3060
3326
  };
3061
3327
  }
3062
- function optionalsGateQuestion(path, pending) {
3328
+ function objectOptionalPropertiesQuestion(path, pending) {
3063
3329
  return {
3064
3330
  type: "collection",
3065
3331
  path,
@@ -3076,8 +3342,8 @@ function optionalsGateQuestion(path, pending) {
3076
3342
  ...leaf.valueType ? { valueType: leaf.valueType } : {}
3077
3343
  })),
3078
3344
  actions: [
3079
- { action: "add", description: "Configure the optional fields" },
3080
- { action: "done", description: "Skip the optional fields" }
3345
+ affordance(AFFORDANCE.add, "Configure the optional fields"),
3346
+ affordance(AFFORDANCE.done, "Skip the optional fields")
3081
3347
  ]
3082
3348
  };
3083
3349
  }
@@ -3095,7 +3361,8 @@ function finalize(ctx, resolved) {
3095
3361
  }));
3096
3362
  return { status: "invalid", issues };
3097
3363
  }
3098
- var optionalsMarker = (path) => `${key(path)}?optionals`;
3364
+ var optionalsMarker = (path) => `${pathToKey(path)}?optionals`;
3365
+ var UNSAFE_RECORD_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3099
3366
  async function findInArray(ctx, state, path) {
3100
3367
  if (isSettled(state, path)) return null;
3101
3368
  if (getAtPath(state.resolved, path) == null)
@@ -3116,7 +3383,28 @@ async function findInArray(ctx, state, path) {
3116
3383
  }
3117
3384
  }
3118
3385
  if (len < min) return descendItem(ctx, state, path, len, item);
3119
- if (len < max) return { kind: "array", path, count: len, min, max };
3386
+ if (len < max) return { type: "array_items", path, count: len, min, max };
3387
+ settle(state, path);
3388
+ return null;
3389
+ }
3390
+ async function findInRecord(ctx, state, path) {
3391
+ if (isSettled(state, path)) return null;
3392
+ if (getAtPath(state.resolved, path) == null)
3393
+ setAtPath(state.resolved, path, {});
3394
+ if (!state.interactive) {
3395
+ settle(state, path);
3396
+ return null;
3397
+ }
3398
+ const { min, max, keyLeaf, fixedKeys } = await recordInfoAt(
3399
+ ctx,
3400
+ path,
3401
+ state.resolved
3402
+ );
3403
+ const container = getAtPath(state.resolved, path);
3404
+ const fixed = new Set(fixedKeys);
3405
+ const count = Object.keys(container).filter((k) => !fixed.has(k)).length;
3406
+ if (count < min) return { type: "record_key", path, leaf: keyLeaf };
3407
+ if (count < max) return { type: "record_entries", path, count, min, max };
3120
3408
  settle(state, path);
3121
3409
  return null;
3122
3410
  }
@@ -3134,10 +3422,10 @@ function seedItemSlot(state, itemPath, item) {
3134
3422
  }
3135
3423
  async function descendItem(ctx, state, arrayPath, index, item) {
3136
3424
  const itemPath = [...arrayPath, index];
3137
- const kind = seedItemSlot(state, itemPath, item);
3138
- if (kind === "object") return findNext(ctx, state, itemPath);
3139
- if (kind === "array") return findInArray(ctx, state, itemPath);
3140
- return { kind: "leaf", path: itemPath, leaf: item };
3425
+ const slotType = seedItemSlot(state, itemPath, item);
3426
+ if (slotType === "object") return findNext(ctx, state, itemPath);
3427
+ if (slotType === "array") return findInArray(ctx, state, itemPath);
3428
+ return { type: "leaf", path: itemPath, leaf: item };
3141
3429
  }
3142
3430
  async function findNext(ctx, state, path = []) {
3143
3431
  const container = getAtPath(state.resolved, path) ?? {};
@@ -3162,7 +3450,7 @@ async function findNext(ctx, state, path = []) {
3162
3450
  const pending = ordered.filter(
3163
3451
  (c) => !c.required && asksUser(c) && isPendingChild(c)
3164
3452
  );
3165
- return { kind: "optionals", path, pending };
3453
+ return { type: "object_optional_properties", path, pending };
3166
3454
  }
3167
3455
  if (leaf.resolver?.type === "object") {
3168
3456
  if (isSettled(state, childPath)) continue;
@@ -3172,7 +3460,7 @@ async function findNext(ctx, state, path = []) {
3172
3460
  settle(state, childPath);
3173
3461
  continue;
3174
3462
  }
3175
- return { kind: "object", path: childPath, leaf };
3463
+ return { type: "object_optional", path: childPath, leaf };
3176
3464
  }
3177
3465
  setAtPath(state.resolved, childPath, {});
3178
3466
  }
@@ -3204,13 +3492,21 @@ async function findNext(ctx, state, path = []) {
3204
3492
  }
3205
3493
  if (container[leaf.name] !== void 0 || isSettled(state, childPath))
3206
3494
  continue;
3207
- return { kind: "leaf", path: childPath, leaf };
3495
+ return { type: "leaf", path: childPath, leaf };
3496
+ }
3497
+ if (inObject && !isSettled(state, path)) {
3498
+ const self = await leafAt(ctx, path, state.resolved);
3499
+ if (self?.resolver?.type === "object" && self.resolver.additionalKeys) {
3500
+ const rec = await findInRecord(ctx, state, path);
3501
+ if (rec) return rec;
3502
+ }
3208
3503
  }
3209
3504
  return null;
3210
3505
  }
3211
3506
  async function askLeaf(state, path, leaf, opts = {}) {
3212
3507
  state.current = path;
3213
- delete state.gate;
3508
+ if (opts.gate) state.gate = opts.gate;
3509
+ else delete state.gate;
3214
3510
  try {
3215
3511
  const { question, pagination } = await buildQuestion(
3216
3512
  leaf,
@@ -3231,6 +3527,28 @@ async function askLeaf(state, path, leaf, opts = {}) {
3231
3527
  return failedResult(state, leaf.name, error);
3232
3528
  }
3233
3529
  }
3530
+ async function askRecordKey(state, path, keyLeaf, opts = {}) {
3531
+ return askLeaf(state, path, keyLeaf, { gate: "record_key", ...opts });
3532
+ }
3533
+ async function autoResolveLeaf(state, path, leaf) {
3534
+ const resolver = leaf.resolver;
3535
+ if (resolver && autoSettles(resolver)) {
3536
+ if (resolver.type === "constant")
3537
+ setAtPath(state.resolved, path, resolver.value);
3538
+ settle(state, path);
3539
+ return true;
3540
+ }
3541
+ const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
3542
+ input: mergeInput(state.resolved, leaf.extraInput)
3543
+ }) : void 0;
3544
+ if (auto) {
3545
+ if (auto.resolvedValue !== void 0)
3546
+ setAtPath(state.resolved, path, auto.resolvedValue);
3547
+ settle(state, path);
3548
+ return true;
3549
+ }
3550
+ return false;
3551
+ }
3234
3552
  async function advance(ctx, state) {
3235
3553
  for (; ; ) {
3236
3554
  const target = await findNext(ctx, state);
@@ -3240,54 +3558,56 @@ async function advance(ctx, state) {
3240
3558
  delete state.pagination;
3241
3559
  return { state, result: finalize(ctx, state.resolved) };
3242
3560
  }
3243
- if (target.kind === "array") {
3561
+ if (target.type === "array_items") {
3244
3562
  state.current = target.path;
3245
- state.gate = "array";
3563
+ state.gate = "array_items";
3246
3564
  delete state.pagination;
3247
3565
  return {
3248
3566
  state,
3249
- result: { status: "ask", question: collectionQuestion(target) }
3567
+ result: { status: "ask", question: arrayItemsQuestion(target) }
3250
3568
  };
3251
3569
  }
3252
- if (target.kind === "object") {
3570
+ if (target.type === "object_optional") {
3253
3571
  state.current = target.path;
3254
- state.gate = "entry";
3572
+ state.gate = "object_optional";
3255
3573
  delete state.pagination;
3256
3574
  return {
3257
3575
  state,
3258
- result: { status: "ask", question: objectGateQuestion(target.path) }
3576
+ result: {
3577
+ status: "ask",
3578
+ question: objectOptionalQuestion(target.path)
3579
+ }
3259
3580
  };
3260
3581
  }
3261
- if (target.kind === "optionals") {
3582
+ if (target.type === "object_optional_properties") {
3262
3583
  state.current = target.path;
3263
- state.gate = "optionals";
3584
+ state.gate = "object_optional_properties";
3264
3585
  delete state.pagination;
3265
3586
  return {
3266
3587
  state,
3267
3588
  result: {
3268
3589
  status: "ask",
3269
- question: optionalsGateQuestion(target.path, target.pending)
3590
+ question: objectOptionalPropertiesQuestion(
3591
+ target.path,
3592
+ target.pending
3593
+ )
3270
3594
  }
3271
3595
  };
3272
3596
  }
3273
- const { path, leaf } = target;
3274
- const resolver = leaf.resolver;
3275
- if (resolver && autoSettles(resolver)) {
3276
- if (resolver.type === "constant") {
3277
- setAtPath(state.resolved, path, resolver.value);
3278
- }
3279
- settle(state, path);
3280
- continue;
3597
+ if (target.type === "record_entries") {
3598
+ state.current = target.path;
3599
+ state.gate = "record_entries";
3600
+ delete state.pagination;
3601
+ return {
3602
+ state,
3603
+ result: { status: "ask", question: recordEntriesQuestion(target) }
3604
+ };
3281
3605
  }
3282
- const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
3283
- input: mergeInput(state.resolved, leaf.extraInput)
3284
- }) : void 0;
3285
- if (auto) {
3286
- if (auto.resolvedValue !== void 0)
3287
- setAtPath(state.resolved, path, auto.resolvedValue);
3288
- settle(state, path);
3289
- continue;
3606
+ if (target.type === "record_key") {
3607
+ return askRecordKey(state, target.path, target.leaf);
3290
3608
  }
3609
+ const { path, leaf } = target;
3610
+ if (await autoResolveLeaf(state, path, leaf)) continue;
3291
3611
  if (!state.interactive) {
3292
3612
  if (!leaf.required) {
3293
3613
  settle(state, path);
@@ -3320,10 +3640,18 @@ async function step(ctx, prior, action) {
3320
3640
  delete state.pagination;
3321
3641
  return { state, result: { status: "cancelled" } };
3322
3642
  }
3643
+ if (state.gate === "record_key") {
3644
+ return stepRecordKey(ctx, state, action);
3645
+ }
3323
3646
  const path = state.current;
3324
3647
  if (!path) throw new Error("step called with no outstanding question");
3325
3648
  const leaf = await leafAt(ctx, path, state.resolved);
3326
- if (leaf && (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry")) {
3649
+ if (!leaf) {
3650
+ throw new Error(
3651
+ `no resolver for the outstanding question at "${pathToKey(path)}"`
3652
+ );
3653
+ }
3654
+ if (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry") {
3327
3655
  return refine(ctx, state, leaf, path, action);
3328
3656
  }
3329
3657
  if (action.type === "add" || action.type === "done") {
@@ -3340,19 +3668,26 @@ async function step(ctx, prior, action) {
3340
3668
  settle(state, path);
3341
3669
  return advance(ctx, state);
3342
3670
  }
3343
- if (gate === "entry") {
3671
+ if (gate === "object_optional") {
3344
3672
  setAtPath(state.resolved, path, {});
3345
3673
  return advance(ctx, state);
3346
3674
  }
3347
- if (gate === "optionals") {
3675
+ if (gate === "object_optional_properties") {
3348
3676
  remember(state, optionalsMarker(path));
3349
3677
  return advance(ctx, state);
3350
3678
  }
3679
+ if (gate === "record_entries") {
3680
+ const { keyLeaf } = await recordInfoAt(ctx, path, state.resolved);
3681
+ return askRecordKey(state, path, keyLeaf);
3682
+ }
3351
3683
  const items = getAtPath(state.resolved, path) ?? [];
3352
3684
  const { item } = await arrayInfoAt(ctx, path, state.resolved);
3353
3685
  const itemPath = [...path, items.length];
3354
- if (seedItemSlot(state, itemPath, item) === "leaf")
3686
+ if (seedItemSlot(state, itemPath, item) === "leaf") {
3687
+ if (await autoResolveLeaf(state, itemPath, item))
3688
+ return advance(ctx, state);
3355
3689
  return askLeaf(state, itemPath, item);
3690
+ }
3356
3691
  return advance(ctx, state);
3357
3692
  }
3358
3693
  if (state.gate) {
@@ -3363,81 +3698,37 @@ async function step(ctx, prior, action) {
3363
3698
  switch (action.type) {
3364
3699
  case "choose":
3365
3700
  case "custom": {
3366
- if (leaf) {
3367
- let error;
3368
- try {
3369
- error = await validationError(leaf, action.value, state);
3370
- } catch (thrown) {
3371
- return failedResult(state, leaf.name, thrown);
3372
- }
3373
- if (error) {
3374
- if (state.pagination && leaf.resolver?.type === "dynamic") {
3375
- try {
3376
- const context = await resolveContext(leaf, state.resolved);
3377
- const page = await fetchListing(
3378
- leaf,
3379
- state.resolved,
3380
- state.pagination.position,
3381
- context
3382
- );
3383
- state.pagination = toPagination(page);
3384
- return {
3385
- state,
3386
- result: {
3387
- status: "ask",
3388
- question: selectQuestion(
3389
- leaf,
3390
- path,
3391
- state.resolved,
3392
- page,
3393
- context
3394
- ),
3395
- error
3396
- }
3397
- };
3398
- } catch (fetchError) {
3399
- state.pagination = failedPagination(
3400
- state.pagination,
3401
- state.pagination.position
3402
- );
3403
- return failedResult(state, leaf.name, fetchError);
3404
- }
3405
- }
3406
- return askLeaf(state, path, leaf, { error });
3701
+ let error;
3702
+ try {
3703
+ error = await validationError(leaf, action.value, state);
3704
+ } catch (thrown) {
3705
+ return failedResult(state, leaf.name, thrown);
3706
+ }
3707
+ if (error) {
3708
+ if (state.pagination && leaf.resolver?.type === "dynamic") {
3709
+ return renderPageAt(state, leaf, path, state.pagination.position, {
3710
+ error
3711
+ });
3407
3712
  }
3713
+ return askLeaf(state, path, leaf, { error });
3408
3714
  }
3409
- setAtPath(
3410
- state.resolved,
3411
- path,
3412
- leaf ? coerce(leaf, action.value) : action.value
3413
- );
3715
+ setAtPath(state.resolved, path, coerce(leaf, action.value));
3414
3716
  break;
3415
3717
  }
3416
3718
  case "skip":
3417
3719
  settle(state, path);
3418
3720
  break;
3419
3721
  default:
3420
- throw new Error(`action "${action.type}" is not supported here`);
3722
+ throw new Error(
3723
+ `action "${action.type}" is not supported here`
3724
+ );
3421
3725
  }
3422
3726
  delete state.current;
3423
3727
  delete state.pagination;
3424
3728
  return advance(ctx, state);
3425
3729
  }
3426
- async function refine(ctx, state, leaf, path, action) {
3427
- const position = positionAfter(state.pagination, action);
3730
+ async function renderPageAt(state, leaf, path, position, opts = {}) {
3428
3731
  try {
3429
- if (action.type === "search") {
3430
- const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
3431
- input: mergeInput(state.resolved, leaf.extraInput),
3432
- search: action.term
3433
- }) : void 0;
3434
- if (exact) {
3435
- setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
3436
- delete state.current;
3437
- delete state.pagination;
3438
- return advance(ctx, state);
3439
- }
3440
- }
3441
3732
  const context = await resolveContext(leaf, state.resolved);
3442
3733
  const page = await fetchListing(leaf, state.resolved, position, context);
3443
3734
  state.pagination = toPagination(page);
@@ -3445,7 +3736,8 @@ async function refine(ctx, state, leaf, path, action) {
3445
3736
  state,
3446
3737
  result: {
3447
3738
  status: "ask",
3448
- question: selectQuestion(leaf, path, state.resolved, page, context)
3739
+ question: selectQuestion(leaf, path, state.resolved, page, context),
3740
+ ...opts.error ? { error: opts.error } : {}
3449
3741
  }
3450
3742
  };
3451
3743
  } catch (error) {
@@ -3453,6 +3745,65 @@ async function refine(ctx, state, leaf, path, action) {
3453
3745
  return failedResult(state, leaf.name, error);
3454
3746
  }
3455
3747
  }
3748
+ async function refine(ctx, state, leaf, path, action) {
3749
+ const position = positionAfter(state.pagination, action);
3750
+ if (action.type === "search") {
3751
+ try {
3752
+ const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
3753
+ input: mergeInput(state.resolved, leaf.extraInput),
3754
+ search: action.term
3755
+ }) : void 0;
3756
+ if (exact) {
3757
+ setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
3758
+ delete state.current;
3759
+ delete state.pagination;
3760
+ return advance(ctx, state);
3761
+ }
3762
+ } catch (error) {
3763
+ state.pagination = failedPagination(state.pagination, position);
3764
+ return failedResult(state, leaf.name, error);
3765
+ }
3766
+ }
3767
+ return renderPageAt(state, leaf, path, position);
3768
+ }
3769
+ async function stepRecordKey(ctx, state, action) {
3770
+ const path = state.current;
3771
+ if (!path)
3772
+ throw new Error("record key step called with no outstanding question");
3773
+ const { keyLeaf, valueLeaf } = await recordInfoAt(ctx, path, state.resolved);
3774
+ if (action.type === "skip") {
3775
+ delete state.gate;
3776
+ delete state.current;
3777
+ delete state.pagination;
3778
+ return advance(ctx, state);
3779
+ }
3780
+ if (action.type !== "custom" && action.type !== "choose") {
3781
+ throw new Error(
3782
+ `action "${action.type}" is not supported while entering a record key`
3783
+ );
3784
+ }
3785
+ const raw = Array.isArray(action.value) ? action.value[0] : action.value;
3786
+ const entryKey = String(coerce(keyLeaf, raw));
3787
+ if (entryKey.trim() === "") {
3788
+ return askRecordKey(state, path, keyLeaf, { error: "A key is required." });
3789
+ }
3790
+ if (UNSAFE_RECORD_KEYS.has(entryKey)) {
3791
+ return askRecordKey(state, path, keyLeaf, {
3792
+ error: `"${entryKey}" is not an allowed key.`
3793
+ });
3794
+ }
3795
+ const container = getAtPath(state.resolved, path);
3796
+ if (Object.prototype.hasOwnProperty.call(container, entryKey)) {
3797
+ return askRecordKey(state, path, keyLeaf, {
3798
+ error: `"${entryKey}" is already set.`
3799
+ });
3800
+ }
3801
+ const valuePath = [...path, entryKey];
3802
+ if (await autoResolveLeaf(state, valuePath, valueLeaf)) {
3803
+ return advance(ctx, state);
3804
+ }
3805
+ return askLeaf(state, valuePath, valueLeaf);
3806
+ }
3456
3807
  function failedPagination(pagination, retryPosition) {
3457
3808
  return {
3458
3809
  position: pagination?.position ?? firstPagePosition(),
@@ -3508,7 +3859,7 @@ function projectSummary(entry) {
3508
3859
  };
3509
3860
  }
3510
3861
  function projectMethod(entry) {
3511
- const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
3862
+ const inputProperties = toJsonSchema(canonicalInputSchema(entry.inputSchema))?.properties;
3512
3863
  const parameters = {};
3513
3864
  for (const spec of planParameters(entry).parameters) {
3514
3865
  const dynamic = spec.resolver?.type === "dynamic" ? spec.resolver : void 0;
@@ -3541,7 +3892,12 @@ function createController(sdk) {
3541
3892
  const entry = entryFor(method);
3542
3893
  return {
3543
3894
  method,
3544
- schema: entry.inputSchema,
3895
+ // A method that owns its input validation (`skipInputValidation`, e.g.
3896
+ // fetch) must not be re-validated by the controller's final `safeParse`;
3897
+ // drop the schema so `finalize` returns the resolved input untouched.
3898
+ // Planning still reads `entry.inputSchema` directly, so parameters are
3899
+ // unaffected.
3900
+ schema: entry.skipInputValidation ? void 0 : entry.inputSchema,
3545
3901
  parameters: planParameters(entry).parameters
3546
3902
  };
3547
3903
  }
@@ -3606,59 +3962,6 @@ function createCorePlugin(options) {
3606
3962
  }
3607
3963
  });
3608
3964
  }
3609
-
3610
- // src/utils/schema-utils.ts
3611
- import { z as z5 } from "zod";
3612
- function getOutputSchema(inputSchema) {
3613
- return inputSchema._zod.def.outputSchema;
3614
- }
3615
- function withOutputSchema(inputSchema, outputSchema) {
3616
- Object.assign(inputSchema._zod.def, {
3617
- outputSchema
3618
- });
3619
- return inputSchema;
3620
- }
3621
- function withResolver(schema, config) {
3622
- schema._zod.def.resolverMeta = config;
3623
- return schema;
3624
- }
3625
- function getSchemaDescription(schema) {
3626
- return schema.description;
3627
- }
3628
- function getFieldDescriptions(schema) {
3629
- const descriptions = {};
3630
- const shape = schema.shape;
3631
- for (const [key2, fieldSchema] of Object.entries(shape)) {
3632
- if (fieldSchema instanceof z5.ZodType && fieldSchema.description) {
3633
- descriptions[key2] = fieldSchema.description;
3634
- }
3635
- }
3636
- return descriptions;
3637
- }
3638
- function withPositional(schema) {
3639
- Object.assign(schema._zod.def, {
3640
- positionalMeta: { positional: true }
3641
- });
3642
- return schema;
3643
- }
3644
- function schemaHasPositionalMeta(schema) {
3645
- return "positionalMeta" in schema._zod.def;
3646
- }
3647
- function isPositional(schema) {
3648
- if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
3649
- return true;
3650
- }
3651
- if (schema instanceof z5.ZodOptional) {
3652
- return isPositional(schema._zod.def.innerType);
3653
- }
3654
- if (schema instanceof z5.ZodDefault) {
3655
- return isPositional(schema._zod.def.innerType);
3656
- }
3657
- return false;
3658
- }
3659
- function openEnum(values, description) {
3660
- return z5.union([z5.enum(values), z5.string()]).describe(description);
3661
- }
3662
3965
  export {
3663
3966
  CONTEXT,
3664
3967
  CORE_ERROR_SYMBOL,
@@ -3670,6 +3973,7 @@ export {
3670
3973
  CoreErrorCode,
3671
3974
  CoreSignal,
3672
3975
  addPlugin,
3976
+ canonicalInputSchema,
3673
3977
  composePlugins,
3674
3978
  concatLists,
3675
3979
  concatPaginated,