/** * Delivery Router + Session Store — route inbound channel messages to the agent * and route the agent's reply back to the originating channel. * * This is the minimal Session Store + Delivery Router stub from T1797 (epic * T11854 · saga T10419). It tracks per-channel session affinity in memory and * resolves an {@link OutboundReply} back to the {@link ChannelAdapter} that * originated the inbound exchange. Channels register with the router; inbound * messages flow `adapter.receive() → router.ingest() → handler`, and the * handler's reply flows `router.dispatch() → adapter.send()` over the SAME * channel by session affinity. * * The store is in-memory by design for the contained increment: the underlying * conduit `LocalTransport` already persists the message rows in conduit.db * (via the consolidated `conduit_*` domain through the store chokepoint), so the * router does NOT open its own database handle and introduces no new schema. * A durable session table is a follow-up if cross-process routing is needed. * * @see T1797 — Session Store + Delivery Router. * @epic T11854 * @saga T10419 * @module conduit/delivery-router */ import type { ChannelAdapter, ChannelSession, InboundMsg, OutboundReply } from '@cleocode/contracts'; /** Handler invoked for each routed inbound message; returns an optional reply. */ export type InboundHandler = (msg: InboundMsg) => Promise; /** * In-memory per-channel session-affinity store. * * Keyed on `(channelId, sessionKey)`, it remembers the last inbound exchange so * the {@link DeliveryRouter} can route a reply back to the originating channel * without the caller threading routing state. * * @see T1797 */ export declare class SessionStore { private readonly sessions; /** * Record (or refresh) the session for an inbound message. * * @param msg - The inbound message establishing the session. * @returns The stored {@link ChannelSession}. */ touch(msg: InboundMsg): ChannelSession; /** * Look up a stored session by channel + session key. * * @param channelId - The channel id. * @param sessionKey - The channel-native session key. * @returns The session if present, else `undefined`. */ get(channelId: string, sessionKey: string): ChannelSession | undefined; /** * List every tracked session (newest-touched first). * * @returns A snapshot array of sessions. */ list(): ChannelSession[]; /** Remove all tracked sessions. */ clear(): void; } /** * Routes inbound channel messages to a handler and delivers replies back to the * originating channel by session affinity. * * Channels register via {@link DeliveryRouter.register}. Calling * {@link DeliveryRouter.run} drains each registered adapter's `receive()` * stream, invokes the handler, and — when the handler returns a reply — routes * it back over the same channel. * * @see T1797 */ export declare class DeliveryRouter { private readonly adapters; private readonly store; /** * Create a router. * * @param store - Optional injected {@link SessionStore} (a fresh in-memory * store is created when omitted). */ constructor(store?: SessionStore); /** The session store backing this router. */ get sessions(): SessionStore; /** * Register a channel adapter with the router. * * @param adapter - The adapter to route for. * @throws When an adapter with the same id is already registered. */ register(adapter: ChannelAdapter): void; /** * Resolve the adapter registered for a channel id. * * @param channelId - The channel id. * @returns The adapter if registered, else `undefined`. */ adapter(channelId: string): ChannelAdapter | undefined; /** * Ingest a single inbound message: record session affinity and invoke the * handler. When the handler returns a reply, it is dispatched back to the * originating channel. * * Exposed independently of {@link DeliveryRouter.run} so a caller (or test) * can drive one message at a time over an in-memory transport. * * @param msg - The inbound message. * @param handler - The agent handler producing an optional reply. */ ingest(msg: InboundMsg, handler: InboundHandler): Promise; /** * Dispatch an outbound reply back to a channel. * * Resolution order for the destination channel + session: * 1. Explicit `reply.channelId` / `reply.sessionKey`. * 2. The originating inbound message's channel + session (affinity). * 3. The adapter's `config.homeChannel` session fallback. * * @param reply - The reply to deliver. * @param origin - The inbound message being replied to (for affinity). * @throws When no destination channel can be resolved or is unregistered. */ dispatch(reply: OutboundReply, origin?: InboundMsg): Promise; /** * Drain a registered adapter's inbound stream, routing each message through * the handler until the adapter stops (its `receive()` iterator ends). * * @param channelId - The channel to run. * @param handler - The agent handler producing optional replies. * @throws When the channel is not registered. */ run(channelId: string, handler: InboundHandler): Promise; } //# sourceMappingURL=delivery-router.d.ts.map