import type { Link } from "../link/link"; import type { ConversationAddress } from "../types/conversation/address"; import { ConversationStream, ConversationTransport, SteerResult, TransportAttachRequest, TransportHandlers, TransportSteerRequest, TransportStopRequest, TransportTurnRequest, TurnStopped } from "./transport"; export type LinkSessionConfig = { model?: string; personality?: string; instructions?: string; platform?: string; /** Where on the platform the conversation is: on Discord, the channel and thread. */ address?: ConversationAddress; }; /** * Carries a turn over an existing Link connection. * * Sessions are ephemeral: the server drops them when the socket goes, so one is * opened on demand and reopened transparently after a reconnect. The conversation * itself is persisted server-side, so nothing is lost when that happens. */ export declare class LinkConversationTransport implements ConversationTransport { private readonly link; private readonly config; private sessionId?; private sessionChatId?; constructor(link: Link, config: () => LinkSessionConfig); send(request: TransportTurnRequest, handlers: TransportHandlers): ConversationStream; /** * Stops a running turn. * * Addressed by conversation rather than by session, so a client that reconnected — losing * its session but not the turn — can still stop what it is watching. The turn's own stream * ends normally afterwards, carrying whatever the model produced before the stop. */ stop(request: TransportStopRequest): Promise; /** * Says something to a turn that is still running. * * A refusal is the interesting case: the caller is holding a message the user typed and * has to send it as an ordinary turn instead of dropping it. */ steer(request: TransportSteerRequest): Promise; /** Ends the session, if one is open. The conversation can still be resumed later. */ end(): Promise; /** * Follows a turn that is already running, over the link this conversation already holds. * * A turn belongs to the conversation rather than to the socket that started it, so * reopening a conversation mid-answer — a reload, a second tab, a turn started from * another device or over HTTP — streams here instead of dropping to an SSE connection * just to watch. Needs no session: watching is not speaking. * * A conversation with nothing running ends the stream immediately, which is the * ordinary answer for one that is simply idle. */ attach(request: TransportAttachRequest, handlers: TransportHandlers): ConversationStream; private runTurn; /** * One `conversation.attach`: streams a turn's events to the caller and resolves with how * that watch ended. * * Shared by watching somebody else's turn and by rejoining one of our own, because from * here the two are the same act. `progress` is carried rather than returned: a watch that * dies halfway still has to leave behind where it got to, or a resume would replay from * the beginning and the caller would read the answer twice. */ private watch; /** * Picks a turn back up after losing sight of it. * * Two ways to lose one, one way to get it back. The socket can go — a deploy of the link * service, a proxy timing out — which rejects the exchange carrying the turn. Or core can * suspend the turn at its own deploy and hand it to another instance, which the link * service follows for two minutes before giving up and saying `turn_suspended`. Either way * the turn is still being answered and the conversation still holds it, so this re-attaches * from the last event the caller was actually given and the stream reads as one answer. * * `no_active_turn` is the ambiguous reply and it is deliberately not treated as an ending: * during a handover it means "not picked up yet" far more often than it means "gone", and * the window is what decides between them. * * Nothing here throws. It runs behind a stream the caller already holds, so the outcomes * that matter are the ones delivered into it: a completion, or a failure that says plainly * that the answer was lost track of rather than that it failed. */ private resume; /** Opens a session, or reuses the open one when it is for the same conversation. */ private session; }