/** * LocalTransport — In-process SQLite transport for fully offline agent messaging. * * Reads and writes messages directly to conduit.db via node:sqlite. * No network calls. Works fully offline. Messages are stored in the * project-local conduit.db (ADR-037), keeping agent messaging isolated * from the global-identity signaldock.db. * * Priority: LocalTransport is preferred over HttpTransport when * conduit.db is available (see factory.ts). * * @see docs/specs/SIGNALDOCK-UNIFIED-AGENT-REGISTRY.md Section 4.4 * @task T213 * @task T356 * @epic T310 */ import type { ConduitMessage, ConduitTopicPublishOptions, ConduitTopicSubscribeOptions, ConduitUnsubscribe, Transport, TransportConnectConfig } from '@cleocode/contracts'; /** In-process SQLite transport for fully offline agent messaging. */ export declare class LocalTransport implements Transport { readonly name = "local"; private state; /** * Connect to conduit.db for in-process messaging. * * Opens the database, sets WAL mode pragmas, and verifies * the messages table exists. Throws if conduit.db is missing * or uninitialized (run `cleo init` first). * * @task T356 * @epic T310 */ connect(config: TransportConnectConfig): Promise; /** Close the database connection and stop any subscriber polling. */ disconnect(): Promise; /** * Store a message in conduit.db. * * Inserts into the messages table with status 'pending'. * For conversation messages, also links via conversation_participants * if not already present. */ push(to: string, content: string, options?: { conversationId?: string; replyTo?: string; }): Promise<{ messageId: string; }>; /** * Poll for messages addressed to this agent. * * Returns messages with status 'pending' where to_agent_id matches * the connected agent. Messages are returned oldest-first. */ poll(options?: { limit?: number; since?: string; }): Promise; /** * Acknowledge messages by marking them as 'delivered'. * * Updates the status and delivered_at timestamp for each message ID. */ ack(messageIds: string[]): Promise; /** * Subscribe to real-time local messages. * * Since this is in-process, subscribers are notified synchronously * when push() is called. Additionally, a polling interval checks * for messages inserted by other processes (e.g., Rust CLI). * * @returns Unsubscribe function. */ subscribe(handler: (message: ConduitMessage) => void): () => void; /** * Subscribe this agent to a named topic in conduit.db. * * Creates the topic row if it does not exist (idempotent via ON CONFLICT DO NOTHING). * Inserts a `topic_subscriptions` row linking this agent to the topic. * * Topic name convention: `.` or `.coordination`. * * @param topicName - Topic name, e.g. `"epic-T1149.wave-2"`. * @param _options - Reserved for future filter support (not stored in DB yet). * @task T1252 */ subscribeTopic(topicName: string, _options?: ConduitTopicSubscribeOptions): Promise; /** * Publish a message to a named topic in conduit.db. * * The message is broadcast to all current subscribers via the in-process * handler map (`topicHandlers`). Cross-process subscribers receive it via * the periodic poll timer started by `onTopic()`. * * The topic must already exist (via a prior `subscribeTopic()` call), or * this method creates it automatically with a synthetic creator. * * @param topicName - Target topic name. * @param content - Human-readable message content. * @param options - Message kind and optional structured payload. * @returns Object containing the assigned `messageId`. * @task T1252 */ publishToTopic(topicName: string, content: string, options?: ConduitTopicPublishOptions): Promise<{ messageId: string; }>; /** * Register a real-time handler for messages on a named topic. * * In-process publishers notify the handler synchronously via `publishToTopic()`. * Cross-process publishers are picked up via a 1-second polling interval * that is started on the first `onTopic()` call and stopped when all * topic handlers are removed. * * @param topicName - Topic name to watch. * @param handler - Callback invoked for each incoming message. * @returns Unsubscribe function that removes this handler. * @task T1252 */ onTopic(topicName: string, handler: (message: ConduitMessage) => void): ConduitUnsubscribe; /** * Unsubscribe this agent from a named topic. * * Removes the `topic_subscriptions` row; does not delete the topic or messages. * In-process `onTopic` handlers are NOT automatically removed — callers should * call the unsubscribe function returned by `onTopic()` before `unsubscribeTopic()`. * * @param topicName - Topic name to leave. * @task T1252 */ unsubscribeTopic(topicName: string): Promise; /** * Poll for new topic messages and deliver to registered handlers. * * Queries each topic with registered handlers for messages newer than * `topicLastSeen`. Updates `topicLastSeen` after each poll. Called * periodically by the `topicPollTimer`. * * @task T1252 */ pollTopic(topicName: string, options?: { limit?: number; since?: string; }): Promise; /** * Check whether conduit.db is available for local transport. * * Used by factory.ts to decide whether to use LocalTransport. * * @task T356 * @epic T310 * @param cwd - Optional working directory override (defaults to process.cwd()). * @returns `true` if conduit.db exists at the expected path. */ static isAvailable(cwd?: string): boolean; /** Poll for new messages and notify subscribers (cross-process sync). */ private pollAndNotify; /** Poll all watched topics for new messages and notify in-process handlers. */ private pollAndNotifyTopics; /** Notify all handlers registered for a specific topic. */ private notifyTopicHandlers; /** Notify all active subscribers of a new message. */ private notifySubscribers; /** * Ensure a DM conversation exists between two agents. * * Conversations store participants as a comma-separated TEXT field. * We search for existing private conversations containing both agents. * * @returns The conversation ID. */ private ensureDmConversation; /** Throw if not connected. */ private ensureConnected; /** * Seam 3 (T11627): conduit.db is a raw bypass writer (project-tier, sidesteps * the tasks chokepoint via its own `openFreshConduitDb` handle). Hold the * project `bulk` lease around a synchronous write block so conduit writes * serialize against other writers on the same scope. `off` mode → pass-through * (busy_timeout serializes as before). The wrapped `fn` is synchronous because * every conduit write is a synchronous `db.prepare(...).run(...)`. * * @param fn - The synchronous write block to run under the lease. * @returns The value returned by `fn`. */ private runWrite; } //# sourceMappingURL=local-transport.d.ts.map