import type { TurnOrigin } from '@xopcai/endpoint-tools-protocol'; import type { ClarificationResponseAction } from '@xopcai/gateway-contract'; import type { Config } from '../../config/schema.js'; import type { MessageBus } from '../../infra/bus/index.js'; import type { AgentService } from '../../agent/service.js'; import type { ChannelManager } from '../../channels/manager.js'; import type { SessionIndex } from '../../session/index.js'; import { type ClarificationStreamEvent } from '../ephemeral-clarification-waiter.js'; import type { ClarifyRequestPayload, ClarifyRequestResult } from '../../agent/tools/clarify-tool.js'; import type { UserTurnAttachment, UserTurnInput } from '../user-turn-input.js'; import type { AgentSourceContext, TurnContextRef } from '../../agent/source-context/types.js'; import { SessionInputCoordinator, type ReplaceLatestTurnInput, type SubmitSessionInput } from './session-input-coordinator.js'; export interface GatewayAgentRunnerOptions { validateConnectionResume?: (input: import('../../storage/sqlite/session-input-repository.js').SessionInput) => Promise; bus: MessageBus; sessionIndex: SessionIndex; /** Resolved lazily — the runner is constructed before AgentService exists. */ getAgentService: () => AgentService; getChannelManager: () => ChannelManager; getConfig: () => Config; /** Publish low-frequency gateway state changes. */ emit: (type: string, payload: unknown) => void; publishRealtime: (topic: string, event: string, data: unknown) => void; completeRealtimeTopic: (topic: string) => void; resolveTurnContext: (ref: TurnContextRef) => Promise; } export declare class GatewayAgentRunner { private readonly opts; /** Per-run abort for webchat (POST /api/agent/abort or client disconnect). */ private readonly runAbortControllers; private readonly runCompletions; private readonly resolveRunCompletions; private readonly ephemeralClarifications; /** Maps webchat session key → active `runId` for `clarify` tool routing. */ private readonly activeWebchatRunBySession; private readonly externalStreamBySession; private readonly externalClarificationResponses; readonly inputs: SessionInputCoordinator; private readonly unsubscribeConnectionWait; constructor(opts: GatewayAgentRunnerOptions); /** True when a webchat agent run is currently in-flight for `sessionKey`. */ hasActiveRun(sessionKey: string): boolean; getActiveRunId(sessionKey: string): string | undefined; listActiveRuns(): Array<{ sessionKey: string; runId: string; }>; disposeClarifications(): void; registerExternalWebchatRun(sessionKey: string, runId: string, publish: (event: ClarificationStreamEvent) => void, options?: { beforeClarificationResponse?: () => boolean; }): void; unregisterExternalWebchatRun(sessionKey: string, runId: string): void; cancelClarificationForRun(runId: string): void; runAgent(message: string, channel: string, chatId: string, origin: TurnOrigin, attachments?: UserTurnAttachment[], thinking?: string, runOptions?: { signal?: AbortSignal; runId?: string; taskRunId?: string; sourceContexts?: AgentSourceContext[]; presentation?: 'voice'; }): AsyncGenerator<{ type: string; [key: string]: unknown; }, { status: string; summary: string; }, unknown>; submitSessionInput(input: SubmitSessionInput): Promise<{ ok: true; effectiveDelivery: import("../../storage/sqlite/session-input-repository.js").SessionInputDelivery; state: import("../../storage/sqlite/session-input-repository.js").SessionInputState; } | { ok: false; code: "BAD_REQUEST" | "QUEUE_FULL" | "CONTEXT_UNAVAILABLE" | "SESSION_CHANGED"; }>; replaceLatestSessionTurn(input: ReplaceLatestTurnInput): Promise<{ ok: true; effectiveDelivery: "next"; state: import("../../storage/sqlite/session-input-repository.js").SessionInputState; } | { ok: false; code: "BAD_REQUEST" | "TARGET_NOT_FOUND" | "NOT_LATEST" | "SESSION_BUSY" | "CONTEXT_UNAVAILABLE"; }>; getSessionInputState(sessionKey: string): import("../../storage/sqlite/session-input-repository.js").SessionInputState; updateSessionInput(sessionKey: string, id: string, body: { version: number; content?: string; attachments?: UserTurnAttachment[]; contextRefs?: TurnContextRef[]; thinking?: string; position?: number; }): Promise<{ ok: boolean; state: import("../../storage/sqlite/session-input-repository.js").SessionInputState; contextUnavailable?: boolean; }>; removeSessionInput(sessionKey: string, id: string, version: number): { ok: boolean; state: import("../../storage/sqlite/session-input-repository.js").SessionInputState; }; recoverSessionInputs(): void; /** Abort an in-flight webchat agent run. */ abortAgentRun(runId: string): Promise<{ aborted: boolean; idle: boolean; }>; answerEphemeralClarification(requestId: string, answer: string): boolean; getClarificationState(sessionKey: string): import("@xopcai/gateway-contract").ClarificationWaitSnapshot; resolveClarificationResponse(input: { id: string; expectedVersion: number; idempotencyKey: string; action: ClarificationResponseAction; answer?: string; }): import("../../storage/sqlite/clarification-wait-repository.js").ResolveClarificationResult; getClarificationById(id: string): import("@xopcai/gateway-contract").ClarificationWait; answerClarificationChoice(requestId: string, choiceIndex: number, idempotencyKey: string): boolean; answerClarificationText(sessionKey: string, answer: string, idempotencyKey: string): boolean; /** Same execution path as scheduled continuation, but lets callers observe failures. */ runScheduledWebchatTurn(sessionKey: string, userTurn: UserTurnInput): Promise; runScheduledWebchatContinuation(sessionKey: string, message: string): Promise; /** Background drain for extension-initiated webchat turns (`scheduleWebchatContinuation`). */ drainScheduledWebchatContinuation(sessionKey: string, message: string): Promise; /** * Persist normal clarification waits and use the lifecycle-bound waiter only * for caller-owned ephemeral sessions. * Rejects when neither path is available (e.g. CLI without webchat or TG). * * `publishStreamFor(runId)` is the bridge into AgentService's * `turnDispatcher.enqueueWebchatStreamEvent`. We take it as a callback so the * runner does not import AgentService statically. */ requestClarification(opts: { sessionKey: string; runId: string; toolCallId: string; request: ClarifyRequestPayload; publishStreamFor: (runId: string) => (event: ClarificationStreamEvent) => void; }): Promise; private deliverTelegramClarify; }