import { AsyncLocalStorage } from 'node:async_hooks'; import type { SwarmModeTrigger } from '@moonshot-ai/agent-core-v2/features/swarm/agent/swarm'; import type { Kaos } from '@moonshot-ai/kaos'; import type { AgentContextData } from '#/context'; import { ErrorCodes, makeErrorPayload } from '#/errors'; import type { ApprovalHandler, Event, QuestionHandler, } from '#/events'; import type { ExperimentalFeatureState } from '#/flag'; import type { ApprovalRequest, ApprovalResponse, QuestionRequest, QuestionResult, ToolCallRequest, ToolCallResponse, } from '#/interaction'; import type { BeginGlobalMcpServerAuthResult } from '#/mcp'; import type { AddAdditionalDirInput, AddAdditionalDirResult, AgentCommandInfo, AgentRuntimeBinding, AppMcpServerInspection, BackgroundTaskInfo, ConfigDiagnostics, CreateSessionOptions, ExportSessionInput, ExportSessionResult, CreateGoalInput, FileMeta, ForkSessionInput, GenerateSessionTitleInput, GetConfigOptions, GetCronTasksResult, GlobalMcpServerAuthStatus, McpManagedServerInfo, McpServerConfig, McpServerLocator, GoalSnapshot, GoalToolResult, JsonObject, KimiConfig, KimiConfigPatch, ListSessionsOptions, McpServerInfo, McpStartupMetrics, McpTestResult, PermissionMode, PluginInfo, PluginSummary, ReloadSummary, CompactOptions, SessionPlan, SessionStatus, SessionTodoItem, SessionUsage, PromptInput, PromptSkillActivation, RenameSessionInput, ResumeSessionInput, ResumedSessionSummary, SessionSummary, SessionSummaryPage, SkillSummary, PluginCommandDef, SuggestFilesInput, SuggestFilesResult, Unsubscribe, UploadFileOptions, WorkspaceTrustInfo, } from '#/types'; const MAIN_AGENT_ID = 'main'; export interface SessionPromptRpcInput { readonly sessionId: string; readonly input: PromptInput; readonly promptId?: string; } export interface SessionPromptWithSkillsRpcInput extends SessionPromptRpcInput { readonly skills: readonly PromptSkillActivation[]; } export interface SessionIdRpcInput { readonly sessionId: string; } export interface ImportContextRpcInput extends SessionIdRpcInput { readonly content: string; readonly source: string; } export interface ReloadSessionRpcInput extends SessionIdRpcInput { readonly forcePluginSessionStartReminder?: boolean; } export interface SetSessionModelRpcInput extends SessionIdRpcInput { readonly model: string; } export interface SetSessionModelRpcResult { readonly model: string; readonly providerName?: string | undefined; } export interface SetSessionThinkingRpcInput extends SessionIdRpcInput { readonly effort: string; } export interface SetSessionPermissionRpcInput extends SessionIdRpcInput { readonly mode: PermissionMode; } export interface UpdateSessionMetadataRpcInput extends SessionIdRpcInput { readonly metadata: JsonObject; } export interface SetSessionPlanModeRpcInput extends SessionIdRpcInput { readonly enabled: boolean; } export type SetSessionSwarmModeRpcInput = | (SessionIdRpcInput & { readonly enabled: true; readonly trigger: SwarmModeTrigger }) | (SessionIdRpcInput & { readonly enabled: false }); export interface SetSessionTowerModeRpcInput extends SessionIdRpcInput { readonly enabled: boolean; readonly base?: string; } export interface ActivateSkillRpcInput extends SessionIdRpcInput { readonly name: string; readonly args?: string | undefined; } export interface ActivatePluginCommandRpcInput extends SessionIdRpcInput { readonly pluginId: string; readonly commandName: string; readonly args?: string | undefined; } export interface RunCommandRpcInput extends SessionIdRpcInput { readonly name: string; readonly args?: string | undefined; } export interface SwitchSessionRuntimeRpcInput extends SessionIdRpcInput { readonly runtimeId: string; } export interface ReconnectMcpServerRpcInput extends SessionIdRpcInput { readonly name: string; readonly config?: McpServerConfig; } export interface SessionWarningInfo { readonly code: string; readonly message: string; readonly severity: 'info' | 'warning' | 'error'; } export abstract class SDKRpcClientBase { private readonly interactiveAgentScope = new AsyncLocalStorage(); private readonly eventListeners = new Set<(event: Event) => void>(); private readonly approvalHandlers = new Map(); private readonly questionHandlers = new Map(); get interactiveAgentId(): string { return this.interactiveAgentScope.getStore() ?? MAIN_AGENT_ID; } withInteractiveAgent(agentId: string, fn: () => T): T { return this.interactiveAgentScope.run(agentId, fn); } abstract createSession(input: CreateSessionOptions): Promise; async createSessionWithKaos( input: CreateSessionOptions, kaos: Kaos, persistenceKaos?: Kaos, ): Promise { void kaos; void persistenceKaos; return this.createSession(input); } abstract resumeSession(input: ResumeSessionInput): Promise; async resumeSessionWithKaos( input: ResumeSessionInput, kaos: Kaos, persistenceKaos?: Kaos, ): Promise { void kaos; void persistenceKaos; return this.resumeSession(input); } abstract reloadSession(input: ReloadSessionRpcInput): Promise; abstract forkSession(input: ForkSessionInput): Promise; abstract closeSession(input: SessionIdRpcInput): Promise; abstract deleteSession(input: SessionIdRpcInput): Promise; abstract listSessions(input?: ListSessionsOptions): Promise; abstract listSessionsPage(input?: ListSessionsOptions): Promise; abstract listWorkspaceSkills(workDir: string): Promise; abstract getWorkspaceTrustInfo(workDir: string): Promise; abstract trustWorkspace(workDir: string): Promise; abstract renameSession(input: RenameSessionInput): Promise; abstract generateSessionTitle(input: GenerateSessionTitleInput): Promise; abstract exportSession(input: ExportSessionInput): Promise; abstract getConfig(input?: GetConfigOptions): Promise; abstract getConfigDiagnostics(): Promise; abstract getExperimentalFeatures(): Promise; abstract setConfig(input: KimiConfigPatch): Promise; abstract removeProvider(providerId: string): Promise; abstract supportsAtomicSectionReplace(): boolean; abstract replaceConfigSections(sections: Record): Promise; abstract uploadFile(data: Uint8Array, options: UploadFileOptions): Promise; abstract deleteFile(fileId: string): Promise; abstract listGlobalMcpServers(options?: { readonly cwd?: string; }): Promise; abstract getGlobalMcpServer( name: string, options?: { readonly cwd?: string }, ): Promise; abstract listGlobalMcpServerAuthStatuses(options?: { readonly cwd?: string; readonly verify?: boolean; }): Promise; abstract inspectAppMcpServers( targets?: readonly McpServerLocator[], options?: { readonly cwd?: string }, ): Promise; abstract addGlobalMcpServer( server: McpServerConfig, options?: { readonly cwd?: string }, ): Promise; abstract updateGlobalMcpServer( server: McpServerConfig, options?: { readonly cwd?: string }, ): Promise; abstract removeGlobalMcpServer( name: string, options?: { readonly cwd?: string }, ): Promise; abstract beginGlobalMcpServerAuth( name: string, options?: { readonly cwd?: string }, ): Promise; abstract beginMcpServerAuth( locator: McpServerLocator, options?: { readonly cwd?: string }, ): Promise; abstract completeGlobalMcpServerAuth( input: { readonly flowId: string; readonly timeoutMs?: number }, signal?: AbortSignal, ): Promise; abstract completeMcpServerAuth( input: { readonly flowId: string; readonly timeoutMs?: number }, signal?: AbortSignal, ): Promise; abstract cancelGlobalMcpServerAuth(flowId: string): Promise; abstract cancelMcpServerAuth(flowId: string): Promise; abstract resetGlobalMcpServerAuth(name: string, options?: { readonly cwd?: string }): Promise; abstract resetMcpServerAuth( locator: McpServerLocator, options?: { readonly cwd?: string }, ): Promise; abstract testGlobalMcpServer( name: string, options?: { readonly cwd?: string }, ): Promise; abstract testGlobalMcpServerConfig( server: McpServerConfig, options?: { readonly cwd?: string }, ): Promise; abstract prompt(input: SessionPromptRpcInput): Promise; abstract promptWithSkills(input: SessionPromptWithSkillsRpcInput): Promise; abstract runShellCommand(input: { sessionId: string; command: string; commandId?: string; }): Promise<{ stdout: string; stderr: string; isError?: boolean; backgrounded?: boolean }>; abstract cancelShellCommand(input: { sessionId: string; commandId: string }): Promise; abstract steer(input: SessionPromptRpcInput): Promise; abstract generateAgentsMd(input: SessionIdRpcInput): Promise; abstract getSessionWarnings(input: SessionIdRpcInput): Promise; abstract addAdditionalDir(input: AddAdditionalDirInput): Promise; abstract startBtw(input: SessionIdRpcInput): Promise; abstract cancel(input: SessionIdRpcInput): Promise; abstract clearContext(input: SessionIdRpcInput): Promise; abstract importContext(input: ImportContextRpcInput): Promise; abstract setModel(input: SetSessionModelRpcInput): Promise; abstract setThinking(input: SetSessionThinkingRpcInput): Promise; abstract setPermission(input: SetSessionPermissionRpcInput): Promise; abstract updateSessionMetadata(input: UpdateSessionMetadataRpcInput): Promise; abstract setPlanMode(input: SetSessionPlanModeRpcInput): Promise; abstract setSwarmMode(input: SetSessionSwarmModeRpcInput): Promise; abstract swarm(input: SessionPromptRpcInput): Promise; abstract setTowerMode(input: SetSessionTowerModeRpcInput): Promise; abstract getPlan(input: SessionIdRpcInput): Promise; abstract clearPlan(input: SessionIdRpcInput): Promise; abstract compact(input: SessionIdRpcInput & CompactOptions): Promise; abstract cancelCompaction(input: SessionIdRpcInput): Promise; abstract getTodos(input: SessionIdRpcInput): Promise; abstract undoHistory(input: SessionIdRpcInput & { count: number }): Promise; abstract getContext(input: SessionIdRpcInput): Promise; abstract getUsage(input: SessionIdRpcInput): Promise; abstract getStatus(input: SessionIdRpcInput): Promise; abstract listSkills(input: SessionIdRpcInput): Promise; abstract listPluginCommands(input: SessionIdRpcInput): Promise; abstract listPluginCommandsGlobal(): Promise; abstract suggestFiles( workDir: string, input: SuggestFilesInput, ): Promise; abstract listBackgroundTasks( input: SessionIdRpcInput & { activeOnly?: boolean; limit?: number }, ): Promise; abstract getBackgroundTaskOutput( input: SessionIdRpcInput & { taskId: string; tail?: number }, ): Promise; abstract stopBackgroundTask( input: SessionIdRpcInput & { taskId: string; reason?: string }, ): Promise; abstract detachBackgroundTask( input: SessionIdRpcInput & { taskId: string }, ): Promise; abstract waitForBackgroundTasksOnPrint(input: SessionIdRpcInput): Promise; abstract handlePrintMainTurnCompleted(input: SessionIdRpcInput): Promise<'finish' | 'continue'>; abstract createGoal(input: SessionIdRpcInput & CreateGoalInput): Promise; abstract getGoal(input: SessionIdRpcInput): Promise; abstract pauseGoal(input: SessionIdRpcInput): Promise; abstract resumeGoal(input: SessionIdRpcInput): Promise; abstract cancelGoal(input: SessionIdRpcInput): Promise; abstract getCronTasks(input: SessionIdRpcInput): Promise; abstract listMcpServers(input: SessionIdRpcInput): Promise; abstract listWorkspaceMcpServers(workDir: string): Promise; abstract getMcpStartupMetrics(input: SessionIdRpcInput): Promise; abstract reconnectMcpServer(input: ReconnectMcpServerRpcInput): Promise; abstract addSessionMcpServer(input: { readonly sessionId: string; readonly server: McpServerConfig; readonly persist?: boolean; }): Promise; abstract listPlugins(): Promise; abstract installPlugin(source: string): Promise; abstract setPluginEnabled(id: string, enabled: boolean): Promise; abstract setPluginMcpServerEnabled(id: string, server: string, enabled: boolean): Promise; abstract removePlugin(id: string): Promise; abstract reloadPlugins(): Promise; abstract getPluginInfo(id: string): Promise; abstract activateSkill(input: ActivateSkillRpcInput): Promise; abstract activatePluginCommand(input: ActivatePluginCommandRpcInput): Promise; abstract listCommands(input: SessionIdRpcInput): Promise; abstract runCommand(input: RunCommandRpcInput): Promise; abstract getRuntime(input: SessionIdRpcInput): Promise; abstract switchRuntime(input: SwitchSessionRuntimeRpcInput): Promise; onEvent(listener: (event: Event) => void): Unsubscribe { this.eventListeners.add(listener); return () => { this.eventListeners.delete(listener); }; } receiveEvent(event: Event): void { for (const listener of this.eventListeners) { listener(event); } } setApprovalHandler(sessionId: string, handler: ApprovalHandler | undefined): void { if (handler === undefined) { this.approvalHandlers.delete(sessionId); return; } this.approvalHandlers.set(sessionId, handler); } setQuestionHandler(sessionId: string, handler: QuestionHandler | undefined): void { if (handler === undefined) { this.questionHandlers.delete(sessionId); return; } this.questionHandlers.set(sessionId, handler); } clearSessionHandlers(sessionId: string): void { this.approvalHandlers.delete(sessionId); this.questionHandlers.delete(sessionId); } async requestApproval( request: ApprovalRequest & { sessionId: string; agentId: string }, ): Promise { const handler = this.approvalHandlers.get(request.sessionId); if (handler === undefined) { return { decision: 'cancelled', feedback: 'No approval handler registered.', }; } try { return await handler(request); } catch (error) { this.receiveEvent({ type: 'error', sessionId: request.sessionId, agentId: request.agentId, ...makeErrorPayload(ErrorCodes.SESSION_APPROVAL_HANDLER_ERROR, errorMessage(error)), }); return { decision: 'cancelled', feedback: 'Approval handler failed.', }; } } async requestQuestion( request: QuestionRequest & { sessionId: string; agentId: string }, ): Promise { const handler = this.questionHandlers.get(request.sessionId); if (handler === undefined) return null; try { return await handler(request); } catch (error) { this.receiveEvent({ type: 'error', sessionId: request.sessionId, agentId: request.agentId, ...makeErrorPayload(ErrorCodes.SESSION_QUESTION_HANDLER_ERROR, errorMessage(error)), }); return null; } } async toolCall(request: ToolCallRequest): Promise { return { output: `SDK custom tool calls are not supported: ${request.toolCallId}`, isError: true, }; } } function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); }