| /** |
| * Maka-owned provider-boundary model protocol types (#1381 slice 1). |
| * |
| * This module is the single seam where the provider-boundary message/value |
| * contract is *owned* by Maka. Runtime consumers (history projection, |
| * compaction, context budget, request shape, tool output, the adapter |
| * itself) import these names from here — never from `ai` — so AI SDK type |
| * changes no longer propagate past the `ModelAdapter` boundary. |
| * |
| * The shapes defined here are structurally equivalent to the AI SDK 7 |
| * `@ai-sdk/provider-utils` message/value unions, but they are **Maka-owned**: |
| * the generated declaration of this module imports nothing from `ai` or |
| * `@ai-sdk/*`. Lowering Maka messages to AI SDK types, and normalizing AI SDK |
| * responses back to Maka types, happens only inside `ModelAdapter` |
| * (`model-adapter.ts`); that module is the lone runtime file permitted to |
| * import the SDK protocol types for the lowering/normalization cast. |
| * |
| * Schema helpers (`jsonSchema` / `zodSchema`) and SDK value imports |
| * (`generateText`, `RetryError`, ...) remain local implementation details or |
| * follow-up work (RFC #1381 follow-up Q2/Q4) and are deliberately out of scope |
| * for this seam. |
| */ |
| |
| import type { CacheMissInputSource } from '@maka/core/usage-stats/types'; |
| |
| // --------------------------------------------------------------------------- |
| // JSON value contract |
| // --------------------------------------------------------------------------- |
| |
| export type JSONValue = null | string | number | boolean | JSONObject | JSONArray; |
| export type JSONObject = { [key: string]: JSONValue | undefined }; |
| export type JSONArray = JSONValue[]; |
| |
| // --------------------------------------------------------------------------- |
| // Provider metadata / options contract |
| // --------------------------------------------------------------------------- |
| |
| /** |
| * Provider-specific metadata/option bag, keyed by provider name. Mirrors the |
| * AI SDK `SharedV4ProviderOptions` / `SharedV4ProviderMetadata` shape so |
| * pass-through values stay structurally compatible across the lowering cast. |
| */ |
| export type ProviderOptions = Record<string, JSONObject>; |
| export type ProviderMetadata = Record<string, JSONObject>; |
| |
| /** |
| * A mapping of provider names to provider-specific file identifiers. A |
| * provider reference identifies a file across providers without re-uploading. |
| * The `type?: never` constraint excludes any object that has a `type` property, |
| * so a provider reference cannot be confused with a tagged file-data shape |
| * (`{ type: 'data', data }` / `{ type: 'reference', reference }`) when both |
| * appear in the same union. |
| */ |
| export type ProviderReference = Record<string, string> & { type?: never }; |
| |
| // --------------------------------------------------------------------------- |
| // File / data content contract |
| // --------------------------------------------------------------------------- |
| |
| export type DataContent = string | Uint8Array | ArrayBuffer | Buffer; |
| |
| export interface FileDataData { |
| type: 'data'; |
| data: DataContent; |
| } |
| export interface FileDataUrl { |
| type: 'url'; |
| url: URL; |
| } |
| export interface FileDataReference { |
| type: 'reference'; |
| reference: ProviderReference; |
| } |
| export interface FileDataText { |
| type: 'text'; |
| text: string; |
| } |
| export type FileData = FileDataData | FileDataUrl | FileDataReference | FileDataText; |
| |
| // --------------------------------------------------------------------------- |
| // Content part contract |
| // --------------------------------------------------------------------------- |
| |
| export interface TextPart { |
| type: 'text'; |
| text: string; |
| providerOptions?: ProviderOptions; |
| } |
| |
| export interface ImagePart { |
| type: 'image'; |
| image: DataContent | URL | ProviderReference; |
| mediaType?: string; |
| providerOptions?: ProviderOptions; |
| } |
| |
| export interface FilePart { |
| type: 'file'; |
| data: FileData | DataContent | URL | ProviderReference; |
| filename?: string; |
| mediaType: string; |
| providerOptions?: ProviderOptions; |
| } |
| |
| /** |
| * Explicit native-audio intent. ModelAdapter lowers this only for a route that |
| * was admitted by the voice capability router; a generic FilePart is never |
| * promoted into audio by MIME sniffing. |
| */ |
| export interface AudioPart { |
| type: 'audio'; |
| data: DataContent; |
| mediaType: string; |
| format: 'wav' | 'webm' | 'mp3' | 'm4a'; |
| durationMs: number; |
| transcript?: string; |
| retention: 'operation_memory'; |
| } |
| |
| export interface ReasoningPart { |
| type: 'reasoning'; |
| text: string; |
| providerOptions?: ProviderOptions; |
| } |
| |
| export interface CustomPart { |
| type: 'custom'; |
| kind: `${string}.${string}`; |
| providerOptions?: ProviderOptions; |
| } |
| |
| export interface ReasoningFilePart { |
| type: 'reasoning-file'; |
| data: FileDataData | FileDataUrl | DataContent | URL; |
| mediaType: string; |
| providerOptions?: ProviderOptions; |
| } |
| |
| export interface ToolCallPart { |
| type: 'tool-call'; |
| toolCallId: string; |
| toolName: string; |
| input: unknown; |
| providerOptions?: ProviderOptions; |
| providerExecuted?: boolean; |
| } |
| |
| export type ToolApprovalRequest = { |
| type: 'tool-approval-request'; |
| approvalId: string; |
| toolCallId: string; |
| isAutomatic?: boolean; |
| signature?: string; |
| }; |
| |
| export type ToolApprovalResponse = { |
| type: 'tool-approval-response'; |
| approvalId: string; |
| approved: boolean; |
| reason?: string; |
| providerExecuted?: boolean; |
| }; |
| |
| // --------------------------------------------------------------------------- |
| // Tool result output contract |
| // --------------------------------------------------------------------------- |
| |
| /** |
| * One part of a `content`-shaped tool result output. Includes the legacy |
| * `file-data` / `file-url` / `file-id` / `file-reference` / `image-*` arms so |
| * any provider-emitted tool result stays structurally compatible with the |
| * Maka-owned union. |
| */ |
| export type ToolResultContentPart = |
| | { type: 'text'; text: string; providerOptions?: ProviderOptions } |
| | { |
| type: 'file'; |
| data: FileData; |
| mediaType: string; |
| filename?: string; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', data: { type: 'data', data } }` */ |
| type: 'file-data'; |
| data: string; |
| mediaType: string; |
| filename?: string; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', data: { type: 'url', url } }` */ |
| type: 'file-url'; |
| url: string; |
| mediaType?: string; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', data: { type: 'reference', reference } }` */ |
| type: 'file-id'; |
| fileId: string | Record<string, string>; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', data: { type: 'reference', reference } }` */ |
| type: 'file-reference'; |
| providerReference: ProviderReference; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', mediaType: 'image', data }` */ |
| type: 'image-data'; |
| data: string; |
| mediaType: string; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', mediaType: 'image', data: { type: 'url', url } }` */ |
| type: 'image-url'; |
| url: string; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', data: { type: 'reference', reference } }` */ |
| type: 'image-file-id'; |
| fileId: string | Record<string, string>; |
| providerOptions?: ProviderOptions; |
| } |
| | { |
| /** @deprecated use `{ type: 'file', data: { type: 'reference', reference } }` */ |
| type: 'image-file-reference'; |
| providerReference: ProviderReference; |
| providerOptions?: ProviderOptions; |
| } |
| | { type: 'custom'; providerOptions?: ProviderOptions }; |
| |
| export type ToolResultOutput = |
| | { type: 'text'; value: string; providerOptions?: ProviderOptions } |
| | { type: 'json'; value: JSONValue; providerOptions?: ProviderOptions } |
| | { type: 'execution-denied'; reason?: string; providerOptions?: ProviderOptions } |
| | { type: 'error-text'; value: string; providerOptions?: ProviderOptions } |
| | { type: 'error-json'; value: JSONValue; providerOptions?: ProviderOptions } |
| | { type: 'content'; value: ToolResultContentPart[] }; |
| |
| /** |
| * Maka-owned provider-facing tool contract. The schema stays opaque because |
| * schema construction is a local implementation detail. Execution belongs to |
| * ToolRuntime and never crosses the provider adapter boundary. |
| */ |
| export interface ModelToolDefinition { |
| description?: string; |
| inputSchema: unknown; |
| } |
| |
| export type ModelToolSet = Record<string, ModelToolDefinition>; |
| |
| export interface ToolResultPart { |
| type: 'tool-result'; |
| toolCallId: string; |
| toolName: string; |
| output: ToolResultOutput; |
| providerOptions?: ProviderOptions; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Message contract |
| // --------------------------------------------------------------------------- |
| |
| export type AssistantContent = |
| | string |
| | Array< |
| | TextPart |
| | CustomPart |
| | FilePart |
| | ReasoningPart |
| | ReasoningFilePart |
| | ToolCallPart |
| | ToolResultPart |
| | ToolApprovalRequest |
| >; |
| export type UserContent = string | Array<TextPart | ImagePart | FilePart | AudioPart>; |
| export type ToolContent = Array<ToolResultPart | ToolApprovalResponse>; |
| |
| export interface SystemModelMessage { |
| role: 'system'; |
| content: string; |
| providerOptions?: ProviderOptions; |
| } |
| export interface UserModelMessage { |
| role: 'user'; |
| content: UserContent; |
| providerOptions?: ProviderOptions; |
| } |
| export interface AssistantModelMessage { |
| role: 'assistant'; |
| content: AssistantContent; |
| providerOptions?: ProviderOptions; |
| } |
| export interface ToolModelMessage { |
| role: 'tool'; |
| content: ToolContent; |
| providerOptions?: ProviderOptions; |
| } |
| |
| /** |
| * The canonical provider-boundary message shape. One arm per role, matching |
| * the AI SDK `ModelMessage` union used by `streamText` / `generateText`. |
| * Consumers build and read this Maka-owned union; `ModelAdapter` lowers it to |
| * the AI SDK message type at the request boundary. |
| */ |
| export type ModelMessage = |
| | SystemModelMessage |
| | UserModelMessage |
| | AssistantModelMessage |
| | ToolModelMessage; |
| |
| // --------------------------------------------------------------------------- |
| // Completion / usage / finish-reason contract |
| // --------------------------------------------------------------------------- |
| |
| /** |
| * Raw provider usage fields the AI SDK surfaces, mirrored here so the |
| * normalized usage can carry a verbatim provider view without importing `ai`. |
| * Owned by Maka; only the `ModelAdapter` normalization helper reads it. |
| */ |
| export interface RawUsageFields { |
| prompt_tokens?: number; |
| completion_tokens?: number; |
| total_tokens?: number; |
| prompt_cache_hit_tokens?: number; |
| prompt_cache_miss_tokens?: number; |
| prompt_tokens_details?: { cached_tokens?: number }; |
| completion_tokens_details?: { reasoning_tokens?: number }; |
| } |
| |
| /** |
| * Maka-owned, provider-agnostic token-usage contract. `ModelAdapter` |
| * normalizes the AI SDK usage shape into this stable contract; runtime |
| * consumers (compaction cost, telemetry, token-usage events) read only this |
| * shape and never the SDK usage union. |
| */ |
| export interface NormalizedUsage { |
| inputTokens: number; |
| outputTokens: number; |
| cacheHitInputTokens: number; |
| cacheMissInputTokens: number; |
| cacheMissInputSource: CacheMissInputSource; |
| cacheWriteInputTokens: number; |
| reasoningTokens: number; |
| totalTokens: number; |
| rawFinishReason?: string; |
| raw?: RawUsageFields; |
| /** Backward-compatible alias for `cacheHitInputTokens`. */ |
| cachedInputTokens: number; |
| } |
| |
| export function rawFinishReasonString(reason: unknown): string | undefined { |
| if (typeof reason === 'string') return reason; |
| if (!reason || typeof reason !== 'object') return undefined; |
| const value = reason as { raw?: unknown; unified?: unknown }; |
| if (typeof value.raw === 'string') return value.raw; |
| return typeof value.unified === 'string' ? value.unified : undefined; |
| } |
| |
| /** |
| * Normalized provider finish reason as a Maka-owned string. The raw AI SDK |
| * finish-reason value (string or `{ raw, unified }` object) is reduced at the |
| * provider boundary; downstream code compares against the string literal |
| * (e.g. `'tool-calls'`) or maps it via `ModelAdapter.mapFinishReason`. |
| */ |
| export type ModelFinishReason = string; |
| |
| // --------------------------------------------------------------------------- |
| // Failure contract |
| // --------------------------------------------------------------------------- |
| |
| /** |
| * Stable failure categories consumed by Runtime policy. Provider-specific |
| * error objects and AI SDK wrappers are classified inside `ModelAdapter` and |
| * never cross the boundary. |
| */ |
| export type ModelFailureKind = |
| | 'abort' |
| | 'auth' |
| | 'context_overflow' |
| | 'network' |
| | 'provider_billing' |
| | 'provider_unavailable' |
| | 'rate_limit' |
| | 'timeout' |
| | 'unknown'; |
| |
| export interface ModelFailure { |
| type: 'model_failure'; |
| kind: ModelFailureKind; |
| message: string; |
| /** Adapter-owned transport fact consumed by Runtime retry policy. */ |
| retryable: boolean; |
| /** Provider-requested delay for the next physical attempt, in milliseconds. */ |
| retryAfterMs?: number; |
| code?: string; |
| } |
| |
| /** |
| * Provider request metadata reduced to the Maka-owned message projection. |
| * Headers and provider request bodies stay with ProviderRequestTracker, their |
| * existing capture owner, instead of being retained again by the stream result. |
| */ |
| export interface ModelRequestMetadata { |
| messages?: readonly ModelMessage[]; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Stream-event / stream-result contract |
| // --------------------------------------------------------------------------- |
| |
| /** |
| * Maka-owned discriminated stream event. `ModelAdapter` translates each raw |
| * AI SDK stream chunk into zero or more of these events; runtime consumers |
| * iterate `ModelStreamResult.events` and never see raw SDK chunk names. |
| * |
| * - `text` / `thinking`: incremental assistant content deltas for the current |
| * step. The backend accumulates them per step and flushes one |
| * `AssistantMessage` (+ terminal text/thinking `SessionEvent`s) at the |
| * next `step-finish`. |
| * - `thinking-signature`: a provider-signed reasoning signature (Anthropic) |
| * delivered out-of-band from the thinking text. |
| * - `step-finish`: a provider step boundary. Carries the step's normalized |
| * usage (already reduced to `NormalizedUsage`) and normalized finish |
| * reason. The backend owns step counting, the per-step `AssistantMessage` |
| * flush, and the messageId rotation. |
| * - `finish`: the terminal stream boundary, carrying the normalized finish |
| * reason. |
| * - `error`: a request-level provider failure, already classified and scrubbed |
| * by the adapter. The backend uses its stable kind for overflow/transport |
| * recovery and terminal error emission. |
| */ |
| export type ModelStreamEvent = |
| | { kind: 'text'; text: string } |
| | { kind: 'thinking'; text: string; providerOptions?: ProviderOptions } |
| | { kind: 'thinking-signature'; signature: string } |
| | { kind: 'tool-call'; toolCall: ToolCallPart } |
| | { kind: 'step-finish'; usage?: NormalizedUsage; finishReason?: ModelFinishReason } |
| | { kind: 'finish'; finishReason?: ModelFinishReason } |
| | { kind: 'error'; failure: ModelFailure }; |
| |
| /** |
| * Maka-owned result of a single `ModelAdapter.startStream` provider call. The |
| * backend consumes `events` for streaming + step accounting, awaits `usage` |
| * for the final billing-relevant token totals, `finishReason` for the terminal |
| * stop reason, and `request` for the final Maka-owned message projection. All |
| * four are normalized to Maka-owned types; no AI SDK type crosses this surface. |
| */ |
| export interface ModelStreamResult { |
| events: AsyncIterable<ModelStreamEvent>; |
| usage: Promise<NormalizedUsage | undefined>; |
| finishReason: Promise<ModelFinishReason | undefined>; |
| request: Promise<ModelRequestMetadata | undefined>; |
| } |