@zapier/kitcore 0.7.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.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,7 +30,9 @@ __export(index_exports, {
30
30
  CoreErrorCode: () => CoreErrorCode,
31
31
  CoreSignal: () => CoreSignal,
32
32
  addPlugin: () => addPlugin,
33
+ canonicalInputSchema: () => canonicalInputSchema,
33
34
  composePlugins: () => composePlugins,
35
+ concatLists: () => concatLists,
34
36
  concatPaginated: () => concatPaginated,
35
37
  coreOptionsPluginRef: () => coreOptionsPluginRef,
36
38
  createAsyncContext: () => createAsyncContext,
@@ -97,9 +99,6 @@ __export(index_exports, {
97
99
  });
98
100
  module.exports = __toCommonJS(index_exports);
99
101
 
100
- // src/registry.ts
101
- var import_zod = require("zod");
102
-
103
102
  // src/utils/string-utils.ts
104
103
  function toTitleCase(input) {
105
104
  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(" ");
@@ -123,6 +122,65 @@ function pluralizeLastWord(title) {
123
122
  return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
124
123
  }
125
124
 
125
+ // src/utils/schema-utils.ts
126
+ var import_zod = require("zod");
127
+ function canonicalInputSchema(schema) {
128
+ if (schema instanceof import_zod.z.ZodUnion) {
129
+ return schema.options[0];
130
+ }
131
+ return schema;
132
+ }
133
+ function getOutputSchema(inputSchema) {
134
+ return inputSchema._zod.def.outputSchema;
135
+ }
136
+ function withOutputSchema(inputSchema, outputSchema) {
137
+ Object.assign(inputSchema._zod.def, {
138
+ outputSchema
139
+ });
140
+ return inputSchema;
141
+ }
142
+ function withResolver(schema, config) {
143
+ schema._zod.def.resolverMeta = config;
144
+ return schema;
145
+ }
146
+ function getSchemaDescription(schema) {
147
+ return schema.description;
148
+ }
149
+ function getFieldDescriptions(schema) {
150
+ const descriptions = {};
151
+ const shape = schema.shape;
152
+ for (const [key, fieldSchema] of Object.entries(shape)) {
153
+ if (fieldSchema instanceof import_zod.z.ZodType && fieldSchema.description) {
154
+ descriptions[key] = fieldSchema.description;
155
+ }
156
+ }
157
+ return descriptions;
158
+ }
159
+ function withPositional(schema) {
160
+ Object.assign(schema._zod.def, {
161
+ positionalMeta: { positional: true }
162
+ });
163
+ return schema;
164
+ }
165
+ function schemaHasPositionalMeta(schema) {
166
+ return "positionalMeta" in schema._zod.def;
167
+ }
168
+ function isPositional(schema) {
169
+ if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
170
+ return true;
171
+ }
172
+ if (schema instanceof import_zod.z.ZodOptional) {
173
+ return isPositional(schema._zod.def.innerType);
174
+ }
175
+ if (schema instanceof import_zod.z.ZodDefault) {
176
+ return isPositional(schema._zod.def.innerType);
177
+ }
178
+ return false;
179
+ }
180
+ function openEnum(values, description) {
181
+ return import_zod.z.union([import_zod.z.enum(values), import_zod.z.string()]).describe(description);
182
+ }
183
+
126
184
  // src/registry.ts
127
185
  function resolveCategoryDefinition(ref) {
128
186
  const def = typeof ref === "string" ? { key: ref } : ref;
@@ -133,30 +191,25 @@ function resolveCategoryDefinition(ref) {
133
191
  titlePlural: def.titlePlural ?? pluralizeLastWord(title)
134
192
  };
135
193
  }
136
- function canonicalInputSchema(schema) {
137
- if (schema instanceof import_zod.z.ZodUnion) {
138
- return schema.options[0];
139
- }
140
- return schema;
141
- }
142
194
  function buildRegistry({
143
195
  sdk,
144
196
  meta,
145
197
  formatters,
146
- boundResolvers,
198
+ resolvers,
147
199
  positional,
200
+ skipInputValidation,
148
201
  packageFilter
149
202
  }) {
150
203
  const definitionsByKey = /* @__PURE__ */ new Map();
151
204
  const objectDeclaredKeys = /* @__PURE__ */ new Set();
152
205
  for (const m of Object.values(meta)) {
153
206
  for (const ref of m.categories ?? []) {
154
- const key2 = typeof ref === "string" ? ref : ref.key;
207
+ const key = typeof ref === "string" ? ref : ref.key;
155
208
  if (typeof ref === "object") {
156
- objectDeclaredKeys.add(key2);
157
- definitionsByKey.set(key2, resolveCategoryDefinition(ref));
158
- } else if (!objectDeclaredKeys.has(key2)) {
159
- definitionsByKey.set(key2, resolveCategoryDefinition(ref));
209
+ objectDeclaredKeys.add(key);
210
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
211
+ } else if (!objectDeclaredKeys.has(key)) {
212
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
160
213
  }
161
214
  }
162
215
  }
@@ -164,30 +217,29 @@ function buildRegistry({
164
217
  definitionsByKey.set("other", resolveCategoryDefinition("other"));
165
218
  }
166
219
  const knownCategories = Array.from(definitionsByKey.keys());
167
- const functions = Object.keys(meta).filter((key2) => {
168
- const property = sdk[key2];
220
+ const functions = Object.keys(meta).filter((key) => {
221
+ const property = sdk[key];
169
222
  if (typeof property === "function") return true;
170
- const [rootKey] = key2.split(".");
223
+ const [rootKey] = key.split(".");
171
224
  const rootProperty = sdk[rootKey];
172
225
  return typeof rootProperty === "object" && rootProperty !== null;
173
- }).map((key2) => {
174
- const m = meta[key2];
226
+ }).map((key) => {
227
+ const m = meta[key];
175
228
  return {
176
- name: key2,
229
+ name: key,
177
230
  description: m.description,
178
231
  type: m.type,
179
232
  itemType: m.itemType,
180
233
  returnType: m.returnType,
181
234
  inputSchema: canonicalInputSchema(m.inputSchema),
182
- inputParameters: m.inputParameters,
183
235
  outputSchema: m.outputSchema,
184
- positional: positional?.[key2],
236
+ positional: positional?.[key],
237
+ skipInputValidation: skipInputValidation?.[key],
185
238
  categories: (m.categories ?? []).map(
186
239
  (c) => typeof c === "string" ? c : c.key
187
240
  ),
188
- resolvers: m.resolvers,
189
- boundResolvers: boundResolvers?.[key2],
190
- formatter: formatters?.[key2],
241
+ resolvers: resolvers?.[key],
242
+ formatter: formatters?.[key],
191
243
  experimental: m.experimental,
192
244
  packages: m.packages,
193
245
  confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
@@ -544,53 +596,49 @@ function decodeConcatCursor(incoming) {
544
596
  }
545
597
  return { index: 0, cursor: incoming };
546
598
  }
547
- function concatPaginated({
599
+ async function concatLists({
548
600
  sources,
549
601
  pageSize = 100,
550
602
  cursor
551
603
  }) {
552
604
  if (sources.length === 0) {
553
- const empty = { data: [] };
554
- return Object.assign(Promise.resolve(empty), {
555
- [Symbol.asyncIterator]: async function* () {
556
- yield empty;
557
- }
558
- });
605
+ return { data: [] };
559
606
  }
560
607
  const pageFunction = async (options) => {
561
- let { index, cursor: sourceCursor } = decodeConcatCursor(options.cursor);
608
+ let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
562
609
  while (index < sources.length) {
563
- const page = await sources[index]({ cursor: sourceCursor });
564
- const hasMoreInSource = page.nextCursor != null;
565
- if (page.data.length === 0 && !hasMoreInSource) {
610
+ const page = await sources[index]({ cursor: listCursor });
611
+ const hasMoreInList = page.nextCursor != null;
612
+ if (page.data.length === 0 && !hasMoreInList) {
566
613
  index++;
567
- sourceCursor = void 0;
614
+ listCursor = void 0;
568
615
  continue;
569
616
  }
570
617
  return {
571
618
  data: page.data,
572
- nextCursor: hasMoreInSource ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
619
+ nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
573
620
  };
574
621
  }
575
622
  return { data: [] };
576
623
  };
577
- const iterator = paginateBuffered(pageFunction, { pageSize, cursor });
578
- const firstPagePromise = iterator.next().then((result) => {
579
- if (result.done) {
580
- return { data: [] };
581
- }
582
- return result.value;
583
- });
584
- return Object.assign(firstPagePromise, {
585
- [Symbol.asyncIterator]: async function* () {
586
- yield await firstPagePromise;
587
- for await (const page of { [Symbol.asyncIterator]: () => iterator }) {
588
- yield page;
589
- }
590
- }
591
- });
624
+ const result = await paginateBuffered(pageFunction, {
625
+ pageSize,
626
+ cursor
627
+ }).next();
628
+ return result.done ? { data: [] } : result.value;
629
+ }
630
+ function concatPaginated({
631
+ sources,
632
+ pageSize,
633
+ cursor
634
+ }) {
635
+ logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
636
+ return concatLists({ sources, pageSize, cursor });
592
637
  }
593
638
  function toIterable(source) {
639
+ logDeprecation(
640
+ "toIterable() is deprecated. Call .pages() on the paginated result instead."
641
+ );
594
642
  return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
595
643
  }
596
644
 
@@ -678,6 +726,52 @@ function runInMethodScope(fn) {
678
726
  var runWithTelemetryContext = runInMethodScope;
679
727
  var isTelemetryNested = isNestedMethodCall;
680
728
 
729
+ // src/utils/call-context.ts
730
+ var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
731
+ function isCallContext(value) {
732
+ return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
733
+ }
734
+ function generateCallId() {
735
+ try {
736
+ const webCrypto = globalThis.crypto;
737
+ if (webCrypto?.randomUUID) {
738
+ return webCrypto.randomUUID();
739
+ }
740
+ if (webCrypto?.getRandomValues) {
741
+ const bytes = webCrypto.getRandomValues(new Uint8Array(16));
742
+ const hex = Array.from(bytes, (byte, i) => {
743
+ const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
744
+ return value.toString(16).padStart(2, "0");
745
+ });
746
+ return [
747
+ hex.slice(0, 4).join(""),
748
+ hex.slice(4, 6).join(""),
749
+ hex.slice(6, 8).join(""),
750
+ hex.slice(8, 10).join(""),
751
+ hex.slice(10, 16).join("")
752
+ ].join("-");
753
+ }
754
+ } catch {
755
+ }
756
+ return null;
757
+ }
758
+ function rootCallContext() {
759
+ return {
760
+ callId: generateCallId(),
761
+ depth: 0,
762
+ annotations: {},
763
+ [CALL_CONTEXT_BRAND]: true
764
+ };
765
+ }
766
+ function childCallContext(parent) {
767
+ return {
768
+ callId: parent.callId,
769
+ depth: parent.depth + 1,
770
+ annotations: {},
771
+ [CALL_CONTEXT_BRAND]: true
772
+ };
773
+ }
774
+
681
775
  // src/utils/core-options.ts
682
776
  function defaultLogDeprecation({
683
777
  methodName,
@@ -696,6 +790,9 @@ function resolveCoreOptions(context) {
696
790
  return context.core;
697
791
  }
698
792
  var INTERNAL_CALL = Symbol("kitcore.internalCall");
793
+ function resolveCallContext(secondArg) {
794
+ return isCallContext(secondArg) ? secondArg : rootCallContext();
795
+ }
699
796
  function signalDeprecation(context, methodName, getDeprecation) {
700
797
  if (isInsideObserver()) return;
701
798
  const deprecation = getDeprecation?.();
@@ -725,14 +822,16 @@ function createFunction(coreFn, options) {
725
822
  const functionName = name || coreFn.name;
726
823
  const namedFunctions = {
727
824
  [functionName]: async function(callOptions) {
728
- if (arguments[1] !== INTERNAL_CALL) {
825
+ const internal = arguments[1];
826
+ const context = resolveCallContext(internal);
827
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
729
828
  signalDeprecation(sdk.context, functionName, getDeprecation);
730
829
  }
731
830
  return runInMethodScope(async () => {
732
831
  const startTime = Date.now();
733
832
  const normalizedOptions = callOptions ?? {};
734
833
  const args = [normalizedOptions];
735
- const depth = getCurrentDepth();
834
+ const depth = Math.max(context.depth, getCurrentDepth());
736
835
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
737
836
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
738
837
  hooks?.onMethodStart?.({
@@ -751,12 +850,15 @@ function createFunction(coreFn, options) {
751
850
  adaptError
752
851
  }
753
852
  );
754
- result = await coreFn({
755
- ...normalizedOptions,
756
- ...validatedOptions
757
- });
853
+ result = await coreFn(
854
+ {
855
+ ...normalizedOptions,
856
+ ...validatedOptions
857
+ },
858
+ context
859
+ );
758
860
  } else {
759
- result = await coreFn(normalizedOptions);
861
+ result = await coreFn(normalizedOptions, context);
760
862
  }
761
863
  hooks?.onMethodEnd?.({
762
864
  methodName: functionName,
@@ -786,17 +888,19 @@ function createFunction(coreFn, options) {
786
888
  function createRawFunction(coreFn, options) {
787
889
  const { sdk, name, schema, positional, getDeprecation } = options;
788
890
  return function(rawInput) {
789
- if (arguments[1] !== INTERNAL_CALL) {
891
+ const internal = arguments[1];
892
+ const context = resolveCallContext(internal);
893
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
790
894
  signalDeprecation(sdk.context, name, getDeprecation);
791
895
  }
792
896
  return runInMethodScope(() => {
793
897
  const startTime = Date.now();
794
- const depth = getCurrentDepth();
898
+ const depth = Math.max(context.depth, getCurrentDepth());
795
899
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
796
900
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
797
901
  const input = schema ? rawInput ?? {} : rawInput;
798
902
  const record = input;
799
- const args = positional ? positional.filter((key2) => record?.[key2] !== void 0).map((key2) => record?.[key2]) : [input];
903
+ const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
800
904
  hooks?.onMethodStart?.({
801
905
  methodName: name,
802
906
  args,
@@ -815,7 +919,7 @@ function createRawFunction(coreFn, options) {
815
919
  };
816
920
  try {
817
921
  const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
818
- const result = coreFn(parsed);
922
+ const result = coreFn(parsed, context);
819
923
  if (result !== null && typeof result === "object" && typeof result.then === "function") {
820
924
  return result.then(
821
925
  (value) => {
@@ -854,9 +958,9 @@ function createPageFunction(coreFn, {
854
958
  }) {
855
959
  const functionName = coreFn.name + "Page";
856
960
  const namedFunctions = {
857
- [functionName]: async function(options) {
961
+ [functionName]: async function(options, callContext) {
858
962
  try {
859
- const response = await coreFn(options);
963
+ const response = await coreFn(options, callContext);
860
964
  const page = adaptPage ? adaptPage(response) : response;
861
965
  if (!isSdkPage(page)) {
862
966
  throw new Error(
@@ -880,14 +984,16 @@ function createPaginatedFunction(coreFn, options) {
880
984
  const functionName = name || coreFn.name;
881
985
  const namedFunctions = {
882
986
  [functionName]: function(callOptions) {
883
- if (arguments[1] !== INTERNAL_CALL) {
987
+ const internal = arguments[1];
988
+ const context = resolveCallContext(internal);
989
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
884
990
  signalDeprecation(sdk.context, functionName, getDeprecation);
885
991
  }
886
992
  return runInMethodScope(() => {
887
993
  const startTime = Date.now();
888
994
  const normalizedOptions = callOptions ?? {};
889
995
  const args = [normalizedOptions];
890
- const depth = getCurrentDepth();
996
+ const depth = Math.max(context.depth, getCurrentDepth());
891
997
  const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
892
998
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
893
999
  hooks?.onMethodStart?.({
@@ -906,7 +1012,11 @@ function createPaginatedFunction(coreFn, options) {
906
1012
  ...validatedOptions,
907
1013
  pageSize
908
1014
  };
909
- const iterator = paginate(pageFunction, optimizedOptions);
1015
+ const iterator = paginate(
1016
+ (pageOptions) => pageFunction(pageOptions, context),
1017
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1018
+ optimizedOptions
1019
+ );
910
1020
  const firstPagePromise = iterator.next().then((result) => {
911
1021
  if (result.done) {
912
1022
  throw new Error("Paginate should always iterate at least once");
@@ -943,6 +1053,13 @@ function createPaginatedFunction(coreFn, options) {
943
1053
  [Symbol.asyncIterator]() {
944
1054
  return pageStream;
945
1055
  },
1056
+ pages: function() {
1057
+ return {
1058
+ [Symbol.asyncIterator]() {
1059
+ return pageStream;
1060
+ }
1061
+ };
1062
+ },
946
1063
  items: function() {
947
1064
  return {
948
1065
  [Symbol.asyncIterator]: async function* () {
@@ -1050,11 +1167,11 @@ var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
1050
1167
  "context",
1051
1168
  "getRegistry"
1052
1169
  ]);
1053
- function hasOwn(obj, key2) {
1054
- return Object.prototype.hasOwnProperty.call(obj, key2);
1170
+ function hasOwn(obj, key) {
1171
+ return Object.prototype.hasOwnProperty.call(obj, key);
1055
1172
  }
1056
- function setOwn(target, key2, value) {
1057
- Object.defineProperty(target, key2, {
1173
+ function setOwn(target, key, value) {
1174
+ Object.defineProperty(target, key, {
1058
1175
  value,
1059
1176
  enumerable: true,
1060
1177
  configurable: true,
@@ -1066,31 +1183,31 @@ function checkCollisions(target, source, kind, callerLabel, override) {
1066
1183
  checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
1067
1184
  return;
1068
1185
  }
1069
- for (const key2 of Object.keys(source)) {
1070
- if (!override && hasOwn(target, key2)) {
1186
+ for (const key of Object.keys(source)) {
1187
+ if (!override && hasOwn(target, key)) {
1071
1188
  throw new Error(
1072
- `${callerLabel}: duplicate ${kind} "${key2}". If the override is intentional, pass { override: true } in the options.`
1189
+ `${callerLabel}: duplicate ${kind} "${key}". If the override is intentional, pass { override: true } in the options.`
1073
1190
  );
1074
1191
  }
1075
1192
  }
1076
1193
  }
1077
1194
  function checkRootKeyCollisions(target, keys, override, callerLabel) {
1078
- for (const key2 of keys) {
1079
- if (RESERVED_ROOT_KEYS.has(key2)) {
1195
+ for (const key of keys) {
1196
+ if (RESERVED_ROOT_KEYS.has(key)) {
1080
1197
  throw new Error(
1081
- `${callerLabel}: plugin attempted to register reserved root key "${key2}". The SDK uses this key for its own accessor; rename the plugin's method.`
1198
+ `${callerLabel}: plugin attempted to register reserved root key "${key}". The SDK uses this key for its own accessor; rename the plugin's method.`
1082
1199
  );
1083
1200
  }
1084
- if (!override && hasOwn(target, key2)) {
1201
+ if (!override && hasOwn(target, key)) {
1085
1202
  throw new Error(
1086
- `${callerLabel}: duplicate root key "${key2}". If the override is intentional, pass { override: true } in the options.`
1203
+ `${callerLabel}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
1087
1204
  );
1088
1205
  }
1089
1206
  }
1090
1207
  }
1091
1208
  function applyOwnProperties(target, source) {
1092
- for (const key2 of Object.keys(source)) {
1093
- setOwn(target, key2, source[key2]);
1209
+ for (const key of Object.keys(source)) {
1210
+ setOwn(target, key, source[key]);
1094
1211
  }
1095
1212
  }
1096
1213
  function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
@@ -1308,7 +1425,6 @@ var LEAF_META_KEYS = [
1308
1425
  "itemType",
1309
1426
  "returnType",
1310
1427
  "outputSchema",
1311
- "inputParameters",
1312
1428
  "packages",
1313
1429
  "experimental",
1314
1430
  "confirm",
@@ -1348,8 +1464,8 @@ function normalizeImports(deps) {
1348
1464
  }
1349
1465
  function collectLeafMeta(config) {
1350
1466
  let meta;
1351
- for (const key2 of LEAF_META_KEYS) {
1352
- if (config[key2] !== void 0) (meta ?? (meta = {}))[key2] = config[key2];
1467
+ for (const key of LEAF_META_KEYS) {
1468
+ if (config[key] !== void 0) (meta ?? (meta = {}))[key] = config[key];
1353
1469
  }
1354
1470
  return meta;
1355
1471
  }
@@ -1432,7 +1548,8 @@ function defineResolver(config) {
1432
1548
  type: "object",
1433
1549
  properties: config.properties,
1434
1550
  definitions: config.definitions,
1435
- getProperties: config.getProperties
1551
+ getProperties: config.getProperties,
1552
+ additionalKeys: config.additionalKeys
1436
1553
  };
1437
1554
  case "array":
1438
1555
  return {
@@ -1744,7 +1861,7 @@ function normalizeFormatter(entry, sdk) {
1744
1861
  const legacy = entry.meta?.formatter;
1745
1862
  return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
1746
1863
  }
1747
- function normalizeBoundResolvers(entry) {
1864
+ function normalizeResolvers(entry) {
1748
1865
  if (entry.pluginType !== "method") return void 0;
1749
1866
  return entry.resolvers;
1750
1867
  }
@@ -1785,17 +1902,20 @@ function collectSurfaceProjection(context, formatterSdk) {
1785
1902
  foldDynamicMembers(entry, surfaceBindings, meta);
1786
1903
  }
1787
1904
  const formatters = {};
1788
- const boundResolvers = {};
1905
+ const resolvers = {};
1789
1906
  const positional = {};
1907
+ const skipInputValidation = {};
1790
1908
  for (const [binding, entry] of Object.entries(entries)) {
1791
1909
  const f = normalizeFormatter(entry, formatterSdk);
1792
1910
  if (f) formatters[binding] = f;
1793
- const r = normalizeBoundResolvers(entry);
1794
- if (r) boundResolvers[binding] = r;
1911
+ const r = normalizeResolvers(entry);
1912
+ if (r) resolvers[binding] = r;
1795
1913
  const p = methodPositional(entry);
1796
1914
  if (p) positional[binding] = p;
1915
+ if (entry.pluginType === "method" && entry.skipInputValidation)
1916
+ skipInputValidation[binding] = true;
1797
1917
  }
1798
- return { meta, formatters, boundResolvers, positional };
1918
+ return { meta, formatters, resolvers, positional, skipInputValidation };
1799
1919
  }
1800
1920
  function buildSurfaceRegistry(context, packageFilter) {
1801
1921
  const surface = {};
@@ -1852,6 +1972,11 @@ function nestedResolvers(resolver) {
1852
1972
  for (const field of Object.values(resolver.properties ?? {})) {
1853
1973
  if (!isResolverRef(field.resolver)) out.push(field.resolver);
1854
1974
  }
1975
+ const ak = resolver.additionalKeys;
1976
+ if (ak) {
1977
+ if (!isResolverRef(ak.values)) out.push(ak.values);
1978
+ if (ak.keys && !isResolverRef(ak.keys)) out.push(ak.keys);
1979
+ }
1855
1980
  out.push(...Object.values(resolver.definitions ?? {}));
1856
1981
  } else if (resolver.type === "array") {
1857
1982
  if (!isResolverRef(resolver.items)) out.push(resolver.items);
@@ -1976,16 +2101,16 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
1976
2101
  }
1977
2102
  return byId;
1978
2103
  }
1979
- function bindValue(target, key2, entry, callType = "surface") {
2104
+ function bindValue(target, key, entry, callType = "surface", ctx) {
1980
2105
  if (entry.pluginType === "property" && entry.getValue) {
1981
- Object.defineProperty(target, key2, {
2106
+ Object.defineProperty(target, key, {
1982
2107
  get: entry.getValue,
1983
2108
  enumerable: true,
1984
2109
  configurable: true
1985
2110
  });
1986
2111
  } else {
1987
- const value = callType === "internal" && entry.pluginType === "method" ? entry.internalValue ?? entry.value : entry.value;
1988
- Object.defineProperty(target, key2, {
2112
+ const value = callType === "internal" && entry.pluginType === "method" ? entry.bindInternal?.(ctx) ?? entry.internalValue ?? entry.value : entry.value;
2113
+ Object.defineProperty(target, key, {
1989
2114
  value,
1990
2115
  writable: true,
1991
2116
  enumerable: true,
@@ -2002,7 +2127,7 @@ function buildSurface(context, ...maps) {
2002
2127
  sdk[CONTEXT] = context;
2003
2128
  return sdk;
2004
2129
  }
2005
- function buildImports(plugins, importBindings) {
2130
+ function buildImports(plugins, importBindings, ctx) {
2006
2131
  const imports = {};
2007
2132
  for (const { binding, id, optional } of importBindings) {
2008
2133
  const entry = plugins[id];
@@ -2015,7 +2140,7 @@ function buildImports(plugins, importBindings) {
2015
2140
  });
2016
2141
  continue;
2017
2142
  }
2018
- bindValue(imports, binding, entry, "internal");
2143
+ bindValue(imports, binding, entry, "internal", ctx);
2019
2144
  }
2020
2145
  return imports;
2021
2146
  }
@@ -2094,6 +2219,19 @@ function bindResolver(resolver, plugins) {
2094
2219
  const { getProperties } = resolver;
2095
2220
  if (getProperties)
2096
2221
  bound.getProperties = ({ input }) => getProperties({ imports, input });
2222
+ if (resolver.additionalKeys) {
2223
+ const ak = resolver.additionalKeys;
2224
+ const boundAk = {
2225
+ values: isResolverRef(ak.values) ? ak.values : bindResolver(ak.values, plugins),
2226
+ minEntries: ak.minEntries,
2227
+ maxEntries: ak.maxEntries,
2228
+ keyValueType: ak.keyValueType,
2229
+ valueValueType: ak.valueValueType
2230
+ };
2231
+ if (ak.keys)
2232
+ boundAk.keys = isResolverRef(ak.keys) ? ak.keys : bindResolver(ak.keys, plugins);
2233
+ bound.additionalKeys = boundAk;
2234
+ }
2097
2235
  return bound;
2098
2236
  }
2099
2237
  case "array": {
@@ -2149,8 +2287,8 @@ function bindResolver(resolver, plugins) {
2149
2287
  }
2150
2288
  function bindFields(fields, plugins) {
2151
2289
  const out = {};
2152
- for (const [key2, field] of Object.entries(fields)) {
2153
- out[key2] = {
2290
+ for (const [key, field] of Object.entries(fields)) {
2291
+ out[key] = {
2154
2292
  ...field,
2155
2293
  resolver: isResolverRef(field.resolver) ? field.resolver : bindResolver(field.resolver, plugins)
2156
2294
  };
@@ -2159,8 +2297,8 @@ function bindFields(fields, plugins) {
2159
2297
  }
2160
2298
  function bindDefinitions(definitions, plugins) {
2161
2299
  const out = {};
2162
- for (const [key2, def] of Object.entries(definitions)) {
2163
- out[key2] = bindResolver(def, plugins);
2300
+ for (const [key, def] of Object.entries(definitions)) {
2301
+ out[key] = bindResolver(def, plugins);
2164
2302
  }
2165
2303
  return out;
2166
2304
  }
@@ -2245,6 +2383,7 @@ function buildMethodEntries(descriptors, context, states) {
2245
2383
  name: descriptor.name,
2246
2384
  chain: [],
2247
2385
  inputSchema: descriptor.inputSchema,
2386
+ skipInputValidation: descriptor.skipInputValidation,
2248
2387
  // Derive the presentation type from the output mode when the author did
2249
2388
  // not set one; an explicit meta.type (e.g. "create") still wins.
2250
2389
  meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
@@ -2252,17 +2391,17 @@ function buildMethodEntries(descriptors, context, states) {
2252
2391
  // Replaced below; never called.
2253
2392
  value: () => void 0
2254
2393
  };
2255
- const callRun = (input) => descriptor.run({
2256
- imports: buildImports(plugins, descriptor.importBindings),
2394
+ const callRun = (input, ctx) => descriptor.run({
2395
+ imports: buildImports(plugins, descriptor.importBindings, ctx),
2257
2396
  state: states.get(id),
2258
2397
  input
2259
2398
  });
2260
- const fold = (coreFn) => (input) => {
2261
- let next = coreFn;
2399
+ const fold = (coreFn) => (input, ctx) => {
2400
+ let next = (i) => coreFn(i, ctx);
2262
2401
  for (const wrap of entry.chain) {
2263
2402
  const inner = next;
2264
2403
  next = (i) => wrap.run({
2265
- imports: buildImports(plugins, wrap.owner.importBindings),
2404
+ imports: buildImports(plugins, wrap.owner.importBindings, ctx),
2266
2405
  next: inner,
2267
2406
  input: i,
2268
2407
  // Overwritten by the chain item's own closure with the owning
@@ -2286,7 +2425,7 @@ function buildMethodEntries(descriptors, context, states) {
2286
2425
  }
2287
2426
  );
2288
2427
  } else if (out.type === "item") {
2289
- const itemCore = async (input) => callRun(input);
2428
+ const itemCore = async (input, ctx) => callRun(input, ctx);
2290
2429
  entry.value = createFunction(
2291
2430
  fold(itemCore),
2292
2431
  {
@@ -2298,7 +2437,7 @@ function buildMethodEntries(descriptors, context, states) {
2298
2437
  );
2299
2438
  } else {
2300
2439
  entry.value = createRawFunction(
2301
- (input) => fold(callRun)(input),
2440
+ (input, ctx) => fold(callRun)(input, ctx),
2302
2441
  {
2303
2442
  sdk,
2304
2443
  name: descriptor.name,
@@ -2321,11 +2460,15 @@ function buildMethodEntries(descriptors, context, states) {
2321
2460
  });
2322
2461
  return packed;
2323
2462
  };
2463
+ const internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2324
2464
  entry.value = (...args) => canonicalValue(pack(args));
2325
- entry.internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2465
+ entry.internalValue = internalValue;
2466
+ entry.bindInternal = (ctx) => ctx ? (...args) => canonicalValue(pack(args), childCallContext(ctx)) : internalValue;
2326
2467
  entry.positional = names;
2327
2468
  } else {
2328
- entry.internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2469
+ const internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2470
+ entry.internalValue = internalValue;
2471
+ entry.bindInternal = (ctx) => ctx ? (input) => canonicalValue(input, childCallContext(ctx)) : internalValue;
2329
2472
  }
2330
2473
  plugins[id] = entry;
2331
2474
  }
@@ -2565,7 +2708,7 @@ function createSdk(root, options) {
2565
2708
  pluginSurface = {};
2566
2709
  bindValue(pluginSurface, plugin.name, plugins2[plugin.id]);
2567
2710
  }
2568
- for (const key2 of Object.keys(legacyExports)) context.surface[key2] = key2;
2711
+ for (const key of Object.keys(legacyExports)) context.surface[key] = key;
2569
2712
  if (plugin.pluginType === "aggregate") {
2570
2713
  recordExportSurface(context, plugin.exports);
2571
2714
  } else {
@@ -2704,6 +2847,7 @@ function valueTypeOf(inner) {
2704
2847
  if (inner instanceof import_zod3.z.ZodEnum) return "string";
2705
2848
  if (inner instanceof import_zod3.z.ZodArray) return "array";
2706
2849
  if (inner instanceof import_zod3.z.ZodObject) return "object";
2850
+ if (inner instanceof import_zod3.z.ZodRecord) return "object";
2707
2851
  return void 0;
2708
2852
  }
2709
2853
  function staticChoicesOf(inner) {
@@ -2714,7 +2858,8 @@ function staticChoicesOf(inner) {
2714
2858
  return void 0;
2715
2859
  }
2716
2860
  function objectShape(schema) {
2717
- const { inner } = schema ? unwrap(schema) : { inner: void 0 };
2861
+ const canonical = canonicalInputSchema(schema);
2862
+ const { inner } = canonical ? unwrap(canonical) : { inner: void 0 };
2718
2863
  if (inner instanceof import_zod3.z.ZodObject) {
2719
2864
  return inner.shape;
2720
2865
  }
@@ -2736,7 +2881,7 @@ function topoOrder2(specs) {
2736
2881
  }
2737
2882
  function planParameters(entry) {
2738
2883
  const shape = objectShape(entry.inputSchema);
2739
- const resolvers = entry.boundResolvers ?? {};
2884
+ const resolvers = entry.resolvers ?? {};
2740
2885
  const names = shape ? [
2741
2886
  ...Object.keys(shape),
2742
2887
  ...Object.keys(resolvers).filter(
@@ -2774,24 +2919,48 @@ function getAtPath(root, path) {
2774
2919
  }
2775
2920
  return node;
2776
2921
  }
2922
+ function defineOwn(node, key, value) {
2923
+ Object.defineProperty(node, key, {
2924
+ value,
2925
+ writable: true,
2926
+ enumerable: true,
2927
+ configurable: true
2928
+ });
2929
+ }
2777
2930
  function setAtPath(root, path, value) {
2778
2931
  let node = root;
2779
2932
  for (let i = 0; i < path.length - 1; i++) {
2780
2933
  const seg = path[i];
2781
- if (node[seg] == null || typeof node[seg] !== "object") node[seg] = {};
2782
- node = node[seg];
2934
+ const existing = Object.prototype.hasOwnProperty.call(node, seg) ? node[seg] : void 0;
2935
+ if (existing != null && typeof existing === "object") {
2936
+ node = existing;
2937
+ } else {
2938
+ const child = {};
2939
+ defineOwn(node, seg, child);
2940
+ node = child;
2941
+ }
2783
2942
  }
2784
- node[path[path.length - 1]] = value;
2943
+ defineOwn(node, path[path.length - 1], value);
2785
2944
  }
2786
- var key = (path) => path.join(".");
2945
+ var SAFE_SEGMENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2946
+ var pathToKey = (path) => {
2947
+ let out = "";
2948
+ for (const segment of path) {
2949
+ if (typeof segment === "number") out += `[${segment}]`;
2950
+ else if (SAFE_SEGMENT.test(segment))
2951
+ out += out === "" ? segment : `.${segment}`;
2952
+ else out += `[${JSON.stringify(segment)}]`;
2953
+ }
2954
+ return out;
2955
+ };
2787
2956
  function isSettled(state, path) {
2788
- return state.settled.includes(key(path));
2957
+ return state.settled.includes(pathToKey(path));
2789
2958
  }
2790
2959
  function remember(state, k) {
2791
2960
  if (!state.settled.includes(k)) state.settled.push(k);
2792
2961
  }
2793
2962
  function settle(state, path) {
2794
- remember(state, key(path));
2963
+ remember(state, pathToKey(path));
2795
2964
  }
2796
2965
  function clone(state) {
2797
2966
  return JSON.parse(JSON.stringify(state));
@@ -2806,6 +2975,28 @@ function coerce(leaf, raw) {
2806
2975
  if (raw === "true") return true;
2807
2976
  if (raw === "false") return false;
2808
2977
  }
2978
+ if (leaf.valueType === "object") {
2979
+ const trimmed = raw.trim();
2980
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2981
+ try {
2982
+ return JSON.parse(trimmed);
2983
+ } catch {
2984
+ return raw;
2985
+ }
2986
+ }
2987
+ return raw;
2988
+ }
2989
+ if (leaf.valueType === "array") {
2990
+ const trimmed = raw.trim();
2991
+ if (trimmed.startsWith("[")) {
2992
+ try {
2993
+ return JSON.parse(trimmed);
2994
+ } catch {
2995
+ return raw;
2996
+ }
2997
+ }
2998
+ return raw;
2999
+ }
2809
3000
  return raw;
2810
3001
  }
2811
3002
  async function validationError(leaf, value, state) {
@@ -2863,27 +3054,6 @@ async function objectChildren(resolver, input) {
2863
3054
  return toLeaf(name, field, resolver.definitions);
2864
3055
  });
2865
3056
  }
2866
- function arrayItem(resolver) {
2867
- const items = resolver.items;
2868
- const valueType = resolver.itemValueType;
2869
- if (isRef(items)) {
2870
- return {
2871
- name: "",
2872
- required: true,
2873
- resolver: resolver.definitions?.[items.ref],
2874
- extraInput: items.input,
2875
- valueType,
2876
- requires: []
2877
- };
2878
- }
2879
- return {
2880
- name: "",
2881
- required: true,
2882
- resolver: items,
2883
- valueType,
2884
- requires: []
2885
- };
2886
- }
2887
3057
  function autoSettles(resolver) {
2888
3058
  return resolver.type === "constant" || resolver.type === "info";
2889
3059
  }
@@ -2903,10 +3073,28 @@ async function leafAt(ctx, path, resolved) {
2903
3073
  const seg = path[i];
2904
3074
  if (typeof seg === "number") {
2905
3075
  if (leaf?.resolver?.type !== "array") return void 0;
2906
- leaf = arrayItem(leaf.resolver);
3076
+ leaf = boundLeaf(
3077
+ "",
3078
+ leaf.resolver.items,
3079
+ leaf.resolver.definitions,
3080
+ leaf.resolver.itemValueType
3081
+ );
2907
3082
  } else {
2908
- leaf = children.find((c) => c.name === seg);
2909
- if (!leaf) return void 0;
3083
+ const parent = leaf;
3084
+ const found = children.find((c) => c.name === seg);
3085
+ if (found) {
3086
+ leaf = found;
3087
+ } else if (parent?.resolver?.type === "object" && parent.resolver.additionalKeys) {
3088
+ const ak = parent.resolver.additionalKeys;
3089
+ leaf = boundLeaf(
3090
+ String(seg),
3091
+ ak.values,
3092
+ parent.resolver.definitions,
3093
+ ak.valueValueType
3094
+ );
3095
+ } else {
3096
+ return void 0;
3097
+ }
2910
3098
  }
2911
3099
  if (i < path.length - 1 && typeof path[i + 1] === "string") {
2912
3100
  if (leaf?.resolver?.type !== "object") return void 0;
@@ -2918,16 +3106,81 @@ async function leafAt(ctx, path, resolved) {
2918
3106
  }
2919
3107
  return leaf;
2920
3108
  }
3109
+ function boundLeaf(name, resolverOrRef, definitions, valueType) {
3110
+ if (isRef(resolverOrRef)) {
3111
+ return {
3112
+ name,
3113
+ required: true,
3114
+ resolver: definitions?.[resolverOrRef.ref],
3115
+ extraInput: resolverOrRef.input,
3116
+ valueType,
3117
+ requires: []
3118
+ };
3119
+ }
3120
+ return {
3121
+ name,
3122
+ required: true,
3123
+ resolver: resolverOrRef,
3124
+ valueType,
3125
+ requires: []
3126
+ };
3127
+ }
3128
+ async function recordInfoAt(ctx, path, resolved) {
3129
+ const leaf = await leafAt(ctx, path, resolved);
3130
+ const resolver = leaf?.resolver;
3131
+ if (resolver?.type !== "object" || !resolver.additionalKeys) {
3132
+ throw new Error(
3133
+ `expected an object resolver with additionalKeys at "${pathToKey(path)}"`
3134
+ );
3135
+ }
3136
+ if (resolver.getProperties) {
3137
+ throw new Error(
3138
+ `object resolver at "${pathToKey(path)}" cannot combine getProperties with additionalKeys`
3139
+ );
3140
+ }
3141
+ const ak = resolver.additionalKeys;
3142
+ const defs = resolver.definitions;
3143
+ const keyLeaf = ak.keys ? boundLeaf("key", ak.keys, defs, ak.keyValueType ?? "string") : {
3144
+ name: "key",
3145
+ required: true,
3146
+ resolver: { type: "static", inputType: "text" },
3147
+ valueType: "string",
3148
+ requires: []
3149
+ };
3150
+ const valueLeaf = boundLeaf("value", ak.values, defs, ak.valueValueType);
3151
+ if (keyLeaf.resolver && keyLeaf.resolver.type !== "static") {
3152
+ throw new Error(
3153
+ `record key resolver at "${pathToKey(path)}" must be a static free-text prompt, not "${keyLeaf.resolver.type}"`
3154
+ );
3155
+ }
3156
+ if (valueLeaf.resolver?.type === "object" || valueLeaf.resolver?.type === "array") {
3157
+ throw new Error(
3158
+ `record value resolver at "${pathToKey(path)}" must be a single value, not "${valueLeaf.resolver.type}"`
3159
+ );
3160
+ }
3161
+ return {
3162
+ min: ak.minEntries ?? 0,
3163
+ max: ak.maxEntries ?? Infinity,
3164
+ keyLeaf,
3165
+ valueLeaf,
3166
+ fixedKeys: Object.keys(resolver.properties ?? {})
3167
+ };
3168
+ }
2921
3169
  async function arrayInfoAt(ctx, path, resolved) {
2922
3170
  const leaf = await leafAt(ctx, path, resolved);
2923
3171
  const resolver = leaf?.resolver;
2924
3172
  if (resolver?.type !== "array") {
2925
- throw new Error(`expected an array resolver at "${key(path)}"`);
3173
+ throw new Error(`expected an array resolver at "${pathToKey(path)}"`);
2926
3174
  }
2927
3175
  return {
2928
3176
  min: resolver.minItems ?? 0,
2929
3177
  max: resolver.maxItems ?? Infinity,
2930
- item: { ...arrayItem(resolver), name: String(path[path.length - 1]) }
3178
+ item: boundLeaf(
3179
+ String(path[path.length - 1]),
3180
+ resolver.items,
3181
+ resolver.definitions,
3182
+ resolver.itemValueType
3183
+ )
2931
3184
  };
2932
3185
  }
2933
3186
  async function firstPage(result) {
@@ -3008,6 +3261,9 @@ var AFFORDANCE = {
3008
3261
  retry: { action: "retry", description: "Retry loading the options" },
3009
3262
  cancel: { action: "cancel", description: "Cancel resolution" }
3010
3263
  };
3264
+ function affordance(base, description) {
3265
+ return { ...base, description };
3266
+ }
3011
3267
  function selectActions(leaf, page, multiple) {
3012
3268
  const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
3013
3269
  if (searchMode && page.position.search === void 0 && page.items.length === 0) {
@@ -3122,7 +3378,7 @@ async function buildQuestion(leaf, path, input) {
3122
3378
  }
3123
3379
  };
3124
3380
  }
3125
- function collectionQuestion(t) {
3381
+ function arrayItemsQuestion(t) {
3126
3382
  const actions = [AFFORDANCE.add];
3127
3383
  if (t.count >= t.min) actions.push(AFFORDANCE.done);
3128
3384
  return {
@@ -3138,22 +3394,37 @@ function collectionQuestion(t) {
3138
3394
  actions
3139
3395
  };
3140
3396
  }
3141
- function objectGateQuestion(path) {
3397
+ function recordEntriesQuestion(t) {
3398
+ const actions = [
3399
+ affordance(AFFORDANCE.add, "Add another entry")
3400
+ ];
3401
+ if (t.count >= t.min) {
3402
+ actions.push(affordance(AFFORDANCE.done, "Finish the entries"));
3403
+ }
3404
+ return {
3405
+ type: "collection",
3406
+ path: t.path,
3407
+ message: `Add another ${t.path[t.path.length - 1]} entry? (${t.count} so far)`,
3408
+ container: "record",
3409
+ count: t.count,
3410
+ min: t.min,
3411
+ ...Number.isFinite(t.max) ? { max: t.max } : {},
3412
+ actions
3413
+ };
3414
+ }
3415
+ function objectOptionalQuestion(path) {
3142
3416
  return {
3143
3417
  type: "collection",
3144
3418
  path,
3145
3419
  message: `Add ${path[path.length - 1]}?`,
3146
3420
  container: "object",
3147
3421
  actions: [
3148
- {
3149
- action: "add",
3150
- description: "Provide values for these fields"
3151
- },
3152
- { action: "done", description: "Skip these fields" }
3422
+ affordance(AFFORDANCE.add, "Provide values for these fields"),
3423
+ affordance(AFFORDANCE.done, "Skip these fields")
3153
3424
  ]
3154
3425
  };
3155
3426
  }
3156
- function optionalsGateQuestion(path, pending) {
3427
+ function objectOptionalPropertiesQuestion(path, pending) {
3157
3428
  return {
3158
3429
  type: "collection",
3159
3430
  path,
@@ -3170,8 +3441,8 @@ function optionalsGateQuestion(path, pending) {
3170
3441
  ...leaf.valueType ? { valueType: leaf.valueType } : {}
3171
3442
  })),
3172
3443
  actions: [
3173
- { action: "add", description: "Configure the optional fields" },
3174
- { action: "done", description: "Skip the optional fields" }
3444
+ affordance(AFFORDANCE.add, "Configure the optional fields"),
3445
+ affordance(AFFORDANCE.done, "Skip the optional fields")
3175
3446
  ]
3176
3447
  };
3177
3448
  }
@@ -3189,7 +3460,8 @@ function finalize(ctx, resolved) {
3189
3460
  }));
3190
3461
  return { status: "invalid", issues };
3191
3462
  }
3192
- var optionalsMarker = (path) => `${key(path)}?optionals`;
3463
+ var optionalsMarker = (path) => `${pathToKey(path)}?optionals`;
3464
+ var UNSAFE_RECORD_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3193
3465
  async function findInArray(ctx, state, path) {
3194
3466
  if (isSettled(state, path)) return null;
3195
3467
  if (getAtPath(state.resolved, path) == null)
@@ -3210,7 +3482,28 @@ async function findInArray(ctx, state, path) {
3210
3482
  }
3211
3483
  }
3212
3484
  if (len < min) return descendItem(ctx, state, path, len, item);
3213
- if (len < max) return { kind: "array", path, count: len, min, max };
3485
+ if (len < max) return { type: "array_items", path, count: len, min, max };
3486
+ settle(state, path);
3487
+ return null;
3488
+ }
3489
+ async function findInRecord(ctx, state, path) {
3490
+ if (isSettled(state, path)) return null;
3491
+ if (getAtPath(state.resolved, path) == null)
3492
+ setAtPath(state.resolved, path, {});
3493
+ if (!state.interactive) {
3494
+ settle(state, path);
3495
+ return null;
3496
+ }
3497
+ const { min, max, keyLeaf, fixedKeys } = await recordInfoAt(
3498
+ ctx,
3499
+ path,
3500
+ state.resolved
3501
+ );
3502
+ const container = getAtPath(state.resolved, path);
3503
+ const fixed = new Set(fixedKeys);
3504
+ const count = Object.keys(container).filter((k) => !fixed.has(k)).length;
3505
+ if (count < min) return { type: "record_key", path, leaf: keyLeaf };
3506
+ if (count < max) return { type: "record_entries", path, count, min, max };
3214
3507
  settle(state, path);
3215
3508
  return null;
3216
3509
  }
@@ -3228,10 +3521,10 @@ function seedItemSlot(state, itemPath, item) {
3228
3521
  }
3229
3522
  async function descendItem(ctx, state, arrayPath, index, item) {
3230
3523
  const itemPath = [...arrayPath, index];
3231
- const kind = seedItemSlot(state, itemPath, item);
3232
- if (kind === "object") return findNext(ctx, state, itemPath);
3233
- if (kind === "array") return findInArray(ctx, state, itemPath);
3234
- return { kind: "leaf", path: itemPath, leaf: item };
3524
+ const slotType = seedItemSlot(state, itemPath, item);
3525
+ if (slotType === "object") return findNext(ctx, state, itemPath);
3526
+ if (slotType === "array") return findInArray(ctx, state, itemPath);
3527
+ return { type: "leaf", path: itemPath, leaf: item };
3235
3528
  }
3236
3529
  async function findNext(ctx, state, path = []) {
3237
3530
  const container = getAtPath(state.resolved, path) ?? {};
@@ -3256,7 +3549,7 @@ async function findNext(ctx, state, path = []) {
3256
3549
  const pending = ordered.filter(
3257
3550
  (c) => !c.required && asksUser(c) && isPendingChild(c)
3258
3551
  );
3259
- return { kind: "optionals", path, pending };
3552
+ return { type: "object_optional_properties", path, pending };
3260
3553
  }
3261
3554
  if (leaf.resolver?.type === "object") {
3262
3555
  if (isSettled(state, childPath)) continue;
@@ -3266,7 +3559,7 @@ async function findNext(ctx, state, path = []) {
3266
3559
  settle(state, childPath);
3267
3560
  continue;
3268
3561
  }
3269
- return { kind: "object", path: childPath, leaf };
3562
+ return { type: "object_optional", path: childPath, leaf };
3270
3563
  }
3271
3564
  setAtPath(state.resolved, childPath, {});
3272
3565
  }
@@ -3298,13 +3591,21 @@ async function findNext(ctx, state, path = []) {
3298
3591
  }
3299
3592
  if (container[leaf.name] !== void 0 || isSettled(state, childPath))
3300
3593
  continue;
3301
- return { kind: "leaf", path: childPath, leaf };
3594
+ return { type: "leaf", path: childPath, leaf };
3595
+ }
3596
+ if (inObject && !isSettled(state, path)) {
3597
+ const self = await leafAt(ctx, path, state.resolved);
3598
+ if (self?.resolver?.type === "object" && self.resolver.additionalKeys) {
3599
+ const rec = await findInRecord(ctx, state, path);
3600
+ if (rec) return rec;
3601
+ }
3302
3602
  }
3303
3603
  return null;
3304
3604
  }
3305
3605
  async function askLeaf(state, path, leaf, opts = {}) {
3306
3606
  state.current = path;
3307
- delete state.gate;
3607
+ if (opts.gate) state.gate = opts.gate;
3608
+ else delete state.gate;
3308
3609
  try {
3309
3610
  const { question, pagination } = await buildQuestion(
3310
3611
  leaf,
@@ -3325,6 +3626,28 @@ async function askLeaf(state, path, leaf, opts = {}) {
3325
3626
  return failedResult(state, leaf.name, error);
3326
3627
  }
3327
3628
  }
3629
+ async function askRecordKey(state, path, keyLeaf, opts = {}) {
3630
+ return askLeaf(state, path, keyLeaf, { gate: "record_key", ...opts });
3631
+ }
3632
+ async function autoResolveLeaf(state, path, leaf) {
3633
+ const resolver = leaf.resolver;
3634
+ if (resolver && autoSettles(resolver)) {
3635
+ if (resolver.type === "constant")
3636
+ setAtPath(state.resolved, path, resolver.value);
3637
+ settle(state, path);
3638
+ return true;
3639
+ }
3640
+ const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
3641
+ input: mergeInput(state.resolved, leaf.extraInput)
3642
+ }) : void 0;
3643
+ if (auto) {
3644
+ if (auto.resolvedValue !== void 0)
3645
+ setAtPath(state.resolved, path, auto.resolvedValue);
3646
+ settle(state, path);
3647
+ return true;
3648
+ }
3649
+ return false;
3650
+ }
3328
3651
  async function advance(ctx, state) {
3329
3652
  for (; ; ) {
3330
3653
  const target = await findNext(ctx, state);
@@ -3334,54 +3657,56 @@ async function advance(ctx, state) {
3334
3657
  delete state.pagination;
3335
3658
  return { state, result: finalize(ctx, state.resolved) };
3336
3659
  }
3337
- if (target.kind === "array") {
3660
+ if (target.type === "array_items") {
3338
3661
  state.current = target.path;
3339
- state.gate = "array";
3662
+ state.gate = "array_items";
3340
3663
  delete state.pagination;
3341
3664
  return {
3342
3665
  state,
3343
- result: { status: "ask", question: collectionQuestion(target) }
3666
+ result: { status: "ask", question: arrayItemsQuestion(target) }
3344
3667
  };
3345
3668
  }
3346
- if (target.kind === "object") {
3669
+ if (target.type === "object_optional") {
3347
3670
  state.current = target.path;
3348
- state.gate = "entry";
3671
+ state.gate = "object_optional";
3349
3672
  delete state.pagination;
3350
3673
  return {
3351
3674
  state,
3352
- result: { status: "ask", question: objectGateQuestion(target.path) }
3675
+ result: {
3676
+ status: "ask",
3677
+ question: objectOptionalQuestion(target.path)
3678
+ }
3353
3679
  };
3354
3680
  }
3355
- if (target.kind === "optionals") {
3681
+ if (target.type === "object_optional_properties") {
3356
3682
  state.current = target.path;
3357
- state.gate = "optionals";
3683
+ state.gate = "object_optional_properties";
3358
3684
  delete state.pagination;
3359
3685
  return {
3360
3686
  state,
3361
3687
  result: {
3362
3688
  status: "ask",
3363
- question: optionalsGateQuestion(target.path, target.pending)
3689
+ question: objectOptionalPropertiesQuestion(
3690
+ target.path,
3691
+ target.pending
3692
+ )
3364
3693
  }
3365
3694
  };
3366
3695
  }
3367
- const { path, leaf } = target;
3368
- const resolver = leaf.resolver;
3369
- if (resolver && autoSettles(resolver)) {
3370
- if (resolver.type === "constant") {
3371
- setAtPath(state.resolved, path, resolver.value);
3372
- }
3373
- settle(state, path);
3374
- continue;
3696
+ if (target.type === "record_entries") {
3697
+ state.current = target.path;
3698
+ state.gate = "record_entries";
3699
+ delete state.pagination;
3700
+ return {
3701
+ state,
3702
+ result: { status: "ask", question: recordEntriesQuestion(target) }
3703
+ };
3375
3704
  }
3376
- const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
3377
- input: mergeInput(state.resolved, leaf.extraInput)
3378
- }) : void 0;
3379
- if (auto) {
3380
- if (auto.resolvedValue !== void 0)
3381
- setAtPath(state.resolved, path, auto.resolvedValue);
3382
- settle(state, path);
3383
- continue;
3705
+ if (target.type === "record_key") {
3706
+ return askRecordKey(state, target.path, target.leaf);
3384
3707
  }
3708
+ const { path, leaf } = target;
3709
+ if (await autoResolveLeaf(state, path, leaf)) continue;
3385
3710
  if (!state.interactive) {
3386
3711
  if (!leaf.required) {
3387
3712
  settle(state, path);
@@ -3414,10 +3739,18 @@ async function step(ctx, prior, action) {
3414
3739
  delete state.pagination;
3415
3740
  return { state, result: { status: "cancelled" } };
3416
3741
  }
3742
+ if (state.gate === "record_key") {
3743
+ return stepRecordKey(ctx, state, action);
3744
+ }
3417
3745
  const path = state.current;
3418
3746
  if (!path) throw new Error("step called with no outstanding question");
3419
3747
  const leaf = await leafAt(ctx, path, state.resolved);
3420
- if (leaf && (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry")) {
3748
+ if (!leaf) {
3749
+ throw new Error(
3750
+ `no resolver for the outstanding question at "${pathToKey(path)}"`
3751
+ );
3752
+ }
3753
+ if (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry") {
3421
3754
  return refine(ctx, state, leaf, path, action);
3422
3755
  }
3423
3756
  if (action.type === "add" || action.type === "done") {
@@ -3434,19 +3767,26 @@ async function step(ctx, prior, action) {
3434
3767
  settle(state, path);
3435
3768
  return advance(ctx, state);
3436
3769
  }
3437
- if (gate === "entry") {
3770
+ if (gate === "object_optional") {
3438
3771
  setAtPath(state.resolved, path, {});
3439
3772
  return advance(ctx, state);
3440
3773
  }
3441
- if (gate === "optionals") {
3774
+ if (gate === "object_optional_properties") {
3442
3775
  remember(state, optionalsMarker(path));
3443
3776
  return advance(ctx, state);
3444
3777
  }
3778
+ if (gate === "record_entries") {
3779
+ const { keyLeaf } = await recordInfoAt(ctx, path, state.resolved);
3780
+ return askRecordKey(state, path, keyLeaf);
3781
+ }
3445
3782
  const items = getAtPath(state.resolved, path) ?? [];
3446
3783
  const { item } = await arrayInfoAt(ctx, path, state.resolved);
3447
3784
  const itemPath = [...path, items.length];
3448
- if (seedItemSlot(state, itemPath, item) === "leaf")
3785
+ if (seedItemSlot(state, itemPath, item) === "leaf") {
3786
+ if (await autoResolveLeaf(state, itemPath, item))
3787
+ return advance(ctx, state);
3449
3788
  return askLeaf(state, itemPath, item);
3789
+ }
3450
3790
  return advance(ctx, state);
3451
3791
  }
3452
3792
  if (state.gate) {
@@ -3457,81 +3797,37 @@ async function step(ctx, prior, action) {
3457
3797
  switch (action.type) {
3458
3798
  case "choose":
3459
3799
  case "custom": {
3460
- if (leaf) {
3461
- let error;
3462
- try {
3463
- error = await validationError(leaf, action.value, state);
3464
- } catch (thrown) {
3465
- return failedResult(state, leaf.name, thrown);
3466
- }
3467
- if (error) {
3468
- if (state.pagination && leaf.resolver?.type === "dynamic") {
3469
- try {
3470
- const context = await resolveContext(leaf, state.resolved);
3471
- const page = await fetchListing(
3472
- leaf,
3473
- state.resolved,
3474
- state.pagination.position,
3475
- context
3476
- );
3477
- state.pagination = toPagination(page);
3478
- return {
3479
- state,
3480
- result: {
3481
- status: "ask",
3482
- question: selectQuestion(
3483
- leaf,
3484
- path,
3485
- state.resolved,
3486
- page,
3487
- context
3488
- ),
3489
- error
3490
- }
3491
- };
3492
- } catch (fetchError) {
3493
- state.pagination = failedPagination(
3494
- state.pagination,
3495
- state.pagination.position
3496
- );
3497
- return failedResult(state, leaf.name, fetchError);
3498
- }
3499
- }
3500
- return askLeaf(state, path, leaf, { error });
3800
+ let error;
3801
+ try {
3802
+ error = await validationError(leaf, action.value, state);
3803
+ } catch (thrown) {
3804
+ return failedResult(state, leaf.name, thrown);
3805
+ }
3806
+ if (error) {
3807
+ if (state.pagination && leaf.resolver?.type === "dynamic") {
3808
+ return renderPageAt(state, leaf, path, state.pagination.position, {
3809
+ error
3810
+ });
3501
3811
  }
3812
+ return askLeaf(state, path, leaf, { error });
3502
3813
  }
3503
- setAtPath(
3504
- state.resolved,
3505
- path,
3506
- leaf ? coerce(leaf, action.value) : action.value
3507
- );
3814
+ setAtPath(state.resolved, path, coerce(leaf, action.value));
3508
3815
  break;
3509
3816
  }
3510
3817
  case "skip":
3511
3818
  settle(state, path);
3512
3819
  break;
3513
3820
  default:
3514
- throw new Error(`action "${action.type}" is not supported here`);
3821
+ throw new Error(
3822
+ `action "${action.type}" is not supported here`
3823
+ );
3515
3824
  }
3516
3825
  delete state.current;
3517
3826
  delete state.pagination;
3518
3827
  return advance(ctx, state);
3519
3828
  }
3520
- async function refine(ctx, state, leaf, path, action) {
3521
- const position = positionAfter(state.pagination, action);
3829
+ async function renderPageAt(state, leaf, path, position, opts = {}) {
3522
3830
  try {
3523
- if (action.type === "search") {
3524
- const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
3525
- input: mergeInput(state.resolved, leaf.extraInput),
3526
- search: action.term
3527
- }) : void 0;
3528
- if (exact) {
3529
- setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
3530
- delete state.current;
3531
- delete state.pagination;
3532
- return advance(ctx, state);
3533
- }
3534
- }
3535
3831
  const context = await resolveContext(leaf, state.resolved);
3536
3832
  const page = await fetchListing(leaf, state.resolved, position, context);
3537
3833
  state.pagination = toPagination(page);
@@ -3539,7 +3835,8 @@ async function refine(ctx, state, leaf, path, action) {
3539
3835
  state,
3540
3836
  result: {
3541
3837
  status: "ask",
3542
- question: selectQuestion(leaf, path, state.resolved, page, context)
3838
+ question: selectQuestion(leaf, path, state.resolved, page, context),
3839
+ ...opts.error ? { error: opts.error } : {}
3543
3840
  }
3544
3841
  };
3545
3842
  } catch (error) {
@@ -3547,6 +3844,65 @@ async function refine(ctx, state, leaf, path, action) {
3547
3844
  return failedResult(state, leaf.name, error);
3548
3845
  }
3549
3846
  }
3847
+ async function refine(ctx, state, leaf, path, action) {
3848
+ const position = positionAfter(state.pagination, action);
3849
+ if (action.type === "search") {
3850
+ try {
3851
+ const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
3852
+ input: mergeInput(state.resolved, leaf.extraInput),
3853
+ search: action.term
3854
+ }) : void 0;
3855
+ if (exact) {
3856
+ setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
3857
+ delete state.current;
3858
+ delete state.pagination;
3859
+ return advance(ctx, state);
3860
+ }
3861
+ } catch (error) {
3862
+ state.pagination = failedPagination(state.pagination, position);
3863
+ return failedResult(state, leaf.name, error);
3864
+ }
3865
+ }
3866
+ return renderPageAt(state, leaf, path, position);
3867
+ }
3868
+ async function stepRecordKey(ctx, state, action) {
3869
+ const path = state.current;
3870
+ if (!path)
3871
+ throw new Error("record key step called with no outstanding question");
3872
+ const { keyLeaf, valueLeaf } = await recordInfoAt(ctx, path, state.resolved);
3873
+ if (action.type === "skip") {
3874
+ delete state.gate;
3875
+ delete state.current;
3876
+ delete state.pagination;
3877
+ return advance(ctx, state);
3878
+ }
3879
+ if (action.type !== "custom" && action.type !== "choose") {
3880
+ throw new Error(
3881
+ `action "${action.type}" is not supported while entering a record key`
3882
+ );
3883
+ }
3884
+ const raw = Array.isArray(action.value) ? action.value[0] : action.value;
3885
+ const entryKey = String(coerce(keyLeaf, raw));
3886
+ if (entryKey.trim() === "") {
3887
+ return askRecordKey(state, path, keyLeaf, { error: "A key is required." });
3888
+ }
3889
+ if (UNSAFE_RECORD_KEYS.has(entryKey)) {
3890
+ return askRecordKey(state, path, keyLeaf, {
3891
+ error: `"${entryKey}" is not an allowed key.`
3892
+ });
3893
+ }
3894
+ const container = getAtPath(state.resolved, path);
3895
+ if (Object.prototype.hasOwnProperty.call(container, entryKey)) {
3896
+ return askRecordKey(state, path, keyLeaf, {
3897
+ error: `"${entryKey}" is already set.`
3898
+ });
3899
+ }
3900
+ const valuePath = [...path, entryKey];
3901
+ if (await autoResolveLeaf(state, valuePath, valueLeaf)) {
3902
+ return advance(ctx, state);
3903
+ }
3904
+ return askLeaf(state, valuePath, valueLeaf);
3905
+ }
3550
3906
  function failedPagination(pagination, retryPosition) {
3551
3907
  return {
3552
3908
  position: pagination?.position ?? firstPagePosition(),
@@ -3602,7 +3958,7 @@ function projectSummary(entry) {
3602
3958
  };
3603
3959
  }
3604
3960
  function projectMethod(entry) {
3605
- const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
3961
+ const inputProperties = toJsonSchema(canonicalInputSchema(entry.inputSchema))?.properties;
3606
3962
  const parameters = {};
3607
3963
  for (const spec of planParameters(entry).parameters) {
3608
3964
  const dynamic = spec.resolver?.type === "dynamic" ? spec.resolver : void 0;
@@ -3635,7 +3991,12 @@ function createController(sdk) {
3635
3991
  const entry = entryFor(method);
3636
3992
  return {
3637
3993
  method,
3638
- schema: entry.inputSchema,
3994
+ // A method that owns its input validation (`skipInputValidation`, e.g.
3995
+ // fetch) must not be re-validated by the controller's final `safeParse`;
3996
+ // drop the schema so `finalize` returns the resolved input untouched.
3997
+ // Planning still reads `entry.inputSchema` directly, so parameters are
3998
+ // unaffected.
3999
+ schema: entry.skipInputValidation ? void 0 : entry.inputSchema,
3639
4000
  parameters: planParameters(entry).parameters
3640
4001
  };
3641
4002
  }
@@ -3700,59 +4061,6 @@ function createCorePlugin(options) {
3700
4061
  }
3701
4062
  });
3702
4063
  }
3703
-
3704
- // src/utils/schema-utils.ts
3705
- var import_zod5 = require("zod");
3706
- function getOutputSchema(inputSchema) {
3707
- return inputSchema._zod.def.outputSchema;
3708
- }
3709
- function withOutputSchema(inputSchema, outputSchema) {
3710
- Object.assign(inputSchema._zod.def, {
3711
- outputSchema
3712
- });
3713
- return inputSchema;
3714
- }
3715
- function withResolver(schema, config) {
3716
- schema._zod.def.resolverMeta = config;
3717
- return schema;
3718
- }
3719
- function getSchemaDescription(schema) {
3720
- return schema.description;
3721
- }
3722
- function getFieldDescriptions(schema) {
3723
- const descriptions = {};
3724
- const shape = schema.shape;
3725
- for (const [key2, fieldSchema] of Object.entries(shape)) {
3726
- if (fieldSchema instanceof import_zod5.z.ZodType && fieldSchema.description) {
3727
- descriptions[key2] = fieldSchema.description;
3728
- }
3729
- }
3730
- return descriptions;
3731
- }
3732
- function withPositional(schema) {
3733
- Object.assign(schema._zod.def, {
3734
- positionalMeta: { positional: true }
3735
- });
3736
- return schema;
3737
- }
3738
- function schemaHasPositionalMeta(schema) {
3739
- return "positionalMeta" in schema._zod.def;
3740
- }
3741
- function isPositional(schema) {
3742
- if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
3743
- return true;
3744
- }
3745
- if (schema instanceof import_zod5.z.ZodOptional) {
3746
- return isPositional(schema._zod.def.innerType);
3747
- }
3748
- if (schema instanceof import_zod5.z.ZodDefault) {
3749
- return isPositional(schema._zod.def.innerType);
3750
- }
3751
- return false;
3752
- }
3753
- function openEnum(values, description) {
3754
- return import_zod5.z.union([import_zod5.z.enum(values), import_zod5.z.string()]).describe(description);
3755
- }
3756
4064
  // Annotate the CommonJS export names for ESM import in node:
3757
4065
  0 && (module.exports = {
3758
4066
  CONTEXT,
@@ -3765,7 +4073,9 @@ function openEnum(values, description) {
3765
4073
  CoreErrorCode,
3766
4074
  CoreSignal,
3767
4075
  addPlugin,
4076
+ canonicalInputSchema,
3768
4077
  composePlugins,
4078
+ concatLists,
3769
4079
  concatPaginated,
3770
4080
  coreOptionsPluginRef,
3771
4081
  createAsyncContext,