import type { NotificationRecord, SendMessageResult } from "@yanlinglabs/winter-agent-sdk/messaging"; import type { RuntimeDirectoryHandle } from "../directory/directory.js"; import type { SeamContext } from "../seams/context.js"; import type { GlobalMessaging, SendMessageRequest } from "../seams/global-messaging.js"; import type { DeliveryOutcome, GlobalAgentMessage, SerializedRuntimeAddress } from "../seams/messaging-contract.js"; import { type InboundPolicyHooks } from "./inbound.js"; import { type AttachedWinterSession } from "./sessions.js"; import { type WinterColdResumeRunHomes, type WinterMessagingAdapter, type WinterMessagingAdapterDeps } from "./winter-adapter.js"; /** The context this factory needs: the seam's, with the directory as the concrete handle. */ export interface GlobalMessagingContext extends SeamContext { directory: RuntimeDirectoryHandle; } export interface GlobalMessagingOptions extends InboundPolicyHooks { now?: () => number; /** Passed to the Winter adapter this factory builds (cold-resume options, child resume context). */ winter?: Omit; /** * R-7b-4's one behavioural seam, forwarded to the core: how a THROW out of an adapter is classified. * * ABSENT MEANS EVERYTHING IS UNCERTAIN, which is the conservative reading and the core's own default * — `refused` asserts the effect did NOT happen, and from this side "the call failed" and "the * effect happened and then the call failed" are indistinguishable (WS-10 §12). */ classifyDeliveryError?: (error: unknown) => "refused" | "uncertain"; } export interface ReplyRequest { /** The envelope being replied to. Its `to` becomes the reply's `from`, and its `from` the target. */ original: GlobalAgentMessage; body: string; summary?: string; } /** The seam, plus the doors a host and this package's own tool handlers need. */ export interface GlobalMessagingHandle extends GlobalMessaging { readonly winterAdapter: WinterMessagingAdapter; /** Register a live Winter session AND bridge its idle notices. Returns the detach function. */ attachWinterSession(address: SerializedRuntimeAddress, handle: AttachedWinterSession): () => void; /** * `send()`, keeping the shared core's SUPPLEMENTARY fact about a combined call. * * The seam pins `send(): Promise`, and the delivery outcome is rightly primary — * but a call that carried both a message AND `notify_when_idle` has a second, separate result (was * the subscription honoured?), and WS-10 §10.1's own result text allows for it. The core reports it * beside the outcome rather than inventing an eleventh status; this door is where that survives, and * `send()` is exactly `(await sendDetailed(...)).outcome`. */ sendDetailed(request: SendMessageRequest): Promise; /** WS-10 §13's re-evaluation door: sweep, re-decide, deliver what is now acceptable. */ releaseHeld(receiver: SerializedRuntimeAddress): Promise; /** Reply routing — the one path on which `hopCount` grows, and therefore the one the bound binds. */ reply(request: ReplyRequest): Promise; /** WS-06 §3.6's `ReadNotifications` page for one session's own queue. */ readNotifications(sessionId: string): { notifications: NotificationRecord[]; remaining: number; }; /** * "This target has gone idle" — WS-10 §14's firing edge, from whatever noticed it. * * Called by the Winter facet bridge for every live notice and every drained one; a host that detects * idleness some other way calls it too. Returns how many notices were pushed. */ noteIdle(target: SerializedRuntimeAddress, notice?: { notificationId?: string; content?: string; }): Promise; /** The caps and windows this router enforces, so a host can render them without re-deriving them. */ readonly bounds: { heldCap: number; acceptedCap: number; holdExpiryMs: number; idleSubscriptionMs: number; messageTtlMs: number; }; } /** * WS-10 §12's message id: "derived/persisted from the sender session plus tool-call ID", so that "a * retry with the same ID returns the stored outcome". * * DERIVED, NOT COUNTED, and that is the whole point: the shared core allocates `msg-1`, `msg-2` from * an in-process counter, which is stable within one process and COLLIDES across a restart — two * different messages would share an id, and the second would be answered with the first's stored * outcome. Both components are percent-encoded so the pair can never be ambiguous. */ export declare function deriveMessageId(senderSessionId: string, toolUseId: string): string; export declare function createGlobalMessaging(context: GlobalMessagingContext, options?: GlobalMessagingOptions, internal?: { winterRunHomes?: WinterColdResumeRunHomes; }): GlobalMessagingHandle;