/**
 * Channel manager — lifecycle, persistence, and the inbound→agent→outbound pipeline.
 *
 * Connectors are registered once; the manager starts/stops them, persists their
 * config, decides which inbound messages warrant a reply, runs the agent loop to
 * produce that reply, and sends it back through the connector.
 */
import type { SqliteDatabase } from "../db/sqlite-shim.js";
import type { ToolRegistry } from "../tools/registry.js";
import type { AuditWriter } from "../services/audit.js";
import { type LLMConfig, type AgentMessage } from "../tools/agent-loop.js";
import type { ChannelConfig, ChannelConnector, ChannelStatus, ChannelStatusDetail, InboundMessage } from "./types.js";
/** Produces an assistant reply for a conversation. Injectable for testing. */
export interface ReplyGenerator {
    generate(history: AgentMessage[], ctx: {
        channelId: string;
        sessionId: string;
        allowedTools: Set<string>;
    }): Promise<string>;
}
export interface ChannelManagerDeps {
    sqlite: SqliteDatabase;
    /** Resolves the LLM config used for channel replies. */
    resolveLLM: () => LLMConfig;
    toolRegistry?: ToolRegistry;
    audit?: AuditWriter;
    projectRoot?: string;
    /** Auth context for tool execution (per-user keys, backend, model). */
    auth?: {
        userId?: string;
        apiKeys?: Record<string, string>;
        jaitBackend?: string;
        model?: string;
    };
    /** System prompt for the channel agent. */
    systemPrompt?: string;
    /** Max conversation messages to retain (excluding the system prompt). */
    maxHistory?: number;
    /** Override reply generation (defaults to the agent loop). */
    replyGenerator?: ReplyGenerator;
    log?: (msg: string, ...args: unknown[]) => void;
}
interface ManagedChannel {
    connector: ChannelConnector;
    status: ChannelStatus;
    detail: ChannelStatusDetail;
}
/** Normalize a WhatsApp-style id to its bare number for allowlist comparison. */
export declare function normalizeSenderId(id: string): string;
/** Decide whether the agent should reply to an inbound message. */
export declare function shouldRespond(msg: InboundMessage, config: ChannelConfig): boolean;
export declare class ChannelManager {
    private readonly deps;
    private readonly channels;
    /** Per-conversation message history (keyed by `${channelId}:${conversationId}`). */
    private readonly histories;
    /** Per-conversation serialization so messages are answered in order. */
    private readonly locks;
    private readonly replyGenerator;
    constructor(deps: ChannelManagerDeps);
    private log;
    private ensureTable;
    getConfig(id: string): ChannelConfig;
    setConfig(id: string, patch: Partial<ChannelConfig>): ChannelConfig;
    register(connector: ChannelConnector): void;
    /** Remove a connector from the available channel list. Config is preserved. */
    unregister(id: string): Promise<void>;
    list(): {
        id: string;
        label: string;
        status: ChannelStatus;
        enabled: boolean;
        qr: string | null;
        config: ChannelConfig;
    }[];
    get(id: string): ManagedChannel | undefined;
    /** Start a connector and persist enabled=true. */
    start(id: string): Promise<void>;
    /** Stop a connector and persist enabled=false. */
    stop(id: string): Promise<void>;
    /** Start every connector whose persisted config has enabled=true. */
    startEnabled(): Promise<void>;
    dispose(): Promise<void>;
    private handleInbound;
    private replyTo;
}
export declare class AgentLoopReplyGenerator implements ReplyGenerator {
    private readonly deps;
    constructor(deps: ChannelManagerDeps);
    generate(history: AgentMessage[], ctx: {
        channelId: string;
        sessionId: string;
        allowedTools: Set<string>;
    }): Promise<string>;
}
export {};
//# sourceMappingURL=manager.d.ts.map