@zapier/kitcore 0.6.0 → 0.7.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.d.mts CHANGED
@@ -134,15 +134,17 @@ type ListPromptConfig = PromptConfig & {
134
134
  };
135
135
  /**
136
136
  * The prompt config the NEW-model resolvers (`defineResolver`) return. It omits
137
- * three fields the resolution controller does not honor, so authors can't
137
+ * four fields the resolution controller does not honor, so authors can't
138
138
  * supply a silent no-op:
139
- * - `name` — the framework supplies the answer key (always was overwritten).
140
- * - `default`— no resolver uses it; the controller has no preselect concept.
141
- * - `filter` — no resolver uses it; transform values in `listItems` instead.
142
- * (The legacy `SchemaParameterResolver` still honors `default`/`filter`, so the
143
- * full `PromptConfig` stays for that path.)
144
- */
145
- type ResolverPromptConfig = Omit<PromptConfig, "name" | "default" | "filter">;
139
+ * - `name` — the framework supplies the answer key (always was overwritten).
140
+ * - `default` no resolver uses it; the controller has no preselect concept.
141
+ * - `filter` — no resolver uses it; transform values in `listItems` instead.
142
+ * - `validate`— validation is the resolver's top-level `validate`, which
143
+ * never routes through rendering (and gets `imports`).
144
+ * (The legacy `SchemaParameterResolver` still honors `default`/`filter`/
145
+ * `validate`, so the full `PromptConfig` stays for that path.)
146
+ */
147
+ type ResolverPromptConfig = Omit<PromptConfig, "name" | "default" | "filter" | "validate">;
146
148
  interface Resolver$1 {
147
149
  type: string;
148
150
  depends?: readonly string[] | string[];
@@ -571,11 +573,27 @@ interface DynamicResolver extends ResolverBase {
571
573
  cursor?: string;
572
574
  }) => ListItemsResult<unknown>;
573
575
  prompt?: (bag: {
576
+ /** The CURRENT page's items only — the engine windows the listing one
577
+ * page at a time (an accumulating host may be showing more). Rendering
578
+ * input only; validation is the top-level `validate`. */
574
579
  items: unknown[];
575
580
  input: Record<string, unknown>;
576
581
  /** The value `getContext` returned, if any. */
577
582
  context?: unknown;
578
583
  }) => ResolverPromptConfig;
584
+ /** Check a chosen/typed value before the engine accepts it. Async with
585
+ * `imports` so it can verify against the source (`tryResolveFromSearch`'s
586
+ * sibling for picks) — never against a loaded page: pagination means the
587
+ * pick can come from a page the engine no longer holds. Return true to
588
+ * accept or a message to re-ask with. A throw is a lookup failure (the
589
+ * host gets retry/cancel), not a rejection. */
590
+ validate?: (bag: {
591
+ imports: Record<string, unknown>;
592
+ value: unknown;
593
+ input: Record<string, unknown>;
594
+ /** The value `getContext` returned, if any. */
595
+ context?: unknown;
596
+ }) => Promise<true | string> | true | string;
579
597
  /** Resolve with no user input at all (e.g. a configured default), skipping the
580
598
  * prompt. Runs before prompting; used always in non-interactive mode and as a
581
599
  * "can we skip asking?" check otherwise. Returns null to fall through to a prompt. */
@@ -726,6 +744,11 @@ interface BoundDynamicResolver extends BoundResolverBase {
726
744
  input: Record<string, unknown>;
727
745
  context?: unknown;
728
746
  }) => ResolverPromptConfig;
747
+ validate?: (bag: {
748
+ value: unknown;
749
+ input: Record<string, unknown>;
750
+ context?: unknown;
751
+ }) => Promise<true | string> | true | string;
729
752
  tryResolveWithoutPrompt?: (bag: {
730
753
  input: Record<string, unknown>;
731
754
  }) => Promise<{
@@ -2108,6 +2131,18 @@ declare function defineResolver<const TImports extends ImportsInput = readonly [
2108
2131
  /** The value `getContext` returned, if any. */
2109
2132
  context?: TContext;
2110
2133
  }) => ResolverPromptConfig;
2134
+ /** Check a chosen/typed value before the engine accepts it. Async with
2135
+ * `imports` so it can verify against the source — never against a loaded
2136
+ * page (the pick can come from a page the engine no longer holds). Return
2137
+ * true to accept or a message to re-ask with; a throw is a lookup failure
2138
+ * (retry/cancel), not a rejection. */
2139
+ validate?: (bag: {
2140
+ imports: ImportsOf<TImports>;
2141
+ value: unknown;
2142
+ input: TInput;
2143
+ /** The value `getContext` returned, if any. */
2144
+ context?: TContext;
2145
+ }) => Promise<true | string> | true | string;
2111
2146
  /** Resolve with no user input (e.g. a configured default), skipping the prompt. */
2112
2147
  tryResolveWithoutPrompt?: (bag: {
2113
2148
  imports: ImportsOf<TImports>;
@@ -2741,12 +2776,14 @@ interface ControllerAffordance {
2741
2776
  /** A question the host renders. Discriminated on `type`; the available moves are
2742
2777
  * the self-describing `actions` list (single source of truth, no flags).
2743
2778
  * `actions` is emitted in recommended presentation order — answer directly
2744
- * (`choose`/`custom`/`add`), refine (`search`), paginate (`more`), decline
2745
- * (`skip`/`done`), and failure questions offer `retry` then `cancel` — so a
2746
- * minimal host can render the list verbatim, top to bottom. Hosts with richer
2747
- * widgets (windowed lists, filter state) may reorder. */
2779
+ * (`choose`/`custom`/`add`), refine (`search`), paginate
2780
+ * (`next_page`/`previous_page`), decline (`skip`/`done`), and failure
2781
+ * questions offer `retry` then `cancel` so a minimal host can render the
2782
+ * list verbatim, top to bottom. Hosts with richer widgets (windowed lists,
2783
+ * filter state) may reorder. */
2748
2784
  type ControllerQuestion = {
2749
2785
  type: "select";
2786
+ path: ControllerPath;
2750
2787
  message: string;
2751
2788
  /** What this field is, for an agent that lacks the schema. */
2752
2789
  description?: string;
@@ -2769,8 +2806,18 @@ type ControllerQuestion = {
2769
2806
  * in its lead-with-term prompt (e.g. "Enter or search app (e.g. 'slack')").
2770
2807
  * Only meaningful before a search has run. */
2771
2808
  placeholder?: string;
2809
+ /** Where these `choices` sit in the paginated listing. `choices` is ONE
2810
+ * page (payloads stay O(page); the engine never re-sends earlier pages).
2811
+ * A window host renders the page and pages with
2812
+ * `next_page`/`previous_page`; an accumulating host appends pages
2813
+ * client-side: same `path` + same `generation` +
2814
+ * advancing `index` means "extend what you showed", and a `generation`
2815
+ * change (a search ran) means "start over". Absent on unpaginated
2816
+ * selects (static enums). */
2817
+ page?: ControllerSelectPage;
2772
2818
  } | {
2773
2819
  type: "input";
2820
+ path: ControllerPath;
2774
2821
  message: string;
2775
2822
  description?: string;
2776
2823
  inputType: "text" | "password" | "email";
@@ -2778,6 +2825,7 @@ type ControllerQuestion = {
2778
2825
  actions: ControllerAffordance[];
2779
2826
  } | {
2780
2827
  type: "collection";
2828
+ path: ControllerPath;
2781
2829
  message: string;
2782
2830
  description?: string;
2783
2831
  /** Which container kind this decision gates. `array` is the add-another
@@ -2806,6 +2854,15 @@ type ControllerQuestion = {
2806
2854
  max?: number;
2807
2855
  actions: ControllerAffordance[];
2808
2856
  };
2857
+ /** A select question's position in its paginated listing. */
2858
+ interface ControllerSelectPage {
2859
+ /** Increments whenever the listing restarts (a `search` ran, even with the
2860
+ * same term). An accumulating host discards what it has on a new
2861
+ * generation. */
2862
+ generation: number;
2863
+ /** Zero-based page number within this generation. */
2864
+ index: number;
2865
+ }
2809
2866
  /** The host's response to a question. The wire shape is frozen: a fuller
2810
2867
  * HATEOAS affordance schema would still produce exactly these. */
2811
2868
  type ControllerAction = {
@@ -2815,7 +2872,9 @@ type ControllerAction = {
2815
2872
  type: "search";
2816
2873
  term: string;
2817
2874
  } | {
2818
- type: "more";
2875
+ type: "next_page";
2876
+ } | {
2877
+ type: "previous_page";
2819
2878
  } | {
2820
2879
  type: "custom";
2821
2880
  value: string;
@@ -2836,7 +2895,7 @@ type ControllerAction = {
2836
2895
  type ControllerResult = {
2837
2896
  status: "ask";
2838
2897
  question: ControllerQuestion;
2839
- /** The prior answer's validation failure (`PromptConfig.validate`), when
2898
+ /** The prior answer's validation failure (the resolver's `validate`), when
2840
2899
  * this is a re-ask. About the last transition, not the question itself. */
2841
2900
  error?: string;
2842
2901
  } | {
@@ -2889,9 +2948,10 @@ interface ControllerState {
2889
2948
  * add/done handling never infers the decision from value presence or
2890
2949
  * resolver shape. Absent when `current` is a plain leaf question. */
2891
2950
  gate?: "array" | "entry" | "optionals";
2892
- /** Listing progress for the current dynamic leaf (serializable: items +
2893
- * cursor, never a live iterator). */
2894
- listing?: ControllerListing;
2951
+ /** Where pagination stands for the current dynamic leaf: coordinates only
2952
+ * (cursor trail, generation), never items or live iterators — the page's
2953
+ * items ride the ask's question. */
2954
+ pagination?: ControllerPagination;
2895
2955
  /** Whether the host will prompt. Interactive (the default) always asks;
2896
2956
  * non-interactive runs `tryResolveWithoutPrompt` to auto-fill what it can
2897
2957
  * (e.g. configured defaults) before asking for the rest. */
@@ -2900,14 +2960,42 @@ interface ControllerState {
2900
2960
  /** A location in the input tree: top-level `["app"]`, a nested object field
2901
2961
  * `["inputs", "channel"]`, or (later) an array item `["records", 0, "id"]`. */
2902
2962
  type ControllerPath = (string | number)[];
2903
- /** Accumulated candidate items for the current dynamic parameter, plus the
2904
- * serializable cursor for "load more" and the active search term. */
2905
- interface ControllerListing {
2906
- items: unknown[];
2907
- cursor?: string;
2963
+ /** Where a listing currently stands: the coordinates that make one page of a
2964
+ * dynamic parameter's candidates fetchable and re-fetchable with no
2965
+ * shared in-memory state. `previous_page` and `retry` work by re-issuing a
2966
+ * recorded position. */
2967
+ interface ControllerListingPosition {
2968
+ /** The active search term, when the resolver is search-mode. */
2908
2969
  search?: string;
2909
- /** True when the source reported no further pages. */
2910
- exhausted: boolean;
2970
+ /** Cursor that fetched the current page; `null` is the first page. */
2971
+ pageCursor: string | null;
2972
+ /** Cursors of the pages before this one, oldest first (each entry fetches
2973
+ * that page; `null` is the first page). `previous_page` pops the last. */
2974
+ previousCursors: (string | null)[];
2975
+ /** Increments on every listing restart (each `search`). Surfaced to hosts
2976
+ * via {@link ControllerSelectPage} so accumulators know when to reset. */
2977
+ generation: number;
2978
+ }
2979
+ /** The enumeration in progress for the current dynamic parameter:
2980
+ * coordinates only. The fetched items ride the ask's `question.choices`; the
2981
+ * host carries back these cursors between steps, never the items it was just
2982
+ * shown. Anything the engine needs to redraw (a validate-rejection re-ask) it
2983
+ * re-fetches statelessly from `position`. */
2984
+ interface ControllerPagination {
2985
+ position: ControllerListingPosition;
2986
+ /** Resume point for `next_page`; absent on the last page. */
2987
+ nextCursor?: string;
2988
+ /** The position whose fetch failed, kept so `retry` replays exactly it.
2989
+ * Absent when the page loaded. */
2990
+ retryPosition?: ControllerListingPosition;
2991
+ }
2992
+ /** One fetched page of a listing: the position it was fetched at plus what
2993
+ * came back. Never stored: `toPagination` strips it to the coordinates the
2994
+ * state keeps. */
2995
+ interface ControllerListingPage {
2996
+ position: ControllerListingPosition;
2997
+ items: unknown[];
2998
+ nextCursor?: string;
2911
2999
  }
2912
3000
  /**
2913
3001
  * The one pluggable seam for the in-process `resolve` sugar. It receives the
@@ -3287,25 +3375,37 @@ interface PaginatedResult<TItem> {
3287
3375
  data: TItem[];
3288
3376
  nextCursor?: string;
3289
3377
  }
3290
- type PaginatedSource<TItem> = () => PromiseLike<PaginatedResult<TItem>> & AsyncIterable<PaginatedResult<TItem>>;
3291
3378
  /**
3292
- * Concatenate multiple paginated SDK results into a single paginated stream.
3293
- * Each source is a function returning a dual Promise+AsyncIterable (as SDK
3294
- * paginated methods return). Sources are drained in order.
3379
+ * One page-at-a-time source for `concatPaginated`: called with that source's
3380
+ * own cursor, resolves one page. An SDK paginated method fits directly
3381
+ * (`({ cursor }) => sdk.listThings({ cursor })` awaiting a paginated
3382
+ * result yields the requested page).
3383
+ */
3384
+ type PaginatedSource<TItem> = (options: {
3385
+ cursor?: string;
3386
+ }) => PromiseLike<PaginatedResult<TItem>>;
3387
+ /**
3388
+ * Concatenate multiple paginated sources into a single paginated stream.
3389
+ * Sources are drained in order, one page per underlying call.
3295
3390
  *
3296
- * The optional `dedupe` key extractor filters items from source N against
3297
- * all items seen in sources 0 through N-1.
3391
+ * Pagination is stateless: every outgoing cursor encodes which source to
3392
+ * resume plus that source's own cursor, so a fresh `concatPaginated` call
3393
+ * with `cursor` continues exactly where the previous page left off. Sources
3394
+ * must therefore produce disjoint items themselves; there is no cross-source
3395
+ * dedupe (an in-memory seen-set could not survive the cursor round-trip).
3298
3396
  *
3299
3397
  * Uses paginateBuffered internally to normalize page sizes across source
3300
3398
  * boundaries — e.g. if the first source only has 2 items, they'll be
3301
3399
  * buffered with items from the next source into a full page.
3302
3400
  *
3303
- * Returns the same dual Promise+AsyncIterable shape that resolvers expect.
3401
+ * The result is a thenable for the first page that also async-iterates
3402
+ * pages in-process.
3304
3403
  */
3305
- declare function concatPaginated<TItem>({ sources, dedupe, pageSize, }: {
3404
+ declare function concatPaginated<TItem>({ sources, pageSize, cursor, }: {
3306
3405
  sources: PaginatedSource<TItem>[];
3307
- dedupe?: (item: TItem) => string;
3308
3406
  pageSize?: number;
3407
+ /** Cursor from a previous `concatPaginated` page; resumes the stream. */
3408
+ cursor?: string;
3309
3409
  }): PromiseLike<PaginatedResult<TItem>> & AsyncIterable<PaginatedResult<TItem>>;
3310
3410
  /**
3311
3411
  * Strip the PromiseLike from an async iterable, returning a plain
@@ -3395,4 +3495,4 @@ declare class CoreCancelledSignal extends CoreSignal {
3395
3495
  constructor(message?: string);
3396
3496
  }
3397
3497
 
3398
- export { type AdaptError, type AdaptErrorOptions, type AdaptPage, type AggregatePlugin, type ArrayResolver$1 as ArrayResolver, type AsyncContext, type BoundFormatter, type BoundResolver, CONTEXT, CORE_ERROR_SYMBOL, CORE_OPTIONS_ID, CORE_SIGNAL_SYMBOL, type CategoryDefinition, type ConstantResolver$1 as ConstantResolver, type Controller, type ControllerAction, type ControllerAffordance, type ControllerAnswerFn, type ControllerChoice, type ControllerError, type ControllerIssue, type ControllerListing, type ControllerMethodDescription, type ControllerMethodSummary, type ControllerParameterDescription, type ControllerPath, type ControllerQuestion, type ControllerResult, type ControllerSdk, type ControllerState, type CoreApiError, CoreCancelledSignal, CoreDisposeError, CoreError, CoreErrorCode, type CoreErrorOptions, type CoreOptions, CoreSignal, type CreateSdkOptions, type DeprecatedPromptConfigChoice, type DeprecationLogger, type DeprecationWarning, type DisposeFn, type DynamicListResolver, type DynamicMember, type DynamicResolver$1 as DynamicResolver, type DynamicSearchResolver, type FieldsResolver, type FormattedItem, type Formatter, type FunctionDeprecation, type FunctionRegistryEntry, type HookPlugin, type LeafMeta, type LeafSummary, type LegacyMergePlugin, type LegacyPlugin, type ListItemsResult, type ListPromptConfig, type MethodAttachment, type MethodHooks, type MethodOverridePlugin, type MethodPlugin, type MethodScope, type Resolver as ModelResolver, type OnMethodEnd, type OnMethodEndContext, type OnMethodStart, type OnMethodStartContext, type OutputFormatter, type PaginatedSdkFunction, type PaginatedSdkResult, type Plugin, type PluginMeta, type PluginProvides, type PluginStack, type PluginSummary, type PluginSurface, type PositionalMetadata, type PromptConfig, type PromptConfigChoice, type PropertyPlugin, type RegistryResult, type RequiredSdkOf, type Resolver$1 as Resolver, type ResolverConfig, type ResolverFieldItem, type ResolverMetadata, type ResolverPromptConfig, type ResolverType, type Sdk, type SdkContext, type SdkPage, type StaticResolver$1 as StaticResolver, type ValidResolvers, addPlugin, composePlugins, concatPaginated, coreOptionsPluginRef, createAsyncContext, createController, createCoreError, createCorePlugin, createDeprecationLogger, createFunction, createPaginatedFunction, createPaginatedPluginMethod, createPluginMethod, createPluginStack, createPrefixedCursor, createSdk, createValidator, dangerousContextPlugin, declareMethod, declareOptionalProperty, declarePlugin, declareProperty, decodeIncomingCursor, defaultLogDeprecation, defineFormatter, defineHook, defineLegacyMerge, defineMethod, defineMethodOverride, definePlugin, defineProperty, defineResolver, disposeSdk, fromFunctionPlugin, getContext, getCoreErrorCause, getCoreErrorCode, getCurrentDepth, getCurrentScope, getFieldDescriptions, getOutputSchema, getRegistryPlugin, getSchemaDescription, isCoreError, isCoreSignal, isNestedMethodCall, isPositional, isTelemetryNested, omitExports, openEnum, paginate, paginateBuffered, paginateMaxItems, resolvePlugin, runInMethodScope, runWithTelemetryContext, selectExports, splitPrefixedCursor, toIterable, toSnakeCase, toTitleCase, validateOptions, withOutputSchema, withPositional, withResolver };
3498
+ export { type AdaptError, type AdaptErrorOptions, type AdaptPage, type AggregatePlugin, type ArrayResolver$1 as ArrayResolver, type AsyncContext, type BoundFormatter, type BoundResolver, CONTEXT, CORE_ERROR_SYMBOL, CORE_OPTIONS_ID, CORE_SIGNAL_SYMBOL, type CategoryDefinition, type ConstantResolver$1 as ConstantResolver, type Controller, type ControllerAction, type ControllerAffordance, type ControllerAnswerFn, type ControllerChoice, type ControllerError, type ControllerIssue, type ControllerListingPage, type ControllerListingPosition, type ControllerMethodDescription, type ControllerMethodSummary, type ControllerPagination, type ControllerParameterDescription, type ControllerPath, type ControllerQuestion, type ControllerResult, type ControllerSdk, type ControllerSelectPage, type ControllerState, type CoreApiError, CoreCancelledSignal, CoreDisposeError, CoreError, CoreErrorCode, type CoreErrorOptions, type CoreOptions, CoreSignal, type CreateSdkOptions, type DeprecatedPromptConfigChoice, type DeprecationLogger, type DeprecationWarning, type DisposeFn, type DynamicListResolver, type DynamicMember, type DynamicResolver$1 as DynamicResolver, type DynamicSearchResolver, type FieldsResolver, type FormattedItem, type Formatter, type FunctionDeprecation, type FunctionRegistryEntry, type HookPlugin, type LeafMeta, type LeafSummary, type LegacyMergePlugin, type LegacyPlugin, type ListItemsResult, type ListPromptConfig, type MethodAttachment, type MethodHooks, type MethodOverridePlugin, type MethodPlugin, type MethodScope, type Resolver as ModelResolver, type OnMethodEnd, type OnMethodEndContext, type OnMethodStart, type OnMethodStartContext, type OutputFormatter, type PaginatedSdkFunction, type PaginatedSdkResult, type Plugin, type PluginMeta, type PluginProvides, type PluginStack, type PluginSummary, type PluginSurface, type PositionalMetadata, type PromptConfig, type PromptConfigChoice, type PropertyPlugin, type RegistryResult, type RequiredSdkOf, type Resolver$1 as Resolver, type ResolverConfig, type ResolverFieldItem, type ResolverMetadata, type ResolverPromptConfig, type ResolverType, type Sdk, type SdkContext, type SdkPage, type StaticResolver$1 as StaticResolver, type ValidResolvers, addPlugin, composePlugins, concatPaginated, coreOptionsPluginRef, createAsyncContext, createController, createCoreError, createCorePlugin, createDeprecationLogger, createFunction, createPaginatedFunction, createPaginatedPluginMethod, createPluginMethod, createPluginStack, createPrefixedCursor, createSdk, createValidator, dangerousContextPlugin, declareMethod, declareOptionalProperty, declarePlugin, declareProperty, decodeIncomingCursor, defaultLogDeprecation, defineFormatter, defineHook, defineLegacyMerge, defineMethod, defineMethodOverride, definePlugin, defineProperty, defineResolver, disposeSdk, fromFunctionPlugin, getContext, getCoreErrorCause, getCoreErrorCode, getCurrentDepth, getCurrentScope, getFieldDescriptions, getOutputSchema, getRegistryPlugin, getSchemaDescription, isCoreError, isCoreSignal, isNestedMethodCall, isPositional, isTelemetryNested, omitExports, openEnum, paginate, paginateBuffered, paginateMaxItems, resolvePlugin, runInMethodScope, runWithTelemetryContext, selectExports, splitPrefixedCursor, toIterable, toSnakeCase, toTitleCase, validateOptions, withOutputSchema, withPositional, withResolver };
package/dist/index.d.ts CHANGED
@@ -134,15 +134,17 @@ type ListPromptConfig = PromptConfig & {
134
134
  };
135
135
  /**
136
136
  * The prompt config the NEW-model resolvers (`defineResolver`) return. It omits
137
- * three fields the resolution controller does not honor, so authors can't
137
+ * four fields the resolution controller does not honor, so authors can't
138
138
  * supply a silent no-op:
139
- * - `name` — the framework supplies the answer key (always was overwritten).
140
- * - `default`— no resolver uses it; the controller has no preselect concept.
141
- * - `filter` — no resolver uses it; transform values in `listItems` instead.
142
- * (The legacy `SchemaParameterResolver` still honors `default`/`filter`, so the
143
- * full `PromptConfig` stays for that path.)
144
- */
145
- type ResolverPromptConfig = Omit<PromptConfig, "name" | "default" | "filter">;
139
+ * - `name` — the framework supplies the answer key (always was overwritten).
140
+ * - `default` no resolver uses it; the controller has no preselect concept.
141
+ * - `filter` — no resolver uses it; transform values in `listItems` instead.
142
+ * - `validate`— validation is the resolver's top-level `validate`, which
143
+ * never routes through rendering (and gets `imports`).
144
+ * (The legacy `SchemaParameterResolver` still honors `default`/`filter`/
145
+ * `validate`, so the full `PromptConfig` stays for that path.)
146
+ */
147
+ type ResolverPromptConfig = Omit<PromptConfig, "name" | "default" | "filter" | "validate">;
146
148
  interface Resolver$1 {
147
149
  type: string;
148
150
  depends?: readonly string[] | string[];
@@ -571,11 +573,27 @@ interface DynamicResolver extends ResolverBase {
571
573
  cursor?: string;
572
574
  }) => ListItemsResult<unknown>;
573
575
  prompt?: (bag: {
576
+ /** The CURRENT page's items only — the engine windows the listing one
577
+ * page at a time (an accumulating host may be showing more). Rendering
578
+ * input only; validation is the top-level `validate`. */
574
579
  items: unknown[];
575
580
  input: Record<string, unknown>;
576
581
  /** The value `getContext` returned, if any. */
577
582
  context?: unknown;
578
583
  }) => ResolverPromptConfig;
584
+ /** Check a chosen/typed value before the engine accepts it. Async with
585
+ * `imports` so it can verify against the source (`tryResolveFromSearch`'s
586
+ * sibling for picks) — never against a loaded page: pagination means the
587
+ * pick can come from a page the engine no longer holds. Return true to
588
+ * accept or a message to re-ask with. A throw is a lookup failure (the
589
+ * host gets retry/cancel), not a rejection. */
590
+ validate?: (bag: {
591
+ imports: Record<string, unknown>;
592
+ value: unknown;
593
+ input: Record<string, unknown>;
594
+ /** The value `getContext` returned, if any. */
595
+ context?: unknown;
596
+ }) => Promise<true | string> | true | string;
579
597
  /** Resolve with no user input at all (e.g. a configured default), skipping the
580
598
  * prompt. Runs before prompting; used always in non-interactive mode and as a
581
599
  * "can we skip asking?" check otherwise. Returns null to fall through to a prompt. */
@@ -726,6 +744,11 @@ interface BoundDynamicResolver extends BoundResolverBase {
726
744
  input: Record<string, unknown>;
727
745
  context?: unknown;
728
746
  }) => ResolverPromptConfig;
747
+ validate?: (bag: {
748
+ value: unknown;
749
+ input: Record<string, unknown>;
750
+ context?: unknown;
751
+ }) => Promise<true | string> | true | string;
729
752
  tryResolveWithoutPrompt?: (bag: {
730
753
  input: Record<string, unknown>;
731
754
  }) => Promise<{
@@ -2108,6 +2131,18 @@ declare function defineResolver<const TImports extends ImportsInput = readonly [
2108
2131
  /** The value `getContext` returned, if any. */
2109
2132
  context?: TContext;
2110
2133
  }) => ResolverPromptConfig;
2134
+ /** Check a chosen/typed value before the engine accepts it. Async with
2135
+ * `imports` so it can verify against the source — never against a loaded
2136
+ * page (the pick can come from a page the engine no longer holds). Return
2137
+ * true to accept or a message to re-ask with; a throw is a lookup failure
2138
+ * (retry/cancel), not a rejection. */
2139
+ validate?: (bag: {
2140
+ imports: ImportsOf<TImports>;
2141
+ value: unknown;
2142
+ input: TInput;
2143
+ /** The value `getContext` returned, if any. */
2144
+ context?: TContext;
2145
+ }) => Promise<true | string> | true | string;
2111
2146
  /** Resolve with no user input (e.g. a configured default), skipping the prompt. */
2112
2147
  tryResolveWithoutPrompt?: (bag: {
2113
2148
  imports: ImportsOf<TImports>;
@@ -2741,12 +2776,14 @@ interface ControllerAffordance {
2741
2776
  /** A question the host renders. Discriminated on `type`; the available moves are
2742
2777
  * the self-describing `actions` list (single source of truth, no flags).
2743
2778
  * `actions` is emitted in recommended presentation order — answer directly
2744
- * (`choose`/`custom`/`add`), refine (`search`), paginate (`more`), decline
2745
- * (`skip`/`done`), and failure questions offer `retry` then `cancel` — so a
2746
- * minimal host can render the list verbatim, top to bottom. Hosts with richer
2747
- * widgets (windowed lists, filter state) may reorder. */
2779
+ * (`choose`/`custom`/`add`), refine (`search`), paginate
2780
+ * (`next_page`/`previous_page`), decline (`skip`/`done`), and failure
2781
+ * questions offer `retry` then `cancel` so a minimal host can render the
2782
+ * list verbatim, top to bottom. Hosts with richer widgets (windowed lists,
2783
+ * filter state) may reorder. */
2748
2784
  type ControllerQuestion = {
2749
2785
  type: "select";
2786
+ path: ControllerPath;
2750
2787
  message: string;
2751
2788
  /** What this field is, for an agent that lacks the schema. */
2752
2789
  description?: string;
@@ -2769,8 +2806,18 @@ type ControllerQuestion = {
2769
2806
  * in its lead-with-term prompt (e.g. "Enter or search app (e.g. 'slack')").
2770
2807
  * Only meaningful before a search has run. */
2771
2808
  placeholder?: string;
2809
+ /** Where these `choices` sit in the paginated listing. `choices` is ONE
2810
+ * page (payloads stay O(page); the engine never re-sends earlier pages).
2811
+ * A window host renders the page and pages with
2812
+ * `next_page`/`previous_page`; an accumulating host appends pages
2813
+ * client-side: same `path` + same `generation` +
2814
+ * advancing `index` means "extend what you showed", and a `generation`
2815
+ * change (a search ran) means "start over". Absent on unpaginated
2816
+ * selects (static enums). */
2817
+ page?: ControllerSelectPage;
2772
2818
  } | {
2773
2819
  type: "input";
2820
+ path: ControllerPath;
2774
2821
  message: string;
2775
2822
  description?: string;
2776
2823
  inputType: "text" | "password" | "email";
@@ -2778,6 +2825,7 @@ type ControllerQuestion = {
2778
2825
  actions: ControllerAffordance[];
2779
2826
  } | {
2780
2827
  type: "collection";
2828
+ path: ControllerPath;
2781
2829
  message: string;
2782
2830
  description?: string;
2783
2831
  /** Which container kind this decision gates. `array` is the add-another
@@ -2806,6 +2854,15 @@ type ControllerQuestion = {
2806
2854
  max?: number;
2807
2855
  actions: ControllerAffordance[];
2808
2856
  };
2857
+ /** A select question's position in its paginated listing. */
2858
+ interface ControllerSelectPage {
2859
+ /** Increments whenever the listing restarts (a `search` ran, even with the
2860
+ * same term). An accumulating host discards what it has on a new
2861
+ * generation. */
2862
+ generation: number;
2863
+ /** Zero-based page number within this generation. */
2864
+ index: number;
2865
+ }
2809
2866
  /** The host's response to a question. The wire shape is frozen: a fuller
2810
2867
  * HATEOAS affordance schema would still produce exactly these. */
2811
2868
  type ControllerAction = {
@@ -2815,7 +2872,9 @@ type ControllerAction = {
2815
2872
  type: "search";
2816
2873
  term: string;
2817
2874
  } | {
2818
- type: "more";
2875
+ type: "next_page";
2876
+ } | {
2877
+ type: "previous_page";
2819
2878
  } | {
2820
2879
  type: "custom";
2821
2880
  value: string;
@@ -2836,7 +2895,7 @@ type ControllerAction = {
2836
2895
  type ControllerResult = {
2837
2896
  status: "ask";
2838
2897
  question: ControllerQuestion;
2839
- /** The prior answer's validation failure (`PromptConfig.validate`), when
2898
+ /** The prior answer's validation failure (the resolver's `validate`), when
2840
2899
  * this is a re-ask. About the last transition, not the question itself. */
2841
2900
  error?: string;
2842
2901
  } | {
@@ -2889,9 +2948,10 @@ interface ControllerState {
2889
2948
  * add/done handling never infers the decision from value presence or
2890
2949
  * resolver shape. Absent when `current` is a plain leaf question. */
2891
2950
  gate?: "array" | "entry" | "optionals";
2892
- /** Listing progress for the current dynamic leaf (serializable: items +
2893
- * cursor, never a live iterator). */
2894
- listing?: ControllerListing;
2951
+ /** Where pagination stands for the current dynamic leaf: coordinates only
2952
+ * (cursor trail, generation), never items or live iterators — the page's
2953
+ * items ride the ask's question. */
2954
+ pagination?: ControllerPagination;
2895
2955
  /** Whether the host will prompt. Interactive (the default) always asks;
2896
2956
  * non-interactive runs `tryResolveWithoutPrompt` to auto-fill what it can
2897
2957
  * (e.g. configured defaults) before asking for the rest. */
@@ -2900,14 +2960,42 @@ interface ControllerState {
2900
2960
  /** A location in the input tree: top-level `["app"]`, a nested object field
2901
2961
  * `["inputs", "channel"]`, or (later) an array item `["records", 0, "id"]`. */
2902
2962
  type ControllerPath = (string | number)[];
2903
- /** Accumulated candidate items for the current dynamic parameter, plus the
2904
- * serializable cursor for "load more" and the active search term. */
2905
- interface ControllerListing {
2906
- items: unknown[];
2907
- cursor?: string;
2963
+ /** Where a listing currently stands: the coordinates that make one page of a
2964
+ * dynamic parameter's candidates fetchable and re-fetchable with no
2965
+ * shared in-memory state. `previous_page` and `retry` work by re-issuing a
2966
+ * recorded position. */
2967
+ interface ControllerListingPosition {
2968
+ /** The active search term, when the resolver is search-mode. */
2908
2969
  search?: string;
2909
- /** True when the source reported no further pages. */
2910
- exhausted: boolean;
2970
+ /** Cursor that fetched the current page; `null` is the first page. */
2971
+ pageCursor: string | null;
2972
+ /** Cursors of the pages before this one, oldest first (each entry fetches
2973
+ * that page; `null` is the first page). `previous_page` pops the last. */
2974
+ previousCursors: (string | null)[];
2975
+ /** Increments on every listing restart (each `search`). Surfaced to hosts
2976
+ * via {@link ControllerSelectPage} so accumulators know when to reset. */
2977
+ generation: number;
2978
+ }
2979
+ /** The enumeration in progress for the current dynamic parameter:
2980
+ * coordinates only. The fetched items ride the ask's `question.choices`; the
2981
+ * host carries back these cursors between steps, never the items it was just
2982
+ * shown. Anything the engine needs to redraw (a validate-rejection re-ask) it
2983
+ * re-fetches statelessly from `position`. */
2984
+ interface ControllerPagination {
2985
+ position: ControllerListingPosition;
2986
+ /** Resume point for `next_page`; absent on the last page. */
2987
+ nextCursor?: string;
2988
+ /** The position whose fetch failed, kept so `retry` replays exactly it.
2989
+ * Absent when the page loaded. */
2990
+ retryPosition?: ControllerListingPosition;
2991
+ }
2992
+ /** One fetched page of a listing: the position it was fetched at plus what
2993
+ * came back. Never stored: `toPagination` strips it to the coordinates the
2994
+ * state keeps. */
2995
+ interface ControllerListingPage {
2996
+ position: ControllerListingPosition;
2997
+ items: unknown[];
2998
+ nextCursor?: string;
2911
2999
  }
2912
3000
  /**
2913
3001
  * The one pluggable seam for the in-process `resolve` sugar. It receives the
@@ -3287,25 +3375,37 @@ interface PaginatedResult<TItem> {
3287
3375
  data: TItem[];
3288
3376
  nextCursor?: string;
3289
3377
  }
3290
- type PaginatedSource<TItem> = () => PromiseLike<PaginatedResult<TItem>> & AsyncIterable<PaginatedResult<TItem>>;
3291
3378
  /**
3292
- * Concatenate multiple paginated SDK results into a single paginated stream.
3293
- * Each source is a function returning a dual Promise+AsyncIterable (as SDK
3294
- * paginated methods return). Sources are drained in order.
3379
+ * One page-at-a-time source for `concatPaginated`: called with that source's
3380
+ * own cursor, resolves one page. An SDK paginated method fits directly
3381
+ * (`({ cursor }) => sdk.listThings({ cursor })` awaiting a paginated
3382
+ * result yields the requested page).
3383
+ */
3384
+ type PaginatedSource<TItem> = (options: {
3385
+ cursor?: string;
3386
+ }) => PromiseLike<PaginatedResult<TItem>>;
3387
+ /**
3388
+ * Concatenate multiple paginated sources into a single paginated stream.
3389
+ * Sources are drained in order, one page per underlying call.
3295
3390
  *
3296
- * The optional `dedupe` key extractor filters items from source N against
3297
- * all items seen in sources 0 through N-1.
3391
+ * Pagination is stateless: every outgoing cursor encodes which source to
3392
+ * resume plus that source's own cursor, so a fresh `concatPaginated` call
3393
+ * with `cursor` continues exactly where the previous page left off. Sources
3394
+ * must therefore produce disjoint items themselves; there is no cross-source
3395
+ * dedupe (an in-memory seen-set could not survive the cursor round-trip).
3298
3396
  *
3299
3397
  * Uses paginateBuffered internally to normalize page sizes across source
3300
3398
  * boundaries — e.g. if the first source only has 2 items, they'll be
3301
3399
  * buffered with items from the next source into a full page.
3302
3400
  *
3303
- * Returns the same dual Promise+AsyncIterable shape that resolvers expect.
3401
+ * The result is a thenable for the first page that also async-iterates
3402
+ * pages in-process.
3304
3403
  */
3305
- declare function concatPaginated<TItem>({ sources, dedupe, pageSize, }: {
3404
+ declare function concatPaginated<TItem>({ sources, pageSize, cursor, }: {
3306
3405
  sources: PaginatedSource<TItem>[];
3307
- dedupe?: (item: TItem) => string;
3308
3406
  pageSize?: number;
3407
+ /** Cursor from a previous `concatPaginated` page; resumes the stream. */
3408
+ cursor?: string;
3309
3409
  }): PromiseLike<PaginatedResult<TItem>> & AsyncIterable<PaginatedResult<TItem>>;
3310
3410
  /**
3311
3411
  * Strip the PromiseLike from an async iterable, returning a plain
@@ -3395,4 +3495,4 @@ declare class CoreCancelledSignal extends CoreSignal {
3395
3495
  constructor(message?: string);
3396
3496
  }
3397
3497
 
3398
- export { type AdaptError, type AdaptErrorOptions, type AdaptPage, type AggregatePlugin, type ArrayResolver$1 as ArrayResolver, type AsyncContext, type BoundFormatter, type BoundResolver, CONTEXT, CORE_ERROR_SYMBOL, CORE_OPTIONS_ID, CORE_SIGNAL_SYMBOL, type CategoryDefinition, type ConstantResolver$1 as ConstantResolver, type Controller, type ControllerAction, type ControllerAffordance, type ControllerAnswerFn, type ControllerChoice, type ControllerError, type ControllerIssue, type ControllerListing, type ControllerMethodDescription, type ControllerMethodSummary, type ControllerParameterDescription, type ControllerPath, type ControllerQuestion, type ControllerResult, type ControllerSdk, type ControllerState, type CoreApiError, CoreCancelledSignal, CoreDisposeError, CoreError, CoreErrorCode, type CoreErrorOptions, type CoreOptions, CoreSignal, type CreateSdkOptions, type DeprecatedPromptConfigChoice, type DeprecationLogger, type DeprecationWarning, type DisposeFn, type DynamicListResolver, type DynamicMember, type DynamicResolver$1 as DynamicResolver, type DynamicSearchResolver, type FieldsResolver, type FormattedItem, type Formatter, type FunctionDeprecation, type FunctionRegistryEntry, type HookPlugin, type LeafMeta, type LeafSummary, type LegacyMergePlugin, type LegacyPlugin, type ListItemsResult, type ListPromptConfig, type MethodAttachment, type MethodHooks, type MethodOverridePlugin, type MethodPlugin, type MethodScope, type Resolver as ModelResolver, type OnMethodEnd, type OnMethodEndContext, type OnMethodStart, type OnMethodStartContext, type OutputFormatter, type PaginatedSdkFunction, type PaginatedSdkResult, type Plugin, type PluginMeta, type PluginProvides, type PluginStack, type PluginSummary, type PluginSurface, type PositionalMetadata, type PromptConfig, type PromptConfigChoice, type PropertyPlugin, type RegistryResult, type RequiredSdkOf, type Resolver$1 as Resolver, type ResolverConfig, type ResolverFieldItem, type ResolverMetadata, type ResolverPromptConfig, type ResolverType, type Sdk, type SdkContext, type SdkPage, type StaticResolver$1 as StaticResolver, type ValidResolvers, addPlugin, composePlugins, concatPaginated, coreOptionsPluginRef, createAsyncContext, createController, createCoreError, createCorePlugin, createDeprecationLogger, createFunction, createPaginatedFunction, createPaginatedPluginMethod, createPluginMethod, createPluginStack, createPrefixedCursor, createSdk, createValidator, dangerousContextPlugin, declareMethod, declareOptionalProperty, declarePlugin, declareProperty, decodeIncomingCursor, defaultLogDeprecation, defineFormatter, defineHook, defineLegacyMerge, defineMethod, defineMethodOverride, definePlugin, defineProperty, defineResolver, disposeSdk, fromFunctionPlugin, getContext, getCoreErrorCause, getCoreErrorCode, getCurrentDepth, getCurrentScope, getFieldDescriptions, getOutputSchema, getRegistryPlugin, getSchemaDescription, isCoreError, isCoreSignal, isNestedMethodCall, isPositional, isTelemetryNested, omitExports, openEnum, paginate, paginateBuffered, paginateMaxItems, resolvePlugin, runInMethodScope, runWithTelemetryContext, selectExports, splitPrefixedCursor, toIterable, toSnakeCase, toTitleCase, validateOptions, withOutputSchema, withPositional, withResolver };
3498
+ export { type AdaptError, type AdaptErrorOptions, type AdaptPage, type AggregatePlugin, type ArrayResolver$1 as ArrayResolver, type AsyncContext, type BoundFormatter, type BoundResolver, CONTEXT, CORE_ERROR_SYMBOL, CORE_OPTIONS_ID, CORE_SIGNAL_SYMBOL, type CategoryDefinition, type ConstantResolver$1 as ConstantResolver, type Controller, type ControllerAction, type ControllerAffordance, type ControllerAnswerFn, type ControllerChoice, type ControllerError, type ControllerIssue, type ControllerListingPage, type ControllerListingPosition, type ControllerMethodDescription, type ControllerMethodSummary, type ControllerPagination, type ControllerParameterDescription, type ControllerPath, type ControllerQuestion, type ControllerResult, type ControllerSdk, type ControllerSelectPage, type ControllerState, type CoreApiError, CoreCancelledSignal, CoreDisposeError, CoreError, CoreErrorCode, type CoreErrorOptions, type CoreOptions, CoreSignal, type CreateSdkOptions, type DeprecatedPromptConfigChoice, type DeprecationLogger, type DeprecationWarning, type DisposeFn, type DynamicListResolver, type DynamicMember, type DynamicResolver$1 as DynamicResolver, type DynamicSearchResolver, type FieldsResolver, type FormattedItem, type Formatter, type FunctionDeprecation, type FunctionRegistryEntry, type HookPlugin, type LeafMeta, type LeafSummary, type LegacyMergePlugin, type LegacyPlugin, type ListItemsResult, type ListPromptConfig, type MethodAttachment, type MethodHooks, type MethodOverridePlugin, type MethodPlugin, type MethodScope, type Resolver as ModelResolver, type OnMethodEnd, type OnMethodEndContext, type OnMethodStart, type OnMethodStartContext, type OutputFormatter, type PaginatedSdkFunction, type PaginatedSdkResult, type Plugin, type PluginMeta, type PluginProvides, type PluginStack, type PluginSummary, type PluginSurface, type PositionalMetadata, type PromptConfig, type PromptConfigChoice, type PropertyPlugin, type RegistryResult, type RequiredSdkOf, type Resolver$1 as Resolver, type ResolverConfig, type ResolverFieldItem, type ResolverMetadata, type ResolverPromptConfig, type ResolverType, type Sdk, type SdkContext, type SdkPage, type StaticResolver$1 as StaticResolver, type ValidResolvers, addPlugin, composePlugins, concatPaginated, coreOptionsPluginRef, createAsyncContext, createController, createCoreError, createCorePlugin, createDeprecationLogger, createFunction, createPaginatedFunction, createPaginatedPluginMethod, createPluginMethod, createPluginStack, createPrefixedCursor, createSdk, createValidator, dangerousContextPlugin, declareMethod, declareOptionalProperty, declarePlugin, declareProperty, decodeIncomingCursor, defaultLogDeprecation, defineFormatter, defineHook, defineLegacyMerge, defineMethod, defineMethodOverride, definePlugin, defineProperty, defineResolver, disposeSdk, fromFunctionPlugin, getContext, getCoreErrorCause, getCoreErrorCode, getCurrentDepth, getCurrentScope, getFieldDescriptions, getOutputSchema, getRegistryPlugin, getSchemaDescription, isCoreError, isCoreSignal, isNestedMethodCall, isPositional, isTelemetryNested, omitExports, openEnum, paginate, paginateBuffered, paginateMaxItems, resolvePlugin, runInMethodScope, runWithTelemetryContext, selectExports, splitPrefixedCursor, toIterable, toSnakeCase, toTitleCase, validateOptions, withOutputSchema, withPositional, withResolver };