/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ /** * SDKMessage — the JSONL line format the CLI emits to stdout. * * Single source of truth for both the CLI (which writes them) and the SDK * (which parses them). CLI-side zod schemas in * `@google/gemini-cli-core/protocol/schemas` are kept aligned via * `satisfies z.ZodType<...>` against these types. */ import type { BetaMessage, BetaRawMessageStreamEvent, MessageParam, UUID, SDKAssistantMessageError, FastModeState, ApiKeySource, ModelUsage, NonNullableUsage, PluginInfo, ModelInfo, SlashCommand, SDKStatus } from './common.js'; import type { PermissionMode } from './permissions.js'; import type { MemoryConsumptionResult, MemoryGenerationResult } from './memory.js'; import type { SkillEvolutionResult } from './skill-evolution.js'; import type { SDKGoalSnapshot, SDKPlanModeSnapshot } from './control.js'; export type SDKAssistantMessage = { type: 'assistant'; message: BetaMessage; parent_tool_use_id: string | null; /** * Present when qodercli synthesized this Assistant message from an API * failure rather than a model response. Embedded hosts can use the marker to * avoid rendering duplicate error text when the terminal Result already * provides a structured error surface. */ isApiErrorMessage?: true; /** Qoder model request ID, present only when explicitly enabled in the CLI. */ request_id?: string; error?: SDKAssistantMessageError; /** * Present when an interrupt truncated the model stream before a stop reason * arrived. The final content block may end mid-token. */ aborted?: true; uuid: UUID; session_id: string; }; export type SDKToolNonExecutionKind = 'user-rejected' | 'permission-rule' | 'automode-blocked' | 'automode-unavailable' | 'automode-parsing-error' | 'interrupted' | 'cancelled'; /** * Display-only metadata for a tool result whose tool did not run to * completion. This wrapper-level field is never part of the model message. */ export type SDKToolResultMeta = { id: string; non_execution_kind: SDKToolNonExecutionKind; user_feedback?: string; }; /** 触发一条 SDK 消息的公开来源。内部协调器元数据不会暴露。 */ export type SDKMessageOrigin = { kind: 'task-notification'; } | { kind: 'peer'; from?: string; senderTaskId?: string; name?: string; body?: string; } | { kind: 'human'; } | { kind: 'bridge'; sessionId?: string; }; /** A skill explicitly selected by the user for this input turn. */ export type SDKSelectedSkill = { name: string; arguments?: string; }; export type SDKUserMessage = { type: 'user'; message: MessageParam; parent_tool_use_id: string | null; custom_context?: Record; /** The client has resolved slash commands, so a leading slash is literal input. */ client_composed?: boolean; /** Skills the CLI must activate as direct user intent before processing this input. */ selected_skills?: SDKSelectedSkill[]; file_attachments?: SDKFileAttachment[]; isSynthetic?: boolean; origin?: SDKMessageOrigin; tool_use_result?: unknown; tool_result_meta?: SDKToolResultMeta[]; /** * Delivery priority. `next` is the default; `now` interrupts the active * turn, while `later` waits for the session to become idle. */ priority?: 'now' | 'next' | 'later'; /** * When false, do not start an assistant turn while the session is idle. * Delivery still follows `priority`: `next` can inject at a safe boundary, * `later` waits for idle, and `now` interrupts the active turn. */ shouldQuery?: boolean; timestamp?: string; uuid?: UUID; session_id?: string; }; export type SDKUserMessageReplay = { type: 'user'; message: MessageParam; parent_tool_use_id: string | null; selected_skills?: SDKSelectedSkill[]; file_attachments?: SDKFileAttachment[]; isSynthetic?: boolean; origin?: SDKMessageOrigin; tool_use_result?: unknown; tool_result_meta?: SDKToolResultMeta[]; /** * Delivery priority. `next` is the default; `now` interrupts the active * turn, while `later` waits for the session to become idle. */ priority?: 'now' | 'next' | 'later'; /** * When false, do not start an assistant turn while the session is idle. * Delivery still follows `priority`: `next` can inject at a safe boundary, * `later` waits for idle, and `now` interrupts the active turn. */ shouldQuery?: boolean; timestamp?: string; uuid: UUID; session_id: string; isReplay: true; }; export type SDKResultSuccess = { type: 'result'; subtype: 'success'; duration_ms: number; duration_api_ms: number; is_error: boolean; num_turns: number; result: string; /** 最后一条模型回复的结束原因;tool_use 不代表 Agent 本轮仍在运行。 */ stop_reason: string | null; total_cost_usd: number; /** Session-cumulative Qoder credits. Added by newer CLI versions. */ total_credits?: number; usage: NonNullableUsage; modelUsage: Record; permission_denials: SDKPermissionDenial[]; error_code?: number | string; deferred_tool_use?: unknown; /** * Agent 本轮的执行终止原因,与模型 stop_reason 独立。 * hook_stopped 表示 Hook 主动结束本轮,即使 stop_reason 为 tool_use 也已停止。 * 旧版 CLI 可能不返回此字段;SDK 原样透传,不推断或改写模型原因。 */ terminal_reason?: string | null; fast_mode_state?: FastModeState; origin?: SDKMessageOrigin; uuid: UUID; session_id: string; }; export type SDKResultError = { type: 'result'; subtype: 'error_during_execution' | 'error_max_turns' | 'error_max_budget_usd'; duration_ms: number; duration_api_ms: number; is_error: boolean; num_turns: number; stop_reason: string | null; total_cost_usd: number; /** Session-cumulative Qoder credits. Added by newer CLI versions. */ total_credits?: number; usage: NonNullableUsage; modelUsage: Record; permission_denials: SDKPermissionDenial[]; errors: string[]; error_code?: number | string; terminal_reason?: string | null; fast_mode_state?: FastModeState; origin?: SDKMessageOrigin; uuid: UUID; session_id: string; }; export type SDKFileAttachment = { file_id: string; relative_path: string; }; export type SDKResultMessage = SDKResultSuccess | SDKResultError; export type SDKSystemMessage = { type: 'system'; subtype: 'init'; agents?: string[]; apiKeySource: ApiKeySource; qodercli_version: string; /** * Wire protocol version (`WIRE_PROTOCOL_VERSION` from `src/protocol/version.ts`). * * Optional for backward compatibility with older CLIs that predate the * handshake — the SDK falls back to a warning when absent. New CLI * builds always emit this field. */ protocol_version?: string; cwd: string; tools: string[]; mcp_servers: Array<{ name: string; status: string; }>; model: string; permissionMode: PermissionMode; slash_commands: string[]; output_style: string; skills: string[]; plugins: PluginInfo[]; /** * Open protocol capability set advertised by the CLI. * * Consumers must check the capability required by each behavior and ignore * unknown values. */ capabilities?: string[]; fast_mode_state?: FastModeState; uuid: UUID; session_id: string; }; export type SDKPartialAssistantMessage = { type: 'stream_event'; event: BetaRawMessageStreamEvent; parent_tool_use_id: string | null; uuid: UUID; session_id: string; }; export type SDKCompactBoundaryMessage = { type: 'system'; subtype: 'compact_boundary'; compact_metadata: { trigger: 'manual' | 'auto'; pre_tokens: number; preserved_segment?: { head_uuid: UUID; anchor_uuid: UUID; tail_uuid: UUID; }; }; uuid: UUID; session_id: string; }; export type SDKStatusMessage = { type: 'system'; subtype: 'status'; status: SDKStatus; permissionMode?: PermissionMode; uuid: UUID; session_id: string; }; export type SDKAPIRetryMessage = { type: 'system'; subtype: 'api_retry'; attempt: number; max_retries: number; retry_delay_ms: number; error_status: number | null; error: SDKAssistantMessageError; uuid: UUID; session_id: string; }; /** Model capacity queue progress emitted while qodercli waits and retries. */ export type SDKModelQueueStatusMessage = { type: 'system'; subtype: 'model_queue_status'; status: 'queued' | 'ready'; request_id: string; request_set_id: string; model_key: string; queue_type?: string; queue_count?: number; wait_time_ms?: number; queue_wait_elapsed_ms?: number; queue_max_wait_ms?: number; service_available?: boolean; uuid: UUID; session_id: string; }; /** Progress emitted for an SDK-initiated side question. */ export type SDKControlRequestProgressMessage = { type: 'system'; subtype: 'control_request_progress'; request_id: string; status: 'started' | 'api_retry'; attempt?: number; max_retries?: number; retry_delay_ms?: number; error_status?: number | null; uuid: UUID; session_id: string; }; /** Delivery state for one UUID-stamped user command in a Session queue. */ export type SDKCommandLifecycleMessage = { type: 'command_lifecycle'; command_uuid: string; state: 'queued' | 'started' | 'completed' | 'cancelled' | 'discarded'; uuid: UUID; session_id: string; }; export type SDKHookStartedMessage = { type: 'system'; subtype: 'hook_started'; hook_id: string; hook_name: string; hook_event: string; uuid: UUID; session_id: string; }; export type SDKHookProgressMessage = { type: 'system'; subtype: 'hook_progress'; hook_id: string; hook_name: string; hook_event: string; stdout: string; stderr: string; output: string; uuid: UUID; session_id: string; }; export type SDKHookResponseMessage = { type: 'system'; subtype: 'hook_response'; hook_id: string; hook_name: string; hook_event: string; output: string; stdout: string; stderr: string; exit_code?: number; outcome: 'success' | 'error' | 'cancelled'; uuid: UUID; session_id: string; }; export type SDKTaskNotificationMessage = { type: 'system'; subtype: 'task_notification'; task_id: string; tool_use_id?: string; status: 'completed' | 'failed' | 'stopped'; output_file: string; summary: string; usage?: { total_tokens?: number; tool_uses: number; duration_ms: number; }; uuid: UUID; session_id: string; }; export type SDKTaskStartedMessage = { type: 'system'; subtype: 'task_started'; task_id: string; tool_use_id?: string; description: string; subagent_type?: string; task_type?: string; workflow_name?: string; prompt?: string; /** Final tool set available to the subagent after policy filtering. */ tools?: string[]; uuid: UUID; session_id: string; }; export type SDKTaskProgressMessage = { type: 'system'; subtype: 'task_progress'; task_id: string; tool_use_id?: string; description: string; usage?: { total_tokens?: number; tool_uses: number; duration_ms: number; }; last_tool_name?: string; subagent_type?: string; summary?: string; uuid: UUID; session_id: string; }; export type SDKTaskUpdatedMessage = { type: 'system'; subtype: 'task_updated'; task_id: string; /** Wire-safe task fields that changed. Consumers merge this patch. */ patch: { status?: 'pending' | 'running' | 'completed' | 'failed' | 'killed' | 'paused'; description?: string; end_time?: number; total_paused_ms?: number; error?: string; is_backgrounded?: boolean; }; uuid: UUID; session_id: string; }; export type SDKBackgroundTasksChangedMessage = { type: 'system'; subtype: 'background_tasks_changed'; /** * Every live background task after the change. This has REPLACE semantics: * consumers replace their current background task set with this payload. */ tasks: Array<{ task_id: string; task_type: string; description: string; }>; uuid: UUID; session_id: string; }; /** * The main session entered or left Plan Mode. * Requires the CLI `plan_mode_v1` capability. */ export type SDKPlanModeChangedMessage = { type: 'system'; subtype: 'plan_mode_changed'; plan_mode: SDKPlanModeSnapshot; uuid: UUID; session_id: string; }; /** * Goal state changed (created / redirected / transitioned / budget update). * Requires the CLI `goal_v1` capability. */ export type SDKGoalUpdatedMessage = { type: 'system'; subtype: 'goal_updated'; goal: SDKGoalSnapshot; /** Why the goal changed, e.g. 'safety-limit' or 'blocked'. */ reason?: string; uuid: UUID; session_id: string; }; /** Goal was cleared. Requires the CLI `goal_v1` capability. */ export type SDKGoalClearedMessage = { type: 'system'; subtype: 'goal_cleared'; goal_id: string; reason?: string; uuid: UUID; session_id: string; }; export type SDKSessionStateChangedMessage = { type: 'system'; subtype: 'session_state_changed'; state: 'idle' | 'running' | 'requires_action'; uuid: UUID; session_id: string; }; /** * Session title update. Emitted only when the host negotiated support. * (Was missing on the SDK side before protocol extraction.) */ export type SDKSessionTitleChangedMessage = { type: 'system'; subtype: 'session_title_changed'; title: string; source: 'ai' | 'custom'; revision: number; uuid: UUID; session_id: string; }; export type SDKFilesPersistedEvent = { type: 'system'; subtype: 'files_persisted'; files: Array<{ filename: string; file_id: string; }>; failed: Array<{ filename: string; error: string; }>; processed_at: string; uuid: UUID; session_id: string; }; export type SDKElicitationCompleteMessage = { type: 'system'; subtype: 'elicitation_complete'; mcp_server_name: string; elicitation_id: string; uuid: UUID; session_id: string; }; export type SDKPromptSuggestionMessage = { type: 'prompt_suggestion'; suggestion: string; uuid: UUID; session_id: string; }; export type SDKCloudAgentEventMessage = { type: 'cloud_agent_event'; event: string; id?: string; data: unknown; uuid: UUID; session_id: string; }; export type SDKMemoryGenerationMessage = { type: 'system'; subtype: 'memory_generation'; result: MemoryGenerationResult; uuid: UUID; session_id: string; }; export type SDKMemoryConsumptionMessage = { type: 'system'; subtype: 'memory_consumption'; result: MemoryConsumptionResult; uuid: UUID; session_id: string; }; export interface SDKAgentsMdSizeWarningMessage { type: 'system'; subtype: 'agents_md_size_warning'; code: 'AGENTS_MD_SIZE_THRESHOLD_REACHED'; severity: 'warning'; total_bytes: number; threshold_bytes: number; threshold_source: 'qcs' | 'default'; files: Array<{ path: string; bytes: number; }>; load_status: 'complete' | 'partial'; truncated: false; uuid: string; session_id: string; } export type SDKSkillEvolutionMessage = { type: 'system'; subtype: 'skill_evolution'; result: SkillEvolutionResult; uuid: UUID; session_id: string; }; export type SDKPermissionDenial = { tool_name: string; tool_use_id: string; tool_input: Record; message?: string; decision_reason_type?: SDKPermissionDeniedReasonType; decision_reason?: string; }; export type SDKPermissionDeniedReasonType = 'rule' | 'mode' | 'subcommandResults' | 'permissionPromptTool' | 'hook' | 'asyncAgent' | 'workingDir' | 'safetyCheck' | 'classifier' | 'other'; export type SDKPermissionDeniedMessage = { type: 'system'; subtype: 'permission_denied'; tool_name: string; tool_use_id: string; message: string; uuid: UUID; session_id: string; agent_id?: string; decision_reason_type?: SDKPermissionDeniedReasonType; decision_reason?: string; }; export type SDKArtifactInfo = { path: string; display_path: string; name: string; group?: { key: string; name: string; }; relative_path?: string; size?: number; mime?: string; mtime?: number; } & ({ kind: 'changed'; additions: number; deletions: number; is_new: boolean; } | { kind: 'presented'; }); export type SDKArtifactUpdateMessage = { type: 'system'; subtype: 'artifacts_update'; artifacts: readonly SDKArtifactInfo[]; uuid: string; session_id: string; }; /** * Authoritative snapshot of the models available to a running Session. * * The CLI emits this after the active model or model catalog changes. Daemon * transports replay the latest snapshot when a Host attaches to an existing * Session, so consumers should replace their local view rather than merge it. */ export type SDKAvailableModelsUpdateMessage = { type: 'system'; subtype: 'available_models_update'; models: ModelInfo[]; currentModel: string; uuid: UUID; session_id: string; }; /** * Authoritative snapshot of commands available to a running Session. * * Consumers should replace their local command catalog instead of merging this * event, because removed commands are intentionally absent from the snapshot. */ export type SDKCommandsChangedMessage = { type: 'system'; subtype: 'commands_changed'; commands: SlashCommand[]; uuid: UUID; session_id: string; }; export type SDKMessage = SDKAgentsMdSizeWarningMessage | SDKAssistantMessage | SDKUserMessage | SDKUserMessageReplay | SDKResultMessage | SDKSystemMessage | SDKArtifactUpdateMessage | SDKAvailableModelsUpdateMessage | SDKCommandsChangedMessage | SDKPermissionDeniedMessage | SDKPartialAssistantMessage | SDKCompactBoundaryMessage | SDKStatusMessage | SDKAPIRetryMessage | SDKModelQueueStatusMessage | SDKControlRequestProgressMessage | SDKCommandLifecycleMessage | SDKHookStartedMessage | SDKHookProgressMessage | SDKHookResponseMessage | SDKTaskNotificationMessage | SDKTaskStartedMessage | SDKTaskProgressMessage | SDKTaskUpdatedMessage | SDKBackgroundTasksChangedMessage | SDKSessionStateChangedMessage | SDKPlanModeChangedMessage | SDKGoalUpdatedMessage | SDKGoalClearedMessage | SDKSessionTitleChangedMessage | SDKFilesPersistedEvent | SDKElicitationCompleteMessage | SDKPromptSuggestionMessage | SDKCloudAgentEventMessage | SDKMemoryGenerationMessage | SDKMemoryConsumptionMessage | SDKSkillEvolutionMessage;