@kaana.ai/contracts 8.4.0 → 8.6.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.ts CHANGED
@@ -24,6 +24,7 @@ export * from './public/images.js';
24
24
  export * from './public/messages.js';
25
25
  export * from './public/money.js';
26
26
  export * from './public/policy.js';
27
+ export * from './public/providerPreferences.js';
27
28
  export * from './public/realtime.js';
28
29
  export * from './public/responses.js';
29
30
  export * from './public/streamEvents.js';
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export * from './public/images.js';
24
24
  export * from './public/messages.js';
25
25
  export * from './public/money.js';
26
26
  export * from './public/policy.js';
27
+ export * from './public/providerPreferences.js';
27
28
  export * from './public/realtime.js';
28
29
  export * from './public/responses.js';
29
30
  export * from './public/streamEvents.js';
@@ -314,10 +314,10 @@ export declare const routingProfileSchema: z.ZodObject<{
314
314
  displayName: z.ZodString;
315
315
  description: z.ZodOptional<z.ZodString>;
316
316
  optimiseFor: z.ZodEnum<{
317
- quality: "quality";
318
- price: "price";
319
317
  latency: "latency";
320
318
  throughput: "throughput";
319
+ quality: "quality";
320
+ price: "price";
321
321
  balanced: "balanced";
322
322
  }>;
323
323
  candidates: z.ZodArray<z.ZodObject<{
@@ -95,6 +95,10 @@ export declare const inferenceInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
95
95
  }, z.core.$strict>, z.ZodObject<{
96
96
  type: z.ZodLiteral<"refusal">;
97
97
  text: z.ZodString;
98
+ }, z.core.$strict>, z.ZodObject<{
99
+ type: z.ZodLiteral<"reasoning">;
100
+ text: z.ZodOptional<z.ZodString>;
101
+ opaque: z.ZodOptional<z.ZodString>;
98
102
  }, z.core.$strict>], "type">>;
99
103
  name: z.ZodOptional<z.ZodString>;
100
104
  toolCallId: z.ZodOptional<z.ZodString>;
@@ -279,6 +283,10 @@ export declare const inferenceRequestSchema: z.ZodObject<{
279
283
  }, z.core.$strict>, z.ZodObject<{
280
284
  type: z.ZodLiteral<"refusal">;
281
285
  text: z.ZodString;
286
+ }, z.core.$strict>, z.ZodObject<{
287
+ type: z.ZodLiteral<"reasoning">;
288
+ text: z.ZodOptional<z.ZodString>;
289
+ opaque: z.ZodOptional<z.ZodString>;
282
290
  }, z.core.$strict>], "type">>;
283
291
  name: z.ZodOptional<z.ZodString>;
284
292
  toolCallId: z.ZodOptional<z.ZodString>;
@@ -307,11 +315,13 @@ export declare const inferenceRequestSchema: z.ZodObject<{
307
315
  stopSequences: z.ZodOptional<z.ZodArray<z.ZodString>>;
308
316
  }, z.core.$strict>;
309
317
  reasoning: z.ZodOptional<z.ZodObject<{
310
- effort: z.ZodEnum<{
318
+ effort: z.ZodOptional<z.ZodEnum<{
311
319
  low: "low";
312
320
  medium: "medium";
313
321
  high: "high";
314
- }>;
322
+ }>>;
323
+ maxTokens: z.ZodOptional<z.ZodNumber>;
324
+ exclude: z.ZodOptional<z.ZodBoolean>;
315
325
  }, z.core.$strict>>;
316
326
  speech: z.ZodOptional<z.ZodObject<{
317
327
  voice: z.ZodString;
@@ -253,6 +253,7 @@ export declare const inferenceStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodO
253
253
  output_audio_transcript: "output_audio_transcript";
254
254
  }>;
255
255
  text: z.ZodString;
256
+ opaque: z.ZodOptional<z.ZodString>;
256
257
  }, z.core.$strip>, z.ZodObject<{
257
258
  schemaVersion: z.ZodLiteral<1>;
258
259
  type: z.ZodLiteral<"audio">;
@@ -17,18 +17,30 @@
17
17
  * over input, so honouring the marker while folding the write into
18
18
  * `input_tokens` would report less than the request cost. The marker is
19
19
  * refused, by name and path, instead of being dropped.
20
- * - **`thinking` other than `{ type: 'disabled' }`.** The contract's reasoning
21
- * control is an effort (`low`, `medium`, `high`), not a token budget, and a
22
- * thinking block has no part in a message: reasoning is private and is never
23
- * returned. Reasoning effort is asked with `output_config.effort`, which
24
- * maps one to one. A `thinking` or `redacted_thinking` block in a message is
25
- * refused for the same reason.
20
+ * - **`thinking.display`** and every other `thinking` member this file does
21
+ * not name.
26
22
  * - **Server tools** (`web_search`, `bash`, `text_editor`, …), `mcp_servers`,
27
23
  * `container`, `service_tier`, `disable_parallel_tool_use`, `document`
28
24
  * blocks and any other member this file does not name.
29
25
  * - **An image `url` source**: nothing fetches an image URL (ADR 0007); a
30
26
  * `base64` source is carried inline under the image rules.
31
27
  *
28
+ * ## Reasoning (contract set 8.6.0, ADR 0009 decision 2)
29
+ *
30
+ * `thinking: {type: 'enabled', budget_tokens}` is the contract's
31
+ * `reasoning.maxTokens` and `thinking: {type: 'adaptive'}` opts in to reasoning
32
+ * with the route's own sizing; `output_config.effort` is `reasoning.effort`, and
33
+ * a budget beside an effort is refused (the contract treats them as
34
+ * alternatives). A route that cannot take a budget refuses it by name.
35
+ *
36
+ * An answer carries `thinking` blocks before its text and tool calls, and an
37
+ * assistant message sent back carries them again, UNMODIFIED, ahead of its
38
+ * `tool_use` blocks. The block's `signature` is the contract's opaque
39
+ * continuation token (the sealed string a `reasoning` part carries), which is
40
+ * why a stock Anthropic SDK continues a tool turn without knowing anything
41
+ * else: it returns what it received. `redacted_thinking` is accepted on the way
42
+ * in (its `data` is the same token) and never produced.
43
+ *
32
44
  * `stop_reason: 'stop_sequence'` is never produced: the finish reason Kaana
33
45
  * measures does not say WHICH stop sequence ended the turn, and `stop_sequence`
34
46
  * would then have to name one. A turn that ended on a stop sequence reports
@@ -94,6 +106,13 @@ export declare const anthropicMessagesRequestSchema: z.ZodObject<{
94
106
  }, z.core.$strict>], "type">;
95
107
  }, z.core.$strict>]>>]>>;
96
108
  is_error: z.ZodOptional<z.ZodBoolean>;
109
+ }, z.core.$strict>, z.ZodObject<{
110
+ type: z.ZodLiteral<"thinking">;
111
+ thinking: z.ZodString;
112
+ signature: z.ZodString;
113
+ }, z.core.$strict>, z.ZodObject<{
114
+ type: z.ZodLiteral<"redacted_thinking">;
115
+ data: z.ZodString;
97
116
  }, z.core.$strict>]>>]>;
98
117
  }, z.core.$strict>>;
99
118
  system: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
@@ -122,9 +141,14 @@ export declare const anthropicMessagesRequestSchema: z.ZodObject<{
122
141
  type: z.ZodLiteral<"tool">;
123
142
  name: z.ZodString;
124
143
  }, z.core.$strict>], "type">>;
125
- thinking: z.ZodOptional<z.ZodObject<{
144
+ thinking: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
126
145
  type: z.ZodLiteral<"disabled">;
127
- }, z.core.$strict>>;
146
+ }, z.core.$strict>, z.ZodObject<{
147
+ type: z.ZodLiteral<"adaptive">;
148
+ }, z.core.$strict>, z.ZodObject<{
149
+ type: z.ZodLiteral<"enabled">;
150
+ budget_tokens: z.ZodNumber;
151
+ }, z.core.$strict>], "type">>;
128
152
  output_config: z.ZodOptional<z.ZodObject<{
129
153
  effort: z.ZodOptional<z.ZodEnum<{
130
154
  low: "low";
@@ -143,6 +167,10 @@ export declare const anthropicMessageSchema: z.ZodObject<{
143
167
  role: z.ZodLiteral<"assistant">;
144
168
  model: z.ZodString;
145
169
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
170
+ type: z.ZodLiteral<"thinking">;
171
+ thinking: z.ZodString;
172
+ signature: z.ZodString;
173
+ }, z.core.$strict>, z.ZodObject<{
146
174
  type: z.ZodLiteral<"text">;
147
175
  text: z.ZodString;
148
176
  }, z.core.$strict>, z.ZodObject<{
@@ -198,6 +226,10 @@ export declare const anthropicStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodO
198
226
  role: z.ZodLiteral<"assistant">;
199
227
  model: z.ZodString;
200
228
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
229
+ type: z.ZodLiteral<"thinking">;
230
+ thinking: z.ZodString;
231
+ signature: z.ZodString;
232
+ }, z.core.$strict>, z.ZodObject<{
201
233
  type: z.ZodLiteral<"text">;
202
234
  text: z.ZodString;
203
235
  }, z.core.$strict>, z.ZodObject<{
@@ -218,6 +250,10 @@ export declare const anthropicStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodO
218
250
  type: z.ZodLiteral<"content_block_start">;
219
251
  index: z.ZodNumber;
220
252
  content_block: z.ZodDiscriminatedUnion<[z.ZodObject<{
253
+ type: z.ZodLiteral<"thinking">;
254
+ thinking: z.ZodLiteral<"">;
255
+ signature: z.ZodLiteral<"">;
256
+ }, z.core.$strict>, z.ZodObject<{
221
257
  type: z.ZodLiteral<"text">;
222
258
  text: z.ZodLiteral<"">;
223
259
  }, z.core.$strict>, z.ZodObject<{
@@ -232,6 +268,12 @@ export declare const anthropicStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodO
232
268
  delta: z.ZodDiscriminatedUnion<[z.ZodObject<{
233
269
  type: z.ZodLiteral<"text_delta">;
234
270
  text: z.ZodString;
271
+ }, z.core.$strict>, z.ZodObject<{
272
+ type: z.ZodLiteral<"thinking_delta">;
273
+ thinking: z.ZodString;
274
+ }, z.core.$strict>, z.ZodObject<{
275
+ type: z.ZodLiteral<"signature_delta">;
276
+ signature: z.ZodString;
235
277
  }, z.core.$strict>, z.ZodObject<{
236
278
  type: z.ZodLiteral<"input_json_delta">;
237
279
  partial_json: z.ZodString;
@@ -17,18 +17,30 @@
17
17
  * over input, so honouring the marker while folding the write into
18
18
  * `input_tokens` would report less than the request cost. The marker is
19
19
  * refused, by name and path, instead of being dropped.
20
- * - **`thinking` other than `{ type: 'disabled' }`.** The contract's reasoning
21
- * control is an effort (`low`, `medium`, `high`), not a token budget, and a
22
- * thinking block has no part in a message: reasoning is private and is never
23
- * returned. Reasoning effort is asked with `output_config.effort`, which
24
- * maps one to one. A `thinking` or `redacted_thinking` block in a message is
25
- * refused for the same reason.
20
+ * - **`thinking.display`** and every other `thinking` member this file does
21
+ * not name.
26
22
  * - **Server tools** (`web_search`, `bash`, `text_editor`, …), `mcp_servers`,
27
23
  * `container`, `service_tier`, `disable_parallel_tool_use`, `document`
28
24
  * blocks and any other member this file does not name.
29
25
  * - **An image `url` source**: nothing fetches an image URL (ADR 0007); a
30
26
  * `base64` source is carried inline under the image rules.
31
27
  *
28
+ * ## Reasoning (contract set 8.6.0, ADR 0009 decision 2)
29
+ *
30
+ * `thinking: {type: 'enabled', budget_tokens}` is the contract's
31
+ * `reasoning.maxTokens` and `thinking: {type: 'adaptive'}` opts in to reasoning
32
+ * with the route's own sizing; `output_config.effort` is `reasoning.effort`, and
33
+ * a budget beside an effort is refused (the contract treats them as
34
+ * alternatives). A route that cannot take a budget refuses it by name.
35
+ *
36
+ * An answer carries `thinking` blocks before its text and tool calls, and an
37
+ * assistant message sent back carries them again, UNMODIFIED, ahead of its
38
+ * `tool_use` blocks. The block's `signature` is the contract's opaque
39
+ * continuation token (the sealed string a `reasoning` part carries), which is
40
+ * why a stock Anthropic SDK continues a tool turn without knowing anything
41
+ * else: it returns what it received. `redacted_thinking` is accepted on the way
42
+ * in (its `data` is the same token) and never produced.
43
+ *
32
44
  * `stop_reason: 'stop_sequence'` is never produced: the finish reason Kaana
33
45
  * measures does not say WHICH stop sequence ended the turn, and `stop_sequence`
34
46
  * would then have to name one. A turn that ended on a stop sequence reports
@@ -63,6 +75,21 @@ const imageBlockSchema = z
63
75
  ]),
64
76
  })
65
77
  .strict();
78
+ /**
79
+ * An assistant's reasoning. `thinking` is the readable text (empty when the
80
+ * model returned none); `signature` is the continuation token to return
81
+ * unmodified, empty only for a block that cannot be continued.
82
+ */
83
+ const thinkingBlockSchema = z
84
+ .object({
85
+ type: z.literal('thinking'),
86
+ thinking: z.string(),
87
+ signature: z.string(),
88
+ })
89
+ .strict();
90
+ const redactedThinkingBlockSchema = z
91
+ .object({ type: z.literal('redacted_thinking'), data: nonEmpty(1_000_000) })
92
+ .strict();
66
93
  const toolUseBlockSchema = z
67
94
  .object({
68
95
  type: z.literal('tool_use'),
@@ -91,7 +118,14 @@ const messageSchema = z
91
118
  content: z.union([
92
119
  z.string(),
93
120
  z
94
- .array(z.union([textBlockSchema, imageBlockSchema, toolUseBlockSchema, toolResultBlockSchema]))
121
+ .array(z.union([
122
+ textBlockSchema,
123
+ imageBlockSchema,
124
+ toolUseBlockSchema,
125
+ toolResultBlockSchema,
126
+ thinkingBlockSchema,
127
+ redactedThinkingBlockSchema,
128
+ ]))
95
129
  .min(1),
96
130
  ]),
97
131
  })
@@ -100,9 +134,10 @@ const messageSchema = z
100
134
  if (typeof message.content === 'string')
101
135
  return;
102
136
  for (const [index, block] of message.content.entries()) {
137
+ const reasoning = block.type === 'thinking' || block.type === 'redacted_thinking';
103
138
  const allowed = message.role === 'assistant'
104
- ? block.type === 'text' || block.type === 'tool_use'
105
- : block.type !== 'tool_use';
139
+ ? block.type === 'text' || block.type === 'tool_use' || reasoning
140
+ : block.type !== 'tool_use' && !reasoning;
106
141
  if (!allowed) {
107
142
  ctx.addIssue({
108
143
  code: 'custom',
@@ -142,10 +177,22 @@ export const anthropicMessagesRequestSchema = z
142
177
  stream: z.boolean().optional(),
143
178
  tools: z.array(toolSchema).max(128).optional(),
144
179
  tool_choice: toolChoiceSchema.optional(),
145
- /** Only "off" is accepted: see the file comment. */
180
+ /**
181
+ * `disabled` asks for nothing; `adaptive` opts in to reasoning (the
182
+ * contract's `reasoning: {}`); `enabled` sets a token budget
183
+ * (`reasoning.maxTokens`). See the file comment.
184
+ */
146
185
  thinking: z
147
- .object({ type: z.literal('disabled') })
148
- .strict()
186
+ .discriminatedUnion('type', [
187
+ z.object({ type: z.literal('disabled') }).strict(),
188
+ z.object({ type: z.literal('adaptive') }).strict(),
189
+ z
190
+ .object({
191
+ type: z.literal('enabled'),
192
+ budget_tokens: z.number().int().positive().safe(),
193
+ })
194
+ .strict(),
195
+ ])
149
196
  .optional(),
150
197
  /** `effort` is the contract's reasoning effort; no other member is carried. */
151
198
  output_config: z.object({ effort: reasoningEffortSchema.optional() }).strict().optional(),
@@ -175,6 +222,7 @@ const usageSchema = z
175
222
  .strict();
176
223
  const stopReasonSchema = z.enum(['end_turn', 'max_tokens', 'tool_use', 'refusal']);
177
224
  const outputBlockSchema = z.discriminatedUnion('type', [
225
+ thinkingBlockSchema,
178
226
  textBlockSchema,
179
227
  toolUseBlockSchema.extend({ input: z.record(z.string(), z.unknown()) }),
180
228
  ]);
@@ -243,6 +291,13 @@ export const anthropicStreamEventSchema = z.discriminatedUnion('type', [
243
291
  type: z.literal('content_block_start'),
244
292
  index: z.number().int().nonnegative(),
245
293
  content_block: z.discriminatedUnion('type', [
294
+ z
295
+ .object({
296
+ type: z.literal('thinking'),
297
+ thinking: z.literal(''),
298
+ signature: z.literal(''),
299
+ })
300
+ .strict(),
246
301
  z.object({ type: z.literal('text'), text: z.literal('') }).strict(),
247
302
  toolUseBlockSchema.extend({ input: z.object({}).strict() }),
248
303
  ]),
@@ -254,6 +309,8 @@ export const anthropicStreamEventSchema = z.discriminatedUnion('type', [
254
309
  index: z.number().int().nonnegative(),
255
310
  delta: z.discriminatedUnion('type', [
256
311
  z.object({ type: z.literal('text_delta'), text: z.string() }).strict(),
312
+ z.object({ type: z.literal('thinking_delta'), thinking: z.string() }).strict(),
313
+ z.object({ type: z.literal('signature_delta'), signature: z.string() }).strict(),
257
314
  z.object({ type: z.literal('input_json_delta'), partial_json: z.string() }).strict(),
258
315
  ]),
259
316
  })
@@ -2,9 +2,10 @@
2
2
  * `POST /v1/chat/completions` — the OpenAI-compatible surface.
3
3
  *
4
4
  * It speaks the flat OpenAI request and response shapes so an unmodified OpenAI
5
- * SDK works against `baseURL: 'https://kaana.ai/v1'` with an `oxy_sk_…` key, and
6
- * it gains NO Kaana-specific request field: a capability with no OpenAI
7
- * representation lives on `/v1/responses`. `.strict()` everywhere: a stock SDK
5
+ * SDK works against `baseURL: 'https://kaana.ai/v1'` with an `oxy_sk_…` key. Its
6
+ * one extension is `provider`, the OpenRouter-style routing preference (ADR
7
+ * 0008); every other capability with no OpenAI representation lives on
8
+ * `/v1/responses`. `.strict()` everywhere: a stock SDK
8
9
  * sending a parameter Kaana does not implement is told so rather than having it
9
10
  * silently dropped.
10
11
  *
@@ -69,6 +70,10 @@ export declare const chatCompletionMessageSchema: z.ZodObject<{
69
70
  }, z.core.$strict>, z.ZodObject<{
70
71
  type: z.ZodLiteral<"refusal">;
71
72
  text: z.ZodString;
73
+ }, z.core.$strict>, z.ZodObject<{
74
+ type: z.ZodLiteral<"reasoning">;
75
+ text: z.ZodOptional<z.ZodString>;
76
+ opaque: z.ZodOptional<z.ZodString>;
72
77
  }, z.core.$strict>], "type">>]>>>;
73
78
  name: z.ZodOptional<z.ZodString>;
74
79
  tool_call_id: z.ZodOptional<z.ZodString>;
@@ -141,6 +146,10 @@ export declare const chatCompletionsRequestSchema: z.ZodObject<{
141
146
  }, z.core.$strict>, z.ZodObject<{
142
147
  type: z.ZodLiteral<"refusal">;
143
148
  text: z.ZodString;
149
+ }, z.core.$strict>, z.ZodObject<{
150
+ type: z.ZodLiteral<"reasoning">;
151
+ text: z.ZodOptional<z.ZodString>;
152
+ opaque: z.ZodOptional<z.ZodString>;
144
153
  }, z.core.$strict>], "type">>]>>>;
145
154
  name: z.ZodOptional<z.ZodString>;
146
155
  tool_call_id: z.ZodOptional<z.ZodString>;
@@ -214,6 +223,34 @@ export declare const chatCompletionsRequestSchema: z.ZodObject<{
214
223
  }>;
215
224
  }, z.core.$strict>>;
216
225
  user: z.ZodOptional<z.ZodString>;
226
+ provider: z.ZodOptional<z.ZodObject<{
227
+ order: z.ZodOptional<z.ZodArray<z.ZodString>>;
228
+ only: z.ZodOptional<z.ZodArray<z.ZodString>>;
229
+ ignore: z.ZodOptional<z.ZodArray<z.ZodString>>;
230
+ allow_fallbacks: z.ZodOptional<z.ZodBoolean>;
231
+ require_parameters: z.ZodOptional<z.ZodBoolean>;
232
+ quantizations: z.ZodOptional<z.ZodArray<z.ZodEnum<{
233
+ unknown: "unknown";
234
+ int4: "int4";
235
+ int8: "int8";
236
+ fp4: "fp4";
237
+ mxfp4: "mxfp4";
238
+ nvfp4: "nvfp4";
239
+ fp6: "fp6";
240
+ fp8: "fp8";
241
+ mxfp8: "mxfp8";
242
+ fp16: "fp16";
243
+ bf16: "bf16";
244
+ fp32: "fp32";
245
+ }>>>;
246
+ sort: z.ZodOptional<z.ZodEnum<{
247
+ latency: "latency";
248
+ throughput: "throughput";
249
+ }>>;
250
+ preferred_max_latency: z.ZodOptional<z.ZodNumber>;
251
+ preferred_min_throughput: z.ZodOptional<z.ZodNumber>;
252
+ }, z.core.$strict>>;
253
+ prompt_cache_key: z.ZodOptional<z.ZodString>;
217
254
  }, z.core.$strict>;
218
255
  /** OpenAI's usage block, with the audio members only when audio tokens were metered. */
219
256
  export declare const chatCompletionUsageSchema: z.ZodObject<{
@@ -2,9 +2,10 @@
2
2
  * `POST /v1/chat/completions` — the OpenAI-compatible surface.
3
3
  *
4
4
  * It speaks the flat OpenAI request and response shapes so an unmodified OpenAI
5
- * SDK works against `baseURL: 'https://kaana.ai/v1'` with an `oxy_sk_…` key, and
6
- * it gains NO Kaana-specific request field: a capability with no OpenAI
7
- * representation lives on `/v1/responses`. `.strict()` everywhere: a stock SDK
5
+ * SDK works against `baseURL: 'https://kaana.ai/v1'` with an `oxy_sk_…` key. Its
6
+ * one extension is `provider`, the OpenRouter-style routing preference (ADR
7
+ * 0008); every other capability with no OpenAI representation lives on
8
+ * `/v1/responses`. `.strict()` everywhere: a stock SDK
8
9
  * sending a parameter Kaana does not implement is told so rather than having it
9
10
  * silently dropped.
10
11
  *
@@ -18,6 +19,7 @@ import { maxUtf16Length, minUtf16Length } from '../stringBounds.js';
18
19
  import { reasoningEffortSchema } from './catalogue.js';
19
20
  import { publicErrorCodeSchema } from './errors.js';
20
21
  import { inferenceContentPartSchema } from './messages.js';
22
+ import { promptCacheKeySchema, providerPreferencesSchema } from './providerPreferences.js';
21
23
  import { modelTargetSchema } from './responses.js';
22
24
  /** `stop` in the OpenAI dialect: one sequence or a list of them. */
23
25
  const openAiStopSchema = z.union([
@@ -139,6 +141,10 @@ export const chatCompletionsRequestSchema = z
139
141
  * only. `X-Oxy-User-Id` wins when both are present.
140
142
  */
141
143
  user: z.string().check(minUtf16Length(1)).check(maxUtf16Length(64)).optional(),
144
+ /** Routing preferences that can only narrow what the grant admits (ADR 0008). */
145
+ provider: providerPreferencesSchema.optional(),
146
+ /** OpenAI's field: keeps requests sharing a prompt prefix on one deployment. */
147
+ prompt_cache_key: promptCacheKeySchema.optional(),
142
148
  })
143
149
  .strict()
144
150
  .superRefine((request, ctx) => {
@@ -23,34 +23,44 @@ export declare const inferenceContentSourceSchema: z.ZodDiscriminatedUnion<[z.Zo
23
23
  /**
24
24
  * One part of a message's content. A message is always a list of parts.
25
25
  *
26
- * ## `refusal` is a member and `reasoning` is not, and the asymmetry is the point
26
+ * ## `refusal` and `reasoning` are members, and neither is ever answer text
27
27
  *
28
28
  * A model that DECLINES says why, and those words are meant for the customer:
29
29
  * they are the difference between "rephrase this" and "stop asking". Before this
30
30
  * member existed there was nowhere in an {@link InferenceMessage} to put them, so
31
- * a non-streaming fold kept `finishReason: 'refusal'` and dropped the sentence —
32
- * the customer learned they were refused and not why, while a streaming caller of
33
- * the same request got the whole explanation on the `refusal` delta channel.
31
+ * a non-streaming fold kept `finishReason: 'refusal'` and dropped the sentence.
32
+ * The part is a MEMBER rather than a field because a refusal need not have text:
33
+ * an Anthropic `stop_reason: "refusal"` maps to the finish reason and separates
34
+ * no words from the answer, while an OpenAI-compatible `refusal` does.
34
35
  *
35
- * Reasoning is the opposite case and stays absent. It is the model's private
36
- * working, and a `text` part is where somebody would put it — which renders
37
- * private reasoning to the customer AS the answer, the product bug the delta
38
- * channels exist to prevent. An opaque per-block reasoning blob crossing this
39
- * boundary needs a home nobody has chosen yet, and inventing one here would
40
- * choose it by accident.
36
+ * ## `reasoning` (contract set 8.6.0, Kaana ADR 0009 decision 2)
41
37
  *
42
- * Both public dialects can carry a refusal, which is what separates the two
43
- * cases at the boundary as well as in principle: `refusal` is OpenAI's OWN field
44
- * in both of its shapes (`delta.refusal` streaming, `message.refusal`
45
- * non-streaming), while reasoning has no OpenAI field at all — the
46
- * `reasoning_content`/`reasoning` spellings an OpenAI-compatible provider emits
47
- * are provider extensions. So carrying the refusal costs no dialect its
48
- * standard-client parseability, and carrying reasoning would.
38
+ * Reasoning was deliberately absent until the owner chose its home, because an
39
+ * opaque per-block reasoning blob had none. The cost of that absence was
40
+ * correctness: a thinking model that calls a tool cannot be continued unless the
41
+ * assistant's reasoning blocks, with their signatures, come back to the provider
42
+ * unmodified and in order, and a customer who only ever received reasoning TEXT
43
+ * on the `reasoning` delta channel had nothing to send back.
49
44
  *
50
- * The part is a MEMBER rather than a field because a refusal need not have text:
51
- * an Anthropic `stop_reason: "refusal"` maps to the finish reason and separates
52
- * no words from the answer, while an OpenAI-compatible `refusal` does. A required
53
- * field would force the first provider to invent a sentence.
45
+ * The part is `{type: 'reasoning', text?, opaque?}`:
46
+ *
47
+ * - `text` is what a person may read. It is a display value and is never
48
+ * forwarded to a provider on its own.
49
+ * - `opaque` is a continuation token the customer MUST return UNMODIFIED, in the
50
+ * position it arrived. Its content is Kaana's to define and the customer's to
51
+ * treat as a sealed string: it is provider-neutral on the surface and carries
52
+ * whatever the serving provider needs to continue. Sealing is NOT
53
+ * cryptographic: a forged or edited token is no more than a request the
54
+ * provider refuses, so nothing may rely on it for trust.
55
+ * - at least one of the two is present.
56
+ *
57
+ * A reasoning part is the assistant's alone, exactly as a refusal is. It is
58
+ * never a `text` part, so no renderer can present private reasoning as the
59
+ * answer, and the delta channels keep reasoning apart for the same reason.
60
+ *
61
+ * What a route does with a token it cannot read (it was issued by another
62
+ * provider family, after a failover or a model switch) is drop it, never fail:
63
+ * the continuation is lost, the conversation is not.
54
64
  */
55
65
  export declare const inferenceContentPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
56
66
  type: z.ZodLiteral<"text">;
@@ -94,6 +104,10 @@ export declare const inferenceContentPartSchema: z.ZodDiscriminatedUnion<[z.ZodO
94
104
  }, z.core.$strict>, z.ZodObject<{
95
105
  type: z.ZodLiteral<"refusal">;
96
106
  text: z.ZodString;
107
+ }, z.core.$strict>, z.ZodObject<{
108
+ type: z.ZodLiteral<"reasoning">;
109
+ text: z.ZodOptional<z.ZodString>;
110
+ opaque: z.ZodOptional<z.ZodString>;
97
111
  }, z.core.$strict>], "type">;
98
112
  /**
99
113
  * The image formats a request may carry. Every provider that serves image input
@@ -227,6 +241,10 @@ export declare const inferenceMessageSchema: z.ZodObject<{
227
241
  }, z.core.$strict>, z.ZodObject<{
228
242
  type: z.ZodLiteral<"refusal">;
229
243
  text: z.ZodString;
244
+ }, z.core.$strict>, z.ZodObject<{
245
+ type: z.ZodLiteral<"reasoning">;
246
+ text: z.ZodOptional<z.ZodString>;
247
+ opaque: z.ZodOptional<z.ZodString>;
230
248
  }, z.core.$strict>], "type">>;
231
249
  name: z.ZodOptional<z.ZodString>;
232
250
  toolCallId: z.ZodOptional<z.ZodString>;
@@ -280,17 +298,30 @@ export declare const responseFormatSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
280
298
  }, z.core.$strict>], "type">;
281
299
  /**
282
300
  * The caller's reasoning control. Strict, like every leaf: a provider-specific
283
- * knob (`budget_tokens`, `summary`) is refused rather than silently dropped.
301
+ * knob (`summary`) is refused rather than silently dropped.
284
302
  *
285
- * Oxy forwards it only after checking the resolved model advertises the effort
286
- * in its catalogue `reasoningEfforts`; Kaana translates it per provider.
303
+ * - `effort` is how much to reason, in the catalogue's words. Oxy forwards it
304
+ * only after checking the resolved model advertises it in `reasoningEfforts`.
305
+ * - `maxTokens` is a thinking BUDGET in tokens. It is the other way to size
306
+ * reasoning, so it excludes `effort`. A route that cannot take a budget
307
+ * refuses it with `invalid_request` naming `reasoning.maxTokens`.
308
+ * - `exclude: true` means the model may still reason, and be billed for it, but
309
+ * no reasoning is returned: no `reasoning` delta, no reasoning part, no opaque
310
+ * token. A caller that excludes reasoning cannot continue it.
311
+ *
312
+ * Carrying `reasoning` at all, even as `{}`, opts in to receiving reasoning
313
+ * continuation tokens (the `opaque` field of a `reasoning` delta); a request
314
+ * without it never receives one, which is what keeps older readers of the
315
+ * stream unaffected.
287
316
  */
288
317
  export declare const inferenceReasoningSchema: z.ZodObject<{
289
- effort: z.ZodEnum<{
318
+ effort: z.ZodOptional<z.ZodEnum<{
290
319
  low: "low";
291
320
  medium: "medium";
292
321
  high: "high";
293
- }>;
322
+ }>>;
323
+ maxTokens: z.ZodOptional<z.ZodNumber>;
324
+ exclude: z.ZodOptional<z.ZodBoolean>;
294
325
  }, z.core.$strict>;
295
326
  /** Parameters for text-to-speech, preserved in the signed request. */
296
327
  export declare const inferenceSpeechParametersSchema: z.ZodObject<{