@kaana.ai/contracts 5.0.0 → 6.1.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.
@@ -5,8 +5,7 @@
5
5
  */
6
6
  import { z } from 'zod';
7
7
  import { realtimeOutputModalitySchema } from './audioFormats.js';
8
- import { type ModelAudioCapabilities, type ModelCapabilities, type RealtimeParameters, realtimeSessionKindSchema } from './catalogue.js';
9
- import { toolChoiceSchema } from './messages.js';
8
+ import { type ModelAudioCapabilities, type ModelCapabilities, realtimeSessionKindSchema } from './catalogue.js';
10
9
  /**
11
10
  * The most base64 characters one audio frame may carry: 49 152 bytes of audio,
12
11
  * about one second of 24 kHz mono PCM16. Bounded so no frame is large enough to
@@ -24,6 +23,13 @@ export declare const MAX_REALTIME_SESSION_DURATION_MS = 3600000;
24
23
  export declare const MAX_REALTIME_SESSION_AUDIO_BYTES = 172800000;
25
24
  /** The longest a dropped connection may be resumed after: one minute. */
26
25
  export declare const MAX_REALTIME_RESUME_WINDOW_MS = 60000;
26
+ /**
27
+ * The most characters (UTF-16 code units, a JavaScript string's `length`) the
28
+ * text of one conversation item may hold: an `input_text` part, or a
29
+ * `function_call_output`'s `output`. A client that bounds a tool's result
30
+ * reads it here, never copies it.
31
+ */
32
+ export declare const MAX_REALTIME_ITEM_TEXT_CHARS = 1048576;
27
33
  /** A client-chosen command identity, unique within one session. */
28
34
  export declare const realtimeCommandIdSchema: z.ZodString;
29
35
  /** A conversation item's identity within one session. */
@@ -38,6 +44,10 @@ export declare const realtimeResponseIdSchema: z.ZodString;
38
44
  * them: whether the model answers on its own, and whether new speech cuts it
39
45
  * off, are the two behaviours a caller most needs to have chosen, and a default
40
46
  * would choose them silently — and differently per provider.
47
+ *
48
+ * A `server_vad` that names no `silenceDurationMs` waits Kaana's
49
+ * {@link REALTIME_DEFAULT_SILENCE_DURATION_MS}, on every provider (contract set
50
+ * 6.0.0), never the provider's own default.
41
51
  */
42
52
  export declare const realtimeTurnDetectionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
43
53
  type: z.ZodLiteral<"none">;
@@ -66,13 +76,15 @@ export declare const realtimeTurnDetectionSchema: z.ZodDiscriminatedUnion<[z.Zod
66
76
  *
67
77
  * In a `conversation` it is served only by a model whose catalogue entry says
68
78
  * so (`capabilities.realtime.inputAudioTranscription`, contract set 4.1.0),
69
- * and each transcribed turn arrives as `transcript.delta` / `transcript.done`
70
- * with `source: 'input_audio'`. What it costs is settled with the session:
71
- * where the provider bills the transcription apart from the session model, as
72
- * `input_transcription_milliseconds` (see `USAGE_UNITS`); where the session's
73
- * own price includes it, as nothing more.
74
- *
75
- * `language`, when given, is one of the model's published `audio.languages`.
79
+ * and each transcribed turn arrives as deltas that only append and EXACTLY
80
+ * ONE `transcript.done` with `source: 'input_audio'`, the final transcript of
81
+ * that item (contract set 6.0.0), whatever its provider sends. Both hold only
82
+ * the words of that item's own audio, never an earlier item's
83
+ * (`@kaana.ai/contracts` 6.1.0), even where the provider transcribes a turn
84
+ * the caller went on with as one text. What it costs is settled with the
85
+ * session: where the provider bills the transcription
86
+ * apart from the session model, as `input_transcription_milliseconds` (see
87
+ * `USAGE_UNITS`); where the session's own price includes it, as nothing more.
76
88
  */
77
89
  export declare const realtimeInputTranscriptionSchema: z.ZodObject<{
78
90
  language: z.ZodOptional<z.ZodString>;
@@ -89,8 +101,8 @@ export declare const realtimeTranslationSchema: z.ZodObject<{
89
101
  export declare const realtimeToolDefinitionSchema: z.ZodObject<{
90
102
  type: z.ZodLiteral<"function">;
91
103
  name: z.ZodString;
92
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
93
104
  description: z.ZodOptional<z.ZodString>;
105
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
94
106
  }, z.core.$strict>;
95
107
  /**
96
108
  * A session's full configuration, as signed at open and as echoed back by
@@ -148,12 +160,12 @@ export declare const realtimeSessionConfigSchema: z.ZodObject<{
148
160
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
149
161
  type: z.ZodLiteral<"function">;
150
162
  name: z.ZodString;
151
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
152
163
  description: z.ZodOptional<z.ZodString>;
164
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
153
165
  }, z.core.$strict>>>;
154
166
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
155
- none: "none";
156
167
  auto: "auto";
168
+ none: "none";
157
169
  required: "required";
158
170
  }>, z.ZodObject<{
159
171
  type: z.ZodLiteral<"function">;
@@ -202,12 +214,12 @@ export declare const realtimeSessionConfigUpdateSchema: z.ZodObject<{
202
214
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
203
215
  type: z.ZodLiteral<"function">;
204
216
  name: z.ZodString;
205
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
206
217
  description: z.ZodOptional<z.ZodString>;
218
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
207
219
  }, z.core.$strict>>>;
208
220
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
209
- none: "none";
210
221
  auto: "auto";
222
+ none: "none";
211
223
  required: "required";
212
224
  }>, z.ZodObject<{
213
225
  type: z.ZodLiteral<"function">;
@@ -318,8 +330,8 @@ export declare const realtimeResponseParametersSchema: z.ZodObject<{
318
330
  }>>>;
319
331
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
320
332
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
321
- none: "none";
322
333
  auto: "auto";
334
+ none: "none";
323
335
  required: "required";
324
336
  }>, z.ZodObject<{
325
337
  type: z.ZodLiteral<"function">;
@@ -361,12 +373,12 @@ export declare const realtimeSessionUpdateCommandSchema: z.ZodObject<{
361
373
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
362
374
  type: z.ZodLiteral<"function">;
363
375
  name: z.ZodString;
364
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
365
376
  description: z.ZodOptional<z.ZodString>;
377
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
366
378
  }, z.core.$strict>>>;
367
379
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
368
- none: "none";
369
380
  auto: "auto";
381
+ none: "none";
370
382
  required: "required";
371
383
  }>, z.ZodObject<{
372
384
  type: z.ZodLiteral<"function">;
@@ -479,8 +491,8 @@ export declare const realtimeResponseCreateCommandSchema: z.ZodObject<{
479
491
  }>>>;
480
492
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
481
493
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
482
- none: "none";
483
494
  auto: "auto";
495
+ none: "none";
484
496
  required: "required";
485
497
  }>, z.ZodObject<{
486
498
  type: z.ZodLiteral<"function">;
@@ -551,12 +563,12 @@ export declare const realtimeClientCommandSchema: z.ZodDiscriminatedUnion<[z.Zod
551
563
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
552
564
  type: z.ZodLiteral<"function">;
553
565
  name: z.ZodString;
554
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
555
566
  description: z.ZodOptional<z.ZodString>;
567
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
556
568
  }, z.core.$strict>>>;
557
569
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
558
- none: "none";
559
570
  auto: "auto";
571
+ none: "none";
560
572
  required: "required";
561
573
  }>, z.ZodObject<{
562
574
  type: z.ZodLiteral<"function">;
@@ -657,8 +669,8 @@ export declare const realtimeClientCommandSchema: z.ZodDiscriminatedUnion<[z.Zod
657
669
  }>>>;
658
670
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
659
671
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
660
- none: "none";
661
672
  auto: "auto";
673
+ none: "none";
662
674
  required: "required";
663
675
  }>, z.ZodObject<{
664
676
  type: z.ZodLiteral<"function">;
@@ -735,12 +747,12 @@ export declare const realtimeSessionUpdatedEventSchema: z.ZodObject<{
735
747
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
736
748
  type: z.ZodLiteral<"function">;
737
749
  name: z.ZodString;
738
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
739
750
  description: z.ZodOptional<z.ZodString>;
751
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
740
752
  }, z.core.$strict>>>;
741
753
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
742
- none: "none";
743
754
  auto: "auto";
755
+ none: "none";
744
756
  required: "required";
745
757
  }>, z.ZodObject<{
746
758
  type: z.ZodLiteral<"function">;
@@ -1079,6 +1091,16 @@ export declare const realtimeOpenLimitsSchema: z.ZodObject<{
1079
1091
  * frame closes the connection before it is read.
1080
1092
  */
1081
1093
  export declare const MAX_REALTIME_OPEN_FRAME_BYTES: number;
1094
+ /**
1095
+ * The silence, in milliseconds, after which `server_vad` ends a caller's turn
1096
+ * when the session names none (contract set 6.0.0, Kaana ADR 0003). Kaana
1097
+ * sends it to every provider, so a turn ends the same way whichever serves
1098
+ * the session. Measured on x-ai/grok-voice-3.0 (2026-10-16): with xAI's own
1099
+ * default a phrase with pauses of 0.56 s and 0.79 s was cut into three
1100
+ * responses, two cancelled, the model speaking before the phrase ended; at
1101
+ * 500 ms it was one response, answered 490–576 ms after the phrase.
1102
+ */
1103
+ export declare const REALTIME_DEFAULT_SILENCE_DURATION_MS = 500;
1082
1104
  /**
1083
1105
  * The first frame a client sends on `GET /v1/realtime?model=…` to OPEN a
1084
1106
  * session (a reconnection instead sends `session.resume`). One grant covers the
@@ -1143,12 +1165,12 @@ export declare const realtimeSessionOpenSchema: z.ZodObject<{
1143
1165
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
1144
1166
  type: z.ZodLiteral<"function">;
1145
1167
  name: z.ZodString;
1146
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1147
1168
  description: z.ZodOptional<z.ZodString>;
1169
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1148
1170
  }, z.core.$strict>>>;
1149
1171
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
1150
- none: "none";
1151
1172
  auto: "auto";
1173
+ none: "none";
1152
1174
  required: "required";
1153
1175
  }>, z.ZodObject<{
1154
1176
  type: z.ZodLiteral<"function">;
@@ -1192,28 +1214,6 @@ export declare function checkRealtimeSessionOpen(open: z.infer<typeof realtimeSe
1192
1214
  readonly capabilities: ModelCapabilities;
1193
1215
  readonly audio?: ModelAudioCapabilities;
1194
1216
  }): RealtimeOpenIssue[];
1195
- /**
1196
- * The part of a session configuration, of a `session.update` or of a
1197
- * `response.create` that a model's {@link RealtimeParameters} govern.
1198
- */
1199
- export interface RealtimeGovernedFields {
1200
- readonly outputModalities?: readonly RealtimeOutputModality[];
1201
- readonly turnDetection?: RealtimeTurnDetection;
1202
- readonly inputAudioTranscription?: {
1203
- readonly prompt?: string;
1204
- };
1205
- readonly toolChoice?: z.infer<typeof toolChoiceSchema>;
1206
- readonly maxOutputTokens?: number;
1207
- }
1208
- /**
1209
- * Every field that `parameters` do not serve, each with the `param` Kaana's
1210
- * refusal names (`prefix` is `config` for an open or a `session.update`,
1211
- * `response` for a `response.create`), in the order Kaana checks them. It is
1212
- * exactly Kaana's own check (Go's `RealtimeParameters.Issues`, held to the
1213
- * same vectors), so a client that builds its session from a model's
1214
- * parameters, or filters models with this, is never refused for one.
1215
- */
1216
- export declare function realtimeParameterIssues(prefix: 'config' | 'response', fields: RealtimeGovernedFields, parameters: RealtimeParameters): RealtimeOpenIssue[];
1217
1217
  /**
1218
1218
  * The session opened: the revision-pinned model, fixed for the rest of the
1219
1219
  * session, and the effective configuration and limits. The signed lane's
@@ -1275,12 +1275,12 @@ export declare const realtimePublicSessionCreatedEventSchema: z.ZodObject<{
1275
1275
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
1276
1276
  type: z.ZodLiteral<"function">;
1277
1277
  name: z.ZodString;
1278
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1279
1278
  description: z.ZodOptional<z.ZodString>;
1279
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1280
1280
  }, z.core.$strict>>>;
1281
1281
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
1282
- none: "none";
1283
1282
  auto: "auto";
1283
+ none: "none";
1284
1284
  required: "required";
1285
1285
  }>, z.ZodObject<{
1286
1286
  type: z.ZodLiteral<"function">;
@@ -1512,12 +1512,12 @@ export declare const realtimePublicServerEventSchema: z.ZodDiscriminatedUnion<[z
1512
1512
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
1513
1513
  type: z.ZodLiteral<"function">;
1514
1514
  name: z.ZodString;
1515
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1516
1515
  description: z.ZodOptional<z.ZodString>;
1516
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1517
1517
  }, z.core.$strict>>>;
1518
1518
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
1519
- none: "none";
1520
1519
  auto: "auto";
1520
+ none: "none";
1521
1521
  required: "required";
1522
1522
  }>, z.ZodObject<{
1523
1523
  type: z.ZodLiteral<"function">;
@@ -1588,12 +1588,12 @@ export declare const realtimePublicServerEventSchema: z.ZodDiscriminatedUnion<[z
1588
1588
  tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
1589
1589
  type: z.ZodLiteral<"function">;
1590
1590
  name: z.ZodString;
1591
- parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1592
1591
  description: z.ZodOptional<z.ZodString>;
1592
+ parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1593
1593
  }, z.core.$strict>>>;
1594
1594
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
1595
- none: "none";
1596
1595
  auto: "auto";
1596
+ none: "none";
1597
1597
  required: "required";
1598
1598
  }>, z.ZodObject<{
1599
1599
  type: z.ZodLiteral<"function">;
@@ -28,6 +28,13 @@ export const MAX_REALTIME_SESSION_DURATION_MS = 3_600_000;
28
28
  export const MAX_REALTIME_SESSION_AUDIO_BYTES = 172_800_000;
29
29
  /** The longest a dropped connection may be resumed after: one minute. */
30
30
  export const MAX_REALTIME_RESUME_WINDOW_MS = 60_000;
31
+ /**
32
+ * The most characters (UTF-16 code units, a JavaScript string's `length`) the
33
+ * text of one conversation item may hold: an `input_text` part, or a
34
+ * `function_call_output`'s `output`. A client that bounds a tool's result
35
+ * reads it here, never copies it.
36
+ */
37
+ export const MAX_REALTIME_ITEM_TEXT_CHARS = 1_048_576;
31
38
  /** A client-chosen command identity, unique within one session. */
32
39
  export const realtimeCommandIdSchema = z.string().min(1).max(128);
33
40
  /** A conversation item's identity within one session. */
@@ -49,6 +56,10 @@ const milliseconds = z.number().int().nonnegative().safe();
49
56
  * them: whether the model answers on its own, and whether new speech cuts it
50
57
  * off, are the two behaviours a caller most needs to have chosen, and a default
51
58
  * would choose them silently — and differently per provider.
59
+ *
60
+ * A `server_vad` that names no `silenceDurationMs` waits Kaana's
61
+ * {@link REALTIME_DEFAULT_SILENCE_DURATION_MS}, on every provider (contract set
62
+ * 6.0.0), never the provider's own default.
52
63
  */
53
64
  export const realtimeTurnDetectionSchema = z.discriminatedUnion('type', [
54
65
  z.object({ type: z.literal('none') }).strict(),
@@ -78,18 +89,25 @@ export const realtimeTurnDetectionSchema = z.discriminatedUnion('type', [
78
89
  *
79
90
  * In a `conversation` it is served only by a model whose catalogue entry says
80
91
  * so (`capabilities.realtime.inputAudioTranscription`, contract set 4.1.0),
81
- * and each transcribed turn arrives as `transcript.delta` / `transcript.done`
82
- * with `source: 'input_audio'`. What it costs is settled with the session:
83
- * where the provider bills the transcription apart from the session model, as
84
- * `input_transcription_milliseconds` (see `USAGE_UNITS`); where the session's
85
- * own price includes it, as nothing more.
86
- *
87
- * `language`, when given, is one of the model's published `audio.languages`.
92
+ * and each transcribed turn arrives as deltas that only append and EXACTLY
93
+ * ONE `transcript.done` with `source: 'input_audio'`, the final transcript of
94
+ * that item (contract set 6.0.0), whatever its provider sends. Both hold only
95
+ * the words of that item's own audio, never an earlier item's
96
+ * (`@kaana.ai/contracts` 6.1.0), even where the provider transcribes a turn
97
+ * the caller went on with as one text. What it costs is settled with the
98
+ * session: where the provider bills the transcription
99
+ * apart from the session model, as `input_transcription_milliseconds` (see
100
+ * `USAGE_UNITS`); where the session's own price includes it, as nothing more.
88
101
  */
89
102
  export const realtimeInputTranscriptionSchema = z
90
103
  .object({
91
- /** A BCP 47 tag, when the caller knows the language. */
104
+ /**
105
+ * The client's own BCP 47 tag, when it knows the language. Kaana sends
106
+ * the provider its spelling of that language, or none for the model to
107
+ * detect; it is never refused.
108
+ */
92
109
  language: z.string().min(2).max(35).optional(),
110
+ /** A hint — vocabulary or context — used where the model takes one. */
93
111
  prompt: z.string().max(4_096).optional(),
94
112
  })
95
113
  .strict();
@@ -180,7 +198,9 @@ export const realtimeSessionLimitsSchema = z
180
198
  * item echoing it carries only the format and, once known, the transcript.
181
199
  */
182
200
  export const realtimeContentPartSchema = z.discriminatedUnion('type', [
183
- z.object({ type: z.literal('input_text'), text: z.string().max(1_048_576) }).strict(),
201
+ z
202
+ .object({ type: z.literal('input_text'), text: z.string().max(MAX_REALTIME_ITEM_TEXT_CHARS) })
203
+ .strict(),
184
204
  z
185
205
  .object({
186
206
  type: z.literal('input_audio'),
@@ -229,7 +249,7 @@ export const realtimeConversationItemSchema = z
229
249
  type: z.literal('function_call_output'),
230
250
  itemId: realtimeItemIdSchema.optional(),
231
251
  callId: z.string().min(1).max(128),
232
- output: z.string().max(1_048_576),
252
+ output: z.string().max(MAX_REALTIME_ITEM_TEXT_CHARS),
233
253
  })
234
254
  .strict(),
235
255
  ])
@@ -598,6 +618,16 @@ export const realtimeOpenLimitsSchema = realtimeSessionLimitsSchema.partial().st
598
618
  * frame closes the connection before it is read.
599
619
  */
600
620
  export const MAX_REALTIME_OPEN_FRAME_BYTES = 1024 * 1024;
621
+ /**
622
+ * The silence, in milliseconds, after which `server_vad` ends a caller's turn
623
+ * when the session names none (contract set 6.0.0, Kaana ADR 0003). Kaana
624
+ * sends it to every provider, so a turn ends the same way whichever serves
625
+ * the session. Measured on x-ai/grok-voice-3.0 (2026-10-16): with xAI's own
626
+ * default a phrase with pauses of 0.56 s and 0.79 s was cut into three
627
+ * responses, two cancelled, the model speaking before the phrase ended; at
628
+ * 500 ms it was one response, answered 490–576 ms after the phrase.
629
+ */
630
+ export const REALTIME_DEFAULT_SILENCE_DURATION_MS = 500;
601
631
  /**
602
632
  * The first frame a client sends on `GET /v1/realtime?model=…` to OPEN a
603
633
  * session (a reconnection instead sends `session.resume`). One grant covers the
@@ -693,84 +723,6 @@ export function checkRealtimeSessionOpen(open, model) {
693
723
  message: 'this model does not transcribe its input inside a conversation',
694
724
  });
695
725
  }
696
- if (transcription?.language !== undefined &&
697
- audio !== undefined &&
698
- !audio.languages.includes(transcription.language)) {
699
- issues.push({
700
- param: 'config.inputAudioTranscription.language',
701
- message: `this model does not declare the language ${transcription.language}` +
702
- (audio.detectsLanguage === true ? '; omit it to let the model detect the language' : ''),
703
- });
704
- }
705
- if (realtime.parameters !== undefined) {
706
- issues.push(...realtimeParameterIssues('config', config, realtime.parameters));
707
- }
708
- return issues;
709
- }
710
- function toolChoiceKind(choice) {
711
- return typeof choice === 'string' ? choice : 'function';
712
- }
713
- function sameModalities(a, b) {
714
- const left = [...a].sort();
715
- const right = [...b].sort();
716
- return left.length === right.length && left.every((value, index) => value === right[index]);
717
- }
718
- /**
719
- * Every field that `parameters` do not serve, each with the `param` Kaana's
720
- * refusal names (`prefix` is `config` for an open or a `session.update`,
721
- * `response` for a `response.create`), in the order Kaana checks them. It is
722
- * exactly Kaana's own check (Go's `RealtimeParameters.Issues`, held to the
723
- * same vectors), so a client that builds its session from a model's
724
- * parameters, or filters models with this, is never refused for one.
725
- */
726
- export function realtimeParameterIssues(prefix, fields, parameters) {
727
- const issues = [];
728
- const issue = (param, message) => issues.push({ param: `${prefix}.${param}`, message });
729
- if (fields.outputModalities !== undefined &&
730
- !parameters.outputModalities.some((set) => sameModalities(set, fields.outputModalities ?? []))) {
731
- issue('outputModalities', `this model produces ${parameters.outputModalities.map((set) => `[${set.join(' ')}]`).join(' or ')} in one response`);
732
- }
733
- const detection = fields.turnDetection;
734
- if (detection !== undefined) {
735
- const served = parameters.turnDetection.find((candidate) => candidate.type === detection.type);
736
- if (served === undefined) {
737
- issue('turnDetection', `this model has no ${detection.type} turn detection`);
738
- }
739
- else {
740
- if (served.fixedResponses && detection.type !== 'none') {
741
- if (!detection.createResponse) {
742
- issue('turnDetection.createResponse', `this model's ${detection.type} always answers the turn it detected; createResponse must be true`);
743
- }
744
- if (!detection.interruptResponse) {
745
- issue('turnDetection.interruptResponse', `this model's ${detection.type} lets the caller's speech interrupt an answer; interruptResponse must be true`);
746
- }
747
- }
748
- if (detection.type === 'server_vad') {
749
- const threshold = served.threshold;
750
- if (threshold !== undefined &&
751
- detection.threshold !== undefined &&
752
- (detection.threshold < threshold.min || detection.threshold > threshold.max)) {
753
- issue('turnDetection.threshold', `this model's ${detection.type} threshold is ${threshold.min} to ${threshold.max}`);
754
- }
755
- }
756
- }
757
- }
758
- if (fields.inputAudioTranscription?.prompt !== undefined &&
759
- !parameters.inputTranscriptionPrompt) {
760
- issue('inputAudioTranscription.prompt', "this model's input transcription takes no prompt");
761
- }
762
- if (fields.toolChoice !== undefined &&
763
- !parameters.toolChoice.includes(toolChoiceKind(fields.toolChoice))) {
764
- issue('toolChoice', `this model serves the tool choices ${parameters.toolChoice.join(', ')}`);
765
- }
766
- if (fields.maxOutputTokens !== undefined) {
767
- if (parameters.maxOutputTokens === undefined) {
768
- issue('maxOutputTokens', 'this model has no output token cap');
769
- }
770
- else if (fields.maxOutputTokens > parameters.maxOutputTokens) {
771
- issue('maxOutputTokens', `this model caps a response at most at ${parameters.maxOutputTokens} output tokens`);
772
- }
773
- }
774
726
  return issues;
775
727
  }
776
728
  /* -------------------------------------------------------------------------- */
@@ -118,8 +118,8 @@ export declare const responsesRequestSchema: z.ZodObject<{
118
118
  strict: z.ZodOptional<z.ZodBoolean>;
119
119
  }, z.core.$strict>>>;
120
120
  toolChoice: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
121
- none: "none";
122
121
  auto: "auto";
122
+ none: "none";
123
123
  required: "required";
124
124
  }>, z.ZodObject<{
125
125
  type: z.ZodLiteral<"function">;
@@ -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 = "5.0.0";
103
+ export declare const INFERENCE_CONTRACT_VERSION = "6.0.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:
@@ -155,6 +155,21 @@ export declare const INFERENCE_CONTRACT_VERSION = "5.0.0";
155
155
  * `MAX_REALTIME_TEXT_ITEMS`, and a transcription's `timestamps`, `segments`
156
156
  * and `words`. A request that parsed under 4.5.0 and that Kaana served
157
157
  * still parses and is served the same.
158
+ *
159
+ * Contract set 6.0.0 (Kaana ADR 0003) is one canonical API: no provider's
160
+ * variant reaches a client. Kaana translates or emulates every value the
161
+ * contract offers on every model, so the catalogue publishes product
162
+ * capabilities only. MAJOR because it REMOVES what 5.0.0 published of each
163
+ * provider's wire: `capabilities.realtime.parameters`
164
+ * (`realtimeParametersSchema`, `realtimeTurnDetectionParametersSchema`,
165
+ * `numberRangeSchema`, `realtimeToolChoiceKindSchema`),
166
+ * `realtimeParameterIssues`, `audio.detectsLanguage` and
167
+ * `audio.transcriptionPrompt`; and `checkTranscriptionRequest` takes the
168
+ * upload's media type alone, since a language or a prompt is never refused.
169
+ * Additive: a model's `pricePerMinute`, exactly one input `transcript.done`
170
+ * per item, and `REALTIME_DEFAULT_SILENCE_DURATION_MS`, the silence a
171
+ * `server_vad` that names none waits on every provider. A client on 5.0.0
172
+ * parses every 6.0.0 response.
158
173
  */
159
174
  /** The public API's one origin and version. */
160
175
  export declare const KAANA_API_BASE_URL = "https://kaana.ai/v1";
@@ -163,9 +178,8 @@ export declare const KAANA_REQUEST_ID_HEADER = "Kaana-Request-Id";
163
178
  /**
164
179
  * The header that carries {@link INFERENCE_CONTRACT_VERSION}: on every
165
180
  * response, the set Kaana serves; on a request (`@kaana.ai/sdk` 2.0.0 and
166
- * later), the set the client parses. The catalogue's leaves are strict, so
167
- * Kaana renders `GET /v1/models` in the shape of the client's set: a client
168
- * that states none is served the 4.x shape, without what set 5.0.0 added.
181
+ * later), the set the client parses. Every set since 4.0.0 parses the
182
+ * catalogue Kaana serves.
169
183
  */
170
184
  export declare const KAANA_CONTRACT_VERSION_HEADER = "Kaana-Contract-Version";
171
185
  /**
@@ -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 = '5.0.0';
103
+ export const INFERENCE_CONTRACT_VERSION = '6.0.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:
@@ -155,6 +155,21 @@ export const INFERENCE_CONTRACT_VERSION = '5.0.0';
155
155
  * `MAX_REALTIME_TEXT_ITEMS`, and a transcription's `timestamps`, `segments`
156
156
  * and `words`. A request that parsed under 4.5.0 and that Kaana served
157
157
  * still parses and is served the same.
158
+ *
159
+ * Contract set 6.0.0 (Kaana ADR 0003) is one canonical API: no provider's
160
+ * variant reaches a client. Kaana translates or emulates every value the
161
+ * contract offers on every model, so the catalogue publishes product
162
+ * capabilities only. MAJOR because it REMOVES what 5.0.0 published of each
163
+ * provider's wire: `capabilities.realtime.parameters`
164
+ * (`realtimeParametersSchema`, `realtimeTurnDetectionParametersSchema`,
165
+ * `numberRangeSchema`, `realtimeToolChoiceKindSchema`),
166
+ * `realtimeParameterIssues`, `audio.detectsLanguage` and
167
+ * `audio.transcriptionPrompt`; and `checkTranscriptionRequest` takes the
168
+ * upload's media type alone, since a language or a prompt is never refused.
169
+ * Additive: a model's `pricePerMinute`, exactly one input `transcript.done`
170
+ * per item, and `REALTIME_DEFAULT_SILENCE_DURATION_MS`, the silence a
171
+ * `server_vad` that names none waits on every provider. A client on 5.0.0
172
+ * parses every 6.0.0 response.
158
173
  */
159
174
  /** The public API's one origin and version. */
160
175
  export const KAANA_API_BASE_URL = 'https://kaana.ai/v1';
@@ -163,9 +178,8 @@ export const KAANA_REQUEST_ID_HEADER = 'Kaana-Request-Id';
163
178
  /**
164
179
  * The header that carries {@link INFERENCE_CONTRACT_VERSION}: on every
165
180
  * response, the set Kaana serves; on a request (`@kaana.ai/sdk` 2.0.0 and
166
- * later), the set the client parses. The catalogue's leaves are strict, so
167
- * Kaana renders `GET /v1/models` in the shape of the client's set: a client
168
- * that states none is served the 4.x shape, without what set 5.0.0 added.
181
+ * later), the set the client parses. Every set since 4.0.0 parses the
182
+ * catalogue Kaana serves.
169
183
  */
170
184
  export const KAANA_CONTRACT_VERSION_HEADER = 'Kaana-Contract-Version';
171
185
  /**