@fgv/ts-extras 5.1.0-54 → 5.1.0-56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CAPABILITIES.md +95 -0
  2. package/dist/packlets/ai-assist/cacheRequest.js +118 -0
  3. package/dist/packlets/ai-assist/cacheRequest.js.map +1 -0
  4. package/dist/packlets/ai-assist/chatRequestBuilders.js +70 -5
  5. package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
  6. package/dist/packlets/ai-assist/completionClient.js +122 -45
  7. package/dist/packlets/ai-assist/completionClient.js.map +1 -1
  8. package/dist/packlets/ai-assist/imageGenerationClient.js +1 -1
  9. package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -1
  10. package/dist/packlets/ai-assist/index.js +3 -1
  11. package/dist/packlets/ai-assist/index.js.map +1 -1
  12. package/dist/packlets/ai-assist/model.js.map +1 -1
  13. package/dist/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js +200 -0
  14. package/dist/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js.map +1 -0
  15. package/dist/packlets/ai-assist/promptCacheObservabilityHarness.js +137 -0
  16. package/dist/packlets/ai-assist/promptCacheObservabilityHarness.js.map +1 -0
  17. package/dist/packlets/ai-assist/promptCacheRoutingAbHarness.js +156 -0
  18. package/dist/packlets/ai-assist/promptCacheRoutingAbHarness.js.map +1 -0
  19. package/dist/packlets/ai-assist/promptCacheUsageMath.js +124 -0
  20. package/dist/packlets/ai-assist/promptCacheUsageMath.js.map +1 -0
  21. package/dist/packlets/ai-assist/registry.js +9 -11
  22. package/dist/packlets/ai-assist/registry.js.map +1 -1
  23. package/dist/packlets/ai-assist/streamUsageCapability.js +124 -0
  24. package/dist/packlets/ai-assist/streamUsageCapability.js.map +1 -0
  25. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +24 -5
  26. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  27. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +64 -33
  28. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
  29. package/dist/packlets/ai-assist/streamingAdapters/common.js +25 -1
  30. package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  31. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +12 -5
  32. package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  33. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +34 -8
  34. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  35. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +22 -14
  36. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  37. package/dist/packlets/ai-assist/streamingClient.js +3 -2
  38. package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
  39. package/dist/packlets/ai-assist/structuredOutput.js +84 -7
  40. package/dist/packlets/ai-assist/structuredOutput.js.map +1 -1
  41. package/dist/packlets/ai-assist/structuredOutputTypes.js.map +1 -1
  42. package/dist/packlets/ai-assist/thinkingOptionsResolver.js +18 -3
  43. package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
  44. package/dist/packlets/ai-assist/toolFormats.js +80 -0
  45. package/dist/packlets/ai-assist/toolFormats.js.map +1 -1
  46. package/dist/packlets/ai-assist/toolTypes.js +21 -0
  47. package/dist/packlets/ai-assist/toolTypes.js.map +1 -0
  48. package/dist/packlets/ai-assist/usageNormalization.js +178 -0
  49. package/dist/packlets/ai-assist/usageNormalization.js.map +1 -0
  50. package/dist/packlets/ai-assist/usageTypes.js +21 -0
  51. package/dist/packlets/ai-assist/usageTypes.js.map +1 -0
  52. package/dist/packlets/mustache/index.js.map +1 -1
  53. package/dist/packlets/mustache/interfaces.js.map +1 -1
  54. package/dist/packlets/mustache/mustacheTemplate.js +91 -0
  55. package/dist/packlets/mustache/mustacheTemplate.js.map +1 -1
  56. package/dist/ts-extras.d.ts +577 -30
  57. package/lib/packlets/ai-assist/cacheRequest.d.ts +100 -0
  58. package/lib/packlets/ai-assist/cacheRequest.d.ts.map +1 -0
  59. package/lib/packlets/ai-assist/cacheRequest.js +123 -0
  60. package/lib/packlets/ai-assist/cacheRequest.js.map +1 -0
  61. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +34 -4
  62. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -1
  63. package/lib/packlets/ai-assist/chatRequestBuilders.js +73 -5
  64. package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
  65. package/lib/packlets/ai-assist/completionClient.d.ts +6 -0
  66. package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -1
  67. package/lib/packlets/ai-assist/completionClient.js +121 -44
  68. package/lib/packlets/ai-assist/completionClient.js.map +1 -1
  69. package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -1
  70. package/lib/packlets/ai-assist/imageGenerationClient.js +1 -1
  71. package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -1
  72. package/lib/packlets/ai-assist/index.d.ts +7 -2
  73. package/lib/packlets/ai-assist/index.d.ts.map +1 -1
  74. package/lib/packlets/ai-assist/index.js +11 -1
  75. package/lib/packlets/ai-assist/index.js.map +1 -1
  76. package/lib/packlets/ai-assist/model.d.ts +59 -288
  77. package/lib/packlets/ai-assist/model.d.ts.map +1 -1
  78. package/lib/packlets/ai-assist/model.js.map +1 -1
  79. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.d.ts +124 -0
  80. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.d.ts.map +1 -0
  81. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js +208 -0
  82. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js.map +1 -0
  83. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.d.ts +77 -0
  84. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.d.ts.map +1 -0
  85. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.js +143 -0
  86. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.js.map +1 -0
  87. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.d.ts +84 -0
  88. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.d.ts.map +1 -0
  89. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.js +163 -0
  90. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.js.map +1 -0
  91. package/lib/packlets/ai-assist/promptCacheUsageMath.d.ts +105 -0
  92. package/lib/packlets/ai-assist/promptCacheUsageMath.d.ts.map +1 -0
  93. package/lib/packlets/ai-assist/promptCacheUsageMath.js +130 -0
  94. package/lib/packlets/ai-assist/promptCacheUsageMath.js.map +1 -0
  95. package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
  96. package/lib/packlets/ai-assist/registry.js +9 -11
  97. package/lib/packlets/ai-assist/registry.js.map +1 -1
  98. package/lib/packlets/ai-assist/streamUsageCapability.d.ts +119 -0
  99. package/lib/packlets/ai-assist/streamUsageCapability.d.ts.map +1 -0
  100. package/lib/packlets/ai-assist/streamUsageCapability.js +130 -0
  101. package/lib/packlets/ai-assist/streamUsageCapability.js.map +1 -0
  102. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
  103. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +23 -4
  104. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  105. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +21 -12
  106. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
  107. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +64 -34
  108. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
  109. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +21 -0
  110. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
  111. package/lib/packlets/ai-assist/streamingAdapters/common.js +25 -1
  112. package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  113. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
  114. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +12 -5
  115. package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  116. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +1 -1
  117. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
  118. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +33 -7
  119. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  120. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +1 -1
  121. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
  122. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +21 -13
  123. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  124. package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
  125. package/lib/packlets/ai-assist/streamingClient.js +3 -2
  126. package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
  127. package/lib/packlets/ai-assist/structuredOutput.d.ts +32 -0
  128. package/lib/packlets/ai-assist/structuredOutput.d.ts.map +1 -1
  129. package/lib/packlets/ai-assist/structuredOutput.js +85 -7
  130. package/lib/packlets/ai-assist/structuredOutput.js.map +1 -1
  131. package/lib/packlets/ai-assist/structuredOutputTypes.d.ts +30 -0
  132. package/lib/packlets/ai-assist/structuredOutputTypes.d.ts.map +1 -1
  133. package/lib/packlets/ai-assist/structuredOutputTypes.js.map +1 -1
  134. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -1
  135. package/lib/packlets/ai-assist/thinkingOptionsResolver.js +18 -3
  136. package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
  137. package/lib/packlets/ai-assist/toolFormats.d.ts +55 -1
  138. package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -1
  139. package/lib/packlets/ai-assist/toolFormats.js +82 -0
  140. package/lib/packlets/ai-assist/toolFormats.js.map +1 -1
  141. package/lib/packlets/ai-assist/toolTypes.d.ts +328 -0
  142. package/lib/packlets/ai-assist/toolTypes.d.ts.map +1 -0
  143. package/lib/packlets/ai-assist/toolTypes.js +22 -0
  144. package/lib/packlets/ai-assist/toolTypes.js.map +1 -0
  145. package/lib/packlets/ai-assist/usageNormalization.d.ts +58 -0
  146. package/lib/packlets/ai-assist/usageNormalization.d.ts.map +1 -0
  147. package/lib/packlets/ai-assist/usageNormalization.js +184 -0
  148. package/lib/packlets/ai-assist/usageNormalization.js.map +1 -0
  149. package/lib/packlets/ai-assist/usageTypes.d.ts +84 -0
  150. package/lib/packlets/ai-assist/usageTypes.d.ts.map +1 -0
  151. package/lib/packlets/ai-assist/usageTypes.js +22 -0
  152. package/lib/packlets/ai-assist/usageTypes.js.map +1 -0
  153. package/lib/packlets/mustache/index.d.ts +1 -1
  154. package/lib/packlets/mustache/index.d.ts.map +1 -1
  155. package/lib/packlets/mustache/index.js.map +1 -1
  156. package/lib/packlets/mustache/interfaces.d.ts +30 -0
  157. package/lib/packlets/mustache/interfaces.d.ts.map +1 -1
  158. package/lib/packlets/mustache/interfaces.js.map +1 -1
  159. package/lib/packlets/mustache/mustacheTemplate.d.ts +56 -1
  160. package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -1
  161. package/lib/packlets/mustache/mustacheTemplate.js +91 -0
  162. package/lib/packlets/mustache/mustacheTemplate.js.map +1 -1
  163. package/package.json +16 -16
  164. package/perf/promptCacheAnthropicBreakpoint.js +135 -0
  165. package/perf/promptCacheObservability.js +107 -0
  166. package/perf/promptCacheRoutingAb.js +105 -0
@@ -82,6 +82,8 @@ declare namespace AiAssist {
82
82
  AiServerToolType,
83
83
  AiServerToolConfig,
84
84
  AiToolConfig,
85
+ AiToolConflictPolicy,
86
+ IAiToolConflictReport,
85
87
  IAiWebSearchToolConfig,
86
88
  IAiClientToolConfig,
87
89
  IAiToolAnnotations,
@@ -155,7 +157,6 @@ declare namespace AiAssist {
155
157
  isAdaptiveThinkingModel,
156
158
  usesMaxCompletionTokensField,
157
159
  toDataUrl,
158
- AiThinkingMode,
159
160
  IThinkingConfig,
160
161
  IThinkingProviderConfig,
161
162
  IAnthropicThinkingOptions,
@@ -215,6 +216,16 @@ declare namespace AiAssist {
215
216
  modelSpecKey,
216
217
  modelSpec,
217
218
  resolveEffectiveTools,
219
+ resolveToolConflicts,
220
+ defaultToolConflictPolicy,
221
+ IAiResolvedToolConflicts,
222
+ AiCacheReportingLevel,
223
+ IAiCompletionUsage,
224
+ supportsCacheUsageReporting,
225
+ supportsPromptCacheBreakpoints,
226
+ supportsPromptCacheRouting,
227
+ supportsStreamUsageOption,
228
+ IAiPromptCacheRoutingSupport,
218
229
  ANTHROPIC_STRUCTURED_OUTPUT_TOOL_NAME,
219
230
  AiStructuredOutputFormat,
220
231
  IAiStructuredOutputCapability,
@@ -236,7 +247,10 @@ declare namespace AiAssist {
236
247
  IGenerateJsonCompletionResult,
237
248
  JsonPromptHint,
238
249
  anthropicEffortToBudgetTokens,
239
- IResolvedThinkingConfig
250
+ IResolvedThinkingConfig,
251
+ IAiCacheRequest,
252
+ validateAiCacheRequest,
253
+ validateCacheBreakpoints
240
254
  }
241
255
  }
242
256
  export { AiAssist }
@@ -253,6 +267,27 @@ declare const aiAssistProviderConfig: Converter<IAiAssistProviderConfig>;
253
267
  */
254
268
  declare const aiAssistSettings: Converter<IAiAssistSettings>;
255
269
 
270
+ /**
271
+ * What a provider's wire shape is able to report about prompt-cache usage,
272
+ * given that a usage block was present on the response at all.
273
+ *
274
+ * @remarks
275
+ * `'reads'` — cache reads are reported; cache writes cannot be (either the API
276
+ * has no write concept, or the field structurally does not exist on this route).
277
+ * `'reads-and-writes'` — both reads and writes are reported.
278
+ *
279
+ * There is deliberately no `'none'` member: "no usage was reported at all" is
280
+ * already expressed by {@link AiAssist.IAiCompletionResponse.usage} itself
281
+ * being absent, and a third value here would be a second, reachable-only-via-
282
+ * bug way to say the same thing — check `usage === undefined`, not a `reports`
283
+ * value, to detect that case.
284
+ *
285
+ * See {@link AiAssist.IAiCompletionUsage.reports} for why this is required
286
+ * rather than optional.
287
+ * @public
288
+ */
289
+ declare type AiCacheReportingLevel = 'reads' | 'reads-and-writes';
290
+
256
291
  /**
257
292
  * Converter for {@link AiAssist.IAiClientToolConfig}. Validates the wrapper shape: `type`,
258
293
  * `name`, `description`, the presence of a usable `parametersSchema`, and optional
@@ -417,12 +452,6 @@ declare const aiServerToolType: Converter<AiServerToolType>;
417
452
  */
418
453
  declare type AiStructuredOutputFormat = 'openai-json-schema' | 'openai-responses-format' | 'gemini-response-schema' | 'anthropic-tool-forced';
419
454
 
420
- /**
421
- * Thinking/reasoning mode support for a provider.
422
- * @public
423
- */
424
- declare type AiThinkingMode = 'optional' | 'required' | 'unsupported';
425
-
426
455
  /**
427
456
  * Converter for {@link AiAssist.IAiToolAnnotations} — the five optional host-advisory hints.
428
457
  * @public
@@ -436,6 +465,28 @@ declare const aiToolAnnotations: Converter<IAiToolAnnotations>;
436
465
  */
437
466
  declare type AiToolConfig = AiServerToolConfig | IAiClientToolConfig;
438
467
 
468
+ /**
469
+ * What to do when a request would combine a server tool with client (function)
470
+ * tools on a provider that declares the two mutually exclusive (see
471
+ * `IAiProviderDescriptor.serverToolsExclusiveWithClientTools`).
472
+ *
473
+ * @remarks
474
+ * - `'drop-server-tools'` (the default) removes the offending server tools and
475
+ * keeps every client tool. Client tools are what the host registered
476
+ * explicitly for this turn; a built-in like grounding is a per-provider
477
+ * nicety, and dropping it degrades the turn rather than losing it.
478
+ * - `'prefer-server-tools'` is the mirror image: keep the server tools and drop
479
+ * every client tool. For hosts whose turn is *about* the grounded answer.
480
+ * - `'fail'` refuses the request up front with a message naming the conflict —
481
+ * the behavior before this policy existed. For hosts that would rather
482
+ * choose themselves than have either half silently disappear.
483
+ *
484
+ * Whichever is chosen, what actually happened is reported, never inferred: see
485
+ * {@link AiAssist.IAiToolConflictReport}.
486
+ * @public
487
+ */
488
+ declare type AiToolConflictPolicy = 'drop-server-tools' | 'prefer-server-tools' | 'fail';
489
+
439
490
  /**
440
491
  * Converter for {@link IAiToolEnablement}.
441
492
  * @public
@@ -1273,6 +1324,19 @@ declare const DEFAULT_SECRET_ITERATIONS: number;
1273
1324
  */
1274
1325
  declare const DEFAULT_TIMEOUT_MS: number;
1275
1326
 
1327
+ /**
1328
+ * The default {@link AiAssist.AiToolConflictPolicy} applied when a caller passes none.
1329
+ *
1330
+ * @remarks
1331
+ * Keeping the client tools is the right default because they are what the host
1332
+ * registered explicitly for *this* turn, while a built-in server tool is a
1333
+ * per-provider nicety the same host would get on some providers and not others.
1334
+ * Dropping the nicety degrades a turn; dropping the client tools changes what the
1335
+ * turn can do.
1336
+ * @public
1337
+ */
1338
+ declare const defaultToolConflictPolicy: AiToolConflictPolicy;
1339
+
1276
1340
  /**
1277
1341
  * Derives an asymmetric keypair *deterministically* from a fixed secret seed
1278
1342
  * using `globalThis.crypto.subtle`, shared by the Node and browser
@@ -1533,6 +1597,13 @@ declare const encryptionAlgorithm: Converter<EncryptionAlgorithm>;
1533
1597
  * a forced `tool_choice`. Only `tool_choice: 'auto'` (the default, i.e.
1534
1598
  * omitted) is compatible with extended thinking.
1535
1599
  *
1600
+ * **Provider tool exclusions:** when the descriptor declares a server tool
1601
+ * mutually exclusive with client tools (today only Gemini, for `web_search`),
1602
+ * `toolConflictPolicy` decides — by default the server tool is dropped and the
1603
+ * turn proceeds. The turn result's `toolConflicts` always says what happened, so
1604
+ * a host can tell the person that e.g. web search was unavailable this turn.
1605
+ * Pass `'fail'` to refuse the request up front instead.
1606
+ *
1536
1607
  * @param params - Turn parameters
1537
1608
  * @returns `{ events, nextTurn }` — stream iterable + completion promise
1538
1609
  * @public
@@ -2485,6 +2556,41 @@ declare interface IAiAssistSettings {
2485
2556
  readonly proxyAllProviders?: boolean;
2486
2557
  }
2487
2558
 
2559
+ /**
2560
+ * A request-level prompt-caching plan.
2561
+ *
2562
+ * @remarks
2563
+ * Offsets rather than content blocks — see design.md §6.1 for why: `AiPrompt`/`IChatRequest`
2564
+ * carry `system` as a plain string, offsets are usable without `@fgv/ts-prompt-assist` (a
2565
+ * caller can say "the first 4,000 characters of my system prompt are stable" directly), and
2566
+ * they are already the currency `IPromptSection.start`/`.chars` report.
2567
+ * @public
2568
+ */
2569
+ declare interface IAiCacheRequest {
2570
+ /**
2571
+ * Strictly ascending UTF-16 offsets into `AiPrompt.system` (equivalently
2572
+ * `IProviderCompletionParams.system`), each marking the end of a cacheable prefix. Each must
2573
+ * be an integer `> 0` and `< system.length`. Validated where the offsets are actually used
2574
+ * (against the request's own `system.length`, and optionally against a cap) — never clamped;
2575
+ * an invalid plan fails the request rather than silently caching less than declared. Sent where
2576
+ * the provider has a breakpoint mechanism (Anthropic `cache_control`, OpenAI
2577
+ * `prompt_cache_breakpoint`). Ignored by adapters that have no such concept (Gemini).
2578
+ */
2579
+ readonly systemBreakpoints?: ReadonlyArray<number>;
2580
+ /**
2581
+ * Opaque cache-routing key, sent where the provider has one and by that provider's transport:
2582
+ * `prompt_cache_key` as a body field on OpenAI (both routes) and on xAI Responses, and an
2583
+ * `x-grok-conv-id` header on xAI Chat Completions. See `supportsPromptCacheRouting`.
2584
+ *
2585
+ * @remarks
2586
+ * On xAI this is not a nicety. Its prompt cache is per-server and evictable, so two requests
2587
+ * sharing a prefix can still miss if they are routed to different servers; the key pins them
2588
+ * together. Providers with no routing concept ignore it and receive a request byte-identical
2589
+ * to one built without a `cache` at all.
2590
+ */
2591
+ readonly cacheKey?: string;
2592
+ }
2593
+
2488
2594
  /**
2489
2595
  * A client-defined tool: configuration + execution callback pair.
2490
2596
  *
@@ -2552,7 +2658,7 @@ declare interface IAiClientToolConfig<TParams = unknown> {
2552
2658
  /**
2553
2659
  * Optional host-advisory behavior annotations (read-only / destructive /
2554
2660
  * idempotent / open-world hints + display title). Consumed by the host's
2555
- * tool loop; never serialized to the model. See {@link IAiToolAnnotations}.
2661
+ * tool loop; never serialized to the model. See {@link AiAssist.IAiToolAnnotations}.
2556
2662
  */
2557
2663
  readonly annotations?: IAiToolAnnotations;
2558
2664
  }
@@ -2622,6 +2728,16 @@ declare interface IAiClientToolTurnResult {
2622
2728
  readonly truncated: boolean;
2623
2729
  /** The full concatenated text from all `text-delta` events in this turn. */
2624
2730
  readonly fullText: string;
2731
+ /**
2732
+ * What the provider tool-conflict resolution did to this turn's tools.
2733
+ * Always present; empty arrays mean nothing conflicted.
2734
+ *
2735
+ * @remarks
2736
+ * Read this to tell the person (or the model, on a later turn) that a
2737
+ * capability they expected was unavailable — e.g. that web search was dropped
2738
+ * because the pinned model cannot ground and call functions in one request.
2739
+ */
2740
+ readonly toolConflicts: IAiToolConflictReport;
2625
2741
  }
2626
2742
 
2627
2743
  /**
@@ -2651,6 +2767,61 @@ declare interface IAiCompletionResponse {
2651
2767
  * split.
2652
2768
  */
2653
2769
  readonly structuredOutput: StructuredOutputEnforcement;
2770
+ /** Token usage, when the provider reports it. */
2771
+ readonly usage?: IAiCompletionUsage;
2772
+ }
2773
+
2774
+ /**
2775
+ * Token accounting for a completion, normalized across providers.
2776
+ *
2777
+ * @remarks
2778
+ * Every field but `reports` is best-effort: a provider's wire shape reports
2779
+ * what it reports, and an absent field here means the corresponding wire
2780
+ * field was itself absent — never a guessed zero. See `reports` for the one
2781
+ * exception this rule has to be explicit about.
2782
+ * @public
2783
+ */
2784
+ declare interface IAiCompletionUsage {
2785
+ /**
2786
+ * What this response's wire shape *can* report about caching.
2787
+ *
2788
+ * @remarks
2789
+ * **Required, not optional.** An optional field would make an absent
2790
+ * `cacheWriteTokens` three-ways ambiguous — no write happened / this API
2791
+ * cannot report writes / a build predating the field — and disambiguating
2792
+ * exactly that is what this field exists for. Under `'reads-and-writes'`, an
2793
+ * absent `cacheWriteTokens` genuinely means zero tokens were written this
2794
+ * request — the actionable signal that a breakpoint did not take. Under
2795
+ * `'reads'` it means the API cannot say, and inferring "zero writes" from it
2796
+ * would be a fabricated fact. Same remedy as
2797
+ * `IAiCompletionResponse.structuredOutput`.
2798
+ */
2799
+ readonly reports: AiCacheReportingLevel;
2800
+ /** Input tokens **not** served from cache. Normalized — see `CAPABILITIES.md` for the per-provider table. */
2801
+ readonly uncachedInputTokens?: number;
2802
+ /** Input tokens served from cache. */
2803
+ readonly cachedInputTokens?: number;
2804
+ /**
2805
+ * Input tokens written to cache.
2806
+ *
2807
+ * @remarks
2808
+ * Two absence cases, disambiguated by `reports` — never by a `0` this field
2809
+ * itself carries:
2810
+ * - **`reports: 'reads-and-writes'` and this field absent** — the wire
2811
+ * genuinely reported zero tokens written. Treat absence as `0`, not as
2812
+ * unknown; this is the actionable "the breakpoint did not take" signal.
2813
+ * - **`reports: 'reads'` and this field absent** — the API cannot report
2814
+ * writes on this route at all (structurally unfillable, e.g. OpenAI/xAI
2815
+ * Chat Completions). Never defaulted to `0` here — that would fabricate a
2816
+ * fact the wire never stated.
2817
+ */
2818
+ readonly cacheWriteTokens?: number;
2819
+ /** Generated output tokens. */
2820
+ readonly outputTokens?: number;
2821
+ /** Total input = uncached + cached, when both are known. */
2822
+ readonly totalInputTokens?: number;
2823
+ /** The provider's own usage block, unnormalized, for anything this shape drops. */
2824
+ readonly raw?: JsonObject;
2654
2825
  }
2655
2826
 
2656
2827
  /**
@@ -2964,6 +3135,23 @@ declare interface IAiModelInfo {
2964
3135
  readonly displayName?: string;
2965
3136
  }
2966
3137
 
3138
+ /**
3139
+ * How a provider carries an {@link AiAssist.IAiCacheRequest.cacheKey} on the wire.
3140
+ *
3141
+ * @remarks
3142
+ * Returned by {@link AiAssist.supportsPromptCacheRouting}. The Responses API takes the key as a
3143
+ * `prompt_cache_key` body field on every provider that supports it, so only the Chat Completions
3144
+ * transport varies and only that is described here.
3145
+ * @public
3146
+ */
3147
+ declare interface IAiPromptCacheRoutingSupport {
3148
+ /**
3149
+ * Header name to carry the key on Chat Completions, or `undefined` when the provider takes it
3150
+ * as a `prompt_cache_key` body field there (OpenAI). xAI uses `x-grok-conv-id`.
3151
+ */
3152
+ readonly chatCompletionsHeader: string | undefined;
3153
+ }
3154
+
2967
3155
  /**
2968
3156
  * Describes a single AI provider — single source of truth for all metadata.
2969
3157
  * @public
@@ -2998,6 +3186,29 @@ declare interface IAiProviderDescriptor {
2998
3186
  readonly aliases?: IModelAliasMap;
2999
3187
  /** Which server-side tools this provider supports (empty = none). */
3000
3188
  readonly supportedTools: ReadonlyArray<AiServerToolType>;
3189
+ /**
3190
+ * Server tools this provider's API refuses to accept in the *same request* as
3191
+ * client (function) tools. Absent or empty means the provider imposes no such
3192
+ * constraint and any supported server tool may be mixed with client tools.
3193
+ *
3194
+ * @remarks
3195
+ * This is a narrower statement than {@link AiAssist.IAiProviderDescriptor.supportedTools}:
3196
+ * a tool listed here is genuinely supported, just not *together* with function
3197
+ * calling. Only Gemini declares anything today — its `generateContent` API
3198
+ * HTTP-400s (`INVALID_ARGUMENT`) when built-in grounding (`web_search` →
3199
+ * `google_search`) and `function_declarations` appear in one request.
3200
+ *
3201
+ * Declaring the rule here rather than enforcing it at the call site is the
3202
+ * point: {@link AiAssist.resolveToolConflicts} applies it generically, so a host
3203
+ * never writes `if (descriptor.apiFormat === 'gemini')` in its tool assembly,
3204
+ * and the day a provider lifts its limit only this line changes.
3205
+ *
3206
+ * Optional because most providers have nothing to say. The library's own
3207
+ * registry descriptors are the ones that reach the wire and are pinned by test;
3208
+ * a hand-built descriptor that omits this simply gets the unconstrained
3209
+ * behavior, which is the correct reading of "declares no constraint".
3210
+ */
3211
+ readonly serverToolsExclusiveWithClientTools?: ReadonlyArray<AiServerToolType>;
3001
3212
  /** Whether this provider's API enforces CORS restrictions that prevent direct browser calls. */
3002
3213
  readonly corsRestricted: boolean;
3003
3214
  /**
@@ -3017,13 +3228,6 @@ declare interface IAiProviderDescriptor {
3017
3228
  * `prompt.attachments` are rejected up front.
3018
3229
  */
3019
3230
  readonly acceptsImageInput: boolean;
3020
- /**
3021
- * Whether this provider supports thinking/reasoning mode.
3022
- * - 'optional': thinking can be enabled but is not required
3023
- * - 'required': thinking is always active (e.g. o-series models)
3024
- * - 'unsupported': thinking is not supported
3025
- */
3026
- readonly thinkingMode: AiThinkingMode;
3027
3231
  /**
3028
3232
  * Image-generation capabilities, scoped to model id prefixes. Empty or
3029
3233
  * undefined means the provider does not support image generation.
@@ -3117,6 +3321,20 @@ declare interface IAiProviderDescriptor {
3117
3321
  readonly adaptiveThinkingModelPrefixes?: ReadonlyArray<string>;
3118
3322
  }
3119
3323
 
3324
+ /**
3325
+ * The outcome of {@link AiAssist.resolveToolConflicts}: the tools that survived,
3326
+ * plus the report of what did not.
3327
+ * @public
3328
+ */
3329
+ declare interface IAiResolvedToolConflicts {
3330
+ /** Server tools to send, after the policy was applied. */
3331
+ readonly serverTools: ReadonlyArray<AiServerToolConfig>;
3332
+ /** Client tools to send, after the policy was applied. */
3333
+ readonly clientTools: ReadonlyArray<IAiClientTool>;
3334
+ /** What the resolution did. Always populated; empty arrays mean nothing conflicted. */
3335
+ readonly report: IAiToolConflictReport;
3336
+ }
3337
+
3120
3338
  /**
3121
3339
  * Terminal success event for a streaming completion. Carries the aggregated
3122
3340
  * full text and truncation status for callers that want both the progressive
@@ -3138,6 +3356,8 @@ declare interface IAiStreamDone {
3138
3356
  * reports truncation without a reason).
3139
3357
  */
3140
3358
  readonly incompleteReason?: string;
3359
+ /** Token usage, when the provider reports it on this stream. */
3360
+ readonly usage?: IAiCompletionUsage;
3141
3361
  }
3142
3362
 
3143
3363
  /**
@@ -3303,6 +3523,27 @@ declare interface IAiToolAnnotations {
3303
3523
  readonly openWorldHint?: boolean;
3304
3524
  }
3305
3525
 
3526
+ /**
3527
+ * What the conflict resolution did to a turn's tools.
3528
+ *
3529
+ * @remarks
3530
+ * Required on {@link AiAssist.IAiClientToolTurnResult}, not optional, for the same reason
3531
+ * `structuredOutput` is required on a completion response: an absent report is
3532
+ * three-ways ambiguous — nothing conflicted / this build predates the feature /
3533
+ * the resolution ran and said nothing — and disambiguating exactly that is what
3534
+ * it is for. A turn where nothing conflicted reports the policy that was in
3535
+ * force and two empty arrays.
3536
+ * @public
3537
+ */
3538
+ declare interface IAiToolConflictReport {
3539
+ /** The policy that was in force for this turn (the default when none was passed). */
3540
+ readonly policy: AiToolConflictPolicy;
3541
+ /** Server tool types removed from the request. Empty when nothing was dropped. */
3542
+ readonly droppedServerTools: ReadonlyArray<AiServerToolType>;
3543
+ /** Names of client tools removed from the request. Empty when nothing was dropped. */
3544
+ readonly droppedClientTools: ReadonlyArray<string>;
3545
+ }
3546
+
3306
3547
  /**
3307
3548
  * Declares a tool as enabled/disabled in provider settings.
3308
3549
  * Tools are disabled by default — consuming apps must opt in explicitly.
@@ -4311,6 +4552,19 @@ declare interface IExecuteClientToolTurnParams extends IChatRequest {
4311
4552
  * `DEFAULT_ANTHROPIC_MAX_TOKENS`.
4312
4553
  */
4313
4554
  readonly maxTokens?: number;
4555
+ /**
4556
+ * What to do when this provider declares one of the requested server tools
4557
+ * mutually exclusive with client tools (see
4558
+ * {@link AiAssist.IAiProviderDescriptor.serverToolsExclusiveWithClientTools}). Defaults to
4559
+ * `'drop-server-tools'` — the client tools this host registered for the turn are
4560
+ * kept and the server tool is dropped.
4561
+ *
4562
+ * @remarks
4563
+ * Whatever the policy, the turn result's `toolConflicts` says what actually
4564
+ * happened, so a degraded turn is never a silent one. Pass `'fail'` for the
4565
+ * pre-policy behavior: a `Result.fail` up front, before any wire call.
4566
+ */
4567
+ readonly toolConflictPolicy?: AiToolConflictPolicy;
4314
4568
  }
4315
4569
 
4316
4570
  /**
@@ -5361,6 +5615,11 @@ declare interface IProviderCompletionParams extends IChatRequest {
5361
5615
  * `IAiCompletionResponse.structuredOutput`.
5362
5616
  */
5363
5617
  readonly structuredOutput?: StructuredOutputRequest;
5618
+ /**
5619
+ * Prompt-caching plan for this request. Omitted, nothing cache-related is sent — the request
5620
+ * body is byte-identical to a build predating this feature. See {@link AiAssist.IAiCacheRequest}.
5621
+ */
5622
+ readonly cache?: IAiCacheRequest;
5364
5623
  }
5365
5624
 
5366
5625
  /**
@@ -5530,6 +5789,38 @@ declare interface IRemoveSecretResult {
5530
5789
  readonly warning?: string;
5531
5790
  }
5532
5791
 
5792
+ /**
5793
+ * One contiguous run of the rendered output, attributed to what produced it.
5794
+ * @public
5795
+ */
5796
+ declare interface IRenderedSegment {
5797
+ /** `'literal'` for template text; `'substitution'` for an interpolated value. */
5798
+ readonly kind: 'literal' | 'substitution';
5799
+ /** Variable name, when `kind` is `'substitution'`. */
5800
+ readonly name?: string;
5801
+ /** Offset into `IRenderedTemplate.text`, in UTF-16 code units. */
5802
+ readonly start: number;
5803
+ /** Length in UTF-16 code units. May be `0` for a variable that rendered empty. */
5804
+ readonly length: number;
5805
+ /** `false` for a triple-stash / `{{&}}` interpolation, which bypasses escaping. */
5806
+ readonly escaped: boolean;
5807
+ }
5808
+
5809
+ /**
5810
+ * A render plus the map of which part of the output came from where.
5811
+ * @public
5812
+ */
5813
+ declare interface IRenderedTemplate {
5814
+ /** The rendered output — identical to what `MustacheTemplate.render` returns. */
5815
+ readonly text: string;
5816
+ /**
5817
+ * Document-ordered, contiguous and gapless: every code unit of `text` belongs to exactly one
5818
+ * segment, so concatenating them reproduces `text` exactly. That identity is verified before
5819
+ * this is returned.
5820
+ */
5821
+ readonly segments: ReadonlyArray<IRenderedSegment>;
5822
+ }
5823
+
5533
5824
  /**
5534
5825
  * Policy guard over the outbound request.
5535
5826
  *
@@ -5959,6 +6250,36 @@ declare interface ISchemaStructuredOutputRequest {
5959
6250
  */
5960
6251
  readonly schema: JsonSchema.ISchemaValidator<unknown>;
5961
6252
  readonly onUnsupported?: StructuredOutputFallback;
6253
+ /**
6254
+ * On a format that requires every property to be `required`, send an optional
6255
+ * property as required when its node **already admits `null`**, instead of
6256
+ * refusing the whole schema.
6257
+ *
6258
+ * @remarks
6259
+ * `JsonSchema.optional(...)` emits its inner node verbatim — optionality lives
6260
+ * only in the parent's `required` array — so for a property authored as
6261
+ * `optional(string({ nullable: true }))` the wire node is already
6262
+ * `['string', 'null']`, and adding the key to `required` narrows what the model
6263
+ * may send from *absent-or-null-or-value* to *null-or-value*. **Every reply the
6264
+ * narrowed schema permits, the original schema already accepted**, so the
6265
+ * one-object-cannot-drift property is preserved rather than traded away.
6266
+ *
6267
+ * That is why this is not the caller asserting its validator tolerates `null`.
6268
+ * An assertion could be false; this is read off the schema. A property authored
6269
+ * as plain `optional(string())` rejects `null`, is therefore **not** hoistable,
6270
+ * and the schema still refuses through `onUnsupported` exactly as before — with
6271
+ * an error naming the properties that blocked it. Setting this flag can never
6272
+ * produce a wire schema the supplied schema would reject.
6273
+ *
6274
+ * Opt-in because it is still a **semantic** change to the reply: the model must
6275
+ * now emit `null` where it could previously omit the key. A caller that
6276
+ * distinguishes those two — rather than treating them alike, as
6277
+ * `optional(nullable)` says it does — should leave this off.
6278
+ *
6279
+ * Defaults to `false`. The enforcement report is unaffected: a schema that goes
6280
+ * out reports `'schema'` whether or not any property was hoisted.
6281
+ */
6282
+ readonly adaptOptionalToNullable?: boolean;
5962
6283
  }
5963
6284
 
5964
6285
  /**
@@ -6042,11 +6363,27 @@ declare function isValidMultibaseSpkiPublicKey(value: unknown): value is Multiba
6042
6363
  declare interface IThinkingConfig {
6043
6364
  /**
6044
6365
  * Cross-provider effort level. Common-subset mapping:
6366
+ * - 'none': Anthropic — no thinking param emitted | OpenAI effort:none | Gemini thinkingBudget:0 | xAI reasoning_effort:none
6045
6367
  * - 'low': Anthropic effort:low | OpenAI effort:low | Gemini thinkingBudget:1024 | xAI reasoning_effort:low
6046
- * - 'medium': effort:medium | effort:medium | thinkingBudget:4096 | reasoning_effort:medium
6047
- * - 'high': effort:high | effort:high | thinkingBudget:8192 | reasoning_effort:high
6368
+ * - 'medium': Anthropic effort:medium | OpenAI effort:medium | Gemini thinkingBudget:4096 | xAI reasoning_effort:medium
6369
+ * - 'high': Anthropic effort:high | OpenAI effort:high | Gemini thinkingBudget:8192 | xAI reasoning_effort:high
6370
+ *
6371
+ * @remarks
6372
+ * `'none'` is the one cross-provider spelling for "thinking off". Anthropic has no
6373
+ * off value in its own effort vocabulary — off there means omitting the `thinking`
6374
+ * wire param entirely — so `'none'` maps to that omission rather than to a value.
6375
+ * Whichever provider is in play, `'none'` also re-enables `temperature`: see
6376
+ * `checkTemperatureConflict` in `thinkingOptionsResolver.ts`.
6377
+ *
6378
+ * The mapping is not model-aware (same posture as every other entry in this table —
6379
+ * see `ModelSpecKey`'s remarks for why thinking availability isn't gated at the call
6380
+ * path). For Gemini specifically, `thinkingBudget: 0` is documented as valid only on
6381
+ * Flash and Flash-Lite and erroring on Pro (see `IGeminiThinkingConfig.thinkingBudget`);
6382
+ * `'none'` on a Pro-family model inherits that same caveat, exactly as an explicit
6383
+ * `providers: [{ provider: 'google', config: { thinkingBudget: 0 } }]` block already did
6384
+ * before this field existed.
6048
6385
  */
6049
- readonly effort?: 'low' | 'medium' | 'high';
6386
+ readonly effort?: 'none' | 'low' | 'medium' | 'high';
6050
6387
  /**
6051
6388
  * Optional per-provider precision blocks. Blocks for providers that don't
6052
6389
  * match the resolved model's provider are silently skipped.
@@ -7195,17 +7532,15 @@ declare const modelSpec: Converter<ModelSpec>;
7195
7532
  * `'advanced'` / `'frontier'`) and sets the tools / thinking request params
7196
7533
  * independently. Thinking composes with any tier without a tier-level capability
7197
7534
  * check — but that is a statement about the tier axis, not a claim that every
7198
- * provider supports thinking: several descriptors declare
7199
- * `thinkingMode: 'unsupported'` (e.g. `copy-paste`, `groq`, `mistral`, `ollama`,
7200
- * `openai-compat`).
7201
- *
7202
- * Thinking availability is declared **per provider**, on the descriptor's
7203
- * `thinkingMode`; the descriptor does not encode per-model thinking availability at
7204
- * all, so a provider that declares support may still have individual models its own
7205
- * API rejects thinking on. (`adaptiveThinkingModelPrefixes` is per-model but selects
7206
- * a wire *shape*, not availability.) What the tier axis guarantees is therefore
7207
- * narrow and exact: a tier selects a model within one provider and never changes the
7208
- * provider, so it never changes `thinkingMode`.
7535
+ * provider supports thinking, or that every model of a thinking-capable provider
7536
+ * does: the descriptor itself declares no thinking-availability field at all.
7537
+ * `AiModelCapability`'s `'thinking'` entry (per-model, RegExp-matched on model id
7538
+ * in `DEFAULT_MODEL_CAPABILITY_CONFIG`) is a listing/filtering signal only — the
7539
+ * call path does not gate a thinking request on it, so an unlisted model is not
7540
+ * rejected, merely absent from a capability-filtered menu. (`adaptiveThinkingModelPrefixes`
7541
+ * is per-model but selects a wire *shape*, not availability.) What the tier axis
7542
+ * guarantees is therefore narrow and exact: a tier selects a model within one
7543
+ * provider and never changes the provider.
7209
7544
  *
7210
7545
  * Do not add a `'tools'` or `'thinking'` key here, and do not hand-roll a
7211
7546
  * `resolveModel` + `resolveModelAlias` walk to emulate one — call
@@ -7287,6 +7622,8 @@ declare namespace Mustache {
7287
7622
  IContextValidationResult,
7288
7623
  IMissingVariableDetail,
7289
7624
  IMustacheTemplateOptions,
7625
+ IRenderedSegment,
7626
+ IRenderedTemplate,
7290
7627
  IVariableRef,
7291
7628
  MustacheEscapeStrategy,
7292
7629
  MustacheTokenType,
@@ -7375,6 +7712,61 @@ declare class MustacheTemplate {
7375
7712
  * @returns Success with the rendered string, or Failure if rendering fails
7376
7713
  */
7377
7714
  render(context: unknown): Result<string>;
7715
+ /**
7716
+ * Renders the template and reports which part of the output came from where.
7717
+ *
7718
+ * @remarks
7719
+ * The rendered `text` is identical to `render`'s. What is added is
7720
+ * `segments`: a document-ordered, contiguous, gapless attribution of every code unit to either
7721
+ * template literal text or a named interpolation. It answers "what is in this output, in what
7722
+ * order, and how much of it is each part" — a prompt's composition, a generated document's
7723
+ * section sizes, a diff-free view of where a template's bulk actually goes.
7724
+ *
7725
+ * **Offsets are computed during the render, never recovered afterwards.** Recovering them by
7726
+ * searching the output for each substituted value is the obvious shortcut and it is unsound: a
7727
+ * value can occur more than once, can render empty, can be a substring of the surrounding
7728
+ * literal text, and can be altered by escaping between input and output. That failure is silent
7729
+ * and produces plausible offsets, which is the worst property a diagnostic can have.
7730
+ *
7731
+ * **Sections, inverted sections and partials are refused, not approximated.** Their rendered
7732
+ * output is not a linear image of the token order — a section may repeat its body, or omit it —
7733
+ * so a segment list walked from the template would mis-describe the result. Failing names the
7734
+ * construct. Comments and set-delimiter tags emit nothing and are skipped; because each
7735
+ * interpolation is rendered from its already-parsed token rather than from a re-parsed slice of
7736
+ * the template, a set-delimiter tag has no effect on what this produces.
7737
+ *
7738
+ * **Every context value is evaluated exactly once, as `render()` evaluates it.** A lambda in the
7739
+ * context is therefore invoked the same number of times either way, and a context whose lambda
7740
+ * answers differently per call is rendered rather than rejected — the returned `segments` always
7741
+ * describe the returned `text`, whatever that text was. (The concatenation identity is asserted
7742
+ * across every shape in this package's test suite, not re-checked at runtime: re-checking means
7743
+ * a second full render, which is precisely what would double those invocations.)
7744
+ *
7745
+ * @param context - The context object for template rendering.
7746
+ * @returns Success with the rendered text and its segment map, or Failure if the
7747
+ * template contains a construct this cannot describe
7748
+ */
7749
+ renderWithSegments(context: unknown): Result<IRenderedTemplate>;
7750
+ /**
7751
+ * Renders one interpolation token against a prepared lookup context.
7752
+ *
7753
+ * @remarks
7754
+ * Delegates to the same `Writer` primitives `render()` reaches through `renderTokens`, so the
7755
+ * value semantics — dotted paths, lambdas, `null` and `undefined` rendering empty, non-string
7756
+ * coercion — are mustache.js's rather than a reimplementation of them.
7757
+ *
7758
+ * The token is passed as a freshly built `[type, name]` array rather than the parsed span. Both
7759
+ * primitives read only the name, and `@types/mustache` declares the parameter as `string[]`
7760
+ * while a parsed span carries its source offsets as numbers — so handing over the span would
7761
+ * need a cast to work around the typings. Building the pair the primitives actually read needs
7762
+ * none.
7763
+ *
7764
+ * @param type - `'name'` for an escaped interpolation, `'&'` for an unescaped one.
7765
+ * @param name - The interpolation's name (may be a dotted path).
7766
+ * @param lookup - The context to resolve the name against.
7767
+ * @returns The rendered value; empty when the name resolves to `null` or `undefined`.
7768
+ */
7769
+ private _renderInterpolation;
7378
7770
  /**
7379
7771
  * Validates the context and renders the template if validation passes.
7380
7772
  * @param context - The context object to validate and render with
@@ -8053,6 +8445,34 @@ declare function resolveProviderModel(descriptor: IAiProviderDescriptor, modelOv
8053
8445
  */
8054
8446
  declare function resolveStructuredOutputCapability(descriptor: IAiProviderDescriptor, modelId: string): IAiStructuredOutputCapability | undefined;
8055
8447
 
8448
+ /**
8449
+ * Applies a provider's declared server-tool/client-tool exclusions to one request's
8450
+ * tools.
8451
+ *
8452
+ * @remarks
8453
+ * The generic half of a rule that used to be a provider-identity branch. The
8454
+ * constraint is declared on the descriptor
8455
+ * (`IAiProviderDescriptor.serverToolsExclusiveWithClientTools`); this reads it
8456
+ * and applies `policy`, so no consumer needs to know *which* provider has the limit
8457
+ * or *what* the limit is. A descriptor that declares nothing, or a request with no
8458
+ * client tools, or one with no server tools, passes through untouched with an empty
8459
+ * report — the exclusion is about the *combination*, and either half alone is legal.
8460
+ *
8461
+ * Sibling to {@link AiAssist.resolveEffectiveTools} rather than an extension of it:
8462
+ * that function filters server tools against what the provider supports at all and
8463
+ * is used by every completion path, most of which have no client tools to weigh.
8464
+ *
8465
+ * @param descriptor - The provider descriptor carrying the declared exclusions.
8466
+ * @param serverTools - The server tools the caller wants to send, if any.
8467
+ * @param clientTools - The client tools the caller wants to send.
8468
+ * @param policy - What to do about a conflict. Defaults to
8469
+ * {@link AiAssist.defaultToolConflictPolicy}.
8470
+ * @returns `Success` with the surviving tools and a report, or `Failure` naming the
8471
+ * conflict when `policy` is `'fail'`.
8472
+ * @public
8473
+ */
8474
+ declare function resolveToolConflicts(descriptor: IAiProviderDescriptor, serverTools: ReadonlyArray<AiServerToolConfig> | undefined, clientTools: ReadonlyArray<IAiClientTool>, policy?: AiToolConflictPolicy): Result<IAiResolvedToolConflicts>;
8475
+
8056
8476
  /**
8057
8477
  * Statuses whose `Retry-After` header is honored.
8058
8478
  *
@@ -8396,6 +8816,29 @@ declare type StructuredOutputRequest = ISchemaStructuredOutputRequest | IJsonObj
8396
8816
  */
8397
8817
  declare const SUPPORTED_SCHEMES: ReadonlyArray<string>;
8398
8818
 
8819
+ /**
8820
+ * Whether a provider has confirmed prompt-cache-relevant `usage` reporting on
8821
+ * the shared Chat Completions / Responses completion paths (streaming and
8822
+ * non-streaming alike).
8823
+ *
8824
+ * @remarks
8825
+ * All four call sites (`callOpenAiCompletion`, `callOpenAiResponsesCompletion`,
8826
+ * and their streaming counterparts) are shared by every `apiFormat: 'openai'`
8827
+ * descriptor — xAI Grok, Groq, Mistral, Ollama, and self-hosted
8828
+ * `openai-compat` all route through the same adapter as OpenAI. Only OpenAI
8829
+ * and xAI Grok are confirmed to report cache-relevant `usage` fields; an
8830
+ * ordinary `usage` block from one of the other descriptors (e.g. Groq's plain
8831
+ * `prompt_tokens` / `completion_tokens`, with no cache sub-object at all)
8832
+ * carries no cache information. Normalizing it anyway would stamp
8833
+ * `reports: 'reads'` on a provider that has no prompt-caching concept —
8834
+ * a false cache-reporting signal, not an absent one. So this gate is checked
8835
+ * before every normalize call; when it's `false`, `usage` stays absent
8836
+ * entirely rather than being guessed from whatever shape the wire happens to
8837
+ * send.
8838
+ * @public
8839
+ */
8840
+ declare function supportsCacheUsageReporting(descriptor: IAiProviderDescriptor): boolean;
8841
+
8399
8842
  /**
8400
8843
  * Whether a provider declares any embedding capability at all.
8401
8844
  *
@@ -8416,6 +8859,77 @@ declare function supportsEmbedding(descriptor: IAiProviderDescriptor): boolean;
8416
8859
  */
8417
8860
  declare function supportsImageGeneration(descriptor: IAiProviderDescriptor): boolean;
8418
8861
 
8862
+ /**
8863
+ * Whether a provider has confirmed tolerance for the request-shape change
8864
+ * {@link AiAssist.IAiCacheRequest.systemBreakpoints} produces on the shared Chat Completions /
8865
+ * Responses paths: splitting `system` into content parts each carrying a
8866
+ * `prompt_cache_breakpoint` field.
8867
+ *
8868
+ * This governs **breakpoints only**. The routing key is a separate capability with a different
8869
+ * support set — see {@link AiAssist.supportsPromptCacheRouting}. An earlier version of this
8870
+ * predicate gated both, which withheld the routing key from xAI, the one provider that most
8871
+ * needs it.
8872
+ *
8873
+ * @remarks
8874
+ * Same sharing problem as {@link AiAssist.supportsStreamUsageOption}, on the write side instead of the
8875
+ * read side: `callOpenAiCompletion` and `callOpenAiResponsesCompletion` are shared by every
8876
+ * `apiFormat: 'openai'` descriptor (xAI Grok, Groq, Mistral, Ollama, self-hosted
8877
+ * `openai-compat`), but only OpenAI's own API is confirmed to accept a `system`/first-item
8878
+ * `content` restructured into an array of parts each carrying an unrecognized
8879
+ * `prompt_cache_breakpoint` field. A
8880
+ * schema-strict server on one of the other descriptors could 400 on it — the identical
8881
+ * failure mode `supportsStreamUsageOption` was written to avoid for `stream_options`. So `cache`
8882
+ * is gated to `descriptor.id === 'openai'` at the dispatch site before it ever reaches these
8883
+ * builders; every other descriptor gets the same request body it would have gotten had the
8884
+ * caller passed no `cache` at all.
8885
+ * @public
8886
+ */
8887
+ declare function supportsPromptCacheBreakpoints(descriptor: IAiProviderDescriptor): boolean;
8888
+
8889
+ /**
8890
+ * Whether a provider accepts an opaque prompt-cache **routing** key, and by what transport.
8891
+ *
8892
+ * @remarks
8893
+ * Distinct from {@link AiAssist.supportsPromptCacheBreakpoints}, and deliberately so. A breakpoint
8894
+ * restructures the request body and needs the server to tolerate an unrecognized field inside
8895
+ * `content`; a routing key is a single opaque string whose only job is to make repeated requests
8896
+ * with a shared prefix land on the same cache-holding server.
8897
+ *
8898
+ * The two were originally gated together, which had a perverse effect: xAI does byte-for-byte
8899
+ * prefix matching from the start of the `messages` array, and its cache is **per-server and
8900
+ * evictable**, so without a routing key a caller can miss on an identical prefix simply by being
8901
+ * routed elsewhere. Measured on `grok-4.3`: two requests sharing an 8,684-token system prefix but
8902
+ * differing in their final user turn returned 2.2% cached, while a byte-identical pair returned
8903
+ * 99.5%. Withholding the routing key from xAI withheld it from the provider whose caching depends
8904
+ * on it most.
8905
+ *
8906
+ * The transport differs by route, which is why this returns a descriptor rather than a boolean:
8907
+ *
8908
+ * | provider | Chat Completions | Responses |
8909
+ * |---|---|---|
8910
+ * | `openai` | `prompt_cache_key` body field | `prompt_cache_key` body field |
8911
+ * | `xai-grok` | `x-grok-conv-id` **header** | `prompt_cache_key` body field |
8912
+ *
8913
+ * Every other `apiFormat: 'openai'` descriptor (Groq, Mistral, Ollama, self-hosted
8914
+ * `openai-compat`) returns `undefined` and gets the request it would have gotten had the caller
8915
+ * passed no `cache` at all — the same unconfirmed-tolerance reasoning as
8916
+ * {@link AiAssist.supportsStreamUsageOption}.
8917
+ * @public
8918
+ */
8919
+ declare function supportsPromptCacheRouting(descriptor: IAiProviderDescriptor): IAiPromptCacheRoutingSupport | undefined;
8920
+
8921
+ /**
8922
+ * Whether a provider's streaming Chat Completions request should carry
8923
+ * `stream_options: { include_usage: true }`. A request-shape gate, not a
8924
+ * capability guess: the shared adapter also carries providers (xAI, Groq,
8925
+ * Mistral, Ollama, self-hosted `openai-compat`) not verified to tolerate an
8926
+ * unrecognized field, so this is `true` only for `'openai'`; every other
8927
+ * provider simply never gets a streaming `usage` block — a safe degradation,
8928
+ * since `IAiStreamDone.usage` is optional everywhere.
8929
+ * @public
8930
+ */
8931
+ declare function supportsStreamUsageOption(descriptor: IAiProviderDescriptor): boolean;
8932
+
8419
8933
  /**
8420
8934
  * Whether `descriptor` declares any structured-output capability at all.
8421
8935
  * @public
@@ -8493,6 +9007,39 @@ declare const uint8ArrayFromBase64: Converter<Uint8Array>;
8493
9007
  */
8494
9008
  declare function usesMaxCompletionTokensField(descriptor: IAiProviderDescriptor): boolean;
8495
9009
 
9010
+ /**
9011
+ * Validates an {@link IAiCacheRequest}'s `systemBreakpoints` against the `system` string they
9012
+ * index into. Thin wrapper over {@link validateCacheBreakpoints} for a caller that holds the
9013
+ * actual string (every `ts-extras` adapter) rather than just its length.
9014
+ * @public
9015
+ */
9016
+ declare function validateAiCacheRequest(system: string, cache: IAiCacheRequest, maxBreakpointWrites?: number): Result<IAiCacheRequest>;
9017
+
9018
+ /**
9019
+ * Validates an {@link IAiCacheRequest}'s `systemBreakpoints` against the length of the `system`
9020
+ * string they index into, and optionally against a per-model write cap.
9021
+ *
9022
+ * @remarks
9023
+ * **Fails loudly, never clamps** (design.md §6.1): a non-ascending, out-of-range, or over-cap
9024
+ * offset returns `fail()`. A silently-clamped breakpoint is precisely the silent, non-caching
9025
+ * failure this stream exists to remove. `systemLength` and `maxBreakpointWrites` are themselves
9026
+ * validated as non-negative integers — checked before `systemBreakpoints` even, so a malformed
9027
+ * value (`NaN`, a negative number, a fraction) fails loudly rather than silently passing every
9028
+ * offset check it should have rejected (`offset >= NaN` is always `false`) or, for the cap,
9029
+ * passing unnoticed on an empty offset list. `maxBreakpointWrites` is left to the caller to supply —
9030
+ * there is no model-keyed cap table in this package yet (design.md §7's `IAiCacheCapability` is
9031
+ * out of this slice's scope), and design.md §5.2 establishes that a caller producing breakpoints
9032
+ * from the three-level `PromptCacheStability` vocabulary never emits more than two, so an
9033
+ * omitted cap is not a soundness gap for that caller — only a direct caller supplying more than a
9034
+ * provider's cap would ever need one enforced here.
9035
+ *
9036
+ * Takes a length rather than {@link validateAiCacheRequest}'s `system` string because a caller
9037
+ * building a plan from an `@fgv/ts-prompt-assist` `IPromptComposition` (`totalChars`) has the
9038
+ * length without holding the resolved body text itself.
9039
+ * @public
9040
+ */
9041
+ declare function validateCacheBreakpoints(systemLength: number, cache: IAiCacheRequest, maxBreakpointWrites?: number): Result<IAiCacheRequest>;
9042
+
8496
9043
  /**
8497
9044
  * Validates the resolved options against per-model registry constraints.
8498
9045
  *