/** * Remote Substrate, Transport Contract * * Defines typed message definitions for control/data/ack/failure * message classes with retry/backoff policies per class. * * This module defines the full structural contract for all messages that cross * the remote transport boundary. No raw strings, all messages are typed. */ import { GoodVibesSdkError } from '@pellux/goodvibes-errors'; import type { ControlMessage, DataMessage, AckMessage, FailureMessage, RetryPolicy, TransportErrorCategory, ProtocolVersion, ProtocolSupportMatrix, VersionNegotiationResult, NegotiatedProtocol } from './types.js'; /** * Default retry policy for control plane messages. * Control messages (handshake, ping, config) use aggressive retry with * short initial delay since they are small and critical. */ export declare const CONTROL_RETRY_POLICY: Readonly; /** * Default retry policy for data plane messages. * Data messages carry task payloads and need reliable delivery with * longer backoff to avoid overwhelming a recovering server. */ export declare const DATA_RETRY_POLICY: Readonly; /** * Default retry policy for ack messages. * Ack delivery is bounded; fewer retries are used because missing acks trigger replay. */ export declare const ACK_RETRY_POLICY: Readonly; /** * Default retry policy for failure messages. * Failure reports are fire-and-forget; minimal retry. */ export declare const FAILURE_RETRY_POLICY: Readonly; /** * The current protocol version implemented by this build. * * Increment major on breaking wire changes, minor on additive features, * patch on bug fixes that do not affect the wire contract. */ export declare const CURRENT_PROTOCOL_VERSION: Readonly; /** * Protocol support matrix for v1.x of the transport protocol. * * Each entry records the minor-version range the local build can interoperate * with for a given local version. Major version must always match exactly. * * Policy: * - Peers advertising minor < minSupportedMinor are rejected as unsupported. * - Peers advertising minor > maxSupportedMinor are accepted; we downgrade to * the peer's level (peer is newer, they offer a superset of our features). * - Peers advertising the same minor connect at full capability. */ export declare const TRANSPORT_PROTOCOL_SUPPORT_MATRIX: ProtocolSupportMatrix; /** * VersionMismatchError, thrown (or returned as failure) when a peer presents * an unsupported protocol version and the handshake must be rejected. * * Carries the structured unsupported details so callers can log, * surface diagnostics, and produce a typed HANDSHAKE_REJECT payload. */ export declare class VersionMismatchError extends GoodVibesSdkError { readonly code: 'major_version_mismatch' | 'peer_version_too_old' | 'peer_version_unsupported'; /** Structured mismatch code (distinct from the generic SDK error code string). */ readonly mismatchCode: 'major_version_mismatch' | 'peer_version_too_old' | 'peer_version_unsupported'; readonly offeredVersion: Readonly; readonly peerVersion: Readonly; constructor(mismatchCode: 'major_version_mismatch' | 'peer_version_too_old' | 'peer_version_unsupported', offeredVersion: Readonly, peerVersion: Readonly, message: string); } /** * Negotiate a protocol version between this peer and a remote peer. * * Rules: * 1. Major versions must match exactly, a mismatch is always unsupported. * 2. Find the protocol support entry for the local version in the matrix. * 3. Peer minor below `minSupportedMinor` -> unsupported. * 4. Peer minor equal to local minor → full capability, no downgrade. * 5. Peer minor above local minor → downgrade to local (we are the older peer). * 6. Peer minor in (local, maxSupportedMinor] → downgrade to peer (peer is newer). * * Unsupported peers cannot proceed, callers must reject the handshake. * * @param localVersion - The version this side is running. * @param peerVersion - The version the remote peer advertised. * @param matrix - The protocol support matrix to look up against. * @returns A VersionNegotiationResult, check `proceed` before allowing the session. */ export declare function negotiateProtocolVersion(localVersion: Readonly, peerVersion: Readonly, matrix?: ProtocolSupportMatrix): VersionNegotiationResult; /** Control message type literals. */ export type ControlMessageType = 'HANDSHAKE_INIT' | 'HANDSHAKE_ACCEPT' | 'HANDSHAKE_REJECT' | 'PING' | 'PONG' | 'CONFIG_SYNC' | 'SHUTDOWN'; /** Payload shapes per control message type. */ export interface ControlPayloads { HANDSHAKE_INIT: { readonly sessionId: string; readonly agentId: string; readonly taskId: string; readonly epoch: number; readonly lastAckedOffset: number; readonly authToken: string; /** Semver label of the protocol version the client offers (e.g. "1.2.0"). */ readonly clientVersion: string; /** Structured version object for server-side protocol support matrix lookup. */ readonly protocolVersion: ProtocolVersion; }; HANDSHAKE_ACCEPT: { readonly sessionId: string; readonly epoch: number; readonly serverVersion: string; readonly handshakeToken: string; readonly expiresAt: number; readonly replayFromOffset: number; /** The negotiated protocol version both peers will use for this session. */ readonly negotiatedProtocol: NegotiatedProtocol; }; HANDSHAKE_REJECT: { readonly reason: string; readonly retryable: boolean; /** * Structured unsupported code when the reject is due to version mismatch. * Absent for auth/quota/other rejections. */ readonly unsupportedCode?: 'major_version_mismatch' | 'peer_version_too_old' | 'peer_version_unsupported'; /** The peer's offered version, echoed back for diagnostics. */ readonly peerVersion?: ProtocolVersion | undefined; /** The server's offered version, for operator diagnostics. */ readonly serverVersion?: ProtocolVersion | undefined; }; PING: Record; PONG: { readonly serverTimeMs: number; }; CONFIG_SYNC: { readonly config: Record; }; SHUTDOWN: { readonly graceful: boolean; readonly reason?: string; }; } /** Data message type literals. */ export type DataMessageType = 'TASK_SUBMIT' | 'TASK_CANCEL' | 'TASK_UPDATE' | 'AGENT_SPAWN' | 'AGENT_UPDATE' | 'AGENT_TERMINATE' | 'HEALTH_REPORT' | 'STATE_SNAPSHOT'; /** Payload shapes per data message type. */ export interface DataPayloads { TASK_SUBMIT: { readonly taskId: string; readonly agentId: string; readonly title: string; readonly description?: string | undefined; readonly payload: Record; }; TASK_CANCEL: { readonly taskId: string; readonly reason?: string | undefined; }; TASK_UPDATE: { readonly taskId: string; readonly status: string; readonly progress?: number | undefined; readonly message?: string | undefined; readonly error?: string | undefined; }; AGENT_SPAWN: { readonly agentId: string; readonly taskId: string; readonly role: string; }; AGENT_UPDATE: { readonly agentId: string; readonly state: string; readonly message?: string | undefined; }; AGENT_TERMINATE: { readonly agentId: string; readonly reason?: string | undefined; }; HEALTH_REPORT: { readonly status: string; readonly latencyMs?: number | undefined; readonly serverVersion?: string | undefined; readonly degradedReason?: string | undefined; }; STATE_SNAPSHOT: { readonly tasks: Array>; readonly health: Record; readonly epoch: number; }; } /** * Create a typed control message. * * @param controlType - The control message subtype. * @param payload - The typed payload matching the control type. * @param sessionId - Durable session ID. * @param epoch - Server epoch at time of creation. * @param offset - Monotonic offset within the session. * @returns A frozen ControlMessage. */ export declare function createControlMessage(controlType: T, payload: ControlPayloads[T], sessionId: string, epoch: number, offset: number): Readonly; /** * Create a typed data message. * * @param dataType - The data message subtype. * @param payload - The typed payload matching the data type. * @param sessionId - Durable session ID. * @param epoch - Server epoch at time of creation. * @param offset - Monotonic offset within the session. * @returns A frozen DataMessage. */ export declare function createDataMessage(dataType: T, payload: DataPayloads[T], sessionId: string, epoch: number, offset: number): Readonly; /** * Create an acknowledgement message. * * @param ackedOffset - The offset being acknowledged. * @param sessionId - Durable session ID. * @param epoch - Server epoch at time of creation. * @param offset - Monotonic offset of this ack message. * @returns A frozen AckMessage. */ export declare function createAckMessage(ackedOffset: number, sessionId: string, epoch: number, offset: number): Readonly; /** * Create a failure message. * * @param error - Human-readable error description. * @param errorCategory - Error category for retry routing. * @param recoverable - Whether the remote substrate can recover. * @param sessionId - Durable session ID. * @param epoch - Server epoch at time of creation. * @param offset - Monotonic offset of this failure message. * @param context - Optional structured error context. * @returns A frozen FailureMessage. */ export declare function createFailureMessage(error: string, errorCategory: TransportErrorCategory, recoverable: boolean, sessionId: string, epoch: number, offset: number, context?: Record): Readonly; /** * Compute the delay in ms before the next retry attempt. * * Applies exponential backoff with configurable jitter to spread retries * and avoid thundering herds. * * @param policy - The retry policy to apply. * @param attempt - Current attempt number (1-indexed). * @returns Delay in ms, capped at policy.maxDelayMs. */ export declare function computeRetryDelay(policy: RetryPolicy, attempt: number, rng?: () => number): number; /** * Determine whether a given error category should trigger a retry * according to the supplied policy. * * @param policy - The retry policy to check. * @param category - The error category to test. * @returns True if the policy retries on this category. */ export declare function shouldRetry(policy: RetryPolicy, category: TransportErrorCategory): boolean; //# sourceMappingURL=transport-contract.d.ts.map