/** * Public types for @optima-chat/agentic-sdk. * * These are the types consumers see. They are distinct from the gateway protocol * types in @optima-chat/gateway-protocol — this module performs field/type * mapping (snake_case → camelCase, ISO strings → epoch ms, etc.) before the * state reaches consumers. */ import type { ServerEvent, ClientEvent, FinishInfo, Question, ApprovalRequest, ApprovalResponse, TokenProgress, BillingError, ToolResultContentBlock } from '@optima-chat/gateway-protocol'; import type { WorkspaceProvider } from './workspace/types.js'; import type { TasksProvider } from './tasks/types.js'; /** WebSocket connection lifecycle state. */ export type ConnectionState = 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'auth_failed'; /** Message lifecycle status. */ export type MessageStatus = 'pending' | 'streaming' | 'complete' | 'error'; /** Tool-call lifecycle status. */ export type ToolCallStatus = 'pending' | 'executing' | 'awaiting_approval' | 'completed' | 'error'; /** Error surfaced to consumers. */ export interface ChatError { code: string; message: string; retryable?: boolean; details?: unknown; } /** Last message kind in a conversation. */ export type ConversationLastMessageKind = 'text' | 'image' | 'voice' | 'file' | 'mixed'; /** Conversation summary. */ export interface Conversation { id: string; title: string; /** Epoch ms, converted from protocol's ISO-8601 string. */ lastMessageAt: number; messageCount: number; lastMessagePreview?: string; lastMessageKind?: ConversationLastMessageKind; /** Epoch ms. 0 when session_ready's ConversationSummary doesn't carry it. */ createdAt: number; /** #125(E3-D 分享):'owner'=作者本人;'shared'=经 ShareGrant 可见。旧 gateway 无此字段 → undefined(fail-open)。 */ access?: 'owner' | 'shared'; /** owner 侧 grant 汇总(none=无 / enterprise=含全企业档 / seats=仅定向档)。 */ shareState?: 'none' | 'enterprise' | 'seats'; /** 会话置顶(spec I6):epoch ms=本人已置顶;null=未置顶;undefined=本次未携带(旧网关/服务端降级)。 */ pinnedAt?: number | null; } /** Attachment (image/file/audio) on a Message — v1 only for user messages. */ export interface MessageAttachment { type: 'image' | 'file' | 'audio'; url: string; name?: string; mediaType?: string; /** Audio-only: duration in ms. */ duration?: number; /** Audio-only: transcribed text. */ transcription?: string; } /** Tool call attached to an assistant Message. */ export interface ToolCall { id: string; /** Mapped from protocol's ToolCallInfo.name. */ toolName: string; /** JSON string, mapped from protocol's ToolCallInfo.arguments. */ arguments: string; status: ToolCallStatus; /** Mapped from protocol's ToolResultInfo.output. */ result?: string; /** Mapped from protocol's ToolResultInfo.content_blocks. */ resultContentBlocks?: ToolResultContentBlock[]; error?: string; progress?: { message: string; percentage?: number; }; } /** Chat message — user / assistant / system. */ export interface Message { id: string; conversationId: string; /** * Per-conversation monotonic ordering key from the gateway. Strictly * increasing within a conversation (advisory-lock-protected MAX+1). * Used as the cursor for `getMessages({before})` pagination and as the * canonical sort key when rendering history. * * Undefined for streaming/optimistic messages that haven't been persisted * yet (e.g., a streaming assistant reply, an optimistic user message * shown before the server confirms it). Cursor derivation in * `loadHistory` filters these out so the cursor is always a real * persisted sequenceNumber. */ sequenceNumber?: number; role: 'user' | 'assistant' | 'system'; /** Accumulated full text. */ content: string; /** * Accumulated extended-thinking text for this assistant message, built by * appending gateway `thinking.delta` chunks (gateway#1409). Undefined when * the gateway sent no thinking deltas (older gateway, or a message with no * reasoning phase) — BC: absent field means no UI change. */ reasoning?: string; toolCalls: ToolCall[]; attachments?: MessageAttachment[]; status: MessageStatus; /** * gw#1587 投递保证:消息级投递状态(仅 armed 连接上带 client_msg_id 的用户消息有值)。 * pending=已入 outbox 未回执;delivered=gateway 已持久化(收到 message_receipt); * failed=有限时间内未送达(可 retryMessage 重试)。⚠️ 只驱动消息级 UI 指示—— * conv 级 processing/isThinking 由 turn 事件独占驱动(spec §5.4 分层)。 */ deliveryState?: 'pending' | 'delivered' | 'failed'; /** deliveryState==='failed' 时的成因(arm_timeout/disarm/conn_terminal/terminal/timeout_exhaust/transient_exhaust)。 */ deliveryFailedCause?: string; /** Epoch ms. */ createdAt: number; /** Optional retention of raw protocol events. */ rawEvents?: ServerEvent[]; /** * #161B:本条 user 消息「补答的是哪条提问」——gateway 从 rawData 按需透出的 * question_request_id(与 AskUserQuestion tool result 里的 request_id 同值不同角色: * 后者是提问自身身份)。历史派生 answeredLate 状态的 join 键。缺省 = 非补答消息。 */ questionRequestId?: string; } /** Pending ask_question state. */ export interface QuestionRequest { requestId: string; /** 卡片所属会话。protocol 0.5.2 起 ask_question 事件必带 conversation_id;parser 对 legacy 事件回落 activeConversationId ?? ''。 */ conversationId: string; questions: Question[]; /** #161A:相对超时时长(ms),gateway-protocol 0.5.2 起容器发射;老 gateway 无(BC)。 */ timeoutMs?: number; /** #161A:SDK 收到 ask_question 的本地墙钟(Date.now()),与 timeoutMs 配对做过期判定。 */ receivedAt?: number; } /** Pending approval_request (ApprovalRequest + conversationId). */ export interface ApprovalRequestWithId { requestId: string; /** Filled by agentic-sdk from the currently active conversation. */ conversationId: string; toolName: string; args: Record; description?: string; risk?: 'safe' | 'caution' | 'dangerous'; } /** Options for sendMessage. */ export interface SendMessageOptions { images?: File[]; files?: File[]; audio?: File[]; metadata?: Record; } /** Result of a WorkspaceProvider upload. */ export interface UploadResult { originalName: string; url: string; mediaType: string; } /** * Optional argument shape passed to `tokenProvider`. Reserved for future * growth; currently only `forceRefresh` is honored. When set, the consumer's * token provider should bypass any cached/short-circuit token and refresh. */ export interface TokenProviderOpts { forceRefresh?: boolean; } /** Props for . */ export interface ChatProviderProps { children: React.ReactNode; gatewayUrl: string; tokenProvider: (opts?: TokenProviderOpts) => Promise | string; authenticatedFetch: (url: string, init?: RequestInit) => Promise; /** * Optional. If provided, the SDK subscribes to access-token changes and * sends `update_token` over the WebSocket whenever the token changes, * keeping downstream services authenticated for long-lived sessions. * * Shape: a function the SDK calls once on mount with its internal * callback. Implementations should register the callback as a listener * for token changes and return an unsubscribe function. The SDK calls * the unsubscribe function on unmount. * * Pass the new access token to the callback (as `TokenManager.subscribe` * does). The SDK reads its `sub` to spot a switch to a different principal, * which reconnects instead of pushing the new identity's token onto a socket * bound to the old identity's session (#128). It is treated as a hint and * always confirmed against `tokenProvider()`, so a host whose emitted token * differs from what its provider serves (e.g. admin impersonation) is safe. * Calling the callback with no argument still works: the check then runs on * the freshly minted token instead, at the cost of one forced rotation. * * Typical wiring with @optima-chat/agentic-auth — declare the adapter * at module scope so its reference stays stable across renders: * * const subscribeTokenChange = (cb) => tokenManager.subscribe(cb); * // then: * * Inline lambdas work but re-subscribe on every render (harmless but * wasteful). * * If omitted, the SDK does nothing — long sessions rely on the * connection-time `tokenProvider()` and any server-pushed * `token_refresh_needed` events to refresh tokens. */ onTokenChange?: (callback: (accessToken: string) => void) => () => void; onFinish?: (event: { conversationId: string; finish: FinishInfo; }) => void; onToolCall?: (event: { conversationId: string; toolName: string; toolCallId: string; /** JSON string, matches protocol ToolCallInfo.arguments. */ args: string; }) => void; onNotification?: (event: { type: 'info' | 'warning' | 'error'; message: string; code?: string; }) => void; onError?: (error: ChatError) => void; onAuthError?: (error: ChatError) => void; /** COO 模式状态回调(Sleep→sleeping, Agent→dispatching_worker 等),前端可据此展示状态指示器 */ onCooStatus?: (event: { status: 'sleeping' | 'woke_up' | 'dispatching_worker' | 'worker_launched' | 'idle'; detail?: string; }) => void; onRawEvent?: (event: ServerEvent) => void; /** * Intercept outgoing ClientEvents. * - Return the same event (or `undefined`) to send unchanged * - Return a modified event to send the modification * - Return `null` to drop the event entirely */ onBeforeSend?: (event: ClientEvent) => ClientEvent | null | undefined | void; workspaceProvider?: WorkspaceProvider; tasksProvider?: TasksProvider; /** * gw#2313 S1 — multi-session attach addressing(透传到 GatewayClient 同名 option, * 详细语义见 client/gateway-client.ts docblock)。multi-tab 配方:每个新 tab 先 * `startNewSession()` 拿自己的 session,`session_ready` 时把 sessionId 写进 * sessionStorage,并在这里返回它——F5/断线重连回到同一 session。不配 = 默认复用腿。 */ sessionAttachProvider?: () => string | null | undefined; reconnectOptions?: { /** Default 5. */ maxRetries?: number; /** Default 2000ms. */ baseDelay?: number; /** Default 30000ms. */ maxDelay?: number; }; /** Default 30000ms. */ heartbeatInterval?: number; } export type { FinishInfo, Question, ApprovalRequest, ApprovalResponse, TokenProgress, BillingError }; /** Recovery status for a conversation's restore attempts. */ export type RecoveryStatus = 'ok' | 'restoring' | 'unrecoverable'; //# sourceMappingURL=types.d.ts.map