import type { ContinuationSink, InjectionAckSink, UserMessage } from '../agent-adapter/types.js'; import type { AttachmentMeta } from '../domain/ui-service/types.js'; import type { PendingInjectionRecord } from '../store/pending-injection-repo.js'; import type { ContextUsage } from '../core/types/agent-types.js'; /** The subset of a pooled AgentProcess this path needs. */ export interface InjectableProcess { injectUserMessage?(message: UserMessage): boolean; setInjectionAckSink?(sink: InjectionAckSink): void; setContinuationSink?(sink: ContinuationSink): void; } /** The subset of a RunningExecution this path needs. */ export interface LiveExecutionLike { backend: string; agentProcess?: unknown; } export interface MidTurnInjectDeps { getLiveExecutions: (channel: string) => LiveExecutionLike[]; /** Captured while the running turn still owns it, before a spontaneous turn starts. */ getStreamingCallback: (channel: string) => ((text: string) => void) | null; appendAssistant: (sessionId: string, opts: { text: string; ts: string; }) => void; appendTool: (sessionId: string, opts: { toolName: string; toolInput: string; ts: string; toolUseId?: string; fullInput?: unknown; }) => void; appendToolResult?: (sessionId: string, opts: { toolUseId: string; content: string; isError: boolean; }) => void; publishMessage: (ev: { sessionId: string; channel: string; role: 'user' | 'assistant' | 'tool'; text: string; ts: string; toolName?: string; toolInput?: string; attachments?: AttachmentMeta[]; pending?: boolean; pendingId?: string; }) => void; publishDelivered: (ev: { sessionId: string; channel: string; pendingId: string; messageTs: string; committedTs: string; }) => void; publishStatus: (ev: { sessionId: string; channel: string; running: boolean; }) => void; onContextUsage?: (sessionId: string, channel: string, usage: ContextUsage) => void; persistPending: (record: PendingInjectionRecord) => Promise; commitPending: (record: PendingInjectionRecord) => Promise<{ committedTs: string; }>; markPending?: (record: PendingInjectionRecord) => Promise; unmarkPending?: (record: PendingInjectionRecord) => Promise; createPendingId?: () => string; track: (delta: number) => void; now: () => string; summarizeToolInput?: (input: unknown) => string; captureDebug?: boolean; maxWaitMs?: number; } export interface MidTurnInjectCtx { channel: string; /** Stable Cortex track session id. Null (never-used channel) ⇒ nothing to surface against. */ sessionId: string | null; sessionName?: string | null; profileName?: string | null; text: string; senderId: string; /** The inbound platform message id — the ledger turn's key. */ messageId: string; attachments?: AttachmentMeta[]; /** Lazily download platform-native files only after an injectable target is selected. */ prepareBackendAttachments?: () => Promise; } /** Only plain human messages may fold into an in-flight turn. Commands and synthetic session * re-entry messages own separate dispatch semantics; edits kill the live execution before routing. */ export declare function isInjectableMessage(opts: { text: string; senderId: string; }): boolean; /** True when the backend declares `Capability.MidTurnInject`. */ export declare function backendSupportsInject(backend: string): boolean; /** * Try to deliver `ctx` into the turn already running on its channel. * * Returns true when the message was accepted by the backend — the caller must then NOT enqueue it. * Every rejection path (no live turn, incapable backend, non-plain message, backend refusal) * returns false and leaves no trace, so the caller falls through to today's queue behaviour. */ export declare function tryInjectIntoLiveTurn(deps: MidTurnInjectDeps, ctx: MidTurnInjectCtx): Promise; /** Test hook: drop all per-channel injection state. */ export declare const _test: { reset(): void; };