/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ /** * Shared primitive value types for the qodercli ↔ qoder-agent-sdk wire protocol. * * Anything callback-shaped or carrying a runtime instance lives in the SDK * package, not here. */ export type ContentBlock = { type: string; text?: string; id?: string; name?: string; input?: unknown; content?: unknown; source?: unknown; tool_use_id?: string; is_error?: boolean; [key: string]: unknown; }; export type MessageParam = { role: 'user'; content: string | ContentBlock[]; }; export type BetaStopReason = 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use' | 'pause_turn' | 'refusal' | string; export type BetaUsage = { cache_creation?: { ephemeral_1h_input_tokens?: number | null; ephemeral_5m_input_tokens?: number | null; } | null; cache_creation_input_tokens?: number | null; cache_read_input_tokens?: number | null; inference_geo?: string | null; input_tokens?: number | null; iterations?: unknown[] | null; output_tokens?: number | null; server_tool_use?: { web_fetch_requests?: number | null; web_search_requests?: number | null; } | null; service_tier?: string | null; speed?: string | null; /** Qoder request-level metering value after discounts. */ credits?: number; /** Qoder request-level metering value before discounts. */ original_credits?: number; /** Whether the request contributes to the user's billed usage. */ billable?: boolean; }; export type BetaMessage = { id?: string; type?: 'message'; role: 'assistant'; content: ContentBlock[]; model?: string; stop_reason?: BetaStopReason | null; stop_sequence?: string | null; usage?: BetaUsage; [key: string]: unknown; }; export type BetaRawMessageStreamEvent = { type: string; index?: number; delta?: unknown; content_block?: ContentBlock; message?: BetaMessage; usage?: BetaUsage; [key: string]: unknown; }; /** * Wire-level UUID. We deliberately type it as plain `string` rather than * `crypto.UUID`'s template literal — the wire is a string, and using a * template literal made downstream zod schemas unable to express required * fields (zod treats template literals as `T | undefined` in `_input`). * * Consumers that want stricter UUID handling can cast at the boundary. */ export type UUID = string; /** * Usage with every SDK-consumed field non-null and required. * * Upstream usage types mark fields like `cache_creation_input_tokens` as * `number | null | undefined`, but the runtime always emits a number (zeroed * if absent). We tighten that here to remove null-checking noise on the SDK * side. The optional `request_id` is a Qoder-specific extension threaded * through for tracing persisted transcripts. */ export type NonNullableUsage = { cache_creation: { ephemeral_1h_input_tokens: number; ephemeral_5m_input_tokens: number; }; cache_creation_input_tokens: number; cache_read_input_tokens: number; inference_geo: string; input_tokens: number; iterations: unknown[]; output_tokens: number; server_tool_use: { web_fetch_requests: number; web_search_requests: number; }; service_tier: string; speed: string; request_id?: string; /** Context window usage ratio for the completed turn (0~1). */ context_usage_ratio?: number; /** Qoder request-level metering value after discounts. */ credits?: number; /** Qoder request-level metering value before discounts. */ original_credits?: number; /** Whether the request contributes to the user's billed usage. */ billable?: boolean; }; export type AccountInfo = { userId?: string; name?: string; email?: string; organization?: string; organizationName?: string; subscriptionType?: string; tokenSource?: string; apiKeySource?: string; apiProvider?: 'firstParty' | 'bedrock' | 'vertex' | 'foundry'; }; export type UsageQuotaBucket = { total?: number; used?: number; remaining?: number; percentage?: number; unit?: string; }; export type UsageAddOnQuotaBucket = UsageQuotaBucket & { detailUrl?: string; }; export type UsageOrgResourcePackage = { used?: number; cap?: number; remaining?: number; percentage?: number; available?: boolean; unit?: string; }; export type SessionCreditsUsage = { total_credits: number; model_usage: Record; }; export type UsageInfo = { userId?: string; userType?: string; totalUsagePercentage?: number; isHighestTier?: boolean; expiresAt?: number; upgradeUrl?: string; userQuota?: UsageQuotaBucket; addOnQuota?: UsageAddOnQuotaBucket; isQuotaExceeded?: boolean; isPlanQuotaProrated?: boolean; orgResourcePackage?: UsageOrgResourcePackage; /** * Credits consumed by the current CLI session. * * This is independent from the account quota fields above and may be present * even when the account quota endpoint is unavailable. */ session?: SessionCreditsUsage; }; export type ApiKeySource = 'none' | 'user' | 'project' | 'org' | 'temporary' | 'oauth'; export type ExitReason = 'clear' | 'resume' | 'logout' | 'prompt_input_exit' | 'other' | 'bypass_permissions_disabled'; export type FastModeState = 'off' | 'cooldown' | 'on'; export type ConfigScope = 'local' | 'user' | 'project'; export type SettingSource = 'user' | 'project' | 'local'; export type SDKAssistantMessageError = 'authentication_failed' | 'billing_error' | 'rate_limit' | 'invalid_request' | 'server_error' | 'unknown' | 'max_output_tokens'; export type SDKStatus = 'compacting' | null; export type EffortLevel = 'low' | 'medium' | 'high' | 'max'; export type ThinkingAdaptive = { type: 'adaptive'; }; export type ThinkingEnabled = { type: 'enabled'; budgetTokens?: number; }; export type ThinkingDisabled = { type: 'disabled'; }; export type ThinkingConfig = ThinkingAdaptive | ThinkingEnabled | ThinkingDisabled; export type ModelContextWindowEntry = { token_count: number; is_default?: boolean; }; /** Server-side `context_config`, keyed by tier label such as "200K" / "1M". */ export type ModelContextConfig = Record; export type ModelEffortEntry = { description?: string; is_default?: boolean; }; export type ModelThinkingDisabled = { description?: string; }; export type ModelThinkingEnabled = { description?: string; efforts?: Record; is_default?: boolean; }; /** Server-side `thinking_config` block, preserved 1:1. */ export type ModelThinkingConfig = { disabled?: ModelThinkingDisabled; enabled?: ModelThinkingEnabled; }; /** * Codebase security-tag strategy entry. * * Mirrors the CLI's `ModelStrategy`: whether the model is available when the * workspace repo carries a given tag (e.g. `'C4'`). `disabled_message_key` is * the i18n key for the message shown when the strategy disables the model. * Nested fields stay snake_case to mirror the server payload. */ export type ModelStrategy = { tag: string; enabled: boolean; disabled_message_key: string; }; export type ModelSource = 'system' | 'user'; /** JSON-compatible value inside a raw server model entry. */ export type ServerModelJsonValue = string | number | boolean | null | ServerModelJsonValue[] | { [key: string]: ServerModelJsonValue; }; /** * Raw model entry from the model list API, forwarded verbatim by the CLI. * SDK hosts can read newly-added server fields here without waiting for a * `ModelInfo` DTO change. */ export type ServerModelJson = Record; /** * Localized text bag used by promotion `badge` / `description`. Server provides * at least `en` / `zh`; other locale keys are forwarded verbatim. */ export type LocalizedModelText = { en?: string; zh?: string; } & Record; /** * Per-model promotion / discount info from the model list API's `promotion` * block. Mirrors the CLI catalog shape 1:1; nested fields stay snake_case to * match the server payload. All fields are optional/defensive — the server may * omit any of them. */ export type ModelPromotion = { /** Whether the promotion is currently active for this model. */ active: boolean; /** Short localized label, e.g. `{ zh: '错峰5折', en: 'Off-Peak 50% off' }`. */ badge?: LocalizedModelText; /** Longer localized description, e.g. the off-peak window in words. */ description?: LocalizedModelText; /** Discounted price factor while the promotion is active. */ discount_factor?: number; /** Original price factor before the promotion. */ before_promotion_price_factor?: number; /** IANA timezone the window is evaluated in, e.g. `'Asia/Shanghai'`. */ timezone?: string; /** Server rule identifier, e.g. `'night_qmodel'`. */ rule_id?: string; /** Daily window start (HH:mm) when the promotion applies. */ window_start?: string; /** Daily window end (HH:mm). */ window_end?: string; }; export type ModelInfo = { value: string; /** Canonical model identifier (always equal to `value` for system models). */ modelId?: string; displayName: string; description: string; /** `'system'` (Qoder catalog) or `'user'` (BYOK). */ source?: ModelSource; /** Server-designated default for the current scene. */ isDefault?: boolean; isEnabled?: boolean; isNew?: boolean; isFree?: boolean; /** Whether the model supports reasoning / chain-of-thought. */ isReasoning?: boolean; /** Whether the model accepts multimodal/vision input. */ isVl?: boolean; /** Credit multiplier (e.g. `1.6` → 1.60x credit consumption). */ priceFactor?: number; /** Original price factor before discounts. */ originalPriceFactor?: number; /** Maximum input token count. */ maxInputTokens?: number; /** Maximum output token count. */ maxOutputTokens?: number; /** Explicit reasoning effort levels supported by this model. */ efforts?: string[]; /** Server-designated default reasoning effort. */ defaultEffort?: string; /** Whether the model supports disabling thinking explicitly. */ supportsDisabled?: boolean; /** Supported configurable context window sizes in tokens. */ availableContextWindows?: number[]; /** Server-designated default tier (one of `availableContextWindows`). */ defaultContextWindow?: number; /** Free-form tags (e.g. `'limited_time_free'`, `'DogFooding'`). */ tags?: string[]; /** * Codebase security-tag strategies. Absent → no restriction. * Inner shape mirrors the server / CLI verbatim. */ strategies?: ModelStrategy[]; /** Output format, e.g. `'openai'`, `'dashscope'`. */ format?: string; /** Usage scene this entry belongs to, e.g. `'chat'`, `'assistant'`. */ scene?: string; /** * Original top-level key in the server model-list response before client-side * merging, e.g. `'assistant'` or `'byok_enterprise'`. */ serverScene?: string; /** * Optional model icon — server-provided string (URL, emoji, or provider * identifier). Forwarded verbatim from the model catalog; consumers decide * how to render it. */ icon?: string; /** BYOK: custom endpoint URL. */ url?: string; /** BYOK: bare model identifier within the provider, e.g. `'gpt-4o'`. */ model?: string; /** BYOK: provider key, e.g. `'openai'`, `'deepseek'`. */ provider?: string; /** Rich context-window config from the server (tier labels → token_count). */ context_config?: ModelContextConfig; /** Rich thinking config from the server (effort levels with descriptions). */ thinking_config?: ModelThinkingConfig; /** * Promotion / discount info from the model list API. Present only while a * promotion is configured for the model; absent → no promotion. */ promotion?: ModelPromotion; /** * Raw model entry from the model list API, forwarded verbatim by the CLI. * SDK hosts can read newly-added server fields here without waiting for a * `ModelInfo` DTO change. Sent on the wire as `serverModel`. */ serverModel?: ServerModelJson; }; export type PluginInfo = { name: string; path: string; source?: string; }; export type ModelUsage = { inputTokens: number; outputTokens: number; cacheReadInputTokens: number; cacheCreationInputTokens: number; webSearchRequests: number; costUSD: number; /** Session-cumulative Qoder credits attributed to this model. */ credits?: number; contextWindow: number; maxOutputTokens: number; }; export type SlashCommand = { name: string; description: string; argumentHint: string; }; export type ToolConfig = { askUserQuestion?: { previewFormat?: 'markdown' | 'html'; }; }; export type SdkPluginConfig = { type: 'local'; path: string; };