@kaana.ai/contracts 0.0.0-stage → 4.4.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.
Files changed (79) hide show
  1. package/LICENSE +673 -0
  2. package/NOTICE +21 -0
  3. package/dist/index.d.ts +29 -0
  4. package/dist/index.js +29 -0
  5. package/dist/internal/attribution.d.ts +116 -0
  6. package/dist/internal/attribution.js +82 -0
  7. package/dist/internal/canonical.d.ts +31 -0
  8. package/dist/internal/canonical.js +57 -0
  9. package/dist/internal/catalogue.d.ts +1059 -0
  10. package/dist/internal/catalogue.js +389 -0
  11. package/dist/internal/credentialControl.d.ts +329 -0
  12. package/dist/internal/credentialControl.js +256 -0
  13. package/dist/internal/decisions.d.ts +245 -0
  14. package/dist/internal/decisions.js +69 -0
  15. package/dist/internal/economics.d.ts +27 -0
  16. package/dist/internal/economics.js +26 -0
  17. package/dist/internal/embeddings.d.ts +164 -0
  18. package/dist/internal/embeddings.js +59 -0
  19. package/dist/internal/errors.d.ts +185 -0
  20. package/dist/internal/errors.js +211 -0
  21. package/dist/internal/grant.d.ts +699 -0
  22. package/dist/internal/grant.js +445 -0
  23. package/dist/internal/identifiers.d.ts +63 -0
  24. package/dist/internal/identifiers.js +65 -0
  25. package/dist/internal/privateAutoExecution.d.ts +158 -0
  26. package/dist/internal/privateAutoExecution.js +188 -0
  27. package/dist/internal/realtime.d.ts +1005 -0
  28. package/dist/internal/realtime.js +187 -0
  29. package/dist/internal/request.d.ts +1079 -0
  30. package/dist/internal/request.js +404 -0
  31. package/dist/internal/routing.d.ts +90 -0
  32. package/dist/internal/routing.js +139 -0
  33. package/dist/internal/scopedExecution.d.ts +74 -0
  34. package/dist/internal/scopedExecution.js +61 -0
  35. package/dist/internal/settlement.d.ts +205 -0
  36. package/dist/internal/settlement.js +71 -0
  37. package/dist/internal/streamEvents.d.ts +422 -0
  38. package/dist/internal/streamEvents.js +171 -0
  39. package/dist/internal/usage.d.ts +127 -0
  40. package/dist/internal/usage.js +109 -0
  41. package/dist/internal.d.ts +30 -0
  42. package/dist/internal.js +30 -0
  43. package/dist/public/audio.d.ts +347 -0
  44. package/dist/public/audio.js +236 -0
  45. package/dist/public/audioFormats.d.ts +64 -0
  46. package/dist/public/audioFormats.js +60 -0
  47. package/dist/public/catalogue.d.ts +914 -0
  48. package/dist/public/catalogue.js +516 -0
  49. package/dist/public/chatCompletions.d.ts +381 -0
  50. package/dist/public/chatCompletions.js +321 -0
  51. package/dist/public/decisions.d.ts +182 -0
  52. package/dist/public/decisions.js +257 -0
  53. package/dist/public/embeddings.d.ts +34 -0
  54. package/dist/public/embeddings.js +66 -0
  55. package/dist/public/errors.d.ts +195 -0
  56. package/dist/public/errors.js +274 -0
  57. package/dist/public/generations.d.ts +330 -0
  58. package/dist/public/generations.js +121 -0
  59. package/dist/public/identifiers.d.ts +139 -0
  60. package/dist/public/identifiers.js +160 -0
  61. package/dist/public/images.d.ts +36 -0
  62. package/dist/public/images.js +36 -0
  63. package/dist/public/messages.d.ts +297 -0
  64. package/dist/public/messages.js +251 -0
  65. package/dist/public/money.d.ts +308 -0
  66. package/dist/public/money.js +237 -0
  67. package/dist/public/policy.d.ts +19 -0
  68. package/dist/public/policy.js +20 -0
  69. package/dist/public/realtime.d.ts +2006 -0
  70. package/dist/public/realtime.js +797 -0
  71. package/dist/public/responses.d.ts +253 -0
  72. package/dist/public/responses.js +95 -0
  73. package/dist/public/streamEvents.d.ts +372 -0
  74. package/dist/public/streamEvents.js +157 -0
  75. package/dist/public/version.d.ts +184 -0
  76. package/dist/public/version.js +184 -0
  77. package/openapi.json +6967 -0
  78. package/package.json +44 -4
  79. package/README.md +0 -3
@@ -0,0 +1,445 @@
1
+ /**
2
+ * The grant: how Oxy decides who may spend, for every billable request Kaana
3
+ * serves on `https://kaana.ai/v1` (Kaana ADR 0002, decisions 3 and 4).
4
+ *
5
+ * Kaana parses and validates the public request, then asks
6
+ * `POST https://api.oxy.so/internal/kaana/v1/authorizations` with the caller's
7
+ * credential VERBATIM and content-free facts — never the prompt, the audio or
8
+ * the output. Oxy resolves the principal and lane, applies the Console policy,
9
+ * capacity and rate limits, quotes and RESERVES with its ledger, and answers
10
+ * with a grant signed under {@link GRANT_SIGNATURE_DOMAIN}. Kaana verifies the
11
+ * signature, the {@link GRANT_EXECUTION_WINDOW_MS} window and `bodySha256`, and
12
+ * executes inside the grant; without a valid grant no provider is called.
13
+ *
14
+ * The grant carries the funding model as it is today, field for field: it
15
+ * invents no policy. New in contract set 4.0.0; it replaces what the v1–v4
16
+ * envelopes signed (`authorizedRoutes`, the scoped and Private Auto bindings).
17
+ */
18
+ import { z } from 'zod';
19
+ import { powerLevelSchema, realtimeSessionKindSchema } from '../public/catalogue.js';
20
+ import { generationRecordSchema } from '../public/generations.js';
21
+ import { delegatedUserIdSchema, generationIdSchema, idempotencyKeySchema, inferenceEnvironmentSchema, inferenceRegionSchema, inferenceTimestampSchema, modelIdSchema, modelReferenceSchema, modelRevisionLabelSchema, oxyApplicationIdSchema, oxyCredentialIdSchema, requestIdSchema, } from '../public/identifiers.js';
22
+ import { requestLabelsSchema } from '../public/messages.js';
23
+ import { moneySchema } from '../public/money.js';
24
+ import { routingPolicyReferenceSchema } from '../public/policy.js';
25
+ import { inferenceScopeSchema } from './attribution.js';
26
+ import { inferenceEconomicTreatmentSchema } from './economics.js';
27
+ import { deploymentIdSchema, inferenceFundingTierSchema, inferenceProviderSlugSchema, oxyAccountIdSchema, } from './identifiers.js';
28
+ import { authorizedRouteSchema } from './routing.js';
29
+ /* -------------------------------------------------------------------------- */
30
+ /* Signing */
31
+ /* -------------------------------------------------------------------------- */
32
+ /** The signature domain of a grant. Same Ed25519 keys as the envelope (`KAANA_EDGE_PUBLIC_KEYS`). */
33
+ export const GRANT_SIGNATURE_DOMAIN = 'oxy-kaana-grant:v1';
34
+ /** How long after `issuedAt` Kaana may START executing a grant. */
35
+ export const GRANT_EXECUTION_WINDOW_MS = 60_000;
36
+ /**
37
+ * The bytes a signature covers, the scheme every signed Oxy↔Kaana body already
38
+ * uses (`internal/edgeauth`): the domain, the key id, the Unix-millisecond
39
+ * timestamp and the lowercase hex SHA-256 of the exact body bytes, joined by
40
+ * `\n` with no trailing newline. The key id, timestamp and signature travel in
41
+ * the `X-Oxy-Kaana-Key-Id`, `X-Oxy-Kaana-Timestamp` and `X-Oxy-Kaana-Signature`
42
+ * headers of the response (grant) or request (settlement) that carries the body.
43
+ */
44
+ export function signatureInput(domain, keyId, timestampMillis, bodySha256) {
45
+ if (!/^[0-9a-f]{64}$/.test(bodySha256)) {
46
+ throw new Error('bodySha256 must be 64 lowercase hex characters');
47
+ }
48
+ return [domain, keyId, String(timestampMillis), bodySha256].join('\n');
49
+ }
50
+ /** A lowercase hex SHA-256. */
51
+ const sha256HexSchema = z.string().regex(/^[0-9a-f]{64}$/, 'must be 64 lowercase hex characters');
52
+ /* -------------------------------------------------------------------------- */
53
+ /* The request Kaana makes */
54
+ /* -------------------------------------------------------------------------- */
55
+ /** What a request asks to do, as the authorization names it. */
56
+ export const grantOperationSchema = z.enum([
57
+ 'responses',
58
+ 'chat_completions',
59
+ 'embeddings',
60
+ 'audio_speech',
61
+ 'audio_transcriptions',
62
+ 'images_generations',
63
+ 'decisions',
64
+ 'realtime_session',
65
+ ]);
66
+ /**
67
+ * What the caller asked to be served: a model line, optionally pinned to a
68
+ * revision, or a power level. Absent on the request means the application's
69
+ * default from its Console policy.
70
+ */
71
+ export const grantTargetSchema = z.discriminatedUnion('kind', [
72
+ z
73
+ .object({
74
+ kind: z.literal('model'),
75
+ modelLine: modelIdSchema,
76
+ /** Present when the caller pinned `publisher/model@revision`. */
77
+ revision: modelRevisionLabelSchema.optional(),
78
+ })
79
+ .strict(),
80
+ z.object({ kind: z.literal('level'), level: powerLevelSchema }).strict(),
81
+ ]);
82
+ /**
83
+ * The content-free facts Oxy sizes a hold from. Counts, never content: the
84
+ * prompt, the audio and the output never leave Kaana.
85
+ */
86
+ export const grantRequestFactsSchema = z
87
+ .object({
88
+ /** Kaana's estimate of the input, in tokens. */
89
+ estimatedInputTokens: z.number().int().nonnegative().safe().optional(),
90
+ maxOutputTokens: z.number().int().positive().safe().optional(),
91
+ /** Speech: the characters to read, UTF-16 code units. */
92
+ inputCharacters: z.number().int().nonnegative().safe().optional(),
93
+ /**
94
+ * Transcription: the duration of the uploaded audio, in milliseconds rounded
95
+ * up, as Kaana read it from the file's own container (`internal/audioprobe`)
96
+ * before asking: the WAV data chunk, the MPEG and ADTS frames, the FLAC
97
+ * stream info, the last Ogg granule, the Matroska/WebM blocks or the MP4
98
+ * sample tables. Never estimated from the byte count: an upload whose
99
+ * duration Kaana cannot read (raw `audio/pcm`, a damaged or mislabelled
100
+ * file) is refused `invalid_request` naming `file`, and no grant is asked
101
+ * for. Settlement still charges the provider's own measurement.
102
+ */
103
+ audioInputMilliseconds: z.number().int().nonnegative().safe().optional(),
104
+ /** Embeddings: how many inputs. Images: how many images. */
105
+ embeddingInputs: z.number().int().positive().max(2048).optional(),
106
+ images: z.number().int().positive().max(10).optional(),
107
+ /** How many tools the request declares; their definitions stay in Kaana. */
108
+ declaredTools: z.number().int().nonnegative().max(128),
109
+ stream: z.boolean(),
110
+ /** Realtime: the session kind being opened. */
111
+ realtimeSessionKind: realtimeSessionKindSchema.optional(),
112
+ })
113
+ .strict();
114
+ const credentialSchema = z.string().min(1).max(8192);
115
+ /**
116
+ * `POST /internal/kaana/v1/authorizations` for an inference request. Kaana
117
+ * authenticates as a workload (Oxy ADR 0026); `credential` is the caller's
118
+ * `Authorization: Bearer` value, forwarded opaque and never stored.
119
+ */
120
+ export const inferenceAuthorizationRequestSchema = z
121
+ .object({
122
+ schemaVersion: z.literal(1),
123
+ kind: z.literal('inference'),
124
+ credential: credentialSchema,
125
+ /** Minted by Kaana on admission (`Kaana-Request-Id`). */
126
+ requestId: requestIdSchema,
127
+ operation: grantOperationSchema,
128
+ target: grantTargetSchema.optional(),
129
+ facts: grantRequestFactsSchema,
130
+ idempotencyKey: idempotencyKeySchema.optional(),
131
+ /** `X-Oxy-User-Id`: attribution only, honoured only with a service token. */
132
+ onBehalfOfUserId: delegatedUserIdSchema.optional(),
133
+ /** SHA-256 of the normalized request body; the grant is bound to it. */
134
+ bodySha256: sha256HexSchema,
135
+ /**
136
+ * For an operation under a source approval (scoped execution, Private Auto,
137
+ * the Jev classifier): `canonicalInputSha256(input)`, which Oxy compares
138
+ * with the hash the approval fixed.
139
+ */
140
+ canonicalInputSha256: sha256HexSchema.optional(),
141
+ client: z
142
+ .object({
143
+ /** The public path, e.g. `/v1/responses`. */
144
+ endpoint: z.string().min(1).max(256),
145
+ clientRequestId: z.string().min(1).max(128).optional(),
146
+ receivedAt: inferenceTimestampSchema,
147
+ labels: requestLabelsSchema.optional(),
148
+ })
149
+ .strict(),
150
+ })
151
+ .strict();
152
+ /**
153
+ * Authorization to read the catalogue (`GET /v1/models`, `/v1/power-levels`):
154
+ * no reservation, cached for 60 s per credential.
155
+ */
156
+ export const catalogueAuthorizationRequestSchema = z
157
+ .object({
158
+ schemaVersion: z.literal(1),
159
+ kind: z.literal('catalogue.read'),
160
+ credential: credentialSchema,
161
+ requestId: requestIdSchema,
162
+ })
163
+ .strict();
164
+ /**
165
+ * Authorization to read a generation record (`inference:usage:read`); Oxy
166
+ * answers with the record itself, amount included.
167
+ */
168
+ export const receiptAuthorizationRequestSchema = z
169
+ .object({
170
+ schemaVersion: z.literal(1),
171
+ kind: z.literal('receipt.read'),
172
+ credential: credentialSchema,
173
+ requestId: requestIdSchema,
174
+ generationId: generationIdSchema.optional(),
175
+ idempotencyKey: idempotencyKeySchema.optional(),
176
+ /** With `idempotencyKey`: the original delegated attribution, if any. */
177
+ onBehalfOfUserId: delegatedUserIdSchema.optional(),
178
+ })
179
+ .strict()
180
+ .superRefine((request, ctx) => {
181
+ if ((request.generationId === undefined) === (request.idempotencyKey === undefined)) {
182
+ ctx.addIssue({
183
+ code: 'custom',
184
+ path: ['generationId'],
185
+ message: 'name exactly one of generationId or idempotencyKey',
186
+ });
187
+ }
188
+ });
189
+ export const authorizationRequestSchema = z.discriminatedUnion('kind', [
190
+ inferenceAuthorizationRequestSchema,
191
+ catalogueAuthorizationRequestSchema,
192
+ receiptAuthorizationRequestSchema,
193
+ ]);
194
+ /* -------------------------------------------------------------------------- */
195
+ /* The grant Oxy signs */
196
+ /* -------------------------------------------------------------------------- */
197
+ /** How the caller authenticated, which decides who pays. */
198
+ export const grantLaneSchema = z.enum(['machine_credential', 'service_token', 'product_session']);
199
+ /**
200
+ * Who pays and who called. `billingAccountId` is the owner of the
201
+ * authenticated application, UNRESOLVED: the ledger resolves the billable
202
+ * ancestor. The end user never pays.
203
+ */
204
+ export const grantPrincipalSchema = z
205
+ .object({
206
+ billingAccountId: oxyAccountIdSchema,
207
+ applicationId: oxyApplicationIdSchema,
208
+ credentialId: oxyCredentialIdSchema,
209
+ environment: inferenceEnvironmentSchema,
210
+ scopes: z.array(inferenceScopeSchema),
211
+ lane: grantLaneSchema,
212
+ })
213
+ .strict();
214
+ /** A model line Oxy admits for a step, with the price it settles at. */
215
+ export const admittedModelLineSchema = z
216
+ .object({
217
+ modelLine: modelIdSchema,
218
+ priceVersionId: z.string().min(1).max(128),
219
+ })
220
+ .strict();
221
+ /**
222
+ * What bounds one step: the Console policy, already applied by Oxy, as values
223
+ * Kaana filters by. Kaana never re-derives a policy: it intersects what it can
224
+ * serve with `admittedModelLines` and walks keys free → credit → paid within
225
+ * `maxFundingTier`.
226
+ */
227
+ export const grantStepLimitsSchema = z
228
+ .object({
229
+ /** The most one request may cost under this step. */
230
+ maxPricePerRequest: moneySchema.optional(),
231
+ /** The dearest funding tier Kaana may spend; absent means no ceiling. */
232
+ maxFundingTier: inferenceFundingTierSchema.optional(),
233
+ /** The ONLY model lines this step may serve, each with its settling price. */
234
+ admittedModelLines: z.array(admittedModelLineSchema).min(1),
235
+ providerAllowlist: z.array(inferenceProviderSlugSchema).default([]),
236
+ providerDenylist: z.array(inferenceProviderSlugSchema).default([]),
237
+ allowedRegions: z.array(inferenceRegionSchema).default([]),
238
+ deniedRegions: z.array(inferenceRegionSchema).default([]),
239
+ requireZeroDataRetention: z.boolean(),
240
+ prohibitTrainingOnCustomerData: z.boolean(),
241
+ allowedLicenseIds: z.array(z.string().min(1).max(128)).default([]),
242
+ requireCommercialUseRights: z.boolean(),
243
+ /**
244
+ * Whether Kaana may fail over, inside this step, between providers (and
245
+ * keys) serving the SAME admitted line. `false` serves each admitted line
246
+ * with exactly one provider and one key: the cheapest admissible one, with
247
+ * no move to another provider of the line within the step. Absent means
248
+ * `true`. New in contract set 4.4.0.
249
+ */
250
+ sameModelFailover: z.boolean().default(true),
251
+ })
252
+ .strict()
253
+ .superRefine((limits, ctx) => {
254
+ const lines = limits.admittedModelLines.map((line) => line.modelLine);
255
+ if (new Set(lines).size !== lines.length) {
256
+ ctx.addIssue({
257
+ code: 'custom',
258
+ path: ['admittedModelLines'],
259
+ message: 'each model line is admitted once, at one price',
260
+ });
261
+ }
262
+ const denied = new Set(limits.providerDenylist);
263
+ if (limits.providerAllowlist.some((provider) => denied.has(provider))) {
264
+ ctx.addIssue({
265
+ code: 'custom',
266
+ path: ['providerAllowlist'],
267
+ message: 'a provider cannot be both allowed and denied',
268
+ });
269
+ }
270
+ const deniedRegions = new Set(limits.deniedRegions);
271
+ if (limits.allowedRegions.some((region) => deniedRegions.has(region))) {
272
+ ctx.addIssue({
273
+ code: 'custom',
274
+ path: ['allowedRegions'],
275
+ message: 'a region cannot be both allowed and denied',
276
+ });
277
+ }
278
+ });
279
+ /**
280
+ * One step: a target and its limits. Kaana tries the steps IN ORDER and moves
281
+ * to the next only when no admissible route of this one is left — before any
282
+ * output, never after. Alia's Pro/Ultra lane is two steps: the level under
283
+ * `maxFundingTier: 'credit'`, then `xhigh` on the pilot tuples.
284
+ */
285
+ export const grantStepSchema = z
286
+ .object({
287
+ target: grantTargetSchema,
288
+ limits: grantStepLimitsSchema,
289
+ })
290
+ .strict()
291
+ .superRefine((step, ctx) => {
292
+ const target = step.target;
293
+ if (target.kind === 'model' &&
294
+ !step.limits.admittedModelLines.some((line) => line.modelLine === target.modelLine)) {
295
+ ctx.addIssue({
296
+ code: 'custom',
297
+ path: ['limits', 'admittedModelLines'],
298
+ message: 'a step that names a model line admits it',
299
+ });
300
+ }
301
+ });
302
+ /**
303
+ * The exact route of an operation with a source approval (scoped execution,
304
+ * Private Auto, the Mention classifier): the deployment, provider, key and
305
+ * price the approval fixed, and the input it was approved for.
306
+ */
307
+ export const grantExactRouteSchema = z
308
+ .object({
309
+ approval: z.enum(['scoped_execution', 'private_auto', 'mention_classifier']),
310
+ deploymentId: deploymentIdSchema,
311
+ provider: inferenceProviderSlugSchema,
312
+ /** The provider key the approval fixed (opaque Kaana key identity). */
313
+ keyId: z.string().min(1).max(256),
314
+ /** Always revision-pinned. */
315
+ modelReference: modelReferenceSchema,
316
+ priceVersionId: z.string().min(1).max(128),
317
+ /** `canonicalInputSha256` of the approved input. */
318
+ inputSha256: sha256HexSchema,
319
+ })
320
+ .strict()
321
+ .superRefine((route, ctx) => {
322
+ if (!route.modelReference.includes('@')) {
323
+ ctx.addIssue({
324
+ code: 'custom',
325
+ path: ['modelReference'],
326
+ message: 'an exact route pins an immutable revision',
327
+ });
328
+ }
329
+ });
330
+ /** The BYOK binding, exactly as an authorized route carries it today. */
331
+ const customerProviderCredentialBinding = authorizedRouteSchema.options[0].shape.customerProviderCredential;
332
+ /**
333
+ * The signed grant: the body of a successful authorization for an inference
334
+ * operation. Kaana verifies it and executes inside it; Oxy settles against it.
335
+ */
336
+ export const grantSchema = z
337
+ .object({
338
+ schemaVersion: z.literal(1),
339
+ grantId: z.string().min(1).max(128),
340
+ requestId: requestIdSchema,
341
+ operation: grantOperationSchema,
342
+ issuedAt: inferenceTimestampSchema,
343
+ /** Kaana must start executing by this instant: `issuedAt` + 60 s. */
344
+ executeBy: inferenceTimestampSchema,
345
+ /** The request body this grant authorizes; Kaana re-hashes and compares. */
346
+ bodySha256: sha256HexSchema,
347
+ principal: grantPrincipalSchema,
348
+ /** Delegated or session user. Attribution only. */
349
+ userId: delegatedUserIdSchema.optional(),
350
+ economics: z
351
+ .object({
352
+ treatment: inferenceEconomicTreatmentSchema,
353
+ policyVersion: z.string().min(1).max(128),
354
+ relationshipId: z.string().min(1).max(128).optional(),
355
+ })
356
+ .strict(),
357
+ /** The hold. Absent for `internal_metered`, which holds nothing. */
358
+ reservation: z
359
+ .object({
360
+ reservationId: z.string().min(1).max(128),
361
+ /** The most the hold covers. Shown, never spent by Kaana. */
362
+ ceiling: moneySchema,
363
+ /** `promotional-only` for a scoped permit. */
364
+ fundingRestriction: z.literal('promotional-only').optional(),
365
+ })
366
+ .strict()
367
+ .optional(),
368
+ /** The capacity slot an internal relationship claimed (e.g. `alia-kaana`). */
369
+ capacity: z
370
+ .object({ meteredUsageId: z.string().min(1).max(128) })
371
+ .strict()
372
+ .optional(),
373
+ steps: z.array(grantStepSchema).min(1).max(8),
374
+ exactRoute: grantExactRouteSchema.optional(),
375
+ /** BYOK: the customer's own provider key. Excludes `maxFundingTier`. */
376
+ customerProviderCredential: customerProviderCredentialBinding,
377
+ idempotencyKey: idempotencyKeySchema.optional(),
378
+ client: z.object({ labels: requestLabelsSchema.optional() }).strict(),
379
+ /** The customer's routing-policy revision, as provenance. */
380
+ routingPolicy: routingPolicyReferenceSchema,
381
+ })
382
+ .strict()
383
+ .superRefine((grant, ctx) => {
384
+ const issue = (path, message) => ctx.addIssue({ code: 'custom', path, message });
385
+ const issued = Date.parse(grant.issuedAt);
386
+ const executeBy = Date.parse(grant.executeBy);
387
+ if (executeBy <= issued || executeBy - issued > GRANT_EXECUTION_WINDOW_MS) {
388
+ issue(['executeBy'], 'a grant is executable for at most 60 seconds after it is issued');
389
+ }
390
+ const metered = grant.economics.treatment === 'internal_metered';
391
+ if (metered && grant.reservation !== undefined) {
392
+ issue(['reservation'], 'an internally metered grant holds nothing');
393
+ }
394
+ if (!metered && grant.reservation === undefined) {
395
+ issue(['reservation'], 'a charged grant carries the hold it was sized against');
396
+ }
397
+ const byok = grant.customerProviderCredential !== undefined;
398
+ if (byok !== (grant.economics.treatment === 'platform_fee_only')) {
399
+ issue(['customerProviderCredential'], 'a customer provider credential is exactly the platform-fee-only treatment');
400
+ }
401
+ for (const [index, step] of grant.steps.entries()) {
402
+ if (byok && step.limits.maxFundingTier !== undefined) {
403
+ issue(['steps', index, 'limits', 'maxFundingTier'], 'a funding ceiling bounds platform keys; a customer credential is billed to the customer');
404
+ }
405
+ }
406
+ if (grant.exactRoute !== undefined) {
407
+ const [step] = grant.steps;
408
+ const route = grant.exactRoute;
409
+ const [line, revision] = route.modelReference.split('@');
410
+ if (grant.steps.length !== 1 ||
411
+ step.target.kind !== 'model' ||
412
+ step.target.modelLine !== line ||
413
+ step.target.revision !== revision ||
414
+ byok) {
415
+ issue(['exactRoute'], 'an exact route is one platform route for the one pinned model the grant serves');
416
+ }
417
+ if (step !== undefined &&
418
+ !step.limits.admittedModelLines.some((admitted) => admitted.modelLine === line && admitted.priceVersionId === route.priceVersionId)) {
419
+ issue(['exactRoute', 'priceVersionId'], 'the exact route settles at its admitted price');
420
+ }
421
+ }
422
+ });
423
+ /**
424
+ * The answer to a catalogue authorization: what the credential may see.
425
+ * Kaana filters `GET /v1/models` and `GET /v1/power-levels` with it.
426
+ */
427
+ export const catalogueAccessSchema = z
428
+ .object({
429
+ schemaVersion: z.literal(1),
430
+ kind: z.literal('catalogue.read'),
431
+ audience: z.enum(['public', 'platform_internal']),
432
+ /** The power levels the credential's policy allows; empty means every level. */
433
+ allowedLevels: z.array(powerLevelSchema).default([]),
434
+ /** Kaana caches the answer until then, at most 60 s. */
435
+ expiresAt: inferenceTimestampSchema,
436
+ })
437
+ .strict();
438
+ /** The answer to a receipt authorization: the record, amount included. */
439
+ export const receiptAccessSchema = z
440
+ .object({
441
+ schemaVersion: z.literal(1),
442
+ kind: z.literal('receipt.read'),
443
+ record: generationRecordSchema,
444
+ })
445
+ .strict();
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Identifiers only Oxy and Kaana exchange: the billing account, the opaque
3
+ * deployment and provider identities, and the funding tier.
4
+ *
5
+ * Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/identifiers.ts`) by Kaana ADR 0002.
6
+ */
7
+ import { z } from 'zod';
8
+ /**
9
+ * An Oxy account id — the account that owns the workload and is financially
10
+ * responsible for it.
11
+ *
12
+ * Branded, and that brand is load-bearing rather than decorative: it is the
13
+ * type-level half of the rule that a delegated end-user identity can never
14
+ * become the billing identity (see {@link delegatedUserIdSchema} and
15
+ * `billingPrincipalSchema`). A plain `string` — and therefore any id read out
16
+ * of a header, a JWT claim or a request body — is not assignable to it; the
17
+ * only way to obtain one is to parse a value through this schema.
18
+ */
19
+ export declare const oxyAccountIdSchema: z.core.$ZodBranded<z.ZodString, "OxyAccountId", "out">;
20
+ /**
21
+ * A routing-profile slug, e.g. `auto`, `fast`, `quality`.
22
+ *
23
+ * Deliberately refuses a `/`, so a profile can never be written in the shape of
24
+ * a model id and no caller can be confused about whether they asked for a
25
+ * concrete model or for Oxy to choose one. Modes like `auto`/`fast`/`quality`
26
+ * are profiles or product presets; they are never model objects.
27
+ */
28
+ export declare const routingProfileSlugSchema: z.ZodString;
29
+ /**
30
+ * The opaque PostgreSQL identity of one routing profile.
31
+ *
32
+ * No trim, case-folding or format heuristic: callers selecting by id must name
33
+ * the exact stored bytes. The database lookup is the authority for existence.
34
+ */
35
+ export declare const routingProfileIdSchema: z.ZodString;
36
+ /** An inference provider slug, e.g. `openai`, `bedrock`, `oxy-hosted`. */
37
+ export declare const inferenceProviderSlugSchema: z.ZodString;
38
+ /**
39
+ * A deployment/endpoint id. Opaque to customers: which concrete endpoint served
40
+ * a request is the data plane's operational detail, and only the customer-safe
41
+ * subset of it is ever attributed back (see the catalogue's serving-boundary
42
+ * rules).
43
+ */
44
+ export declare const deploymentIdSchema: z.ZodString;
45
+ /**
46
+ * The economic tier of the provider capacity that funds a request, in the
47
+ * order Kaana spends it: `free` (costs nothing), `credit` (a promotional or
48
+ * trial grant, or a prepaid balance), `paid` (ordinary metered spend).
49
+ *
50
+ * Kaana owns provider keys, their economics and which key serves (Oxy ADR
51
+ * 0038, Kaana ADR 0001); Oxy owns what a customer may spend. The tier is the
52
+ * one economic fact the two exchange: Oxy BOUNDS a route by it
53
+ * (`maxFundingTier`), and Kaana REPORTS the tier that served
54
+ * (`normalizedUsageReportSchema.fundingTier`). No key id, label or price
55
+ * travels with it.
56
+ */
57
+ export declare const inferenceFundingTierSchema: z.ZodEnum<{
58
+ free: "free";
59
+ credit: "credit";
60
+ paid: "paid";
61
+ }>;
62
+ export type OxyAccountId = z.infer<typeof oxyAccountIdSchema>;
63
+ export type InferenceFundingTier = z.infer<typeof inferenceFundingTierSchema>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Identifiers only Oxy and Kaana exchange: the billing account, the opaque
3
+ * deployment and provider identities, and the funding tier.
4
+ *
5
+ * Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/identifiers.ts`) by Kaana ADR 0002.
6
+ */
7
+ import { z } from 'zod';
8
+ /**
9
+ * An Oxy account id — the account that owns the workload and is financially
10
+ * responsible for it.
11
+ *
12
+ * Branded, and that brand is load-bearing rather than decorative: it is the
13
+ * type-level half of the rule that a delegated end-user identity can never
14
+ * become the billing identity (see {@link delegatedUserIdSchema} and
15
+ * `billingPrincipalSchema`). A plain `string` — and therefore any id read out
16
+ * of a header, a JWT claim or a request body — is not assignable to it; the
17
+ * only way to obtain one is to parse a value through this schema.
18
+ */
19
+ export const oxyAccountIdSchema = z.string().min(1).max(64).brand();
20
+ /** One path segment of a canonical model id: a lowercase, URL-safe slug. */
21
+ const SLUG_PATTERN = '[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?';
22
+ /**
23
+ * A routing-profile slug, e.g. `auto`, `fast`, `quality`.
24
+ *
25
+ * Deliberately refuses a `/`, so a profile can never be written in the shape of
26
+ * a model id and no caller can be confused about whether they asked for a
27
+ * concrete model or for Oxy to choose one. Modes like `auto`/`fast`/`quality`
28
+ * are profiles or product presets; they are never model objects.
29
+ */
30
+ export const routingProfileSlugSchema = z
31
+ .string()
32
+ .max(64)
33
+ .regex(new RegExp(`^${SLUG_PATTERN}$`), 'routing profile must be a lowercase URL-safe slug');
34
+ /**
35
+ * The opaque PostgreSQL identity of one routing profile.
36
+ *
37
+ * No trim, case-folding or format heuristic: callers selecting by id must name
38
+ * the exact stored bytes. The database lookup is the authority for existence.
39
+ */
40
+ export const routingProfileIdSchema = z.string().min(1).max(128);
41
+ /** An inference provider slug, e.g. `openai`, `bedrock`, `oxy-hosted`. */
42
+ export const inferenceProviderSlugSchema = z
43
+ .string()
44
+ .max(64)
45
+ .regex(new RegExp(`^${SLUG_PATTERN}$`), 'provider must be a lowercase URL-safe slug');
46
+ /**
47
+ * A deployment/endpoint id. Opaque to customers: which concrete endpoint served
48
+ * a request is the data plane's operational detail, and only the customer-safe
49
+ * subset of it is ever attributed back (see the catalogue's serving-boundary
50
+ * rules).
51
+ */
52
+ export const deploymentIdSchema = z.string().min(1).max(128);
53
+ /**
54
+ * The economic tier of the provider capacity that funds a request, in the
55
+ * order Kaana spends it: `free` (costs nothing), `credit` (a promotional or
56
+ * trial grant, or a prepaid balance), `paid` (ordinary metered spend).
57
+ *
58
+ * Kaana owns provider keys, their economics and which key serves (Oxy ADR
59
+ * 0038, Kaana ADR 0001); Oxy owns what a customer may spend. The tier is the
60
+ * one economic fact the two exchange: Oxy BOUNDS a route by it
61
+ * (`maxFundingTier`), and Kaana REPORTS the tier that served
62
+ * (`normalizedUsageReportSchema.fundingTier`). No key id, label or price
63
+ * travels with it.
64
+ */
65
+ export const inferenceFundingTierSchema = z.enum(['free', 'credit', 'paid']);