@zapier/kitcore 0.8.0 → 0.10.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.cjs CHANGED
@@ -9,9 +9,9 @@ var __export = (target, all) => {
9
9
  };
10
10
  var __copyProps = (to, from, except, desc) => {
11
11
  if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key2 of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key2) && key2 !== except)
14
- __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
15
  }
16
16
  return to;
17
17
  };
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  CoreErrorCode: () => CoreErrorCode,
31
31
  CoreSignal: () => CoreSignal,
32
32
  addPlugin: () => addPlugin,
33
+ canonicalInputSchema: () => canonicalInputSchema,
33
34
  composePlugins: () => composePlugins,
34
35
  concatLists: () => concatLists,
35
36
  concatPaginated: () => concatPaginated,
@@ -73,6 +74,7 @@ __export(index_exports, {
73
74
  getOutputSchema: () => getOutputSchema,
74
75
  getRegistryPlugin: () => getRegistryPlugin,
75
76
  getSchemaDescription: () => getSchemaDescription,
77
+ isCoreCancelledSignal: () => isCoreCancelledSignal,
76
78
  isCoreError: () => isCoreError,
77
79
  isCoreSignal: () => isCoreSignal,
78
80
  isNestedMethodCall: () => isNestedMethodCall,
@@ -98,9 +100,6 @@ __export(index_exports, {
98
100
  });
99
101
  module.exports = __toCommonJS(index_exports);
100
102
 
101
- // src/registry.ts
102
- var import_zod = require("zod");
103
-
104
103
  // src/utils/string-utils.ts
105
104
  function toTitleCase(input) {
106
105
  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(" ");
@@ -124,6 +123,65 @@ function pluralizeLastWord(title) {
124
123
  return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
125
124
  }
126
125
 
126
+ // src/utils/schema-utils.ts
127
+ var import_zod = require("zod");
128
+ function canonicalInputSchema(schema) {
129
+ if (schema instanceof import_zod.z.ZodUnion) {
130
+ return schema.options[0];
131
+ }
132
+ return schema;
133
+ }
134
+ function getOutputSchema(inputSchema) {
135
+ return inputSchema._zod.def.outputSchema;
136
+ }
137
+ function withOutputSchema(inputSchema, outputSchema) {
138
+ Object.assign(inputSchema._zod.def, {
139
+ outputSchema
140
+ });
141
+ return inputSchema;
142
+ }
143
+ function withResolver(schema, config) {
144
+ schema._zod.def.resolverMeta = config;
145
+ return schema;
146
+ }
147
+ function getSchemaDescription(schema) {
148
+ return schema.description;
149
+ }
150
+ function getFieldDescriptions(schema) {
151
+ const descriptions = {};
152
+ const shape = schema.shape;
153
+ for (const [key, fieldSchema] of Object.entries(shape)) {
154
+ if (fieldSchema instanceof import_zod.z.ZodType && fieldSchema.description) {
155
+ descriptions[key] = fieldSchema.description;
156
+ }
157
+ }
158
+ return descriptions;
159
+ }
160
+ function withPositional(schema) {
161
+ Object.assign(schema._zod.def, {
162
+ positionalMeta: { positional: true }
163
+ });
164
+ return schema;
165
+ }
166
+ function schemaHasPositionalMeta(schema) {
167
+ return "positionalMeta" in schema._zod.def;
168
+ }
169
+ function isPositional(schema) {
170
+ if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
171
+ return true;
172
+ }
173
+ if (schema instanceof import_zod.z.ZodOptional) {
174
+ return isPositional(schema._zod.def.innerType);
175
+ }
176
+ if (schema instanceof import_zod.z.ZodDefault) {
177
+ return isPositional(schema._zod.def.innerType);
178
+ }
179
+ return false;
180
+ }
181
+ function openEnum(values, description) {
182
+ return import_zod.z.union([import_zod.z.enum(values), import_zod.z.string()]).describe(description);
183
+ }
184
+
127
185
  // src/registry.ts
128
186
  function resolveCategoryDefinition(ref) {
129
187
  const def = typeof ref === "string" ? { key: ref } : ref;
@@ -134,30 +192,25 @@ function resolveCategoryDefinition(ref) {
134
192
  titlePlural: def.titlePlural ?? pluralizeLastWord(title)
135
193
  };
136
194
  }
137
- function canonicalInputSchema(schema) {
138
- if (schema instanceof import_zod.z.ZodUnion) {
139
- return schema.options[0];
140
- }
141
- return schema;
142
- }
143
195
  function buildRegistry({
144
196
  sdk,
145
197
  meta,
146
198
  formatters,
147
- boundResolvers,
199
+ resolvers,
148
200
  positional,
201
+ skipInputValidation,
149
202
  packageFilter
150
203
  }) {
151
204
  const definitionsByKey = /* @__PURE__ */ new Map();
152
205
  const objectDeclaredKeys = /* @__PURE__ */ new Set();
153
206
  for (const m of Object.values(meta)) {
154
207
  for (const ref of m.categories ?? []) {
155
- const key2 = typeof ref === "string" ? ref : ref.key;
208
+ const key = typeof ref === "string" ? ref : ref.key;
156
209
  if (typeof ref === "object") {
157
- objectDeclaredKeys.add(key2);
158
- definitionsByKey.set(key2, resolveCategoryDefinition(ref));
159
- } else if (!objectDeclaredKeys.has(key2)) {
160
- definitionsByKey.set(key2, resolveCategoryDefinition(ref));
210
+ objectDeclaredKeys.add(key);
211
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
212
+ } else if (!objectDeclaredKeys.has(key)) {
213
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
161
214
  }
162
215
  }
163
216
  }
@@ -165,30 +218,29 @@ function buildRegistry({
165
218
  definitionsByKey.set("other", resolveCategoryDefinition("other"));
166
219
  }
167
220
  const knownCategories = Array.from(definitionsByKey.keys());
168
- const functions = Object.keys(meta).filter((key2) => {
169
- const property = sdk[key2];
221
+ const functions = Object.keys(meta).filter((key) => {
222
+ const property = sdk[key];
170
223
  if (typeof property === "function") return true;
171
- const [rootKey] = key2.split(".");
224
+ const [rootKey] = key.split(".");
172
225
  const rootProperty = sdk[rootKey];
173
226
  return typeof rootProperty === "object" && rootProperty !== null;
174
- }).map((key2) => {
175
- const m = meta[key2];
227
+ }).map((key) => {
228
+ const m = meta[key];
176
229
  return {
177
- name: key2,
230
+ name: key,
178
231
  description: m.description,
179
232
  type: m.type,
180
233
  itemType: m.itemType,
181
234
  returnType: m.returnType,
182
235
  inputSchema: canonicalInputSchema(m.inputSchema),
183
- inputParameters: m.inputParameters,
184
236
  outputSchema: m.outputSchema,
185
- positional: positional?.[key2],
237
+ positional: positional?.[key],
238
+ skipInputValidation: skipInputValidation?.[key],
186
239
  categories: (m.categories ?? []).map(
187
240
  (c) => typeof c === "string" ? c : c.key
188
241
  ),
189
- resolvers: m.resolvers,
190
- boundResolvers: boundResolvers?.[key2],
191
- formatter: formatters?.[key2],
242
+ resolvers: resolvers?.[key],
243
+ formatter: formatters?.[key],
192
244
  experimental: m.experimental,
193
245
  packages: m.packages,
194
246
  confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
@@ -675,6 +727,52 @@ function runInMethodScope(fn) {
675
727
  var runWithTelemetryContext = runInMethodScope;
676
728
  var isTelemetryNested = isNestedMethodCall;
677
729
 
730
+ // src/utils/call-context.ts
731
+ var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
732
+ function isCallContext(value) {
733
+ return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
734
+ }
735
+ function generateCallId() {
736
+ try {
737
+ const webCrypto = globalThis.crypto;
738
+ if (webCrypto?.randomUUID) {
739
+ return webCrypto.randomUUID();
740
+ }
741
+ if (webCrypto?.getRandomValues) {
742
+ const bytes = webCrypto.getRandomValues(new Uint8Array(16));
743
+ const hex = Array.from(bytes, (byte, i) => {
744
+ const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
745
+ return value.toString(16).padStart(2, "0");
746
+ });
747
+ return [
748
+ hex.slice(0, 4).join(""),
749
+ hex.slice(4, 6).join(""),
750
+ hex.slice(6, 8).join(""),
751
+ hex.slice(8, 10).join(""),
752
+ hex.slice(10, 16).join("")
753
+ ].join("-");
754
+ }
755
+ } catch {
756
+ }
757
+ return null;
758
+ }
759
+ function rootCallContext() {
760
+ return {
761
+ callId: generateCallId(),
762
+ depth: 0,
763
+ annotations: {},
764
+ [CALL_CONTEXT_BRAND]: true
765
+ };
766
+ }
767
+ function childCallContext(parent) {
768
+ return {
769
+ callId: parent.callId,
770
+ depth: parent.depth + 1,
771
+ annotations: {},
772
+ [CALL_CONTEXT_BRAND]: true
773
+ };
774
+ }
775
+
678
776
  // src/utils/core-options.ts
679
777
  function defaultLogDeprecation({
680
778
  methodName,
@@ -693,6 +791,9 @@ function resolveCoreOptions(context) {
693
791
  return context.core;
694
792
  }
695
793
  var INTERNAL_CALL = Symbol("kitcore.internalCall");
794
+ function resolveCallContext(secondArg) {
795
+ return isCallContext(secondArg) ? secondArg : rootCallContext();
796
+ }
696
797
  function signalDeprecation(context, methodName, getDeprecation) {
697
798
  if (isInsideObserver()) return;
698
799
  const deprecation = getDeprecation?.();
@@ -722,14 +823,16 @@ function createFunction(coreFn, options) {
722
823
  const functionName = name || coreFn.name;
723
824
  const namedFunctions = {
724
825
  [functionName]: async function(callOptions) {
725
- if (arguments[1] !== INTERNAL_CALL) {
826
+ const internal = arguments[1];
827
+ const context = resolveCallContext(internal);
828
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
726
829
  signalDeprecation(sdk.context, functionName, getDeprecation);
727
830
  }
728
831
  return runInMethodScope(async () => {
729
832
  const startTime = Date.now();
730
833
  const normalizedOptions = callOptions ?? {};
731
834
  const args = [normalizedOptions];
732
- const depth = getCurrentDepth();
835
+ const depth = Math.max(context.depth, getCurrentDepth());
733
836
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
734
837
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
735
838
  hooks?.onMethodStart?.({
@@ -748,12 +851,15 @@ function createFunction(coreFn, options) {
748
851
  adaptError
749
852
  }
750
853
  );
751
- result = await coreFn({
752
- ...normalizedOptions,
753
- ...validatedOptions
754
- });
854
+ result = await coreFn(
855
+ {
856
+ ...normalizedOptions,
857
+ ...validatedOptions
858
+ },
859
+ context
860
+ );
755
861
  } else {
756
- result = await coreFn(normalizedOptions);
862
+ result = await coreFn(normalizedOptions, context);
757
863
  }
758
864
  hooks?.onMethodEnd?.({
759
865
  methodName: functionName,
@@ -783,17 +889,19 @@ function createFunction(coreFn, options) {
783
889
  function createRawFunction(coreFn, options) {
784
890
  const { sdk, name, schema, positional, getDeprecation } = options;
785
891
  return function(rawInput) {
786
- if (arguments[1] !== INTERNAL_CALL) {
892
+ const internal = arguments[1];
893
+ const context = resolveCallContext(internal);
894
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
787
895
  signalDeprecation(sdk.context, name, getDeprecation);
788
896
  }
789
897
  return runInMethodScope(() => {
790
898
  const startTime = Date.now();
791
- const depth = getCurrentDepth();
899
+ const depth = Math.max(context.depth, getCurrentDepth());
792
900
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
793
901
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
794
902
  const input = schema ? rawInput ?? {} : rawInput;
795
903
  const record = input;
796
- const args = positional ? positional.filter((key2) => record?.[key2] !== void 0).map((key2) => record?.[key2]) : [input];
904
+ const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
797
905
  hooks?.onMethodStart?.({
798
906
  methodName: name,
799
907
  args,
@@ -812,7 +920,7 @@ function createRawFunction(coreFn, options) {
812
920
  };
813
921
  try {
814
922
  const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
815
- const result = coreFn(parsed);
923
+ const result = coreFn(parsed, context);
816
924
  if (result !== null && typeof result === "object" && typeof result.then === "function") {
817
925
  return result.then(
818
926
  (value) => {
@@ -851,9 +959,9 @@ function createPageFunction(coreFn, {
851
959
  }) {
852
960
  const functionName = coreFn.name + "Page";
853
961
  const namedFunctions = {
854
- [functionName]: async function(options) {
962
+ [functionName]: async function(options, callContext) {
855
963
  try {
856
- const response = await coreFn(options);
964
+ const response = await coreFn(options, callContext);
857
965
  const page = adaptPage ? adaptPage(response) : response;
858
966
  if (!isSdkPage(page)) {
859
967
  throw new Error(
@@ -877,14 +985,16 @@ function createPaginatedFunction(coreFn, options) {
877
985
  const functionName = name || coreFn.name;
878
986
  const namedFunctions = {
879
987
  [functionName]: function(callOptions) {
880
- if (arguments[1] !== INTERNAL_CALL) {
988
+ const internal = arguments[1];
989
+ const context = resolveCallContext(internal);
990
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
881
991
  signalDeprecation(sdk.context, functionName, getDeprecation);
882
992
  }
883
993
  return runInMethodScope(() => {
884
994
  const startTime = Date.now();
885
995
  const normalizedOptions = callOptions ?? {};
886
996
  const args = [normalizedOptions];
887
- const depth = getCurrentDepth();
997
+ const depth = Math.max(context.depth, getCurrentDepth());
888
998
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
889
999
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
890
1000
  hooks?.onMethodStart?.({
@@ -903,7 +1013,11 @@ function createPaginatedFunction(coreFn, options) {
903
1013
  ...validatedOptions,
904
1014
  pageSize
905
1015
  };
906
- const iterator = paginate(pageFunction, optimizedOptions);
1016
+ const iterator = paginate(
1017
+ (pageOptions) => pageFunction(pageOptions, context),
1018
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1019
+ optimizedOptions
1020
+ );
907
1021
  const firstPagePromise = iterator.next().then((result) => {
908
1022
  if (result.done) {
909
1023
  throw new Error("Paginate should always iterate at least once");
@@ -1054,11 +1168,11 @@ var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
1054
1168
  "context",
1055
1169
  "getRegistry"
1056
1170
  ]);
1057
- function hasOwn(obj, key2) {
1058
- return Object.prototype.hasOwnProperty.call(obj, key2);
1171
+ function hasOwn(obj, key) {
1172
+ return Object.prototype.hasOwnProperty.call(obj, key);
1059
1173
  }
1060
- function setOwn(target, key2, value) {
1061
- Object.defineProperty(target, key2, {
1174
+ function setOwn(target, key, value) {
1175
+ Object.defineProperty(target, key, {
1062
1176
  value,
1063
1177
  enumerable: true,
1064
1178
  configurable: true,
@@ -1070,31 +1184,31 @@ function checkCollisions(target, source, kind, callerLabel, override) {
1070
1184
  checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
1071
1185
  return;
1072
1186
  }
1073
- for (const key2 of Object.keys(source)) {
1074
- if (!override && hasOwn(target, key2)) {
1187
+ for (const key of Object.keys(source)) {
1188
+ if (!override && hasOwn(target, key)) {
1075
1189
  throw new Error(
1076
- `${callerLabel}: duplicate ${kind} "${key2}". If the override is intentional, pass { override: true } in the options.`
1190
+ `${callerLabel}: duplicate ${kind} "${key}". If the override is intentional, pass { override: true } in the options.`
1077
1191
  );
1078
1192
  }
1079
1193
  }
1080
1194
  }
1081
1195
  function checkRootKeyCollisions(target, keys, override, callerLabel) {
1082
- for (const key2 of keys) {
1083
- if (RESERVED_ROOT_KEYS.has(key2)) {
1196
+ for (const key of keys) {
1197
+ if (RESERVED_ROOT_KEYS.has(key)) {
1084
1198
  throw new Error(
1085
- `${callerLabel}: plugin attempted to register reserved root key "${key2}". The SDK uses this key for its own accessor; rename the plugin's method.`
1199
+ `${callerLabel}: plugin attempted to register reserved root key "${key}". The SDK uses this key for its own accessor; rename the plugin's method.`
1086
1200
  );
1087
1201
  }
1088
- if (!override && hasOwn(target, key2)) {
1202
+ if (!override && hasOwn(target, key)) {
1089
1203
  throw new Error(
1090
- `${callerLabel}: duplicate root key "${key2}". If the override is intentional, pass { override: true } in the options.`
1204
+ `${callerLabel}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
1091
1205
  );
1092
1206
  }
1093
1207
  }
1094
1208
  }
1095
1209
  function applyOwnProperties(target, source) {
1096
- for (const key2 of Object.keys(source)) {
1097
- setOwn(target, key2, source[key2]);
1210
+ for (const key of Object.keys(source)) {
1211
+ setOwn(target, key, source[key]);
1098
1212
  }
1099
1213
  }
1100
1214
  function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
@@ -1312,7 +1426,6 @@ var LEAF_META_KEYS = [
1312
1426
  "itemType",
1313
1427
  "returnType",
1314
1428
  "outputSchema",
1315
- "inputParameters",
1316
1429
  "packages",
1317
1430
  "experimental",
1318
1431
  "confirm",
@@ -1352,8 +1465,8 @@ function normalizeImports(deps) {
1352
1465
  }
1353
1466
  function collectLeafMeta(config) {
1354
1467
  let meta;
1355
- for (const key2 of LEAF_META_KEYS) {
1356
- if (config[key2] !== void 0) (meta ?? (meta = {}))[key2] = config[key2];
1468
+ for (const key of LEAF_META_KEYS) {
1469
+ if (config[key] !== void 0) (meta ?? (meta = {}))[key] = config[key];
1357
1470
  }
1358
1471
  return meta;
1359
1472
  }
@@ -1436,7 +1549,8 @@ function defineResolver(config) {
1436
1549
  type: "object",
1437
1550
  properties: config.properties,
1438
1551
  definitions: config.definitions,
1439
- getProperties: config.getProperties
1552
+ getProperties: config.getProperties,
1553
+ additionalKeys: config.additionalKeys
1440
1554
  };
1441
1555
  case "array":
1442
1556
  return {
@@ -1748,7 +1862,7 @@ function normalizeFormatter(entry, sdk) {
1748
1862
  const legacy = entry.meta?.formatter;
1749
1863
  return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
1750
1864
  }
1751
- function normalizeBoundResolvers(entry) {
1865
+ function normalizeResolvers(entry) {
1752
1866
  if (entry.pluginType !== "method") return void 0;
1753
1867
  return entry.resolvers;
1754
1868
  }
@@ -1789,17 +1903,20 @@ function collectSurfaceProjection(context, formatterSdk) {
1789
1903
  foldDynamicMembers(entry, surfaceBindings, meta);
1790
1904
  }
1791
1905
  const formatters = {};
1792
- const boundResolvers = {};
1906
+ const resolvers = {};
1793
1907
  const positional = {};
1908
+ const skipInputValidation = {};
1794
1909
  for (const [binding, entry] of Object.entries(entries)) {
1795
1910
  const f = normalizeFormatter(entry, formatterSdk);
1796
1911
  if (f) formatters[binding] = f;
1797
- const r = normalizeBoundResolvers(entry);
1798
- if (r) boundResolvers[binding] = r;
1912
+ const r = normalizeResolvers(entry);
1913
+ if (r) resolvers[binding] = r;
1799
1914
  const p = methodPositional(entry);
1800
1915
  if (p) positional[binding] = p;
1916
+ if (entry.pluginType === "method" && entry.skipInputValidation)
1917
+ skipInputValidation[binding] = true;
1801
1918
  }
1802
- return { meta, formatters, boundResolvers, positional };
1919
+ return { meta, formatters, resolvers, positional, skipInputValidation };
1803
1920
  }
1804
1921
  function buildSurfaceRegistry(context, packageFilter) {
1805
1922
  const surface = {};
@@ -1856,6 +1973,11 @@ function nestedResolvers(resolver) {
1856
1973
  for (const field of Object.values(resolver.properties ?? {})) {
1857
1974
  if (!isResolverRef(field.resolver)) out.push(field.resolver);
1858
1975
  }
1976
+ const ak = resolver.additionalKeys;
1977
+ if (ak) {
1978
+ if (!isResolverRef(ak.values)) out.push(ak.values);
1979
+ if (ak.keys && !isResolverRef(ak.keys)) out.push(ak.keys);
1980
+ }
1859
1981
  out.push(...Object.values(resolver.definitions ?? {}));
1860
1982
  } else if (resolver.type === "array") {
1861
1983
  if (!isResolverRef(resolver.items)) out.push(resolver.items);
@@ -1980,16 +2102,16 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
1980
2102
  }
1981
2103
  return byId;
1982
2104
  }
1983
- function bindValue(target, key2, entry, callType = "surface") {
2105
+ function bindValue(target, key, entry, callType = "surface", ctx) {
1984
2106
  if (entry.pluginType === "property" && entry.getValue) {
1985
- Object.defineProperty(target, key2, {
2107
+ Object.defineProperty(target, key, {
1986
2108
  get: entry.getValue,
1987
2109
  enumerable: true,
1988
2110
  configurable: true
1989
2111
  });
1990
2112
  } else {
1991
- const value = callType === "internal" && entry.pluginType === "method" ? entry.internalValue ?? entry.value : entry.value;
1992
- Object.defineProperty(target, key2, {
2113
+ const value = callType === "internal" && entry.pluginType === "method" ? entry.bindInternal?.(ctx) ?? entry.internalValue ?? entry.value : entry.value;
2114
+ Object.defineProperty(target, key, {
1993
2115
  value,
1994
2116
  writable: true,
1995
2117
  enumerable: true,
@@ -2006,7 +2128,7 @@ function buildSurface(context, ...maps) {
2006
2128
  sdk[CONTEXT] = context;
2007
2129
  return sdk;
2008
2130
  }
2009
- function buildImports(plugins, importBindings) {
2131
+ function buildImports(plugins, importBindings, ctx) {
2010
2132
  const imports = {};
2011
2133
  for (const { binding, id, optional } of importBindings) {
2012
2134
  const entry = plugins[id];
@@ -2019,7 +2141,7 @@ function buildImports(plugins, importBindings) {
2019
2141
  });
2020
2142
  continue;
2021
2143
  }
2022
- bindValue(imports, binding, entry, "internal");
2144
+ bindValue(imports, binding, entry, "internal", ctx);
2023
2145
  }
2024
2146
  return imports;
2025
2147
  }
@@ -2098,6 +2220,19 @@ function bindResolver(resolver, plugins) {
2098
2220
  const { getProperties } = resolver;
2099
2221
  if (getProperties)
2100
2222
  bound.getProperties = ({ input }) => getProperties({ imports, input });
2223
+ if (resolver.additionalKeys) {
2224
+ const ak = resolver.additionalKeys;
2225
+ const boundAk = {
2226
+ values: isResolverRef(ak.values) ? ak.values : bindResolver(ak.values, plugins),
2227
+ minEntries: ak.minEntries,
2228
+ maxEntries: ak.maxEntries,
2229
+ keyValueType: ak.keyValueType,
2230
+ valueValueType: ak.valueValueType
2231
+ };
2232
+ if (ak.keys)
2233
+ boundAk.keys = isResolverRef(ak.keys) ? ak.keys : bindResolver(ak.keys, plugins);
2234
+ bound.additionalKeys = boundAk;
2235
+ }
2101
2236
  return bound;
2102
2237
  }
2103
2238
  case "array": {
@@ -2153,8 +2288,8 @@ function bindResolver(resolver, plugins) {
2153
2288
  }
2154
2289
  function bindFields(fields, plugins) {
2155
2290
  const out = {};
2156
- for (const [key2, field] of Object.entries(fields)) {
2157
- out[key2] = {
2291
+ for (const [key, field] of Object.entries(fields)) {
2292
+ out[key] = {
2158
2293
  ...field,
2159
2294
  resolver: isResolverRef(field.resolver) ? field.resolver : bindResolver(field.resolver, plugins)
2160
2295
  };
@@ -2163,8 +2298,8 @@ function bindFields(fields, plugins) {
2163
2298
  }
2164
2299
  function bindDefinitions(definitions, plugins) {
2165
2300
  const out = {};
2166
- for (const [key2, def] of Object.entries(definitions)) {
2167
- out[key2] = bindResolver(def, plugins);
2301
+ for (const [key, def] of Object.entries(definitions)) {
2302
+ out[key] = bindResolver(def, plugins);
2168
2303
  }
2169
2304
  return out;
2170
2305
  }
@@ -2249,6 +2384,7 @@ function buildMethodEntries(descriptors, context, states) {
2249
2384
  name: descriptor.name,
2250
2385
  chain: [],
2251
2386
  inputSchema: descriptor.inputSchema,
2387
+ skipInputValidation: descriptor.skipInputValidation,
2252
2388
  // Derive the presentation type from the output mode when the author did
2253
2389
  // not set one; an explicit meta.type (e.g. "create") still wins.
2254
2390
  meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
@@ -2256,17 +2392,17 @@ function buildMethodEntries(descriptors, context, states) {
2256
2392
  // Replaced below; never called.
2257
2393
  value: () => void 0
2258
2394
  };
2259
- const callRun = (input) => descriptor.run({
2260
- imports: buildImports(plugins, descriptor.importBindings),
2395
+ const callRun = (input, ctx) => descriptor.run({
2396
+ imports: buildImports(plugins, descriptor.importBindings, ctx),
2261
2397
  state: states.get(id),
2262
2398
  input
2263
2399
  });
2264
- const fold = (coreFn) => (input) => {
2265
- let next = coreFn;
2400
+ const fold = (coreFn) => (input, ctx) => {
2401
+ let next = (i) => coreFn(i, ctx);
2266
2402
  for (const wrap of entry.chain) {
2267
2403
  const inner = next;
2268
2404
  next = (i) => wrap.run({
2269
- imports: buildImports(plugins, wrap.owner.importBindings),
2405
+ imports: buildImports(plugins, wrap.owner.importBindings, ctx),
2270
2406
  next: inner,
2271
2407
  input: i,
2272
2408
  // Overwritten by the chain item's own closure with the owning
@@ -2290,7 +2426,7 @@ function buildMethodEntries(descriptors, context, states) {
2290
2426
  }
2291
2427
  );
2292
2428
  } else if (out.type === "item") {
2293
- const itemCore = async (input) => callRun(input);
2429
+ const itemCore = async (input, ctx) => callRun(input, ctx);
2294
2430
  entry.value = createFunction(
2295
2431
  fold(itemCore),
2296
2432
  {
@@ -2302,7 +2438,7 @@ function buildMethodEntries(descriptors, context, states) {
2302
2438
  );
2303
2439
  } else {
2304
2440
  entry.value = createRawFunction(
2305
- (input) => fold(callRun)(input),
2441
+ (input, ctx) => fold(callRun)(input, ctx),
2306
2442
  {
2307
2443
  sdk,
2308
2444
  name: descriptor.name,
@@ -2325,11 +2461,15 @@ function buildMethodEntries(descriptors, context, states) {
2325
2461
  });
2326
2462
  return packed;
2327
2463
  };
2464
+ const internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2328
2465
  entry.value = (...args) => canonicalValue(pack(args));
2329
- entry.internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2466
+ entry.internalValue = internalValue;
2467
+ entry.bindInternal = (ctx) => ctx ? (...args) => canonicalValue(pack(args), childCallContext(ctx)) : internalValue;
2330
2468
  entry.positional = names;
2331
2469
  } else {
2332
- entry.internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2470
+ const internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2471
+ entry.internalValue = internalValue;
2472
+ entry.bindInternal = (ctx) => ctx ? (input) => canonicalValue(input, childCallContext(ctx)) : internalValue;
2333
2473
  }
2334
2474
  plugins[id] = entry;
2335
2475
  }
@@ -2569,7 +2709,7 @@ function createSdk(root, options) {
2569
2709
  pluginSurface = {};
2570
2710
  bindValue(pluginSurface, plugin.name, plugins2[plugin.id]);
2571
2711
  }
2572
- for (const key2 of Object.keys(legacyExports)) context.surface[key2] = key2;
2712
+ for (const key of Object.keys(legacyExports)) context.surface[key] = key;
2573
2713
  if (plugin.pluginType === "aggregate") {
2574
2714
  recordExportSurface(context, plugin.exports);
2575
2715
  } else {
@@ -2680,6 +2820,9 @@ var CoreCancelledSignal = class extends CoreSignal {
2680
2820
  this.code = "CANCELLED";
2681
2821
  }
2682
2822
  };
2823
+ function isCoreCancelledSignal(value) {
2824
+ return isCoreSignal(value) && value.code === "CANCELLED";
2825
+ }
2683
2826
 
2684
2827
  // src/model/resolution/plan.ts
2685
2828
  var import_zod3 = require("zod");
@@ -2708,6 +2851,7 @@ function valueTypeOf(inner) {
2708
2851
  if (inner instanceof import_zod3.z.ZodEnum) return "string";
2709
2852
  if (inner instanceof import_zod3.z.ZodArray) return "array";
2710
2853
  if (inner instanceof import_zod3.z.ZodObject) return "object";
2854
+ if (inner instanceof import_zod3.z.ZodRecord) return "object";
2711
2855
  return void 0;
2712
2856
  }
2713
2857
  function staticChoicesOf(inner) {
@@ -2718,7 +2862,8 @@ function staticChoicesOf(inner) {
2718
2862
  return void 0;
2719
2863
  }
2720
2864
  function objectShape(schema) {
2721
- const { inner } = schema ? unwrap(schema) : { inner: void 0 };
2865
+ const canonical = canonicalInputSchema(schema);
2866
+ const { inner } = canonical ? unwrap(canonical) : { inner: void 0 };
2722
2867
  if (inner instanceof import_zod3.z.ZodObject) {
2723
2868
  return inner.shape;
2724
2869
  }
@@ -2740,7 +2885,7 @@ function topoOrder2(specs) {
2740
2885
  }
2741
2886
  function planParameters(entry) {
2742
2887
  const shape = objectShape(entry.inputSchema);
2743
- const resolvers = entry.boundResolvers ?? {};
2888
+ const resolvers = entry.resolvers ?? {};
2744
2889
  const names = shape ? [
2745
2890
  ...Object.keys(shape),
2746
2891
  ...Object.keys(resolvers).filter(
@@ -2778,24 +2923,48 @@ function getAtPath(root, path) {
2778
2923
  }
2779
2924
  return node;
2780
2925
  }
2926
+ function defineOwn(node, key, value) {
2927
+ Object.defineProperty(node, key, {
2928
+ value,
2929
+ writable: true,
2930
+ enumerable: true,
2931
+ configurable: true
2932
+ });
2933
+ }
2781
2934
  function setAtPath(root, path, value) {
2782
2935
  let node = root;
2783
2936
  for (let i = 0; i < path.length - 1; i++) {
2784
2937
  const seg = path[i];
2785
- if (node[seg] == null || typeof node[seg] !== "object") node[seg] = {};
2786
- node = node[seg];
2938
+ const existing = Object.prototype.hasOwnProperty.call(node, seg) ? node[seg] : void 0;
2939
+ if (existing != null && typeof existing === "object") {
2940
+ node = existing;
2941
+ } else {
2942
+ const child = {};
2943
+ defineOwn(node, seg, child);
2944
+ node = child;
2945
+ }
2787
2946
  }
2788
- node[path[path.length - 1]] = value;
2947
+ defineOwn(node, path[path.length - 1], value);
2789
2948
  }
2790
- var key = (path) => path.join(".");
2949
+ var SAFE_SEGMENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2950
+ var pathToKey = (path) => {
2951
+ let out = "";
2952
+ for (const segment of path) {
2953
+ if (typeof segment === "number") out += `[${segment}]`;
2954
+ else if (SAFE_SEGMENT.test(segment))
2955
+ out += out === "" ? segment : `.${segment}`;
2956
+ else out += `[${JSON.stringify(segment)}]`;
2957
+ }
2958
+ return out;
2959
+ };
2791
2960
  function isSettled(state, path) {
2792
- return state.settled.includes(key(path));
2961
+ return state.settled.includes(pathToKey(path));
2793
2962
  }
2794
2963
  function remember(state, k) {
2795
2964
  if (!state.settled.includes(k)) state.settled.push(k);
2796
2965
  }
2797
2966
  function settle(state, path) {
2798
- remember(state, key(path));
2967
+ remember(state, pathToKey(path));
2799
2968
  }
2800
2969
  function clone(state) {
2801
2970
  return JSON.parse(JSON.stringify(state));
@@ -2810,6 +2979,28 @@ function coerce(leaf, raw) {
2810
2979
  if (raw === "true") return true;
2811
2980
  if (raw === "false") return false;
2812
2981
  }
2982
+ if (leaf.valueType === "object") {
2983
+ const trimmed = raw.trim();
2984
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2985
+ try {
2986
+ return JSON.parse(trimmed);
2987
+ } catch {
2988
+ return raw;
2989
+ }
2990
+ }
2991
+ return raw;
2992
+ }
2993
+ if (leaf.valueType === "array") {
2994
+ const trimmed = raw.trim();
2995
+ if (trimmed.startsWith("[")) {
2996
+ try {
2997
+ return JSON.parse(trimmed);
2998
+ } catch {
2999
+ return raw;
3000
+ }
3001
+ }
3002
+ return raw;
3003
+ }
2813
3004
  return raw;
2814
3005
  }
2815
3006
  async function validationError(leaf, value, state) {
@@ -2867,27 +3058,6 @@ async function objectChildren(resolver, input) {
2867
3058
  return toLeaf(name, field, resolver.definitions);
2868
3059
  });
2869
3060
  }
2870
- function arrayItem(resolver) {
2871
- const items = resolver.items;
2872
- const valueType = resolver.itemValueType;
2873
- if (isRef(items)) {
2874
- return {
2875
- name: "",
2876
- required: true,
2877
- resolver: resolver.definitions?.[items.ref],
2878
- extraInput: items.input,
2879
- valueType,
2880
- requires: []
2881
- };
2882
- }
2883
- return {
2884
- name: "",
2885
- required: true,
2886
- resolver: items,
2887
- valueType,
2888
- requires: []
2889
- };
2890
- }
2891
3061
  function autoSettles(resolver) {
2892
3062
  return resolver.type === "constant" || resolver.type === "info";
2893
3063
  }
@@ -2907,10 +3077,28 @@ async function leafAt(ctx, path, resolved) {
2907
3077
  const seg = path[i];
2908
3078
  if (typeof seg === "number") {
2909
3079
  if (leaf?.resolver?.type !== "array") return void 0;
2910
- leaf = arrayItem(leaf.resolver);
3080
+ leaf = boundLeaf(
3081
+ "",
3082
+ leaf.resolver.items,
3083
+ leaf.resolver.definitions,
3084
+ leaf.resolver.itemValueType
3085
+ );
2911
3086
  } else {
2912
- leaf = children.find((c) => c.name === seg);
2913
- if (!leaf) return void 0;
3087
+ const parent = leaf;
3088
+ const found = children.find((c) => c.name === seg);
3089
+ if (found) {
3090
+ leaf = found;
3091
+ } else if (parent?.resolver?.type === "object" && parent.resolver.additionalKeys) {
3092
+ const ak = parent.resolver.additionalKeys;
3093
+ leaf = boundLeaf(
3094
+ String(seg),
3095
+ ak.values,
3096
+ parent.resolver.definitions,
3097
+ ak.valueValueType
3098
+ );
3099
+ } else {
3100
+ return void 0;
3101
+ }
2914
3102
  }
2915
3103
  if (i < path.length - 1 && typeof path[i + 1] === "string") {
2916
3104
  if (leaf?.resolver?.type !== "object") return void 0;
@@ -2922,16 +3110,81 @@ async function leafAt(ctx, path, resolved) {
2922
3110
  }
2923
3111
  return leaf;
2924
3112
  }
3113
+ function boundLeaf(name, resolverOrRef, definitions, valueType) {
3114
+ if (isRef(resolverOrRef)) {
3115
+ return {
3116
+ name,
3117
+ required: true,
3118
+ resolver: definitions?.[resolverOrRef.ref],
3119
+ extraInput: resolverOrRef.input,
3120
+ valueType,
3121
+ requires: []
3122
+ };
3123
+ }
3124
+ return {
3125
+ name,
3126
+ required: true,
3127
+ resolver: resolverOrRef,
3128
+ valueType,
3129
+ requires: []
3130
+ };
3131
+ }
3132
+ async function recordInfoAt(ctx, path, resolved) {
3133
+ const leaf = await leafAt(ctx, path, resolved);
3134
+ const resolver = leaf?.resolver;
3135
+ if (resolver?.type !== "object" || !resolver.additionalKeys) {
3136
+ throw new Error(
3137
+ `expected an object resolver with additionalKeys at "${pathToKey(path)}"`
3138
+ );
3139
+ }
3140
+ if (resolver.getProperties) {
3141
+ throw new Error(
3142
+ `object resolver at "${pathToKey(path)}" cannot combine getProperties with additionalKeys`
3143
+ );
3144
+ }
3145
+ const ak = resolver.additionalKeys;
3146
+ const defs = resolver.definitions;
3147
+ const keyLeaf = ak.keys ? boundLeaf("key", ak.keys, defs, ak.keyValueType ?? "string") : {
3148
+ name: "key",
3149
+ required: true,
3150
+ resolver: { type: "static", inputType: "text" },
3151
+ valueType: "string",
3152
+ requires: []
3153
+ };
3154
+ const valueLeaf = boundLeaf("value", ak.values, defs, ak.valueValueType);
3155
+ if (keyLeaf.resolver && keyLeaf.resolver.type !== "static") {
3156
+ throw new Error(
3157
+ `record key resolver at "${pathToKey(path)}" must be a static free-text prompt, not "${keyLeaf.resolver.type}"`
3158
+ );
3159
+ }
3160
+ if (valueLeaf.resolver?.type === "object" || valueLeaf.resolver?.type === "array") {
3161
+ throw new Error(
3162
+ `record value resolver at "${pathToKey(path)}" must be a single value, not "${valueLeaf.resolver.type}"`
3163
+ );
3164
+ }
3165
+ return {
3166
+ min: ak.minEntries ?? 0,
3167
+ max: ak.maxEntries ?? Infinity,
3168
+ keyLeaf,
3169
+ valueLeaf,
3170
+ fixedKeys: Object.keys(resolver.properties ?? {})
3171
+ };
3172
+ }
2925
3173
  async function arrayInfoAt(ctx, path, resolved) {
2926
3174
  const leaf = await leafAt(ctx, path, resolved);
2927
3175
  const resolver = leaf?.resolver;
2928
3176
  if (resolver?.type !== "array") {
2929
- throw new Error(`expected an array resolver at "${key(path)}"`);
3177
+ throw new Error(`expected an array resolver at "${pathToKey(path)}"`);
2930
3178
  }
2931
3179
  return {
2932
3180
  min: resolver.minItems ?? 0,
2933
3181
  max: resolver.maxItems ?? Infinity,
2934
- item: { ...arrayItem(resolver), name: String(path[path.length - 1]) }
3182
+ item: boundLeaf(
3183
+ String(path[path.length - 1]),
3184
+ resolver.items,
3185
+ resolver.definitions,
3186
+ resolver.itemValueType
3187
+ )
2935
3188
  };
2936
3189
  }
2937
3190
  async function firstPage(result) {
@@ -3012,6 +3265,9 @@ var AFFORDANCE = {
3012
3265
  retry: { action: "retry", description: "Retry loading the options" },
3013
3266
  cancel: { action: "cancel", description: "Cancel resolution" }
3014
3267
  };
3268
+ function affordance(base, description) {
3269
+ return { ...base, description };
3270
+ }
3015
3271
  function selectActions(leaf, page, multiple) {
3016
3272
  const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
3017
3273
  if (searchMode && page.position.search === void 0 && page.items.length === 0) {
@@ -3126,7 +3382,7 @@ async function buildQuestion(leaf, path, input) {
3126
3382
  }
3127
3383
  };
3128
3384
  }
3129
- function collectionQuestion(t) {
3385
+ function arrayItemsQuestion(t) {
3130
3386
  const actions = [AFFORDANCE.add];
3131
3387
  if (t.count >= t.min) actions.push(AFFORDANCE.done);
3132
3388
  return {
@@ -3142,22 +3398,37 @@ function collectionQuestion(t) {
3142
3398
  actions
3143
3399
  };
3144
3400
  }
3145
- function objectGateQuestion(path) {
3401
+ function recordEntriesQuestion(t) {
3402
+ const actions = [
3403
+ affordance(AFFORDANCE.add, "Add another entry")
3404
+ ];
3405
+ if (t.count >= t.min) {
3406
+ actions.push(affordance(AFFORDANCE.done, "Finish the entries"));
3407
+ }
3408
+ return {
3409
+ type: "collection",
3410
+ path: t.path,
3411
+ message: `Add another ${t.path[t.path.length - 1]} entry? (${t.count} so far)`,
3412
+ container: "record",
3413
+ count: t.count,
3414
+ min: t.min,
3415
+ ...Number.isFinite(t.max) ? { max: t.max } : {},
3416
+ actions
3417
+ };
3418
+ }
3419
+ function objectOptionalQuestion(path) {
3146
3420
  return {
3147
3421
  type: "collection",
3148
3422
  path,
3149
3423
  message: `Add ${path[path.length - 1]}?`,
3150
3424
  container: "object",
3151
3425
  actions: [
3152
- {
3153
- action: "add",
3154
- description: "Provide values for these fields"
3155
- },
3156
- { action: "done", description: "Skip these fields" }
3426
+ affordance(AFFORDANCE.add, "Provide values for these fields"),
3427
+ affordance(AFFORDANCE.done, "Skip these fields")
3157
3428
  ]
3158
3429
  };
3159
3430
  }
3160
- function optionalsGateQuestion(path, pending) {
3431
+ function objectOptionalPropertiesQuestion(path, pending) {
3161
3432
  return {
3162
3433
  type: "collection",
3163
3434
  path,
@@ -3174,8 +3445,8 @@ function optionalsGateQuestion(path, pending) {
3174
3445
  ...leaf.valueType ? { valueType: leaf.valueType } : {}
3175
3446
  })),
3176
3447
  actions: [
3177
- { action: "add", description: "Configure the optional fields" },
3178
- { action: "done", description: "Skip the optional fields" }
3448
+ affordance(AFFORDANCE.add, "Configure the optional fields"),
3449
+ affordance(AFFORDANCE.done, "Skip the optional fields")
3179
3450
  ]
3180
3451
  };
3181
3452
  }
@@ -3193,7 +3464,8 @@ function finalize(ctx, resolved) {
3193
3464
  }));
3194
3465
  return { status: "invalid", issues };
3195
3466
  }
3196
- var optionalsMarker = (path) => `${key(path)}?optionals`;
3467
+ var optionalsMarker = (path) => `${pathToKey(path)}?optionals`;
3468
+ var UNSAFE_RECORD_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3197
3469
  async function findInArray(ctx, state, path) {
3198
3470
  if (isSettled(state, path)) return null;
3199
3471
  if (getAtPath(state.resolved, path) == null)
@@ -3214,7 +3486,28 @@ async function findInArray(ctx, state, path) {
3214
3486
  }
3215
3487
  }
3216
3488
  if (len < min) return descendItem(ctx, state, path, len, item);
3217
- if (len < max) return { kind: "array", path, count: len, min, max };
3489
+ if (len < max) return { type: "array_items", path, count: len, min, max };
3490
+ settle(state, path);
3491
+ return null;
3492
+ }
3493
+ async function findInRecord(ctx, state, path) {
3494
+ if (isSettled(state, path)) return null;
3495
+ if (getAtPath(state.resolved, path) == null)
3496
+ setAtPath(state.resolved, path, {});
3497
+ if (!state.interactive) {
3498
+ settle(state, path);
3499
+ return null;
3500
+ }
3501
+ const { min, max, keyLeaf, fixedKeys } = await recordInfoAt(
3502
+ ctx,
3503
+ path,
3504
+ state.resolved
3505
+ );
3506
+ const container = getAtPath(state.resolved, path);
3507
+ const fixed = new Set(fixedKeys);
3508
+ const count = Object.keys(container).filter((k) => !fixed.has(k)).length;
3509
+ if (count < min) return { type: "record_key", path, leaf: keyLeaf };
3510
+ if (count < max) return { type: "record_entries", path, count, min, max };
3218
3511
  settle(state, path);
3219
3512
  return null;
3220
3513
  }
@@ -3232,10 +3525,10 @@ function seedItemSlot(state, itemPath, item) {
3232
3525
  }
3233
3526
  async function descendItem(ctx, state, arrayPath, index, item) {
3234
3527
  const itemPath = [...arrayPath, index];
3235
- const kind = seedItemSlot(state, itemPath, item);
3236
- if (kind === "object") return findNext(ctx, state, itemPath);
3237
- if (kind === "array") return findInArray(ctx, state, itemPath);
3238
- return { kind: "leaf", path: itemPath, leaf: item };
3528
+ const slotType = seedItemSlot(state, itemPath, item);
3529
+ if (slotType === "object") return findNext(ctx, state, itemPath);
3530
+ if (slotType === "array") return findInArray(ctx, state, itemPath);
3531
+ return { type: "leaf", path: itemPath, leaf: item };
3239
3532
  }
3240
3533
  async function findNext(ctx, state, path = []) {
3241
3534
  const container = getAtPath(state.resolved, path) ?? {};
@@ -3260,7 +3553,7 @@ async function findNext(ctx, state, path = []) {
3260
3553
  const pending = ordered.filter(
3261
3554
  (c) => !c.required && asksUser(c) && isPendingChild(c)
3262
3555
  );
3263
- return { kind: "optionals", path, pending };
3556
+ return { type: "object_optional_properties", path, pending };
3264
3557
  }
3265
3558
  if (leaf.resolver?.type === "object") {
3266
3559
  if (isSettled(state, childPath)) continue;
@@ -3270,7 +3563,7 @@ async function findNext(ctx, state, path = []) {
3270
3563
  settle(state, childPath);
3271
3564
  continue;
3272
3565
  }
3273
- return { kind: "object", path: childPath, leaf };
3566
+ return { type: "object_optional", path: childPath, leaf };
3274
3567
  }
3275
3568
  setAtPath(state.resolved, childPath, {});
3276
3569
  }
@@ -3302,13 +3595,21 @@ async function findNext(ctx, state, path = []) {
3302
3595
  }
3303
3596
  if (container[leaf.name] !== void 0 || isSettled(state, childPath))
3304
3597
  continue;
3305
- return { kind: "leaf", path: childPath, leaf };
3598
+ return { type: "leaf", path: childPath, leaf };
3599
+ }
3600
+ if (inObject && !isSettled(state, path)) {
3601
+ const self = await leafAt(ctx, path, state.resolved);
3602
+ if (self?.resolver?.type === "object" && self.resolver.additionalKeys) {
3603
+ const rec = await findInRecord(ctx, state, path);
3604
+ if (rec) return rec;
3605
+ }
3306
3606
  }
3307
3607
  return null;
3308
3608
  }
3309
3609
  async function askLeaf(state, path, leaf, opts = {}) {
3310
3610
  state.current = path;
3311
- delete state.gate;
3611
+ if (opts.gate) state.gate = opts.gate;
3612
+ else delete state.gate;
3312
3613
  try {
3313
3614
  const { question, pagination } = await buildQuestion(
3314
3615
  leaf,
@@ -3329,6 +3630,28 @@ async function askLeaf(state, path, leaf, opts = {}) {
3329
3630
  return failedResult(state, leaf.name, error);
3330
3631
  }
3331
3632
  }
3633
+ async function askRecordKey(state, path, keyLeaf, opts = {}) {
3634
+ return askLeaf(state, path, keyLeaf, { gate: "record_key", ...opts });
3635
+ }
3636
+ async function autoResolveLeaf(state, path, leaf) {
3637
+ const resolver = leaf.resolver;
3638
+ if (resolver && autoSettles(resolver)) {
3639
+ if (resolver.type === "constant")
3640
+ setAtPath(state.resolved, path, resolver.value);
3641
+ settle(state, path);
3642
+ return true;
3643
+ }
3644
+ const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
3645
+ input: mergeInput(state.resolved, leaf.extraInput)
3646
+ }) : void 0;
3647
+ if (auto) {
3648
+ if (auto.resolvedValue !== void 0)
3649
+ setAtPath(state.resolved, path, auto.resolvedValue);
3650
+ settle(state, path);
3651
+ return true;
3652
+ }
3653
+ return false;
3654
+ }
3332
3655
  async function advance(ctx, state) {
3333
3656
  for (; ; ) {
3334
3657
  const target = await findNext(ctx, state);
@@ -3338,54 +3661,56 @@ async function advance(ctx, state) {
3338
3661
  delete state.pagination;
3339
3662
  return { state, result: finalize(ctx, state.resolved) };
3340
3663
  }
3341
- if (target.kind === "array") {
3664
+ if (target.type === "array_items") {
3342
3665
  state.current = target.path;
3343
- state.gate = "array";
3666
+ state.gate = "array_items";
3344
3667
  delete state.pagination;
3345
3668
  return {
3346
3669
  state,
3347
- result: { status: "ask", question: collectionQuestion(target) }
3670
+ result: { status: "ask", question: arrayItemsQuestion(target) }
3348
3671
  };
3349
3672
  }
3350
- if (target.kind === "object") {
3673
+ if (target.type === "object_optional") {
3351
3674
  state.current = target.path;
3352
- state.gate = "entry";
3675
+ state.gate = "object_optional";
3353
3676
  delete state.pagination;
3354
3677
  return {
3355
3678
  state,
3356
- result: { status: "ask", question: objectGateQuestion(target.path) }
3679
+ result: {
3680
+ status: "ask",
3681
+ question: objectOptionalQuestion(target.path)
3682
+ }
3357
3683
  };
3358
3684
  }
3359
- if (target.kind === "optionals") {
3685
+ if (target.type === "object_optional_properties") {
3360
3686
  state.current = target.path;
3361
- state.gate = "optionals";
3687
+ state.gate = "object_optional_properties";
3362
3688
  delete state.pagination;
3363
3689
  return {
3364
3690
  state,
3365
3691
  result: {
3366
3692
  status: "ask",
3367
- question: optionalsGateQuestion(target.path, target.pending)
3693
+ question: objectOptionalPropertiesQuestion(
3694
+ target.path,
3695
+ target.pending
3696
+ )
3368
3697
  }
3369
3698
  };
3370
3699
  }
3371
- const { path, leaf } = target;
3372
- const resolver = leaf.resolver;
3373
- if (resolver && autoSettles(resolver)) {
3374
- if (resolver.type === "constant") {
3375
- setAtPath(state.resolved, path, resolver.value);
3376
- }
3377
- settle(state, path);
3378
- continue;
3700
+ if (target.type === "record_entries") {
3701
+ state.current = target.path;
3702
+ state.gate = "record_entries";
3703
+ delete state.pagination;
3704
+ return {
3705
+ state,
3706
+ result: { status: "ask", question: recordEntriesQuestion(target) }
3707
+ };
3379
3708
  }
3380
- const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
3381
- input: mergeInput(state.resolved, leaf.extraInput)
3382
- }) : void 0;
3383
- if (auto) {
3384
- if (auto.resolvedValue !== void 0)
3385
- setAtPath(state.resolved, path, auto.resolvedValue);
3386
- settle(state, path);
3387
- continue;
3709
+ if (target.type === "record_key") {
3710
+ return askRecordKey(state, target.path, target.leaf);
3388
3711
  }
3712
+ const { path, leaf } = target;
3713
+ if (await autoResolveLeaf(state, path, leaf)) continue;
3389
3714
  if (!state.interactive) {
3390
3715
  if (!leaf.required) {
3391
3716
  settle(state, path);
@@ -3418,10 +3743,18 @@ async function step(ctx, prior, action) {
3418
3743
  delete state.pagination;
3419
3744
  return { state, result: { status: "cancelled" } };
3420
3745
  }
3746
+ if (state.gate === "record_key") {
3747
+ return stepRecordKey(ctx, state, action);
3748
+ }
3421
3749
  const path = state.current;
3422
3750
  if (!path) throw new Error("step called with no outstanding question");
3423
3751
  const leaf = await leafAt(ctx, path, state.resolved);
3424
- if (leaf && (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry")) {
3752
+ if (!leaf) {
3753
+ throw new Error(
3754
+ `no resolver for the outstanding question at "${pathToKey(path)}"`
3755
+ );
3756
+ }
3757
+ if (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry") {
3425
3758
  return refine(ctx, state, leaf, path, action);
3426
3759
  }
3427
3760
  if (action.type === "add" || action.type === "done") {
@@ -3438,19 +3771,26 @@ async function step(ctx, prior, action) {
3438
3771
  settle(state, path);
3439
3772
  return advance(ctx, state);
3440
3773
  }
3441
- if (gate === "entry") {
3774
+ if (gate === "object_optional") {
3442
3775
  setAtPath(state.resolved, path, {});
3443
3776
  return advance(ctx, state);
3444
3777
  }
3445
- if (gate === "optionals") {
3778
+ if (gate === "object_optional_properties") {
3446
3779
  remember(state, optionalsMarker(path));
3447
3780
  return advance(ctx, state);
3448
3781
  }
3782
+ if (gate === "record_entries") {
3783
+ const { keyLeaf } = await recordInfoAt(ctx, path, state.resolved);
3784
+ return askRecordKey(state, path, keyLeaf);
3785
+ }
3449
3786
  const items = getAtPath(state.resolved, path) ?? [];
3450
3787
  const { item } = await arrayInfoAt(ctx, path, state.resolved);
3451
3788
  const itemPath = [...path, items.length];
3452
- if (seedItemSlot(state, itemPath, item) === "leaf")
3789
+ if (seedItemSlot(state, itemPath, item) === "leaf") {
3790
+ if (await autoResolveLeaf(state, itemPath, item))
3791
+ return advance(ctx, state);
3453
3792
  return askLeaf(state, itemPath, item);
3793
+ }
3454
3794
  return advance(ctx, state);
3455
3795
  }
3456
3796
  if (state.gate) {
@@ -3461,81 +3801,37 @@ async function step(ctx, prior, action) {
3461
3801
  switch (action.type) {
3462
3802
  case "choose":
3463
3803
  case "custom": {
3464
- if (leaf) {
3465
- let error;
3466
- try {
3467
- error = await validationError(leaf, action.value, state);
3468
- } catch (thrown) {
3469
- return failedResult(state, leaf.name, thrown);
3470
- }
3471
- if (error) {
3472
- if (state.pagination && leaf.resolver?.type === "dynamic") {
3473
- try {
3474
- const context = await resolveContext(leaf, state.resolved);
3475
- const page = await fetchListing(
3476
- leaf,
3477
- state.resolved,
3478
- state.pagination.position,
3479
- context
3480
- );
3481
- state.pagination = toPagination(page);
3482
- return {
3483
- state,
3484
- result: {
3485
- status: "ask",
3486
- question: selectQuestion(
3487
- leaf,
3488
- path,
3489
- state.resolved,
3490
- page,
3491
- context
3492
- ),
3493
- error
3494
- }
3495
- };
3496
- } catch (fetchError) {
3497
- state.pagination = failedPagination(
3498
- state.pagination,
3499
- state.pagination.position
3500
- );
3501
- return failedResult(state, leaf.name, fetchError);
3502
- }
3503
- }
3504
- return askLeaf(state, path, leaf, { error });
3804
+ let error;
3805
+ try {
3806
+ error = await validationError(leaf, action.value, state);
3807
+ } catch (thrown) {
3808
+ return failedResult(state, leaf.name, thrown);
3809
+ }
3810
+ if (error) {
3811
+ if (state.pagination && leaf.resolver?.type === "dynamic") {
3812
+ return renderPageAt(state, leaf, path, state.pagination.position, {
3813
+ error
3814
+ });
3505
3815
  }
3816
+ return askLeaf(state, path, leaf, { error });
3506
3817
  }
3507
- setAtPath(
3508
- state.resolved,
3509
- path,
3510
- leaf ? coerce(leaf, action.value) : action.value
3511
- );
3818
+ setAtPath(state.resolved, path, coerce(leaf, action.value));
3512
3819
  break;
3513
3820
  }
3514
3821
  case "skip":
3515
3822
  settle(state, path);
3516
3823
  break;
3517
3824
  default:
3518
- throw new Error(`action "${action.type}" is not supported here`);
3825
+ throw new Error(
3826
+ `action "${action.type}" is not supported here`
3827
+ );
3519
3828
  }
3520
3829
  delete state.current;
3521
3830
  delete state.pagination;
3522
3831
  return advance(ctx, state);
3523
3832
  }
3524
- async function refine(ctx, state, leaf, path, action) {
3525
- const position = positionAfter(state.pagination, action);
3833
+ async function renderPageAt(state, leaf, path, position, opts = {}) {
3526
3834
  try {
3527
- if (action.type === "search") {
3528
- const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
3529
- input: mergeInput(state.resolved, leaf.extraInput),
3530
- search: action.term
3531
- }) : void 0;
3532
- if (exact) {
3533
- setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
3534
- delete state.current;
3535
- delete state.pagination;
3536
- return advance(ctx, state);
3537
- }
3538
- }
3539
3835
  const context = await resolveContext(leaf, state.resolved);
3540
3836
  const page = await fetchListing(leaf, state.resolved, position, context);
3541
3837
  state.pagination = toPagination(page);
@@ -3543,7 +3839,8 @@ async function refine(ctx, state, leaf, path, action) {
3543
3839
  state,
3544
3840
  result: {
3545
3841
  status: "ask",
3546
- question: selectQuestion(leaf, path, state.resolved, page, context)
3842
+ question: selectQuestion(leaf, path, state.resolved, page, context),
3843
+ ...opts.error ? { error: opts.error } : {}
3547
3844
  }
3548
3845
  };
3549
3846
  } catch (error) {
@@ -3551,6 +3848,65 @@ async function refine(ctx, state, leaf, path, action) {
3551
3848
  return failedResult(state, leaf.name, error);
3552
3849
  }
3553
3850
  }
3851
+ async function refine(ctx, state, leaf, path, action) {
3852
+ const position = positionAfter(state.pagination, action);
3853
+ if (action.type === "search") {
3854
+ try {
3855
+ const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
3856
+ input: mergeInput(state.resolved, leaf.extraInput),
3857
+ search: action.term
3858
+ }) : void 0;
3859
+ if (exact) {
3860
+ setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
3861
+ delete state.current;
3862
+ delete state.pagination;
3863
+ return advance(ctx, state);
3864
+ }
3865
+ } catch (error) {
3866
+ state.pagination = failedPagination(state.pagination, position);
3867
+ return failedResult(state, leaf.name, error);
3868
+ }
3869
+ }
3870
+ return renderPageAt(state, leaf, path, position);
3871
+ }
3872
+ async function stepRecordKey(ctx, state, action) {
3873
+ const path = state.current;
3874
+ if (!path)
3875
+ throw new Error("record key step called with no outstanding question");
3876
+ const { keyLeaf, valueLeaf } = await recordInfoAt(ctx, path, state.resolved);
3877
+ if (action.type === "skip") {
3878
+ delete state.gate;
3879
+ delete state.current;
3880
+ delete state.pagination;
3881
+ return advance(ctx, state);
3882
+ }
3883
+ if (action.type !== "custom" && action.type !== "choose") {
3884
+ throw new Error(
3885
+ `action "${action.type}" is not supported while entering a record key`
3886
+ );
3887
+ }
3888
+ const raw = Array.isArray(action.value) ? action.value[0] : action.value;
3889
+ const entryKey = String(coerce(keyLeaf, raw));
3890
+ if (entryKey.trim() === "") {
3891
+ return askRecordKey(state, path, keyLeaf, { error: "A key is required." });
3892
+ }
3893
+ if (UNSAFE_RECORD_KEYS.has(entryKey)) {
3894
+ return askRecordKey(state, path, keyLeaf, {
3895
+ error: `"${entryKey}" is not an allowed key.`
3896
+ });
3897
+ }
3898
+ const container = getAtPath(state.resolved, path);
3899
+ if (Object.prototype.hasOwnProperty.call(container, entryKey)) {
3900
+ return askRecordKey(state, path, keyLeaf, {
3901
+ error: `"${entryKey}" is already set.`
3902
+ });
3903
+ }
3904
+ const valuePath = [...path, entryKey];
3905
+ if (await autoResolveLeaf(state, valuePath, valueLeaf)) {
3906
+ return advance(ctx, state);
3907
+ }
3908
+ return askLeaf(state, valuePath, valueLeaf);
3909
+ }
3554
3910
  function failedPagination(pagination, retryPosition) {
3555
3911
  return {
3556
3912
  position: pagination?.position ?? firstPagePosition(),
@@ -3606,7 +3962,7 @@ function projectSummary(entry) {
3606
3962
  };
3607
3963
  }
3608
3964
  function projectMethod(entry) {
3609
- const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
3965
+ const inputProperties = toJsonSchema(canonicalInputSchema(entry.inputSchema))?.properties;
3610
3966
  const parameters = {};
3611
3967
  for (const spec of planParameters(entry).parameters) {
3612
3968
  const dynamic = spec.resolver?.type === "dynamic" ? spec.resolver : void 0;
@@ -3639,7 +3995,12 @@ function createController(sdk) {
3639
3995
  const entry = entryFor(method);
3640
3996
  return {
3641
3997
  method,
3642
- schema: entry.inputSchema,
3998
+ // A method that owns its input validation (`skipInputValidation`, e.g.
3999
+ // fetch) must not be re-validated by the controller's final `safeParse`;
4000
+ // drop the schema so `finalize` returns the resolved input untouched.
4001
+ // Planning still reads `entry.inputSchema` directly, so parameters are
4002
+ // unaffected.
4003
+ schema: entry.skipInputValidation ? void 0 : entry.inputSchema,
3643
4004
  parameters: planParameters(entry).parameters
3644
4005
  };
3645
4006
  }
@@ -3704,59 +4065,6 @@ function createCorePlugin(options) {
3704
4065
  }
3705
4066
  });
3706
4067
  }
3707
-
3708
- // src/utils/schema-utils.ts
3709
- var import_zod5 = require("zod");
3710
- function getOutputSchema(inputSchema) {
3711
- return inputSchema._zod.def.outputSchema;
3712
- }
3713
- function withOutputSchema(inputSchema, outputSchema) {
3714
- Object.assign(inputSchema._zod.def, {
3715
- outputSchema
3716
- });
3717
- return inputSchema;
3718
- }
3719
- function withResolver(schema, config) {
3720
- schema._zod.def.resolverMeta = config;
3721
- return schema;
3722
- }
3723
- function getSchemaDescription(schema) {
3724
- return schema.description;
3725
- }
3726
- function getFieldDescriptions(schema) {
3727
- const descriptions = {};
3728
- const shape = schema.shape;
3729
- for (const [key2, fieldSchema] of Object.entries(shape)) {
3730
- if (fieldSchema instanceof import_zod5.z.ZodType && fieldSchema.description) {
3731
- descriptions[key2] = fieldSchema.description;
3732
- }
3733
- }
3734
- return descriptions;
3735
- }
3736
- function withPositional(schema) {
3737
- Object.assign(schema._zod.def, {
3738
- positionalMeta: { positional: true }
3739
- });
3740
- return schema;
3741
- }
3742
- function schemaHasPositionalMeta(schema) {
3743
- return "positionalMeta" in schema._zod.def;
3744
- }
3745
- function isPositional(schema) {
3746
- if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
3747
- return true;
3748
- }
3749
- if (schema instanceof import_zod5.z.ZodOptional) {
3750
- return isPositional(schema._zod.def.innerType);
3751
- }
3752
- if (schema instanceof import_zod5.z.ZodDefault) {
3753
- return isPositional(schema._zod.def.innerType);
3754
- }
3755
- return false;
3756
- }
3757
- function openEnum(values, description) {
3758
- return import_zod5.z.union([import_zod5.z.enum(values), import_zod5.z.string()]).describe(description);
3759
- }
3760
4068
  // Annotate the CommonJS export names for ESM import in node:
3761
4069
  0 && (module.exports = {
3762
4070
  CONTEXT,
@@ -3769,6 +4077,7 @@ function openEnum(values, description) {
3769
4077
  CoreErrorCode,
3770
4078
  CoreSignal,
3771
4079
  addPlugin,
4080
+ canonicalInputSchema,
3772
4081
  composePlugins,
3773
4082
  concatLists,
3774
4083
  concatPaginated,
@@ -3812,6 +4121,7 @@ function openEnum(values, description) {
3812
4121
  getOutputSchema,
3813
4122
  getRegistryPlugin,
3814
4123
  getSchemaDescription,
4124
+ isCoreCancelledSignal,
3815
4125
  isCoreError,
3816
4126
  isCoreSignal,
3817
4127
  isNestedMethodCall,