/** * Canonical provider-neutral message and streaming vocabulary for the loop, * session log, and plugins. Adapters alone translate provider wire messages; * mapped interfaces make the content, source, and finish unions extensible. */ import type { Branded } from '@deepseek-ai/dsh-brand'; import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'; import type { CallId, ProviderRequestId, ReasoningEffortId } from './brand.ts'; import type { Message } from './message.ts'; export type { AssistantMessage, AssistantProvenance, Message, MessageSource, MessageSourceMap, ModelMessageSource, ToolMessageSource, ToolResultMessage, UserMessage, } from './message.ts'; /** Serializable provider or transport failure facts; policy decides whether they are retryable. */ export interface LlmFailure { /** Human-readable provider or transport failure. */ readonly message: string; /** Stable provider-neutral machine-routing code. */ readonly code: string; /** HTTP status returned by the provider, when available. */ readonly status?: number; /** Provider-requested delay in milliseconds, when valid and available. */ readonly providerRetryAfterMs?: number; /** Opaque provider-issued request identifier for diagnostics. */ readonly requestId?: ProviderRequestId; } /** Plain text visible to the end user. */ export interface TextBlock { type: 'text'; text: string; } /** Reasoning / thinking content, distinct from visible text. */ export interface ReasoningBlock { type: 'reasoning'; text: string; } /** * A durable raster image reference, valid in user or assistant content. The * block is deliberately role-neutral; assistant-side rendering is forward * compatibility — the current production adapters declare text-only output, * so only user content carries images today. */ export interface ImageBlock { type: 'image'; /** Immutable bytes and intrinsic display metadata owned by the attachment service. */ attachment: ImageAttachmentRef; } /** A tool invocation requested by the model. */ export interface ToolCallBlock { type: 'tool-call'; /** Provider-issued call id; correlates with the matching tool result. */ id: CallId; name: string; /** Raw JSON string as produced by the model. */ arguments: string; } /** The result of a tool invocation, sent back to the model. */ export interface ToolResultBlock { type: 'tool-result'; toolCallId: CallId; content: ContentBlock[]; isError?: boolean; } /** * Merge-extensible content blocks keyed by `type`. New core blocks must land * with adapter, UI, and compaction support. */ export interface ContentBlockMap { 'text': TextBlock; 'reasoning': ReasoningBlock; 'image': ImageBlock; 'tool-call': ToolCallBlock; 'tool-result': ToolResultBlock; } /** The block `type` tag vocabulary; widens as plugins add entries to {@link ContentBlockMap}. */ export type ContentBlockType = keyof ContentBlockMap; /** Any known content block, derived from {@link ContentBlockMap}; switch on `type` and fall through unknowns (merge-extensible). */ export type ContentBlock = ContentBlockMap[ContentBlockType]; /** * Why a model response stopped. * Merge-extensible so adapters can surface provider-specific reasons. */ export interface FinishReasonMap { 'stop': { kind: 'stop'; }; 'tool-calls': { kind: 'tool-calls'; }; 'max-tokens': { kind: 'max-tokens'; }; 'aborted': { kind: 'aborted'; failure: LlmFailure; }; 'error': { kind: 'error'; failure: LlmFailure; }; } /** Any known finish reason, derived from {@link FinishReasonMap}; switch on `kind` and fall through unknowns (merge-extensible). */ export type FinishReason = FinishReasonMap[keyof FinishReasonMap]; /** * Token accounting for one model call (cache fields are optional). * * Counts are DISJOINT: `inputTokens` is uncached input only; cached input is * reported separately as `cacheReadTokens`/`cacheWriteTokens` (billed input = * sum of the three). Adapters whose providers fold cache hits into a total * prompt count (DeepSeek's `prompt_tokens`) subtract them out. */ export interface TokenUsage { inputTokens: number; outputTokens: number; cacheReadTokens?: number; cacheWriteTokens?: number; reasoningTokens?: number; } /** Display metadata for one registered provider route. */ export interface LlmProviderInfo { /** Provider route key used by {@link GenerateOptions.provider}. */ id: string; /** Human-readable provider name for selectors and diagnostics. */ name: string; } /** Merge-extensible provider model modality vocabulary. */ export interface ModelModalityMap { text: 'text'; image: 'image'; } /** Any declared provider model modality. */ export type ModelModality = ModelModalityMap[keyof ModelModalityMap]; /** * One provider route an adapter plugin can activate through configuration, * whether or not the route is currently registered. Configuration surfaces * merge this directory with `listProviders()` to offer every configurable * provider alongside its live/dormant state. */ export interface LlmConfigurableProvider { /** Provider route key this entry activates when configured. */ provider: string; /** Human-readable provider name for configuration surfaces. */ displayName: string; /** User-settings namespace whose section configures this provider. */ settingsNs: string; /** * Path from that namespace's section root to this provider's profile * object; empty when the whole section is the profile. */ settingsPath: readonly string[]; /** * Whether the owning adapter knows this route only because configuration * declared it — a gateway or self-hosted server it ships nothing about. * Absent means the adapter draws no such distinction; false means it does * and this route is one of its own. Only the adapter can answer: a stored * profile is how a user-added route AND a corrected shipped one both look * from outside. */ declared?: boolean; } /** * One interrogation of a provider endpoint that configuration has not stored * yet. Configuration surfaces send the draft a user is still editing, so the * request carries the endpoint and credential directly instead of naming a * route: a provider being added has no route to name. */ export interface LlmModelDiscoveryRequest { /** * Route the draft is editing, when it edits an existing one. A route whose * adapter already knows its models answers from that knowledge instead of * asking the endpoint — the adapter's own registry is the better answer, and * it costs no network call. */ provider?: string; /** * Endpoint to interrogate. Optional because a route the adapter already * describes needs none; a route it does not must supply one. */ baseURL?: string; /** Wire protocol the endpoint speaks, when the draft names one. */ api?: string; /** Credential for this interrogation alone; the harness never stores it. */ apiKey?: string; /** Caller cancellation; implementations must settle promptly after it aborts. */ signal?: AbortSignal; } /** * One model an endpoint reports about itself. Every field but the id is * optional because most provider listings disclose an id and nothing else; * a surface adopting one of these still owes the capacities its adapter needs. */ export interface LlmDiscoveredModel { /** Model id the endpoint accepts. */ id: string; /** Human-readable name when the endpoint supplies one. */ name?: string; /** Maximum combined request and response context, when disclosed. */ contextWindow?: number; /** Maximum output tokens, when disclosed. */ maxTokens?: number; } /** One adapter-discovered model; catalog membership is advisory, not request validation. */ export interface LlmModelInfo { /** Provider route that owns this model entry. */ provider: string; /** Model id passed to {@link GenerateOptions.model}. */ id: string; /** Human-readable model name for selectors. */ name: string; /** Optional user-facing distinction from otherwise similar models. */ description?: string; /** Accepted request modalities; absent means unknown, while an explicit omission is negative capability. */ inputModalities?: readonly ModelModality[]; } /** Provider-owned context capacity for one exact provider/model route. */ export interface LlmModelContext { /** Maximum combined request and response context in tokens. */ contextWindow: number; } /** Display metadata for one adapter-owned reasoning effort. */ export interface LlmReasoningEffortInfo { /** Opaque stable value accepted by {@link GenerateOptions.reasoningEffort}. */ id: ReasoningEffortId; /** Human-readable effort name for selectors and diagnostics. */ name: string; /** Optional user-facing distinction from otherwise similar efforts. */ description?: string; } /** Selectable reasoning efforts for one exact provider/model route. */ export interface LlmModelReasoningInfo { /** Supported efforts in adapter-preferred display order. */ efforts: readonly LlmReasoningEffortInfo[]; /** * Adapter-configured default materialized into requests when callers omit * an effort. Absence preserves the provider's own default. */ defaultEffort?: ReasoningEffortId; } /** Exact-route model metadata resolved by its owning adapter. */ export interface LlmResolvedModelInfo extends LlmModelInfo { /** Provider-owned context capacity when known. */ context?: LlmModelContext; /** Adapter-configured per-request output cap materialized when callers omit one. */ defaultMaxTokens?: number; /** Adapter-owned selectable reasoning levels when exposed. */ reasoning?: LlmModelReasoningInfo; } /** * Raw streaming protocol emitted by adapters. * Block indexes correlate interleaved deltas, and `block-end` carries the * assembled block. Adapters emit usage before the terminal finish and nothing * afterward; tool arguments remain raw JSON strings. An adapter implementation * may throw, but `LlmService.stream()` normalizes that failure to a terminal * `error` or `aborted` finish before exposing it to consumers. */ export type StreamChunk = { type: 'block-start'; index: number; blockType: ContentBlockType; } | { type: 'text-delta'; index: number; text: string; } | { type: 'reasoning-delta'; index: number; text: string; } | { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string; } | { type: 'block-end'; index: number; block: ContentBlock; } | { type: 'usage'; usage: TokenUsage; } | { type: 'finish'; reason: FinishReason; /** Adapter-private lossless-JSON state for replaying a successful response. */ replayState?: unknown; }; /** * JSON-schema description of a tool, as sent to the model. * * Declared here (not in dsh-tools) because it is part of {@link GenerateOptions}; * dsh-tools' ToolDefinition and dsh-system-prompt's PromptAssembly both import * it from this package. */ export interface ToolSchema { name: string; description: string; /** JSON Schema object for the arguments. */ parameters: Record; } /** A single model request, fully assembled. */ export interface GenerateOptions { /** Registered provider route selecting the adapter instance. */ provider: string; model: string; /** Adapter-owned reasoning effort selected for this exact model. */ reasoningEffort?: ReasoningEffortId; /** * Ordered conversation messages, exactly as the provider sees them (after * the `system` slot). A loop-built request assembles them as * the derived history (dsh-agent-loop); a hand-built one-shot passes any list. */ messages: Message[]; /** System prompt text (adapters map to the provider's system slot). */ system?: string; /** Tool schemas (adapters map to the provider's `tools` field). */ tools?: ToolSchema[]; temperature?: number; maxTokens?: number; /** * Stop sequences: generation halts as soon as the model produces any one of * these strings (adapters map to the provider's stop field, e.g. OpenAI * `stop`). The stop string itself is not included in the output. */ stop?: string[]; signal?: AbortSignal; /** * Session identity stamped by the loop for listener routing. Adapters ignore * it; replay uses it to keep concurrent parent and child cursors independent. */ sessionId?: Branded<'SessionId'>; /** * Provider-neutral classification for an auxiliary model call. Adapters may * map the purpose to model-hidden transport metadata or purpose-specific * generation policy. Ordinary conversation requests leave it unset. */ purpose?: 'compaction' | 'session-title'; } //# sourceMappingURL=types.d.ts.map