import type { MediaInbound, ScheduleProvenance, ReplyContext, InboundPayload } from './notification.js'; export type ChannelKind = 'whatsapp' | 'webchat'; interface WhatsAppTarget { kind: 'whatsapp'; senderId: string; gatewayUrl: string; } interface WebchatTarget { kind: 'webchat'; key: string; gatewayUrl: string; } export interface TargetSet { whatsapp?: WhatsAppTarget; webchat?: WebchatTarget; } export interface ReplyEndpoint { channel: ChannelKind; url: string; /** The channel-specific recipient field, merged with {text, deliveryId} by * the caller before POSTing. */ body: Record; } export interface InboundEndpoint { channel: ChannelKind; url: string; } /** Parse CHANNEL_TARGETS env; empty/malformed yields an empty set. */ export declare function parseTargets(raw: string | undefined): TargetSet; /** One reply endpoint per bound target. WhatsApp delivers actively; webchat * stamps the lifeline (the JSONL tail carries the text to web readers). */ export declare function replyEndpoints(set: TargetSet): ReplyEndpoint[]; type FetchLike = (url: string, init: { method: string; headers: Record; body: string; }) => Promise<{ ok: boolean; status: number; }>; /** * Fan one agent reply to every bound target's reply endpoint, correlated by a * single deliveryId, emitting the [fanout] observability lines. WhatsApp * delivers actively; webchat stamps the lifeline. Returns the fanout/delivered * counts (delivered < fanout with no result=fail is the silent-drop signature). */ export declare function fanReply(input: { targets: TargetSet; text: string; sessionId: string; deliveryId?: string; fetchFn: FetchLike; log: (line: string) => void; }): Promise<{ fanout: number; delivered: number; deliveryId: string; }>; /** Task 858 — the single WhatsApp document endpoint for multi-target mode. * Documents fan to the WhatsApp door only (webchat has no file transport — the * web reader renders attachments from the JSONL tail). Returns null when no * WhatsApp door is bound, so the caller can both hide the tool and produce an * actionable error if it is somehow still called. */ export declare function whatsappDocumentEndpoint(set: TargetSet): { url: string; senderId: string; } | null; /** Task 1544 — the WhatsApp door's turn-end endpoint for multi-target mode. The * unified follower posts the interactive delivery fallback here (senderId + * redacted finalText), reusing the gateway's single-target force-send + * delivered=fallback|reply|none classification. Returns null when no WhatsApp * door is bound, so the follower posts nothing there. */ export declare function whatsappTurnEndEndpoint(set: TargetSet): { url: string; senderId: string; } | null; /** Task 981 — the webchat door's turn-end endpoint for multi-target mode. The * unified turn-undelivered detector posts here (key + finalBytes), reusing the * webchat gateway's log-only, no-re-delivery semantics. Returns null when no * webchat door is bound — a whatsapp-only set strands nothing on a reply-only * reader, so no follower runs. */ export declare function webchatTurnEndEndpoint(set: TargetSet): { url: string; key: string; } | null; /** Task 853 — one normalised inbound, whichever door it came through. * Task 1527 — the server-injected turn context the gateway stamps onto the SSE * frame also rides here: the origin (`source`), the account's standing rules, * and a scheduled dispatch's provenance marker. The webchat gateway serialises * only `standingRules` (the scheduler never targets the webchat door), so * `source`/`scheduleProvenance` are always absent on a webchat frame. */ export interface DoorInbound { text: string; messageId: string; media: MediaInbound[]; source?: 'user' | 'schedule'; standingRules?: string; scheduleProvenance?: ScheduleProvenance; /** Task 1897 — the message a WhatsApp reply points at. Carried here for the * same reason as the three fields above: the single-target loop JSON-parses * the whole frame, so a field this seam omits is dropped on multi-target * sessions only. */ replyContext?: ReplyContext; } /** Task 1527 — rebuild the notification builder's InboundPayload from a door * inbound. The multi-target door path carries no sender number (and never puts * one in content — the data-minimisation invariant), so `senderId` is ''. This * is the single seam both `runDoorStream` and the parity drift-guard call, so * the multi-target and single-target paths cannot compose divergent turns for * the same frame. */ export declare function doorInboundPayload(msg: DoorInbound): InboundPayload; /** Task 853 — everything the multi-stream loop needs to run one door: * its SSE inbound URL, its ready/received protocol, and a frame parser * that normalises (and guards) that door's payload shape. */ export interface ChannelDoor { channel: ChannelKind; inboundUrl: string; readyUrl: string; readyBody: Record; receivedUrl: string; receivedBody: (messageId: string) => Record; /** Parse one SSE data frame; null = malformed or not this door's recipient. */ parseFrame: (data: string) => DoorInbound | null; } export declare function channelDoors(set: TargetSet): ChannelDoor[]; /** One inbound SSE endpoint per bound target. */ export declare function inboundEndpoints(set: TargetSet): InboundEndpoint[]; export {}; //# sourceMappingURL=targets.d.ts.map