@kaana.ai/contracts 8.1.0 → 8.3.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.
@@ -0,0 +1,274 @@
1
+ /**
2
+ * `POST /v1/messages` — the Anthropic-Messages-compatible surface.
3
+ *
4
+ * It speaks the flat Anthropic request, response and stream shapes so an
5
+ * unmodified Anthropic SDK or Claude Code works with
6
+ * `ANTHROPIC_BASE_URL=https://kaana.ai` and an `oxy_sk_…` key (sent as
7
+ * `x-api-key` or as `Authorization: Bearer`). Like `/v1/chat/completions` it
8
+ * gains NO Kaana-specific request field, and it is `.strict()` everywhere: a
9
+ * stock SDK sending a parameter Kaana does not implement is told so rather than
10
+ * having it silently dropped.
11
+ *
12
+ * ## What is refused, and why
13
+ *
14
+ * - **`cache_control`, anywhere** (on a block, a tool, `system` or the
15
+ * request). Kaana's usage units have no unit for a cache WRITE (see the
16
+ * Anthropic adapter's usage meter): a cache write is billed at a premium
17
+ * over input, so honouring the marker while folding the write into
18
+ * `input_tokens` would report less than the request cost. The marker is
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.
26
+ * - **Server tools** (`web_search`, `bash`, `text_editor`, …), `mcp_servers`,
27
+ * `container`, `service_tier`, `disable_parallel_tool_use`, `document`
28
+ * blocks and any other member this file does not name.
29
+ * - **An image `url` source**: nothing fetches an image URL (ADR 0007); a
30
+ * `base64` source is carried inline under the image rules.
31
+ *
32
+ * `stop_reason: 'stop_sequence'` is never produced: the finish reason Kaana
33
+ * measures does not say WHICH stop sequence ended the turn, and `stop_sequence`
34
+ * would then have to name one. A turn that ended on a stop sequence reports
35
+ * `end_turn`.
36
+ *
37
+ * `metadata.user_id` is accepted and neither forwarded nor stored: it is an
38
+ * end-user identifier, and Kaana persists none.
39
+ *
40
+ * `usage` is derived from the measured units: `input_tokens`, `output_tokens`
41
+ * (reasoning included, as Anthropic counts it) and, only when the route
42
+ * measured it, `cache_read_input_tokens`. There is no `cache_creation_*`.
43
+ */
44
+ import { z } from 'zod';
45
+ import { maxUtf16Length, minUtf16Length } from '../stringBounds.js';
46
+ import { reasoningEffortSchema } from './catalogue.js';
47
+ import { modelTargetSchema } from './responses.js';
48
+ const nonEmpty = (max) => z.string().check(minUtf16Length(1)).check(maxUtf16Length(max));
49
+ const textBlockSchema = z.object({ type: z.literal('text'), text: z.string() }).strict();
50
+ const imageBlockSchema = z
51
+ .object({
52
+ type: z.literal('image'),
53
+ source: z.discriminatedUnion('type', [
54
+ z
55
+ .object({
56
+ type: z.literal('base64'),
57
+ media_type: nonEmpty(255),
58
+ data: z.string().check(minUtf16Length(1)),
59
+ })
60
+ .strict(),
61
+ /** Parses so the refusal can name it; Kaana fetches no image URL. */
62
+ z.object({ type: z.literal('url'), url: nonEmpty(4096) }).strict(),
63
+ ]),
64
+ })
65
+ .strict();
66
+ const toolUseBlockSchema = z
67
+ .object({
68
+ type: z.literal('tool_use'),
69
+ id: nonEmpty(128),
70
+ name: nonEmpty(128),
71
+ input: z.record(z.string(), z.unknown()),
72
+ })
73
+ .strict();
74
+ const toolResultBlockSchema = z
75
+ .object({
76
+ type: z.literal('tool_result'),
77
+ tool_use_id: nonEmpty(128),
78
+ content: z
79
+ .union([z.string(), z.array(z.union([textBlockSchema, imageBlockSchema]))])
80
+ .optional(),
81
+ /**
82
+ * Accepted. The contract's tool message has no error flag, so the result's
83
+ * own content is what the model reads; the flag is not forwarded.
84
+ */
85
+ is_error: z.boolean().optional(),
86
+ })
87
+ .strict();
88
+ const messageSchema = z
89
+ .object({
90
+ role: z.enum(['user', 'assistant']),
91
+ content: z.union([
92
+ z.string(),
93
+ z
94
+ .array(z.union([textBlockSchema, imageBlockSchema, toolUseBlockSchema, toolResultBlockSchema]))
95
+ .min(1),
96
+ ]),
97
+ })
98
+ .strict()
99
+ .superRefine((message, ctx) => {
100
+ if (typeof message.content === 'string')
101
+ return;
102
+ for (const [index, block] of message.content.entries()) {
103
+ const allowed = message.role === 'assistant'
104
+ ? block.type === 'text' || block.type === 'tool_use'
105
+ : block.type !== 'tool_use';
106
+ if (!allowed) {
107
+ ctx.addIssue({
108
+ code: 'custom',
109
+ path: ['content', index, 'type'],
110
+ message: `a ${message.role} message does not carry a ${block.type} block`,
111
+ });
112
+ }
113
+ }
114
+ });
115
+ const toolSchema = z
116
+ .object({
117
+ type: z.literal('custom').optional(),
118
+ name: nonEmpty(128),
119
+ description: z.string().check(maxUtf16Length(32768)).optional(),
120
+ input_schema: z.record(z.string(), z.unknown()),
121
+ strict: z.boolean().optional(),
122
+ })
123
+ .strict();
124
+ const toolChoiceSchema = z.discriminatedUnion('type', [
125
+ z.object({ type: z.literal('auto') }).strict(),
126
+ z.object({ type: z.literal('any') }).strict(),
127
+ z.object({ type: z.literal('none') }).strict(),
128
+ z.object({ type: z.literal('tool'), name: nonEmpty(128) }).strict(),
129
+ ]);
130
+ export const anthropicMessagesRequestSchema = z
131
+ .object({
132
+ /** A model reference (`publisher/model[@revision]`) or a power level. */
133
+ model: modelTargetSchema,
134
+ /** Required, as Anthropic requires it. */
135
+ max_tokens: z.number().int().positive().safe(),
136
+ messages: z.array(messageSchema).min(1),
137
+ system: z.union([z.string(), z.array(textBlockSchema)]).optional(),
138
+ temperature: z.number().min(0).max(1).optional(),
139
+ top_p: z.number().min(0).max(1).optional(),
140
+ top_k: z.number().int().positive().safe().optional(),
141
+ stop_sequences: z.array(nonEmpty(256)).max(8).optional(),
142
+ stream: z.boolean().optional(),
143
+ tools: z.array(toolSchema).max(128).optional(),
144
+ tool_choice: toolChoiceSchema.optional(),
145
+ /** Only "off" is accepted: see the file comment. */
146
+ thinking: z
147
+ .object({ type: z.literal('disabled') })
148
+ .strict()
149
+ .optional(),
150
+ /** `effort` is the contract's reasoning effort; no other member is carried. */
151
+ output_config: z.object({ effort: reasoningEffortSchema.optional() }).strict().optional(),
152
+ /** Accepted and not forwarded or stored. */
153
+ metadata: z
154
+ .object({ user_id: z.string().check(maxUtf16Length(512)).nullable().optional() })
155
+ .strict()
156
+ .optional(),
157
+ })
158
+ .strict()
159
+ .superRefine((request, ctx) => {
160
+ if (request.tool_choice !== undefined && (request.tools ?? []).length === 0) {
161
+ ctx.addIssue({
162
+ code: 'custom',
163
+ path: ['tool_choice'],
164
+ message: 'a tool choice requires at least one tool definition',
165
+ });
166
+ }
167
+ });
168
+ const usageSchema = z
169
+ .object({
170
+ input_tokens: z.number().int().nonnegative(),
171
+ output_tokens: z.number().int().nonnegative(),
172
+ /** Present only when the route measured a cache read. */
173
+ cache_read_input_tokens: z.number().int().nonnegative().optional(),
174
+ })
175
+ .strict();
176
+ const stopReasonSchema = z.enum(['end_turn', 'max_tokens', 'tool_use', 'refusal']);
177
+ const outputBlockSchema = z.discriminatedUnion('type', [
178
+ textBlockSchema,
179
+ toolUseBlockSchema.extend({ input: z.record(z.string(), z.unknown()) }),
180
+ ]);
181
+ const messageBodySchema = z.object({
182
+ id: nonEmpty(256),
183
+ type: z.literal('message'),
184
+ role: z.literal('assistant'),
185
+ /** The revision that served the request. */
186
+ model: nonEmpty(256),
187
+ });
188
+ /** The non-streaming body. The request id rides in `Kaana-Request-Id` and `Request-Id`. */
189
+ export const anthropicMessageSchema = messageBodySchema
190
+ .extend({
191
+ content: z.array(outputBlockSchema),
192
+ stop_reason: stopReasonSchema,
193
+ stop_sequence: z.literal(null),
194
+ usage: usageSchema,
195
+ })
196
+ .strict();
197
+ /** The error body, and the data of a stream's `event: error` frame. */
198
+ export const anthropicErrorSchema = z
199
+ .object({
200
+ type: z.literal('error'),
201
+ error: z
202
+ .object({
203
+ type: z.enum([
204
+ 'invalid_request_error',
205
+ 'authentication_error',
206
+ 'billing_error',
207
+ 'permission_error',
208
+ 'not_found_error',
209
+ 'request_too_large',
210
+ 'rate_limit_error',
211
+ 'timeout_error',
212
+ 'overloaded_error',
213
+ 'api_error',
214
+ ]),
215
+ message: nonEmpty(4096),
216
+ })
217
+ .strict(),
218
+ request_id: nonEmpty(256).optional(),
219
+ })
220
+ .strict();
221
+ /**
222
+ * One frame of a streamed message: `event: <type>` and this as its data. The
223
+ * input tokens are known only when the route reports usage, so `message_start`
224
+ * carries zeros and the final `message_delta` the measured totals. A stream that
225
+ * fails after its first frame ends with an `error` frame and no `message_stop`.
226
+ */
227
+ export const anthropicStreamEventSchema = z.discriminatedUnion('type', [
228
+ z
229
+ .object({
230
+ type: z.literal('message_start'),
231
+ message: messageBodySchema
232
+ .extend({
233
+ content: z.array(outputBlockSchema).length(0),
234
+ stop_reason: z.literal(null),
235
+ stop_sequence: z.literal(null),
236
+ usage: usageSchema,
237
+ })
238
+ .strict(),
239
+ })
240
+ .strict(),
241
+ z
242
+ .object({
243
+ type: z.literal('content_block_start'),
244
+ index: z.number().int().nonnegative(),
245
+ content_block: z.discriminatedUnion('type', [
246
+ z.object({ type: z.literal('text'), text: z.literal('') }).strict(),
247
+ toolUseBlockSchema.extend({ input: z.object({}).strict() }),
248
+ ]),
249
+ })
250
+ .strict(),
251
+ z
252
+ .object({
253
+ type: z.literal('content_block_delta'),
254
+ index: z.number().int().nonnegative(),
255
+ delta: z.discriminatedUnion('type', [
256
+ z.object({ type: z.literal('text_delta'), text: z.string() }).strict(),
257
+ z.object({ type: z.literal('input_json_delta'), partial_json: z.string() }).strict(),
258
+ ]),
259
+ })
260
+ .strict(),
261
+ z
262
+ .object({ type: z.literal('content_block_stop'), index: z.number().int().nonnegative() })
263
+ .strict(),
264
+ z
265
+ .object({
266
+ type: z.literal('message_delta'),
267
+ delta: z.object({ stop_reason: stopReasonSchema, stop_sequence: z.literal(null) }).strict(),
268
+ usage: usageSchema,
269
+ })
270
+ .strict(),
271
+ z.object({ type: z.literal('message_stop') }).strict(),
272
+ z.object({ type: z.literal('ping') }).strict(),
273
+ anthropicErrorSchema,
274
+ ]);
@@ -21,9 +21,9 @@ import { z } from 'zod';
21
21
  */
22
22
  export declare const chatCompletionMessageSchema: z.ZodObject<{
23
23
  role: z.ZodEnum<{
24
- user: "user";
25
24
  system: "system";
26
25
  developer: "developer";
26
+ user: "user";
27
27
  assistant: "assistant";
28
28
  tool: "tool";
29
29
  }>;
@@ -93,9 +93,9 @@ export declare const chatCompletionsRequestSchema: z.ZodObject<{
93
93
  }>]>;
94
94
  messages: z.ZodArray<z.ZodObject<{
95
95
  role: z.ZodEnum<{
96
- user: "user";
97
96
  system: "system";
98
97
  developer: "developer";
98
+ user: "user";
99
99
  assistant: "assistant";
100
100
  tool: "tool";
101
101
  }>;
@@ -95,6 +95,61 @@ export declare const inferenceContentPartSchema: z.ZodDiscriminatedUnion<[z.ZodO
95
95
  type: z.ZodLiteral<"refusal">;
96
96
  text: z.ZodString;
97
97
  }, z.core.$strict>], "type">;
98
+ /**
99
+ * The image formats a request may carry. Every provider that serves image input
100
+ * on a Kaana route reads these four: Anthropic (`image/jpeg`, `image/png`,
101
+ * `image/gif`, `image/webp`) and OpenAI (PNG, JPEG, WEBP, non-animated GIF).
102
+ */
103
+ export declare const INPUT_IMAGE_MEDIA_TYPES: readonly ["image/png", "image/jpeg", "image/webp", "image/gif"];
104
+ /**
105
+ * The most image parts one request may carry, across every message. Above 20
106
+ * images Anthropic applies a stricter per-image dimension limit to EVERY image
107
+ * of the request, so 20 is the most a request can carry and keep one rule per
108
+ * image on every route.
109
+ */
110
+ export declare const MAX_INPUT_IMAGES = 20;
111
+ /**
112
+ * The most decoded bytes one image may carry: 3,750,000, whose base64 is
113
+ * 5,000,000 characters, the smallest per-image limit a provider serving these
114
+ * routes documents (Anthropic on Amazon Bedrock: 5 MB base64-encoded).
115
+ */
116
+ export declare const MAX_INPUT_IMAGE_BYTES = 3750000;
117
+ /** Why one image part cannot be carried, before anything is held. Never the image itself. */
118
+ export interface ImageInputIssue {
119
+ /** The message, as its index in the request's message list. */
120
+ readonly messageIndex: number;
121
+ /** The part, as its index in that message's content; absent for the request-wide count. */
122
+ readonly partIndex?: number;
123
+ /** The field at fault, relative to the part. */
124
+ readonly field: 'role' | 'source' | 'source.mediaType' | 'source.data' | 'count';
125
+ readonly message: string;
126
+ }
127
+ /**
128
+ * Check the image parts of a conversation against the contract's image rules
129
+ * (Kaana ADR 0007). Kaana refuses a request with `invalid_request` and the first
130
+ * issue BEFORE it asks Oxy for a grant; Oxy applies the same rules on its own
131
+ * edge; a client may run it first.
132
+ *
133
+ * - an image is `inline`: no component fetches an image URL, and a `url`
134
+ * source is never forwarded to a provider to fetch either;
135
+ * - its media type is one of {@link INPUT_IMAGE_MEDIA_TYPES};
136
+ * - its data is canonical base64 of at most {@link MAX_INPUT_IMAGE_BYTES} bytes;
137
+ * - it rides on a `user` or `tool` message;
138
+ * - the request carries at most {@link MAX_INPUT_IMAGES} of them.
139
+ *
140
+ * Returns the number of image parts and every issue, in message order. An issue
141
+ * names where and why, never the bytes: an image is payload and is never echoed
142
+ * into an error, a log line or a record.
143
+ */
144
+ export declare function checkImageInput(messages: readonly InferenceMessage[]): {
145
+ readonly images: number;
146
+ readonly issues: ImageInputIssue[];
147
+ };
148
+ /**
149
+ * The decoded length of canonical base64 (RFC 4648 section 4, padded, no line
150
+ * breaks), or `undefined` when the text is not that. Counted, never decoded.
151
+ */
152
+ export declare function base64DecodedLength(data: string): number | undefined;
98
153
  /**
99
154
  * A tool call an assistant made, in the normalized form.
100
155
  *
@@ -108,9 +163,9 @@ export declare const inferenceToolCallSchema: z.ZodObject<{
108
163
  arguments: z.ZodString;
109
164
  }, z.core.$strict>;
110
165
  export declare const inferenceMessageRoleSchema: z.ZodEnum<{
111
- user: "user";
112
166
  system: "system";
113
167
  developer: "developer";
168
+ user: "user";
114
169
  assistant: "assistant";
115
170
  tool: "tool";
116
171
  }>;
@@ -124,9 +179,9 @@ export declare const inferenceMessageRoleSchema: z.ZodEnum<{
124
179
  */
125
180
  export declare const inferenceMessageSchema: z.ZodObject<{
126
181
  role: z.ZodEnum<{
127
- user: "user";
128
182
  system: "system";
129
183
  developer: "developer";
184
+ user: "user";
130
185
  assistant: "assistant";
131
186
  tool: "tool";
132
187
  }>;
@@ -86,6 +86,113 @@ export const inferenceContentPartSchema = z.discriminatedUnion('type', [
86
86
  */
87
87
  z.object({ type: z.literal('refusal'), text: z.string() }).strict(),
88
88
  ]);
89
+ /* -------------------------------------------------------------------------- */
90
+ /* Image input (contract set 8.2.0, Kaana ADR 0007) */
91
+ /* -------------------------------------------------------------------------- */
92
+ /**
93
+ * The image formats a request may carry. Every provider that serves image input
94
+ * on a Kaana route reads these four: Anthropic (`image/jpeg`, `image/png`,
95
+ * `image/gif`, `image/webp`) and OpenAI (PNG, JPEG, WEBP, non-animated GIF).
96
+ */
97
+ export const INPUT_IMAGE_MEDIA_TYPES = [
98
+ 'image/png',
99
+ 'image/jpeg',
100
+ 'image/webp',
101
+ 'image/gif',
102
+ ];
103
+ /**
104
+ * The most image parts one request may carry, across every message. Above 20
105
+ * images Anthropic applies a stricter per-image dimension limit to EVERY image
106
+ * of the request, so 20 is the most a request can carry and keep one rule per
107
+ * image on every route.
108
+ */
109
+ export const MAX_INPUT_IMAGES = 20;
110
+ /**
111
+ * The most decoded bytes one image may carry: 3,750,000, whose base64 is
112
+ * 5,000,000 characters, the smallest per-image limit a provider serving these
113
+ * routes documents (Anthropic on Amazon Bedrock: 5 MB base64-encoded).
114
+ */
115
+ export const MAX_INPUT_IMAGE_BYTES = 3_750_000;
116
+ /**
117
+ * Check the image parts of a conversation against the contract's image rules
118
+ * (Kaana ADR 0007). Kaana refuses a request with `invalid_request` and the first
119
+ * issue BEFORE it asks Oxy for a grant; Oxy applies the same rules on its own
120
+ * edge; a client may run it first.
121
+ *
122
+ * - an image is `inline`: no component fetches an image URL, and a `url`
123
+ * source is never forwarded to a provider to fetch either;
124
+ * - its media type is one of {@link INPUT_IMAGE_MEDIA_TYPES};
125
+ * - its data is canonical base64 of at most {@link MAX_INPUT_IMAGE_BYTES} bytes;
126
+ * - it rides on a `user` or `tool` message;
127
+ * - the request carries at most {@link MAX_INPUT_IMAGES} of them.
128
+ *
129
+ * Returns the number of image parts and every issue, in message order. An issue
130
+ * names where and why, never the bytes: an image is payload and is never echoed
131
+ * into an error, a log line or a record.
132
+ */
133
+ export function checkImageInput(messages) {
134
+ const issues = [];
135
+ let images = 0;
136
+ for (const [messageIndex, message] of messages.entries()) {
137
+ for (const [partIndex, part] of message.content.entries()) {
138
+ if (part.type !== 'image')
139
+ continue;
140
+ images += 1;
141
+ const at = { messageIndex, partIndex };
142
+ if (message.role !== 'user' && message.role !== 'tool') {
143
+ issues.push({ ...at, field: 'role', message: 'an image rides on a user or tool message' });
144
+ }
145
+ const source = part.source;
146
+ if (source.kind !== 'inline') {
147
+ issues.push({
148
+ ...at,
149
+ field: 'source',
150
+ message: 'an image is sent inline as base64; no image URL is fetched',
151
+ });
152
+ continue;
153
+ }
154
+ if (!INPUT_IMAGE_MEDIA_TYPES.includes(source.mediaType)) {
155
+ issues.push({
156
+ ...at,
157
+ field: 'source.mediaType',
158
+ message: `an image is one of ${INPUT_IMAGE_MEDIA_TYPES.join(', ')}`,
159
+ });
160
+ }
161
+ const bytes = base64DecodedLength(source.data);
162
+ if (bytes === undefined) {
163
+ issues.push({ ...at, field: 'source.data', message: 'image data is canonical base64' });
164
+ }
165
+ else if (bytes > MAX_INPUT_IMAGE_BYTES) {
166
+ issues.push({
167
+ ...at,
168
+ field: 'source.data',
169
+ message: `an image is at most ${MAX_INPUT_IMAGE_BYTES} bytes`,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ if (images > MAX_INPUT_IMAGES) {
175
+ issues.push({
176
+ messageIndex: messages.length - 1,
177
+ field: 'count',
178
+ message: `a request carries at most ${MAX_INPUT_IMAGES} images`,
179
+ });
180
+ }
181
+ return { images, issues };
182
+ }
183
+ const BASE64_BODY = /^[A-Za-z0-9+/]*$/;
184
+ /**
185
+ * The decoded length of canonical base64 (RFC 4648 section 4, padded, no line
186
+ * breaks), or `undefined` when the text is not that. Counted, never decoded.
187
+ */
188
+ export function base64DecodedLength(data) {
189
+ if (data.length === 0 || data.length % 4 !== 0)
190
+ return undefined;
191
+ const padding = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0;
192
+ if (!BASE64_BODY.test(data.slice(0, data.length - padding)))
193
+ return undefined;
194
+ return (data.length / 4) * 3 - padding;
195
+ }
89
196
  /**
90
197
  * A tool call an assistant made, in the normalized form.
91
198
  *
@@ -189,7 +296,7 @@ export const toolDefinitionSchema = z
189
296
  .object({
190
297
  type: z.literal('function'),
191
298
  name: z.string().check(minUtf16Length(1)).check(maxUtf16Length(128)),
192
- description: z.string().check(maxUtf16Length(2000)).optional(),
299
+ description: z.string().check(maxUtf16Length(32768)).optional(),
193
300
  parameters: z.record(z.string(), z.unknown()),
194
301
  /** Ask the provider to enforce the schema, where it supports enforcement. */
195
302
  strict: z.boolean().optional(),
@@ -284,8 +284,8 @@ export declare const realtimeConversationItemSchema: z.ZodDiscriminatedUnion<[z.
284
284
  type: z.ZodLiteral<"message">;
285
285
  itemId: z.ZodOptional<z.ZodString>;
286
286
  role: z.ZodEnum<{
287
- user: "user";
288
287
  system: "system";
288
+ user: "user";
289
289
  assistant: "assistant";
290
290
  }>;
291
291
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -401,8 +401,8 @@ export declare const realtimeItemCreateCommandSchema: z.ZodObject<{
401
401
  type: z.ZodLiteral<"message">;
402
402
  itemId: z.ZodOptional<z.ZodString>;
403
403
  role: z.ZodEnum<{
404
- user: "user";
405
404
  system: "system";
405
+ user: "user";
406
406
  assistant: "assistant";
407
407
  }>;
408
408
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -591,8 +591,8 @@ export declare const realtimeClientCommandSchema: z.ZodDiscriminatedUnion<[z.Zod
591
591
  type: z.ZodLiteral<"message">;
592
592
  itemId: z.ZodOptional<z.ZodString>;
593
593
  role: z.ZodEnum<{
594
- user: "user";
595
594
  system: "system";
595
+ user: "user";
596
596
  assistant: "assistant";
597
597
  }>;
598
598
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -796,8 +796,8 @@ export declare const realtimeItemAddedEventSchema: z.ZodObject<{
796
796
  type: z.ZodLiteral<"message">;
797
797
  itemId: z.ZodOptional<z.ZodString>;
798
798
  role: z.ZodEnum<{
799
- user: "user";
800
799
  system: "system";
800
+ user: "user";
801
801
  assistant: "assistant";
802
802
  }>;
803
803
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -848,8 +848,8 @@ export declare const realtimeItemDoneEventSchema: z.ZodObject<{
848
848
  type: z.ZodLiteral<"message">;
849
849
  itemId: z.ZodOptional<z.ZodString>;
850
850
  role: z.ZodEnum<{
851
- user: "user";
852
851
  system: "system";
852
+ user: "user";
853
853
  assistant: "assistant";
854
854
  }>;
855
855
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -1655,8 +1655,8 @@ export declare const realtimePublicServerEventSchema: z.ZodDiscriminatedUnion<[z
1655
1655
  type: z.ZodLiteral<"message">;
1656
1656
  itemId: z.ZodOptional<z.ZodString>;
1657
1657
  role: z.ZodEnum<{
1658
- user: "user";
1659
1658
  system: "system";
1659
+ user: "user";
1660
1660
  assistant: "assistant";
1661
1661
  }>;
1662
1662
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -1706,8 +1706,8 @@ export declare const realtimePublicServerEventSchema: z.ZodDiscriminatedUnion<[z
1706
1706
  type: z.ZodLiteral<"message">;
1707
1707
  itemId: z.ZodOptional<z.ZodString>;
1708
1708
  role: z.ZodEnum<{
1709
- user: "user";
1710
1709
  system: "system";
1710
+ user: "user";
1711
1711
  assistant: "assistant";
1712
1712
  }>;
1713
1713
  content: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -44,9 +44,9 @@ export declare const responsesRequestSchema: z.ZodObject<{
44
44
  }>]>>;
45
45
  input: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
46
46
  role: z.ZodEnum<{
47
- user: "user";
48
47
  system: "system";
49
48
  developer: "developer";
49
+ user: "user";
50
50
  assistant: "assistant";
51
51
  tool: "tool";
52
52
  }>;
@@ -164,9 +164,9 @@ export declare const responseSchema: z.ZodObject<{
164
164
  }>;
165
165
  output: z.ZodArray<z.ZodObject<{
166
166
  role: z.ZodEnum<{
167
- user: "user";
168
167
  system: "system";
169
168
  developer: "developer";
169
+ user: "user";
170
170
  assistant: "assistant";
171
171
  tool: "tool";
172
172
  }>;
@@ -100,7 +100,7 @@
100
100
  * change to, say, the catalogue reject every in-flight inference request; the
101
101
  * per-shape `schemaVersion` is what a message is validated against.
102
102
  */
103
- export declare const INFERENCE_CONTRACT_VERSION = "8.1.0";
103
+ export declare const INFERENCE_CONTRACT_VERSION = "8.3.0";
104
104
  /**
105
105
  * Contract set 4.0.0 (Kaana ADR 0002) is the first published as
106
106
  * `@kaana.ai/contracts`. MAJOR because the set changed hands and split in two:
@@ -187,6 +187,20 @@ export declare const INFERENCE_CONTRACT_VERSION = "8.1.0";
187
187
  * `transcript.interim` event it asks for, the provisional text of an input
188
188
  * item that the item's deltas and its `transcript.done` supersede. Any other
189
189
  * kind asking for interim text is refused. Nothing in 8.0.0 changes.
190
+ *
191
+ * Contract set 8.2.0 (Kaana ADR 0007) is additive: image input. The rules an
192
+ * image part answers to (`checkImageInput`, `INPUT_IMAGE_MEDIA_TYPES`,
193
+ * `MAX_INPUT_IMAGES`, `MAX_INPUT_IMAGE_BYTES`), the internal declared model's
194
+ * `imageInput` (the most input tokens one image costs on the line) and the
195
+ * grant fact `inputImages`. Every 8.1.0 message parses the same; a consumer on
196
+ * 8.1.0 or earlier refuses a declared catalogue or an authorization carrying
197
+ * what is new, so Oxy reads them before Kaana publishes them.
198
+ *
199
+ * Contract set 8.3.0 is additive: the Anthropic-Messages-compatible
200
+ * `POST /v1/messages` (`anthropicMessagesRequestSchema`,
201
+ * `anthropicMessageSchema`, `anthropicStreamEventSchema`,
202
+ * `anthropicErrorSchema`). Nothing in 8.2.0 changes, and the signed grant and
203
+ * envelope are untouched: the endpoint is served as the `responses` operation.
190
204
  */
191
205
  /** The public API's one origin and version. */
192
206
  export declare const KAANA_API_BASE_URL = "https://kaana.ai/v1";
@@ -100,7 +100,7 @@
100
100
  * change to, say, the catalogue reject every in-flight inference request; the
101
101
  * per-shape `schemaVersion` is what a message is validated against.
102
102
  */
103
- export const INFERENCE_CONTRACT_VERSION = '8.1.0';
103
+ export const INFERENCE_CONTRACT_VERSION = '8.3.0';
104
104
  /**
105
105
  * Contract set 4.0.0 (Kaana ADR 0002) is the first published as
106
106
  * `@kaana.ai/contracts`. MAJOR because the set changed hands and split in two:
@@ -187,6 +187,20 @@ export const INFERENCE_CONTRACT_VERSION = '8.1.0';
187
187
  * `transcript.interim` event it asks for, the provisional text of an input
188
188
  * item that the item's deltas and its `transcript.done` supersede. Any other
189
189
  * kind asking for interim text is refused. Nothing in 8.0.0 changes.
190
+ *
191
+ * Contract set 8.2.0 (Kaana ADR 0007) is additive: image input. The rules an
192
+ * image part answers to (`checkImageInput`, `INPUT_IMAGE_MEDIA_TYPES`,
193
+ * `MAX_INPUT_IMAGES`, `MAX_INPUT_IMAGE_BYTES`), the internal declared model's
194
+ * `imageInput` (the most input tokens one image costs on the line) and the
195
+ * grant fact `inputImages`. Every 8.1.0 message parses the same; a consumer on
196
+ * 8.1.0 or earlier refuses a declared catalogue or an authorization carrying
197
+ * what is new, so Oxy reads them before Kaana publishes them.
198
+ *
199
+ * Contract set 8.3.0 is additive: the Anthropic-Messages-compatible
200
+ * `POST /v1/messages` (`anthropicMessagesRequestSchema`,
201
+ * `anthropicMessageSchema`, `anthropicStreamEventSchema`,
202
+ * `anthropicErrorSchema`). Nothing in 8.2.0 changes, and the signed grant and
203
+ * envelope are untouched: the endpoint is served as the `responses` operation.
190
204
  */
191
205
  /** The public API's one origin and version. */
192
206
  export const KAANA_API_BASE_URL = 'https://kaana.ai/v1';