/**
 * companion-chat-manager.ts
 *
 * Disk-backed manager for companion-app chat-mode sessions.
 *
 * Design:
 * - Each session owns a ConversationManager (isolated message history).
 * - Sessions survive daemon restart via CompanionChatPersistence (atomic JSON files).
 * - Inbound messages are rate-limited per session and per client via
 *   CompanionChatRateLimiter (token-bucket, 30 msgs/min per client,
 *   10 msgs/min per session by default, configurable).
 * - When a user message is posted, the manager appends it to the conversation
 *   and runs a lightweight LLM turn using the provider registry.
 * - Tool calls emitted by the LLM are executed via the injected ToolRegistry
 *   (if provided); results are fed back into the stream and published as
 *   turn.tool_result events.
 * - Streaming chunks are fanned out via ControlPlaneGateway.publishEvent
 *   with a per-session clientId filter, so they only reach the subscriber
 *   for that specific session — never the global TUI event feed.
 * - A GC sweep closes sessions that have been idle beyond the TTL.
 */
import { Buffer } from 'node:buffer';
import type { ProviderMessage } from '../providers/interface.js';
import type { ArtifactDescriptor } from '../artifacts/index.js';
import type { CompanionChatMessageAttachmentInput, CompanionChatMessage, CompanionChatSession, CreateCompanionChatSessionInput, UpdateCompanionChatSessionInput } from './companion-chat-types.js';
import type { CompanionChatRateLimiterOptions } from './companion-chat-rate-limiter.js';
import type { ToolRegistry } from '../tools/registry.js';
import type { ToolDefinition } from '../types/tools.js';
import type { PermissionManager } from '../permissions/manager.js';
import type { RuntimeEventBus } from '../runtime/events/index.js';
import type { HookEvent, HookResult } from '../hooks/types.js';
export type CompanionProviderMessage = ProviderMessage;
export interface CompanionProviderChunk {
    readonly type: 'text_delta' | 'tool_call' | 'tool_result' | 'done' | 'error';
    readonly delta?: string | undefined;
    readonly toolCallId?: string | undefined;
    readonly toolName?: string | undefined;
    readonly toolInput?: unknown | undefined;
    readonly result?: unknown | undefined;
    readonly isError?: boolean | undefined;
    readonly error?: string | undefined;
}
export interface CompanionLLMProvider {
    /** Stream a single-turn conversation. Yields chunks. */
    chatStream(messages: CompanionProviderMessage[], options: {
        readonly systemPrompt?: string | null | undefined;
        readonly model?: string | null | undefined;
        readonly provider?: string | null | undefined;
        readonly tools?: readonly ToolDefinition[] | undefined;
        readonly abortSignal?: AbortSignal | undefined;
    }): AsyncIterable<CompanionProviderChunk>;
}
type HookDispatcherLike = {
    fire(event: HookEvent): Promise<HookResult>;
};
export interface CompanionChatArtifactStore {
    get(artifactId: string): ArtifactDescriptor | null;
    readContent(artifactId: string): Promise<{
        readonly record: {
            readonly mimeType: string;
            readonly filename?: string | undefined;
        };
        readonly buffer: ArrayBuffer | Uint8Array | Buffer;
    }>;
}
export interface CompanionChatEventPublisher {
    publishEvent(event: string, payload: unknown, filter?: {
        clientId?: string;
    }): void;
}
export interface CompanionChatReplyResult {
    readonly messageId: string;
    readonly assistantMessageId?: string | undefined;
    readonly response?: string | undefined;
    readonly error?: string | undefined;
}
export interface CompanionChatManagerConfig {
    readonly provider: CompanionLLMProvider;
    readonly eventPublisher: CompanionChatEventPublisher;
    /**
     * ToolRegistry to use for executing tool calls emitted by the LLM.
     * When omitted, tool_call chunks are published as events but not executed;
     * the LLM receives no tool result and must degrade gracefully.
     */
    readonly toolRegistry?: ToolRegistry | undefined;
    /**
     * Permission boundary used when executing model-originated tool calls.
     * Tool calls are denied when a registry is present without this manager.
     */
    readonly permissionManager?: PermissionManager | null | undefined;
    /** Optional hook dispatcher for Pre/Post/Fail tool hooks. */
    readonly hookDispatcher?: HookDispatcherLike | null | undefined;
    /** Optional runtime event bus for typed tool telemetry. */
    readonly runtimeBus?: RuntimeEventBus | null | undefined;
    /** Optional artifact store used to resolve and inline chat attachments. */
    readonly artifactStore?: CompanionChatArtifactStore | null | undefined;
    /**
     * Directory under which session JSON files are persisted.
     * Default: ~/.goodvibes/companion-chat/sessions/
     */
    readonly sessionsDir?: string | undefined;
    /**
     * Pass `false` to disable disk persistence entirely (useful in tests).
     * Default: true
     */
    readonly persist?: boolean | undefined;
    /** Rate-limiting options. Defaults: 30 msgs/min per client, 10/min per session. */
    readonly rateLimiter?: CompanionChatRateLimiterOptions | false | undefined;
    /** Override for tests */
    readonly idleActiveMs?: number | undefined;
    /** Override for tests */
    readonly idleEmptyMs?: number | undefined;
    /** Override for tests */
    readonly gcIntervalMs?: number | undefined;
}
export declare class CompanionChatManager {
    private readonly sessions;
    private readonly provider;
    private readonly eventPublisher;
    private readonly toolRegistry;
    private readonly permissionManager;
    private readonly hookDispatcher;
    private readonly runtimeBus;
    private readonly artifactStore;
    private readonly persistence;
    private readonly rateLimiter;
    private readonly idleActiveMs;
    private readonly idleEmptyMs;
    private gcTimer;
    /** Tracks whether the async init() has completed. */
    private initCompleted;
    private readonly pendingReplies;
    /**
     * Serializes persistence writes per session to prevent write-after-write
     * races where two concurrent saves could result in an older snapshot
     * overwriting a newer one.
     */
    private readonly _pendingSaves;
    constructor(config: CompanionChatManagerConfig);
    /**
     * Load sessions persisted from a previous daemon run.
     * Should be called once after construction before accepting requests.
     * Safe to call multiple times (idempotent after first call).
     */
    init(): Promise<void>;
    createSession(input?: CreateCompanionChatSessionInput): CompanionChatSession;
    getSession(sessionId: string): CompanionChatSession | null;
    listSessions(input?: {
        readonly includeClosed?: boolean | undefined;
        readonly limit?: number | undefined;
    }): {
        readonly sessions: readonly CompanionChatSession[];
        readonly totals: {
            readonly sessions: number;
            readonly active: number;
            readonly closed: number;
        };
    };
    getMessages(sessionId: string): CompanionChatMessage[];
    private normalizeMessage;
    updateSession(sessionId: string, input: UpdateCompanionChatSessionInput): CompanionChatSession;
    /**
     * Register the SSE clientId for this session so events are routed only to
     * the correct subscriber. Replaces any previous registration (single subscriber
     * per session in v1 — the last SSE connection wins).
     */
    registerSubscriber(sessionId: string, clientId: string): void;
    /**
     * Close a session cleanly. Aborts any in-flight turn. Returns the session
     * snapshot, or null if not found.
     */
    closeSession(sessionId: string): CompanionChatSession | null;
    /**
     * Post a user message and start an async LLM turn. Returns the messageId.
     *
     * Rate-limited per session and per client (throws GoodVibesSdkError{kind:'rate-limit'}
     * if limits are exceeded).
     *
     * Throws if the session is closed or not found.
     *
     * @param sessionId - The session to post to.
     * @param content   - The message text.
     * @param clientId  - The SSE/HTTP client identity for per-client rate limiting.
     *                    Pass '' to skip client-level rate limiting.
     */
    postMessage(sessionId: string, content: string, clientId?: string, options?: {
        readonly attachments?: readonly CompanionChatMessageAttachmentInput[] | undefined;
    }): Promise<string>;
    postMessageAndWaitForReply(sessionId: string, content: string, clientId?: string, options?: {
        readonly timeoutMs?: number | undefined;
        readonly attachments?: readonly CompanionChatMessageAttachmentInput[] | undefined;
    }): Promise<CompanionChatReplyResult>;
    private _postMessageInternal;
    private resolveAttachments;
    private formatAttachmentSummary;
    private buildReplayUserContent;
    private isInlineTextAttachment;
    private isInlineImageAttachment;
    private buildProviderUserContent;
    dispose(): void;
    private _runTurn;
    private _executeToolCalls;
    _gcSweep(): void;
    private _updateMeta;
    /**
     * Schedule a persistence save for the given session.
     * Saves are serialized per-session: each new save waits for the prior one to
     * complete before writing. The save always reads the CURRENT session state,
     * so rapid create→update→close sequences correctly persist the final state.
     */
    private _persist;
    private _doSave;
    private resolvePendingReply;
}
export {};
//# sourceMappingURL=companion-chat-manager.d.ts.map