/** * The channel: chat-recall's messages delivered into a session that is idle. * * Every other delivery path chat-recall has is a hook, and a hook runs at the * edge of a turn. A session that finished its turn and is waiting for the user * runs no hooks at all, so the only thing that could reach it was a waiter * process held open with `asyncRewake` and woken by exit code 2 — a process * with a lifetime, a timeout, a lock file and a parent to watch. Claude Code's * channels need none of that: a local MCP server that declares * `capabilities.experimental["claude/channel"]` sends * `notifications/claude/channel` and the session takes a turn with it, exactly * as if the user had typed it. No turn, no timeout, no lock. * * What this file does is the half that is ours: find out which session this * process belongs to, hold the same long-poll the waiter holds, and hand each * batch to the client. The store, the lease and the acknowledgement are the * ones already there — a message that the channel takes is leased like any * other, so a session cannot get it twice. * * It runs only when the session named our server on the command line * (channel-detect.ts). A session that did not gets nothing from here: its * notifications would be dropped by the client, and dropping a message we have * already acknowledged is losing it, so we must not start polling at all. */ import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; export interface Target { base: string; token: string; } /** * Declare the capability the client looks for. * * It must be registered BEFORE the transport connects: the client reads * `capabilities` from the initialize result and decides once, at connect, that * this server may push messages into the session. Registering later is silent — * the client has already made up its mind. */ export declare function declareChannel(server: Server): void; /** * Prepare a session for channel delivery: declare the capability, and return the * function that starts delivering, or null when this session did not ask. * * The two halves are separate because of when the client decides: it reads the * capability once, from the initialize result, and never looks again. So the * capability goes in before `connect()`, and the loop starts after — polling a * server that is not connected yet would deliver into nothing. */ export declare function beginChannel(server: Server, debug?: (reason: string) => void): (() => void) | null; export interface ChannelDeps { server: Server; target: Target; /** The session id, once the hook has recorded it. */ sessionId: () => string | null; debug?: (reason: string) => void; now?: () => number; sleep?: (ms: number) => Promise; post?: (target: Target, path: string, body: unknown, ms: number) => Promise<{ status: number; body: unknown; }>; } /** * Deliver messages until the session ends. * * The loop mirrors the waiter's, on purpose: same endpoint, same lease, same * acknowledgement rule (ack only after the message has left this process), and * the same give-up rules, so a server without this route costs a few retries * rather than a spin. */ export declare function runChannel(deps: ChannelDeps): Promise; //# sourceMappingURL=channel.d.ts.map