/** * KHAELOR * File: src/anthropic/types.ts * Description: Model Runtime types — ModelClient, ModelRequest, ModelEvent stream vocabulary, honest usage (ARCHITECTURE.md §7). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ // Layer 1 — this module imports `shared` only (plus @anthropic-ai/sdk). // It therefore owns its own copies of the model-facing vocabulary; the // session event catalog mirrors these shapes (EVENT_MODEL.md §4), and the // agent-layer reducer maps ModelEvents onto session events: // // started → ModelRequestStarted // text-delta → ModelTextDelta (ephemeral) // thinking-delta → ModelThinkingDelta (ephemeral) // tool-call-started → ToolCallStarted (ephemeral) // tool-input-delta → ToolInputDelta (ephemeral) // text-block-completed → ModelTextBlockCompleted (durable) // thinking-block-completed → ModelThinkingBlockCompleted (durable) // tool-call-completed → ToolRequested (durable) // completed → ModelResponseCompleted (durable) // ───────────────────────── stop reason and usage ───────────────────────── /** Normalized stop reason (EVENT_MODEL.md §4). */ export type StopReason = "end_turn" | "tool_use" | "max_tokens" | "refusal"; /** * Real API usage only — never estimated (Absolute Rule #4). * Field sources (Anthropic Messages API `usage` object): * inputTokens ← input_tokens * outputTokens ← output_tokens * cacheReadTokens ← cache_read_input_tokens * cacheWriteTokens ← cache_creation_input_tokens */ export interface ModelUsage { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; } // ───────────────────────── request shapes ───────────────────────── /** `{ type: "ephemeral" }` cache breakpoint marker (prompt caching). */ export interface CacheControl { type: "ephemeral"; } /** * One stable system-prompt tier (ADR-7): [identity/behavior] → * [tool guidance] → [project instructions]. Built once per session, * never re-rendered; cache breakpoints are placed at tier ends. */ export interface SystemTier { /** Stable label for /context stats — e.g. "identity", "project-instructions". */ name: string; text: string; } /** Text content block. */ export interface TextBlockParam { type: "text"; text: string; cache_control?: CacheControl; } /** Thinking block — replayed byte-exact (thinking + signature) in tool loops. */ export interface ThinkingBlockParam { type: "thinking"; thinking: string; signature: string; } /** Redacted thinking block — opaque, replayed verbatim. */ export interface RedactedThinkingBlockParam { type: "redacted_thinking"; data: string; } /** Assistant tool_use block. */ export interface ToolUseBlockParam { type: "tool_use"; id: string; name: string; input: unknown; cache_control?: CacheControl; } /** User tool_result block — pairs with a tool_use id. */ export interface ToolResultBlockParam { type: "tool_result"; tool_use_id: string; content: string | TextBlockParam[]; is_error?: boolean; cache_control?: CacheControl; } export type ContentBlockParam = | TextBlockParam | ThinkingBlockParam | RedactedThinkingBlockParam | ToolUseBlockParam | ToolResultBlockParam; /** One message of the byte-stable LlmHistory projection (ARCHITECTURE.md §6.5). */ export interface AnthropicMessage { role: "user" | "assistant"; content: ContentBlockParam[]; } /** Tool schema handed to the API (≤5 properties per ADR-8; enforced upstream). */ export interface ToolSchema { name: string; description: string; /** JSON Schema for the tool input (`input_schema` on the wire). */ inputSchema: Record; } /** Extended-thinking configuration. */ export type ThinkingConfig = | { mode: "enabled"; budgetTokens: number } | { mode: "disabled" }; /** The assembled model request (ARCHITECTURE.md §7). */ export interface ModelRequest { model: string; /** Stable tiers; cache_control breakpoints are planned by caching.ts. */ system: SystemTier[]; /** Byte-stable projection (§6.5) — never mutated by the client. */ messages: AnthropicMessage[]; tools: ToolSchema[]; maxOutputTokens: number; thinking?: ThinkingConfig; } // ───────────────────────── the event stream ───────────────────────── /** * Events produced by ModelClient.stream(). Errors are NOT events: the * async iterable throws a typed ModelError (errors.ts) which the kernel * classifies (ARCHITECTURE.md §4.2 / §7). */ export type ModelEvent = | { type: "started"; requestId: string } | { type: "text-delta"; blockIndex: number; text: string } | { type: "thinking-delta"; blockIndex: number; text: string } | { type: "tool-call-started"; blockIndex: number; toolUseId: string; toolName: string } | { type: "tool-input-delta"; blockIndex: number; toolUseId: string; partialJson: string } | { type: "text-block-completed"; blockIndex: number; text: string } | { type: "thinking-block-completed"; blockIndex: number; thinking: string; signature: string } | { type: "tool-call-completed"; blockIndex: number; toolUseId: string; toolName: string; /** Complete parsed input, accumulated across input_json_delta chunks. */ input: unknown; } | { type: "completed"; stopReason: StopReason; /** REAL usage from message_start/message_delta payloads — sole source for /cost. */ usage: ModelUsage; durationMs: number; }; // ───────────────────────── the client boundary ───────────────────────── /** * The single model boundary (CLAUDE.md §6, ADR-10). Anthropic-only in V1; * this interface exists for clean architecture, not multi-provider plumbing. */ export interface ModelClient { /** * Stream a completion. Cancellation is AbortSignal end to end: aborting * `signal` aborts the underlying HTTP request and the iterable throws * ModelError{kind:"cancelled"}. */ stream(request: ModelRequest, signal?: AbortSignal): AsyncIterable; /** Best-effort budgeting aid (real API count, no retry). */ countTokens?(request: ModelRequest): Promise; }