import type { Readable, Writable } from 'stream'; import type { ElicitResult } from '@modelcontextprotocol/sdk/types.js'; import type { AuthOptions, InternalAuthOptions } from './auth.js'; import type { AgentDefinition, AgentInfo } from './agents.js'; import type { AccountInfo, ModelInfo, SdkPluginConfig, SlashCommand, ToolConfig, UsageInfo } from './common.js'; import type { CanUseTool, PermissionMode } from './permissions.js'; import type { HookCallbackMatcher, HookEvent } from './hooks.js'; import type { McpServerConfig, McpServerStatus, McpSetServersResult, OAuthToken } from './mcp.js'; import type { SDKMessage, SDKUserMessage } from './messages.js'; import type { Settings } from './settings.js'; import type { SDKControlInterruptResponse, SDKControlInitializeResponse, SDKControlGetContextUsageResponse, SDKControlGenerateSessionTitleResponse, AskSideQuestionOptions, SideQuestionResult, SDKControlAddDirectoriesResponse, SDKControlReloadPluginsResponse, StdoutMessage } from './control.js'; import type { ModelPolicyProvider } from './model-policy-provider.js'; import type { BYOKModelValidationInput, BYOKProviderInfo } from './byok.js'; import type { RewindFilesResult, RewindResult, RewindScope } from './session.js'; import type { CloudAgentOptions } from './cloud-agent.js'; import type { QueryTransportProviderOption } from '../core/query-transport.js'; import type { PluginDetails } from './plugins.js'; import type { SessionStore, SessionStoreFlush } from '../session/session-store.js'; import type { ModelPromptPatches } from '../protocol/model-prompt-patches.js'; import type { MemoryOptions } from './memory.js'; import type { SDKGoalSnapshot, SDKGoalStatus } from '../protocol/control.js'; export type { CanUseTool }; export type { AuthOptions }; export type ElicitationRequest = { serverName: string; message: string; mode?: 'form' | 'url'; url?: string; elicitationId?: string; requestedSchema?: Record; title?: string; displayName?: string; description?: string; }; export type ElicitationResult = ElicitResult; export type OnElicitation = (request: ElicitationRequest, options: { signal: AbortSignal; }) => Promise; export type PromptRequestOption = { key: string; label: string; description?: string; }; export type PromptRequest = { prompt: string; message: string; options: PromptRequestOption[]; }; export type PromptResponse = { prompt_response: string; selected: string; }; export type SettingSource = 'user' | 'project' | 'local'; export type QueryTransportOptions = QueryTransportProviderOption; /** Built-in code security capabilities. All switches default to false. */ export type SecurityScanOptions = { /** Run the static security check after supported file edits. */ l1StaticCheck?: boolean; /** Allow lightweight repository security scans. */ l2LightweightScan?: boolean; /** Allow deep repository security scans. */ l3DeepScan?: boolean; }; export type Options = { /** * Optional controller for defining when the host ends the session. Calling * `abort()` closes the session and ends message iteration. */ abortController?: AbortController; /** * Authentication configuration for the qodercli child process. * * - `{ type: 'accessToken', accessToken: '...' }` - Use a personal access token. * Prefer the `accessToken(token)` helper in host code. * - `{ type: 'accessToken', accessToken: { envVar: 'QODER_PERSONAL_ACCESS_TOKEN' } }` - * Read a personal access token from an environment variable. * Prefer the `accessTokenFromEnv()` helper in host code. * - `{ type: 'qodercli' }` - Read-only reuse of local `qodercli login` * state. Prefer the `qodercliAuth()` helper in host code. * - `{ type: 'serviceAccount', serviceAccountKey }` - Exchange a Service * Account key for short-lived model access. Prefer `serviceAccount()` or * `serviceAccountFromEnv()`. * - `{ type: 'serviceAccount', fetchServiceAccountToken }` - Let the host * supply and refresh short-lived Service Account tokens through * `serviceAccount()`. * - `{ type: 'jobToken', fetchJobToken }` - Internal first-party host * integration. Prefer the `jobToken(fetchJobToken)` helper in host code. * * Required for `query()` sessions. If omitted, `query()` throws an * `auth_not_configured` error before spawning qodercli. */ auth?: InternalAuthOptions; additionalDirectories?: string[]; /** * Opaque business identifiers forwarded to every local qodercli inference * request unless a user message supplies its own `custom_context`. * * Backend-enforced limits are 1–8 string entries, keys up to 32 Unicode code * points, values up to 128 Unicode code points, and at most 1024 UTF-8 bytes * for the serialized JSON object. Do not include tokens, Service Account * keys, email addresses, phone numbers, other credentials, or sensitive * personal data. Backend acceptance requires Service Account authentication * and a qoder VPC deployment. */ customContext?: Record; /** * @experimental * @unstable Cloud Agent runtime is experimental and may change without * semver guarantees. */ experimentalCloudAgent?: CloudAgentOptions; /** * Selects the local query transport. * * - omitted: use the transport baked into the installed SDK package. * - `ProcessTransport.default`: spawn qodercli as a child process. * - `WorkerTransport.default`: run qoder-worker-runtime in a Node worker thread. * - `new WorkerTransport({ ... })`: run worker runtime with provider options. * - custom `QueryTransportProvider`: provide a fresh transport per query. */ transport?: QueryTransportOptions; agent?: string; agents?: Record; allowedTools?: string[]; canUseTool?: CanUseTool; continue?: boolean; cwd?: string; disallowedTools?: string[]; tools?: string[] | { type: 'preset'; preset: 'qodercli'; }; /** * Skills to enable for the main session. * * - `undefined` (default): no SDK auto-configuration. The CLI's own defaults * still apply. * - `'all'`: enable every discovered skill (adds `"Skill"` to allowedTools). * - `string[]`: enable only the listed skills. Names match the SKILL.md * `name` / directory name, or `plugin:skill` for plugin-qualified skills. * Adds `"Skill()"` for each entry to allowedTools. * * This is a context filter, not a security boundary: unlisted skills are * hidden from the model's listing and rejected by the Skill tool, but their * files remain on disk and are reachable via Read/Bash. */ skills?: string[] | 'all'; extensions?: string[]; /** * Proxy URL used by the qodercli child process for outbound network traffic. * * Supports the same schemes as qodercli: `http://`, `https://`, * `socks5://`, and `socks://`. When set, the SDK writes this value to * `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY`, and `http_proxy` in the child * environment. */ proxy?: string; /** * VPC private-deployment endpoint for the qodercli child process (CN * deployments only). * * Accepts either a bare instance name (`acme`) or a full VPC domain * (`acme.vpc.qoder.com.cn`, `acme-gateway.vpc.qoder.com.cn`, * `acme-openapi.vpc.qoder.com.cn`). When set, the SDK writes this value to * the `QODERCN_VPC_ENDPOINT` (CN) / `QODER_VPC_ENDPOINT` (Global) * environment variable, which the CLI reads at highest priority — above any * `vpcInstanceName` in `settings.json` — to derive its inference, OpenAPI, * and base endpoints locally without remote endpoint election. * * VPC mode only activates in CN builds of the CLI; the value is ignored by * Global builds. */ vpcEndpoint?: string; env?: { [envVar: string]: string | undefined; }; executable?: 'bun' | 'deno' | 'node'; executableArgs?: string[]; extraArgs?: Record; enableFileCheckpointing?: boolean; toolConfig?: ToolConfig; forkSession?: boolean; /** * When resuming, load the conversation chain only through this entry UUID. * Use with `resume`; when combined with `forkSession`, the new session starts * from this historical point while retaining its file checkpoint history. * The UUID may identify any persisted chain entry, not only an assistant * message. Use the last entry belonging to the turn that must be kept. */ resumeSessionAt?: string; /** * With `resumeSessionAt`, declares the prompt UUID of the turn intended to be * discarded. The CLI refuses the truncation if the discarded range contains * unrelated work such as a queued prompt or task notification. * * A refusal is returned as an execution error whose message starts with * `Resume rejected by --resume-drops-turn:`. Treat it as a deterministic * stale-view conflict: resume without truncation to recover the new evidence * instead of retrying the same request. */ resumeDropsTurn?: string; hooks?: Partial>; /** * Configure QoderCLI-owned memory generation and consumption for this query. * Functions stay in the SDK process and are bridged through control requests. */ memory?: MemoryOptions; onElicitation?: OnElicitation; /** * Called when the SDK detects that authentication has expired or * become invalid during a query session. This can happen when: * - An `assistant` message carries `error: 'authentication_failed'` * - The CLI process exits with a non-zero code and stderr indicates * an auth-related failure. * * The callback is invoked at most once per session. Use it to trigger * a re-authentication flow or surface the failure to the user. */ onAuthExpired?: () => void; /** * Upper bound (ms) for any outbound control_request the SDK sends to * the cli (setMcpServers, reconnectMcpServer, mcpServerStatus, …). * On timeout the pending Promise rejects and a control_cancel_request * is written so the cli can clean up. Defaults to 60_000. Pass 0 to * disable. */ controlRequestTimeoutMs?: number; /** * Grace period in milliseconds before the local transport terminates qodercli * after closing stdin. Defaults to 2000. */ closeGraceMs?: number; includePartialMessages?: boolean; includeHookEvents?: boolean; maxTurns?: number; mcpServers?: Record; allowedMcpServerNames?: string[]; model?: string; /** * Path to a local Qoder runtime. * * Native binaries and ordinary JavaScript CLI entries use ProcessTransport. * Recognized `qoder-worker-runtime*.mjs` entries use WorkerTransport. An * explicit `transport` provider takes precedence over this automatic choice. */ pathToQoderCLIExecutable?: string; permissionMode?: PermissionMode; allowDangerouslySkipPermissions?: boolean; /** * MCP tool name to use for permission prompts. Mutually exclusive with * `canUseTool`, which automatically uses the stdio permission prompt. */ permissionPromptToolName?: string; plugins?: SdkPluginConfig[]; promptSuggestions?: boolean; /** * Persist the local session transcript. Defaults to true. * * Set to false for an ephemeral query that cannot be resumed. This option * cannot be combined with sessionStore because external mirroring consumes * locally committed transcript entries. */ persistSession?: boolean; resume?: string; /** Mirrors locally committed transcript entries to an external store. */ sessionStore?: SessionStore; /** `batched` flushes at result boundaries; `eager` drains every CLI frame. */ sessionStoreFlush?: SessionStoreFlush; /** Timeout for loading a session from an external store. Defaults to 60 seconds. */ loadTimeoutMs?: number; sessionId?: string; settings?: string | Settings; /** * Enables built-in code security capabilities for this SDK session. * Omitted switches are disabled. This option is authoritative when provided. */ securityScan?: SecurityScanOptions; settingSources?: SettingSource[]; stderr?: (data: string) => void; strictMcpConfig?: boolean; systemPrompt?: string | { type: 'preset'; preset: 'qodercli'; append?: string; }; /** * Prompt patches forwarded to the model service for model-aware system * prompt selection. Route keys are interpreted by the service. */ modelRequestPatches?: ModelPromptPatches; /** * Pull-mode model selection callback. * * When provided, the SDK enters pull mode: the CLI asks the SDK before * every LLM call, and this callback is the sole decider of the model. * `options.model` and the CLI's model router are bypassed. * * When omitted, the SDK uses push mode: the CLI uses its local resolution * chain (`options.model` / settings / model router). * * Timeout/exceptions are propagated to the caller — no silent fallback. */ resolveModel?: ModelPolicyProvider; /** * Timeout (ms) for the resolveModel callback. * If the callback does not resolve within this time, a `ModelPolicyTimeoutError` * is thrown to the caller. * @default 500 */ resolveModelTimeoutMs?: number; /** * Enable SDK debug logging and forward `--debug` to the qodercli child * process. Without `debugFile`, SDK debug output is written to `stderr` * callback when present, otherwise to process stderr. */ debug?: boolean; /** * Path for SDK-internal debug logs. This implicitly enables SDK debug * logging, but is not forwarded to qodercli. */ debugFile?: string; spawnQoderCLIProcess?: (options: SpawnOptions) => SpawnedProcess; }; export interface SpawnOptions { command: string; args: string[]; cwd?: string; env: { [envVar: string]: string | undefined; }; signal: AbortSignal; } export interface SpawnedProcess { stdin: Writable; stdout: Readable; readonly killed: boolean; readonly exitCode: number | null; kill(signal: NodeJS.Signals): boolean; on(event: 'exit', listener: (code: number | null, signal: NodeJS.Signals | null) => void): void; on(event: 'error', listener: (error: Error) => void): void; once(event: 'exit', listener: (code: number | null, signal: NodeJS.Signals | null) => void): void; once(event: 'error', listener: (error: Error) => void): void; off(event: 'exit', listener: (code: number | null, signal: NodeJS.Signals | null) => void): void; off(event: 'error', listener: (error: Error) => void): void; } /** * Query 公开接口。 * * MCP 鉴权采用"主动驱动"(pull)模式,host 必须在首次 `streamInput` 之前 * 完成所有需要授权的 server: * 1. `await q.mcpServerStatus()` → 找出 `'needs-auth'` 的 server * 2. `await q.mcpAuthenticate(name)` → 拿到 authUrl 让用户授权 * (静默续期成功时返回 `{ requiresUserAction: false }`,无需 UI) * 3. `await q.mcpSubmitOAuthCallbackUrl(name, callbackUrl)` → 闭环 * 4. 然后 `await q.streamInput(...)` 发送首条用户消息 * * **鉴权必须在首次 `streamInput` 之前完成**,否则会话中途完成鉴权会重建 * tools 列表,导致 prompt 前缀缓存失效。 * * 配置 MCP servers 请通过 `Options.mcpServers`(启动时一次性配置)。 */ export interface Query extends AsyncGenerator { /** * Request interruption of the active turn. * * CLIs advertising `interrupt_receipt_v1` return UUIDs of queued messages * that survive the interrupt. Older CLIs return `undefined`. */ interrupt(): Promise; /** * Remove a UUID-stamped async user message while it is still queued. * * Returns `true` only when the CLI removes that UUID from the queue. * * After messages have been dequeued into the in-flight coalesced batch, this * method returns `false`. While that reservation remains active, cancelling * the last UUID-bearing message marks the batch to be dropped; cancelling a * non-representative UUID is a no-op. `false` alone does not prove a batch * was dropped. */ cancelAsyncMessage(messageUuid: string): Promise; /** Stop one running task without interrupting the main session turn. */ stopTask(taskId: string): Promise; /** * Move foreground Bash/Agent executions into the background. When a tool * use ID is provided only that execution is targeted; otherwise all * eligible foreground executions are targeted. */ backgroundTasks(toolUseId?: string): Promise; /** * Create, redirect, transition, or re-budget the session goal. Omitted * fields are left unchanged; `creditsBudget: null` explicitly clears the * budget. Setting `status: 'active'` while the session is idle starts the * goal loop. Requires the CLI `goal_v1` capability. Only present on the * local CLI transport; absent on cloud-agent queries. */ setGoal?(params: { objective?: string; status?: SDKGoalStatus; creditsBudget?: number | null; }): Promise; /** Read the current goal, or null when the session has none. */ getGoal?(): Promise; /** Clear the session goal; returns whether a goal was cleared. */ clearGoal?(): Promise; setPermissionMode(mode: PermissionMode): Promise; setModel(model?: string): Promise; /** * Set or clear the proxy for the running qodercli session. * * Supports `http://`, `https://`, `socks5://`, and `socks://`. * Pass `undefined`, `null`, or an empty string to clear the proxy. */ setProxy(proxy?: string | null): Promise; /** * Generate a concise display title for the current session. * * qodercli returns the generated title through the control response. The * current headless control path does not persist the title; existing * `ai-title` transcript entries are still surfaced by `listSessions()` and * `getSessionInfo()`. */ generateSessionTitle(description: string, options?: { persist?: boolean; }): Promise; /** * Ask a one-off question using the current conversation context without * interrupting or mutating the main agent turn. */ askSideQuestion?(question: string, options?: AskSideQuestionOptions): Promise; /** * Add directories to the running session's workspace context. * * This is session-scoped. It does not write `.qoder/settings.local.json`. * The CLI validates each path and returns both accepted and rejected * entries. */ addDirectories(directories: string[]): Promise; applyFlagSettings(settings: Settings): Promise; initializationResult(): Promise; supportedCommands(): Promise; /** * Fetch the available model list from the CLI in real-time. * Sends a `get_models` control request to the CLI and returns * the latest available models, filtered by the session-level scene. */ getAvailableModels(options?: { fetchStrategy?: 'live' | 'cache'; uid?: string; }): Promise; /** * List the available BYOK (Bring Your Own Key) providers and their supported models. * The CLI queries the server-side BYOK provider catalog (cached for 5 min). * * Returns `null` if BYOK is disabled or the CLI does not support this request. */ listByokProviders(): Promise; /** * Validate a BYOK provider/model/API-key combination through the CLI. * Returns `true` for a valid configuration, `false` for a rejected * configuration, and `null` when the running CLI does not support this * control request. */ validateByokModel(input: BYOKModelValidationInput): Promise; supportedAgents(): Promise; mcpServerStatus(): Promise; getContextUsage(): Promise; /** * Fetch current account quota and usage information from the running CLI. * Returns `null` when the CLI is unauthenticated or does not support the * account usage endpoint. */ getUsageInfo(): Promise; reloadPlugins(): Promise; listPlugins(): Promise; accountInfo(): Promise; rewindFiles(userMessageId: string, options?: { dryRun?: boolean; }): Promise; /** * Rewind the active local CLI session to immediately before a historical * top-level user input. The edited replacement must be submitted as a new * user message with a new UUID after this Promise resolves successfully. */ rewind(userMessageId: string, options?: { scope?: RewindScope; dryRun?: boolean; }): Promise; seedReadState(path: string, mtime: number): Promise; /** Wait until pending TurnComplete memory generation has finished. */ flushMemory(): Promise; /** Reload configured memory consumption files without changing config. */ refreshMemory(): Promise; /** * 主动驱动 MCP OAuth:拉起 OAuth 流程,返回授权 URL 让 host 引导用户跳转。 * * 返回语义: * - `{ requiresUserAction: false }` — cli 用 cached client + 有效 refresh * token 静默完成续期,host 无需弹浏览器/UI。 * - `{ authUrl, requiresUserAction: true }` — host 需要把 URL 拉给用户 * 完成授权,然后调 `mcpSubmitOAuthCallbackUrl` 闭环。 * * `redirectUri` 可选,让 host 指定自定义 redirect(Electron deep-link、 * 企业内网回调地址等)。 * * **必须在首次 `streamInput` 前调用**,否则鉴权完成后会重建 tools 列表, * 破坏 prompt prefix 缓存。 */ mcpAuthenticate(serverName: string, redirectUri?: string): Promise<{ authUrl?: string; requiresUserAction: boolean; }>; /** * 提交 OAuth 回调 URL,完成主动驱动鉴权流程。 * * **必须在首次 `streamInput` 前调用**,否则会破坏 prompt prefix 缓存。 */ mcpSubmitOAuthCallbackUrl(serverName: string, callbackUrl: string): Promise; streamInput(stream: AsyncIterable): Promise; close(): Promise; [Symbol.asyncDispose](): Promise; } /** * SDK 内部 Query 接口,包含会重建 tools 列表 / 破坏 prompt prefix 缓存的 * MCP 运行时方法。仅供 SDK 内部实现与协议测试使用,不从 `src/index.ts` * 公开导出。 * * 公开 API 的对应能力: * - server 集合的配置 → `Options.mcpServers`(启动时)/ `Options.allowedMcpServerNames` * - server 重连 / 凭据清除 → 重启 `query()` * - OAuth → `Query.mcpAuthenticate` / `Query.mcpSubmitOAuthCallbackUrl` * * `injectMcpToken` 在这里仅供 SDK 内部 / 测试使用——它跳过 OAuth 标准流程 * 直接注入 token,把凭据合规性的责任完全交给宿主,不应作为公开 API 推广。 */ export interface InternalQuery extends Query { setMcpServers(servers: Record): Promise; toggleMcpServer(serverName: string, enabled: boolean): Promise; reconnectMcpServer(serverName: string): Promise; mcpClearAuth(serverName: string): Promise; injectMcpToken(serverName: string, token: OAuthToken): Promise; } export interface Transport { initialize(): Promise; write(data: string): void | Promise; close(): void; isReady(): boolean; readMessages(): AsyncGenerator; endInput(): void; }