{"version":3,"sources":["../src/core/FileRef.ts","../src/types.ts","../src/core/auth.ts","../src/core/types.ts","../package.json","../src/core/CoordinatorClient.ts","../src/core/LocalCoordinatorClient.ts","../src/core/WebRTCTransportClient.ts","../src/utils/sdp.ts","../src/utils/webrtc.ts","../src/core/Reactor.ts","../src/utils/recording.ts","../src/core/RecordingClient.ts","../src/react/ReactorProvider.tsx","../src/core/store.ts","../src/react/hooks.ts","../src/react/ReactorView.tsx","../src/react/ReactorController.tsx","../src/react/WebcamStream.tsx","../src/react/ClipPlayer.tsx","../src/react/ClipDownloadButton.tsx","../src/react/useClipDownload.ts"],"sourcesContent":["/**\n * Reference to an uploaded file, returned by {@link Reactor.uploadFile}.\n *\n * Pass a `FileRef` as a value in {@link Reactor.sendCommand} and it will\n * be serialized into the `uploads` section of the wire envelope, separate\n * from scalar arguments. The runtime resolves each reference to bytes\n * before dispatching the event to the model handler.\n */\nexport class FileRef {\n  /** @internal Marker so sendCommand can detect FileRef values. */\n  readonly __isFileRef = true as const;\n\n  constructor(\n    public readonly uploadId: string,\n    public readonly name: string,\n    public readonly mimeType: string,\n    public readonly size: number\n  ) {}\n}\n","export type ReactorStatus =\n  | \"disconnected\" // Not connected to anything\n  | \"connecting\" // Establishing connection to coordinator\n  | \"waiting\" // Connected to coordinator, waiting for GPU assignment\n  | \"ready\"; // Connected to GPU machine, can send/receive messages\n\n/**\n * The message scope identifies the envelope layer a data channel message belongs to.\n * - \"application\": model-defined commands (client->runtime) and model-emitted payloads (runtime->client).\n * - \"runtime\": platform-level control messages (e.g., capabilities exchange).\n */\nexport type MessageScope = \"application\" | \"runtime\";\n\n// Re-export core types that users may need\nexport type {\n  TrackCapability,\n  CommandCapability,\n  Capabilities,\n  SessionResponse,\n} from \"./core/types\";\n\nexport { FileRef } from \"./core/FileRef\";\n\nexport interface ReactorError {\n  code: string;\n  message: string;\n  timestamp: number;\n  recoverable: boolean;\n  component: \"api\" | \"gpu\";\n  retryAfter?: number;\n}\n\nexport class ConflictError extends Error {\n  constructor(message: string) {\n    super(message);\n  }\n}\n\nexport class AbortError extends Error {\n  constructor(message: string) {\n    super(message);\n  }\n}\n\n/** Matches both our custom AbortError and the native DOMException thrown by fetch(). */\nexport function isAbortError(error: unknown): boolean {\n  return (\n    error instanceof AbortError ||\n    (error instanceof Error && error.name === \"AbortError\")\n  );\n}\n\nexport interface ReactorState {\n  status: ReactorStatus;\n  lastError?: ReactorError;\n}\n\n/**\n * Options for configuring the connect polling behavior.\n */\nexport interface ConnectOptions {\n  /** Maximum number of SDP polling attempts before giving up. Default: 6. */\n  maxAttempts?: number;\n  /**\n   * When true (default), sends `resume_track` for every recvonly track\n   * immediately after the connection is established, causing the backend to\n   * begin streaming those tracks — this preserves the pre-multi-connection\n   * behaviour where output tracks flow automatically on connect. Set to false\n   * to keep recvonly tracks paused on connect and resume them individually via\n   * `resumeTrack()` (e.g. multi-connection apps that only subscribe to a\n   * subset of peers).\n   */\n  autoResumeTracks?: boolean;\n  /**\n   * Attach to a session that already exists (e.g. one created by a backend)\n   * instead of creating a new one. When set, `connect()` skips `POST /sessions`\n   * and brings up the transport directly against this id. The JWT passed to\n   * `connect()` must be valid for the account that owns the session.\n   */\n  sessionId?: string;\n  /**\n   * Use a specific WebRTC connection id instead of minting one. When omitted\n   * (default), the transport registers a fresh connection (`POST .../connections`)\n   * and the server mints the id. When set, the transport skips registration and\n   * sends its SDP offer directly to that connection — so the id must already have\n   * been registered under this session (e.g. a backend called `POST .../connections`\n   * and handed the id to this client) and must still be open.\n   *\n   * `connect()` rejects if the id is invalid (out of the server's accepted range)\n   * or if no open connection with that id exists for the session.\n   */\n  connectionId?: number;\n}\n\n/**\n * Transport-agnostic timing breakdown of the connect() handshake, recorded\n * once per connection and included in every subsequent {@link ConnectionStats}\n * update. All durations are in milliseconds (from `performance.now()`).\n *\n * For transport-specific timings (e.g. ICE negotiation, data channel open),\n * see the relevant transport stats type (e.g. {@link WebRTCTransportTimings}).\n */\nexport interface ConnectionTimings {\n  /** POST /sessions round-trip time */\n  sessionCreationMs: number;\n  /** Total time spent in transport.connect() (signaling, negotiation, etc.) */\n  transportConnectingMs: number;\n  /** End-to-end: connect() invocation → status \"ready\" */\n  totalMs: number;\n}\n\nexport interface ConnectionStats {\n  /** ICE candidate-pair round-trip time in milliseconds */\n  rtt?: number;\n  /** ICE candidate type: \"host\", \"srflx\", \"prflx\", or \"relay\" (TURN) */\n  candidateType?: string;\n  /** Estimated available incoming bitrate in bits/second */\n  availableIncomingBitrate?: number;\n  /** Estimated available outgoing bitrate in bits/second */\n  availableOutgoingBitrate?: number;\n  /** Real-time Incoming bitrate in bits/second */\n  incomingBitrate?: number;\n  /** Real-time Outgoing bitrate in bits/second */\n  outgoingBitrate?: number;\n  /** Received video frames per second */\n  framesPerSecond?: number;\n  /** Ratio of packets lost (0-1) */\n  packetLossRatio?: number;\n  /** Network jitter in seconds (from inbound-rtp) */\n  jitter?: number;\n  /** Timing breakdown of the initial connection handshake (set once, persisted until disconnect) */\n  connectionTimings?: ConnectionTimings;\n  timestamp: number;\n}\n\nexport type ReactorEvent =\n  | \"statusChanged\" //updates on the reactor status\n  | \"sessionIdChanged\" //updates on the session ID.\n  | \"message\" //application-scoped messages from the model\n  | \"runtimeMessage\" //internal platform-level control messages (e.g. capabilities, moderation)\n  | \"trackReceived\" // (name: string, track: MediaStreamTrack, stream: MediaStream)\n  | \"error\" //error events with ReactorError details\n  | \"sessionExpirationChanged\" //session expiration has changed\n  | \"capabilitiesReceived\" //server capabilities received after session creation\n  | \"statsUpdate\"; //WebRTC stats update (RTT, etc.)\n\n/**\n * Severity tier of a content moderation event delivered as the inner\n * payload of a `runtimeMessage` with `type === \"moderation\"`.\n *\n * - `\"warn\"`: the input scored above the warn threshold but below the\n *   terminate threshold. The session continues; this is informational\n *   only.\n * - `\"terminate\"`: the input crossed the terminate threshold. The\n *   session will be ended shortly after this message is dispatched.\n */\nexport type ModerationAction = \"warn\" | \"terminate\";\n\n/**\n * Inner payload of a `runtimeMessage` with `type === \"moderation\"`.\n *\n * Surfaces a content-moderation outcome to the client app. Fires on\n * any moderatable input (free-text fields, file uploads) that the\n * configured moderation policy flags. Apps receive it by subscribing\n * to the existing `runtimeMessage` event and filtering on `type`:\n *\n *     reactor.on(\"runtimeMessage\", (m) => {\n *       if (m?.type === \"moderation\") {\n *         const payload = m.data as ModerationEvent;\n *         // ...render banner, log, etc.\n *       }\n *     });\n */\nexport interface ModerationEvent {\n  /** Severity tier — `\"warn\"` continues the session, `\"terminate\"` ends it. */\n  action: ModerationAction;\n  /**\n   * Modality of the flagged input. `\"text\"` for string fields,\n   * `\"image\"` for `UploadedFile` payloads with an image MIME type.\n   */\n  input_kind: \"text\" | \"image\";\n  /**\n   * Name of the inbound command/event whose payload was flagged\n   * (e.g. `\"set_prompt\"`, `\"set_image\"`, `\"fileUploaded\"`).\n   */\n  command: string;\n  /** Category labels that flagged (e.g. `[\"sexual\"]`, `[\"violence/graphic\"]`). */\n  categories: string[];\n  /** Short human-readable summary suitable for UI rendering. */\n  message: string;\n}\n","/**\n * Lazy resolver for the Coordinator bearer token. The SDK calls this\n * immediately before each authenticated HTTP request, so short-lived\n * tokens (Clerk session JWTs, etc.) are refreshed transparently.\n * Returning `\"\"` suppresses the `Authorization` header entirely.\n */\nexport type JwtResolver = () => string | Promise<string>;\n\n/** Static token, or a {@link JwtResolver} invoked per request. */\nexport type JwtSource = string | JwtResolver;\n\n/** Wrap a {@link JwtSource} into a {@link JwtResolver}. */\nexport function normalizeJwtSource(source: JwtSource): JwtResolver {\n  if (typeof source === \"function\") {\n    return source;\n  }\n  return () => source;\n}\n","/**\n * Internal types for the Reactor SDK.\n *\n * All Zod schemas and derived TypeScript types live here.\n * Version constants are sourced from package.json via resolveJsonModule.\n */\n\nimport { z } from \"zod\";\nimport packageJson from \"../../package.json\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Constants (single source of truth: package.json)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const REACTOR_SDK_VERSION: string = packageJson.version;\nexport const REACTOR_API_VERSION: number = (packageJson as any).reactor\n  .apiVersion;\nexport const REACTOR_WEBRTC_VERSION: string = (packageJson as any).reactor\n  .webrtcVersion;\nexport const REACTOR_SDK_TYPE = \"js\" as const;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Versioning Headers\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const API_VERSION_HEADER = \"Reactor-API-Version\";\nexport const API_ACCEPT_VERSION_HEADER = \"Reactor-API-Accept-Version\";\nexport const WEBRTC_VERSION_HEADER = \"Reactor-WebRTC-Version\";\n\nexport const VERSION_ERROR_CODES = {\n  426: \"CLIENT_VERSION_TOO_OLD\",\n  501: \"SERVER_VERSION_TOO_OLD\",\n} as const;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Session States\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum SessionState {\n  CREATED = \"CREATED\",\n  PENDING = \"PENDING\",\n  SUSPENDED = \"SUSPENDED\",\n  WAITING = \"WAITING\",\n  ACTIVE = \"ACTIVE\",\n  INACTIVE = \"INACTIVE\",\n  CLOSED = \"CLOSED\",\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared Schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const ClientInfoSchema = z.object({\n  sdk_version: z.string(),\n  sdk_type: z.literal(\"js\"),\n});\n\nexport const TransportDeclarationSchema = z.object({\n  protocol: z.string(),\n  version: z.string(),\n});\n\nexport const TrackCapabilitySchema = z.object({\n  name: z.string(),\n  kind: z.enum([\"video\", \"audio\"]),\n  direction: z.enum([\"recvonly\", \"sendonly\"]),\n});\n\nexport const TrackMappingEntrySchema = TrackCapabilitySchema.extend({\n  mid: z.string(),\n});\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Session API Schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\n// POST /sessions — Request\nexport const CreateSessionRequestSchema = z.object({\n  model: z.object({ name: z.string() }),\n  client_info: ClientInfoSchema,\n  supported_transports: z.array(TransportDeclarationSchema),\n  extra_args: z.record(z.string(), z.any()).optional(),\n});\n\n// Mirrors the proto Command message.\nexport const CommandCapabilitySchema = z.object({\n  name: z.string(),\n  description: z.string(),\n  schema: z.record(z.string(), z.any()).optional(),\n});\n\n// Mirrors the proto TransportCapabilities message.\nexport const CapabilitiesSchema = z.object({\n  protocol_version: z.string(),\n  tracks: z.array(TrackCapabilitySchema),\n  commands: z.array(CommandCapabilitySchema).optional(),\n  emission_fps: z.number().nullable().optional(),\n});\n\n// GET /sessions/{id}/info — Response (200)\nexport const SessionInfoResponseSchema = z.object({\n  session_id: z.string(),\n  state: z.string(),\n  cluster: z.string(),\n});\n\n// POST /sessions — Response (201)\nexport const CreateSessionResponseSchema = SessionInfoResponseSchema.extend({\n  model: z.object({ name: z.string(), version: z.string().optional() }),\n  server_info: z.object({ server_version: z.string() }),\n});\n\n// GET /sessions/{id} — Response (200)\nexport const SessionResponseSchema = CreateSessionResponseSchema.extend({\n  selected_transport: TransportDeclarationSchema.optional(),\n  capabilities: CapabilitiesSchema.optional(),\n});\n\n// DELETE /sessions/{id} — Request\nexport const TerminateSessionRequestSchema = z.object({\n  reason: z.string().optional(),\n});\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Upload API Schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\n// POST /sessions/{id}/uploads — Request\nexport const CreateUploadRequestSchema = z.object({\n  name: z.string(),\n  size: z.number().int().positive(),\n  mime_type: z.string(),\n});\n\n// POST /sessions/{id}/uploads — Response (201)\nexport const CreateUploadResponseSchema = z.object({\n  presigned_id: z.string(),\n  presigned_url: z.string(),\n  path: z.string(),\n});\n\n// ─────────────────────────────────────────────────────────────────────────────\n// WebRTC Transport Schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\n// GET /sessions/{id}/transport/webrtc/ice_servers — Response (200)\nexport const IceServerCredentialsSchema = z.object({\n  username: z.string(),\n  password: z.string(),\n});\n\nexport const IceServerSchema = z.object({\n  uris: z.array(z.string()),\n  credentials: IceServerCredentialsSchema.optional(),\n});\n\nexport const IceServersResponseSchema = z.object({\n  ice_servers: z.array(IceServerSchema),\n});\n\n// POST/PUT /sessions/{id}/transport/webrtc/sdp_params — Request\nexport const WebRTCSdpOfferRequestSchema = z.object({\n  sdp_offer: z.string(),\n  client_info: ClientInfoSchema.optional(),\n  track_mapping: z.array(TrackMappingEntrySchema),\n});\n\n// POST/PUT /sessions/{id}/transport/webrtc/sdp_params — Response (202)\n// connection_id is returned by multi-connection runtimes; absent on older servers.\nexport const WebRTCSdpOfferResponseSchema = z.object({\n  connection_id: z.number().optional(),\n});\n\n// GET /sessions/{id}/transport/webrtc/sdp_params — Response (200)\nexport const WebRTCSdpAnswerResponseSchema = z.object({\n  sdp_answer: z.string(),\n  connection_id: z.number().optional(),\n});\n\nexport const IceCandidateSchema = z.object({\n  candidate: z.string(),\n  sdp_mid: z.string().optional(),\n  sdp_mline_index: z.number().optional(),\n});\n\n// POST /sessions/{id}/transport/webrtc/ice_candidates — Request\nexport const IceCandidatesRequestSchema = z.object({\n  candidates: z.array(IceCandidateSchema),\n  is_final: z.boolean(),\n  client_info: ClientInfoSchema.optional(),\n});\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Inferred TypeScript Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type ClientInfo = z.infer<typeof ClientInfoSchema>;\nexport type TransportDeclaration = z.infer<typeof TransportDeclarationSchema>;\nexport type TrackCapability = z.infer<typeof TrackCapabilitySchema>;\nexport type CommandCapability = z.infer<typeof CommandCapabilitySchema>;\nexport type TrackMappingEntry = z.infer<typeof TrackMappingEntrySchema>;\n\nexport type CreateSessionRequest = z.infer<typeof CreateSessionRequestSchema>;\nexport type CreateSessionResponse = z.infer<typeof CreateSessionResponseSchema>;\nexport type SessionResponse = z.infer<typeof SessionResponseSchema>;\nexport type Capabilities = z.infer<typeof CapabilitiesSchema>;\n\nexport type SessionInfoResponse = z.infer<typeof SessionInfoResponseSchema>;\nexport type TerminateSessionRequest = z.infer<\n  typeof TerminateSessionRequestSchema\n>;\n\nexport type CreateUploadRequest = z.infer<typeof CreateUploadRequestSchema>;\nexport type CreateUploadResponse = z.infer<typeof CreateUploadResponseSchema>;\n\nexport type IceServer = z.infer<typeof IceServerSchema>;\nexport type IceServersResponse = z.infer<typeof IceServersResponseSchema>;\n\nexport type WebRTCSdpOfferRequest = z.infer<typeof WebRTCSdpOfferRequestSchema>;\nexport type WebRTCSdpOfferResponse = z.infer<\n  typeof WebRTCSdpOfferResponseSchema\n>;\nexport type WebRTCSdpAnswerResponse = z.infer<\n  typeof WebRTCSdpAnswerResponseSchema\n>;\nexport type IceCandidate = z.infer<typeof IceCandidateSchema>;\nexport type IceCandidatesRequest = z.infer<typeof IceCandidatesRequestSchema>;\n","{\n  \"name\": \"@reactor-team/js-sdk\",\n  \"version\": \"2.12.0\",\n  \"description\": \"Reactor JavaScript frontend SDK — connect React and TypeScript apps to real-time AI video models on Reactor.\",\n  \"license\": \"Apache-2.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/reactor-team/js-sdk.git\"\n  },\n  \"keywords\": [\n    \"reactor\",\n    \"sdk\",\n    \"frontend\",\n    \"react\",\n    \"webrtc\",\n    \"real-time\",\n    \"video\",\n    \"ai\"\n  ],\n  \"author\": \"Reactor Technologies, Inc.\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.mjs\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.js\"\n    }\n  },\n  \"sideEffects\": false,\n  \"engines\": {\n    \"node\": \">=18\"\n  },\n  \"files\": [\n    \"dist\",\n    \"README.md\",\n    \"LICENSE\",\n    \"NOTICE\"\n  ],\n  \"scripts\": {\n    \"prepack\": \"cp ../../LICENSE ../../NOTICE .\",\n    \"postpack\": \"rm -f LICENSE NOTICE\",\n    \"build\": \"tsup\",\n    \"dev\": \"tsup --watch\",\n    \"test\": \"vitest run\",\n    \"test:watch\": \"vitest\",\n    \"test:unit\": \"vitest run __tests__/unit\",\n    \"test:integration\": \"vitest run __tests__/integration\",\n    \"format\": \"prettier --write .\",\n    \"format:check\": \"prettier --check .\"\n  },\n  \"reactor\": {\n    \"apiVersion\": 1,\n    \"webrtcVersion\": \"1.0\"\n  },\n  \"packageManager\": \"pnpm@10.12.1\",\n  \"dependencies\": {\n    \"awaitqueue\": \"^3.3.0\",\n    \"mp4box\": \"^2.3.0\",\n    \"sdp-transform\": \"^3.0.0\",\n    \"zod\": \"^4.4.3\"\n  },\n  \"peerDependencies\": {\n    \"hls.js\": \"^1.6.0\",\n    \"react\": \"^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"zustand\": \"^5.0.6\"\n  },\n  \"peerDependenciesMeta\": {\n    \"hls.js\": {\n      \"optional\": true\n    }\n  },\n  \"devDependencies\": {\n    \"@roamhq/wrtc\": \"^0.8.0\",\n    \"@types/node\": \"^25\",\n    \"@types/react\": \"^19.2.15\",\n    \"@types/sdp-transform\": \"^2.15.0\",\n    \"hls.js\": \"^1.6.0\",\n    \"prettier\": \"^3.6.2\",\n    \"tsup\": \"^8.5.0\",\n    \"typescript\": \"^5.8.3\",\n    \"vitest\": \"^4.1.7\"\n  }\n}\n","/**\n * The CoordinatorClient handles session lifecycle via HTTP requests.\n *\n * Transport signaling (ICE servers, SDP exchange) is NOT handled here —\n * that responsibility belongs to the TransportClient implementations.\n */\n\nimport {\n  type CreateSessionRequest,\n  type CreateSessionResponse,\n  type CreateUploadRequest,\n  type CreateUploadResponse,\n  type SessionResponse,\n  type SessionInfoResponse,\n  type TerminateSessionRequest,\n  CreateSessionResponseSchema,\n  CreateUploadResponseSchema,\n  SessionResponseSchema,\n  SessionInfoResponseSchema,\n  SessionState,\n  REACTOR_API_VERSION,\n  REACTOR_SDK_VERSION,\n  REACTOR_SDK_TYPE,\n  REACTOR_WEBRTC_VERSION,\n  API_VERSION_HEADER,\n  API_ACCEPT_VERSION_HEADER,\n  VERSION_ERROR_CODES,\n} from \"./types\";\nimport { AbortError } from \"../types\";\nimport { type JwtResolver, type JwtSource, normalizeJwtSource } from \"./auth\";\n\nconst SESSION_POLL_INITIAL_BACKOFF_MS = 200;\nconst SESSION_POLL_MAX_BACKOFF_MS = 10_000;\nconst SESSION_POLL_BACKOFF_MULTIPLIER = 2;\nconst SESSION_POLL_DEFAULT_MAX_ATTEMPTS = 20;\n\nexport interface CoordinatorClientOptions {\n  baseUrl: string;\n  jwtToken: JwtSource;\n  model: string;\n}\n\nexport class CoordinatorClient {\n  protected readonly baseUrl: string;\n  private readonly resolveJwt: JwtResolver;\n  protected readonly model: string;\n  protected currentSessionId?: string;\n  private abortController: AbortController;\n\n  constructor(options: CoordinatorClientOptions) {\n    this.baseUrl = options.baseUrl;\n    this.resolveJwt = normalizeJwtSource(options.jwtToken);\n    this.model = options.model;\n    this.abortController = new AbortController();\n  }\n\n  /**\n   * Aborts any in-flight HTTP requests.\n   * A fresh AbortController is created so the client remains reusable.\n   */\n  abort(): void {\n    this.abortController.abort();\n    this.abortController = new AbortController();\n  }\n\n  /**\n   * The current abort signal, passed to every fetch() call.\n   * Protected so subclasses can forward it to their own fetch calls.\n   */\n  protected get signal(): AbortSignal {\n    return this.abortController.signal;\n  }\n\n  /**\n   * Returns authorization + versioning headers for all coordinator\n   * requests. Async so the JWT resolver can fetch a fresh token if\n   * needed; an empty token suppresses the `Authorization` header.\n   */\n  protected async getHeaders(): Promise<Record<string, string>> {\n    const headers: Record<string, string> = {\n      [API_VERSION_HEADER]: String(REACTOR_API_VERSION),\n      [API_ACCEPT_VERSION_HEADER]: String(REACTOR_API_VERSION),\n    };\n    const jwt = await this.resolveJwt();\n    if (jwt) {\n      headers.Authorization = `Bearer ${jwt}`;\n    }\n    return headers;\n  }\n\n  /**\n   * Checks an HTTP response for version mismatch errors (426, 501).\n   * Logs a clear message and throws with a descriptive error code.\n   */\n  protected async checkVersionMismatch(response: Response): Promise<void> {\n    if (response.status === 426) {\n      const msg =\n        `Client API version (${REACTOR_API_VERSION}) is too old. ` +\n        `Server requires a newer version. Please upgrade @reactor-team/js-sdk.`;\n      console.error(`[Reactor]`, msg);\n      throw new Error(`${VERSION_ERROR_CODES[426]}: ${msg}`);\n    }\n\n    if (response.status === 501) {\n      const msg =\n        `Server does not support API version ${REACTOR_API_VERSION}. ` +\n        `The server may need to be updated.`;\n      console.error(`[Reactor]`, msg);\n      throw new Error(`${VERSION_ERROR_CODES[501]}: ${msg}`);\n    }\n  }\n\n  protected sleep(ms: number): Promise<void> {\n    return new Promise((resolve, reject) => {\n      const { signal } = this;\n      if (signal.aborted) {\n        reject(new AbortError(\"Sleep aborted\"));\n        return;\n      }\n      const timer = setTimeout(() => {\n        signal.removeEventListener(\"abort\", onAbort);\n        resolve();\n      }, ms);\n      const onAbort = () => {\n        clearTimeout(timer);\n        reject(new AbortError(\"Sleep aborted\"));\n      };\n      signal.addEventListener(\"abort\", onAbort, { once: true });\n    });\n  }\n\n  /**\n   * Creates a new session with the coordinator.\n   * No SDP is sent — transport signaling is decoupled from session creation.\n   *\n   * The POST response is a slim acknowledgment (session_id, model name, status).\n   * Capabilities and transport details are populated later once the Runtime\n   * accepts the session — use {@link pollSessionReady} to wait for them.\n   */\n  async createSession(\n    extraArgs?: Record<string, any>\n  ): Promise<CreateSessionResponse> {\n    console.debug(\"[CoordinatorClient] Creating session...\");\n\n    const requestBody: CreateSessionRequest = {\n      model: { name: this.model },\n      client_info: {\n        sdk_version: REACTOR_SDK_VERSION,\n        sdk_type: REACTOR_SDK_TYPE,\n      },\n      supported_transports: [\n        { protocol: \"webrtc\", version: REACTOR_WEBRTC_VERSION },\n      ],\n      ...(extraArgs ? { extra_args: extraArgs } : {}),\n    };\n\n    const response = await fetch(`${this.baseUrl}/sessions`, {\n      method: \"POST\",\n      headers: {\n        ...(await this.getHeaders()),\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify(requestBody),\n      signal: this.signal,\n    });\n\n    await this.checkVersionMismatch(response);\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to create session: ${response.status} ${errorText}`\n      );\n    }\n\n    const data = await response.json();\n    const parsed = CreateSessionResponseSchema.parse(data);\n    this.currentSessionId = parsed.session_id;\n\n    console.debug(\n      \"[CoordinatorClient] Session created:\",\n      this.currentSessionId,\n      \"state:\",\n      parsed.state\n    );\n\n    return parsed;\n  }\n\n  /**\n   * Attaches to a session created elsewhere (e.g. by a backend) instead of\n   * creating one via {@link createSession}. After this, {@link pollSessionReady}\n   * and {@link getSession} operate on the adopted id. The session must be owned\n   * by the account behind this client's JWT.\n   */\n  async adoptSession(sessionId: string): Promise<void> {\n    this.currentSessionId = sessionId;\n    console.debug(\"[CoordinatorClient] Adopted existing session:\", sessionId);\n  }\n\n  /**\n   * Polls GET /sessions/{id} until the Runtime has accepted the session\n   * and populated capabilities and selected_transport.\n   */\n  async pollSessionReady(opts?: {\n    maxAttempts?: number;\n  }): Promise<SessionResponse> {\n    if (!this.currentSessionId) {\n      throw new Error(\"No active session. Call createSession() first.\");\n    }\n\n    const maxAttempts = opts?.maxAttempts ?? SESSION_POLL_DEFAULT_MAX_ATTEMPTS;\n    let backoffMs = SESSION_POLL_INITIAL_BACKOFF_MS;\n    let attempt = 0;\n\n    console.debug(\n      \"[CoordinatorClient] Polling session until capabilities are available...\"\n    );\n\n    while (true) {\n      if (this.signal.aborted) {\n        throw new AbortError(\"Session polling aborted\");\n      }\n\n      if (attempt >= maxAttempts) {\n        throw new Error(\n          `Session polling exceeded maximum attempts (${maxAttempts}). ` +\n            `The model may be unavailable or overloaded.`\n        );\n      }\n\n      attempt++;\n\n      const response = await fetch(\n        `${this.baseUrl}/sessions/${this.currentSessionId}`,\n        {\n          method: \"GET\",\n          headers: await this.getHeaders(),\n          signal: this.signal,\n        }\n      );\n\n      await this.checkVersionMismatch(response);\n\n      if (!response.ok) {\n        const errorText = await response.text();\n        throw new Error(\n          `Failed to poll session: ${response.status} ${errorText}`\n        );\n      }\n\n      const data = await response.json();\n      const partial = SessionResponseSchema.parse(data);\n\n      const terminalStates: string[] = [\n        SessionState.CLOSED,\n        SessionState.INACTIVE,\n      ];\n      if (terminalStates.includes(partial.state)) {\n        throw new Error(\n          `Session entered terminal state \"${partial.state}\" while waiting for capabilities`\n        );\n      }\n\n      if (partial.capabilities && partial.selected_transport) {\n        console.debug(\n          `[CoordinatorClient] Session ready after ${attempt} poll(s), ` +\n            `transport: ${partial.selected_transport.protocol}, ` +\n            `tracks: ${partial.capabilities.tracks.length}`\n        );\n        return partial;\n      }\n\n      console.debug(\n        `[CoordinatorClient] Session poll ${attempt}/${maxAttempts} — ` +\n          `state: ${partial.state}, waiting ${backoffMs}ms...`\n      );\n\n      await this.sleep(backoffMs);\n      backoffMs = Math.min(\n        backoffMs * SESSION_POLL_BACKOFF_MULTIPLIER,\n        SESSION_POLL_MAX_BACKOFF_MS\n      );\n    }\n  }\n\n  /**\n   * Gets session details from the coordinator.\n   * Fields like selected_transport and capabilities are only present\n   * after the Runtime accepts the session.\n   */\n  async getSession(): Promise<SessionResponse> {\n    if (!this.currentSessionId) {\n      throw new Error(\"No active session. Call createSession() first.\");\n    }\n\n    const response = await fetch(\n      `${this.baseUrl}/sessions/${this.currentSessionId}`,\n      {\n        method: \"GET\",\n        headers: await this.getHeaders(),\n        signal: this.signal,\n      }\n    );\n\n    await this.checkVersionMismatch(response);\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(`Failed to get session: ${response.status} ${errorText}`);\n    }\n\n    const data = await response.json();\n    return SessionResponseSchema.parse(data);\n  }\n\n  /**\n   * Gets lightweight session status (session_id, cluster, status).\n   */\n  async getSessionInfo(): Promise<SessionInfoResponse> {\n    if (!this.currentSessionId) {\n      throw new Error(\"No active session. Call createSession() first.\");\n    }\n\n    const response = await fetch(\n      `${this.baseUrl}/sessions/${this.currentSessionId}/info`,\n      {\n        method: \"GET\",\n        headers: await this.getHeaders(),\n        signal: this.signal,\n      }\n    );\n\n    await this.checkVersionMismatch(response);\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to get session info: ${response.status} ${errorText}`\n      );\n    }\n\n    const data = await response.json();\n    return SessionInfoResponseSchema.parse(data);\n  }\n\n  /**\n   * Restarts an inactive session with a different compute unit.\n   * The session ID is preserved but a new transport must be established.\n   */\n  async restartSession(): Promise<void> {\n    if (!this.currentSessionId) {\n      throw new Error(\"No active session. Call createSession() first.\");\n    }\n\n    console.debug(\n      \"[CoordinatorClient] Restarting session:\",\n      this.currentSessionId\n    );\n\n    const response = await fetch(\n      `${this.baseUrl}/sessions/${this.currentSessionId}`,\n      {\n        method: \"PUT\",\n        headers: await this.getHeaders(),\n        signal: this.signal,\n      }\n    );\n\n    await this.checkVersionMismatch(response);\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to restart session: ${response.status} ${errorText}`\n      );\n    }\n  }\n\n  /**\n   * Terminates the current session by sending a DELETE request.\n   * No-op if no session has been created yet.\n   * @param reason Optional termination reason\n   */\n  async terminateSession(reason?: string): Promise<void> {\n    if (!this.currentSessionId) {\n      return;\n    }\n\n    console.debug(\n      \"[CoordinatorClient] Terminating session:\",\n      this.currentSessionId\n    );\n\n    const body: TerminateSessionRequest | undefined = reason\n      ? { reason }\n      : undefined;\n\n    const response = await fetch(\n      `${this.baseUrl}/sessions/${this.currentSessionId}`,\n      {\n        method: \"DELETE\",\n        headers: {\n          ...(await this.getHeaders()),\n          ...(body ? { \"Content-Type\": \"application/json\" } : {}),\n        },\n        ...(body ? { body: JSON.stringify(body) } : {}),\n        signal: this.signal,\n      }\n    );\n\n    if (response.ok) {\n      this.currentSessionId = undefined;\n      return;\n    }\n\n    if (response.status === 404) {\n      console.debug(\n        \"[CoordinatorClient] Session not found on server, clearing local state:\",\n        this.currentSessionId\n      );\n      this.currentSessionId = undefined;\n      return;\n    }\n\n    const errorText = await response.text();\n    throw new Error(\n      `Failed to terminate session: ${response.status} ${errorText}`\n    );\n  }\n\n  /**\n   * Allocates an upload slot for a file.\n   * Returns a presigned PUT URL the caller can use to upload the file\n   * directly to the object store (production) or local runtime (local dev).\n   */\n  async createUpload(\n    sessionId: string,\n    request: CreateUploadRequest\n  ): Promise<CreateUploadResponse> {\n    console.debug(\"[CoordinatorClient] Creating upload slot...\", {\n      sessionId,\n      name: request.name,\n      size: request.size,\n    });\n\n    const response = await fetch(\n      `${this.baseUrl}/sessions/${sessionId}/uploads`,\n      {\n        method: \"POST\",\n        headers: {\n          ...(await this.getHeaders()),\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify(request),\n        signal: this.signal,\n      }\n    );\n\n    await this.checkVersionMismatch(response);\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to create upload slot: ${response.status} ${errorText}`\n      );\n    }\n\n    const data = await response.json();\n    return CreateUploadResponseSchema.parse(data);\n  }\n\n  getSessionId(): string | undefined {\n    return this.currentSessionId;\n  }\n}\n","/**\n * LocalCoordinatorClient connects to a local runtime instance.\n *\n * The local runtime uses a simpler protocol than the production coordinator:\n *   - POST /start_session  → starts session, returns full capabilities immediately\n *   - GET  /session        → read-only session descriptor (no id; single session)\n *   - POST /stop_session   → stops session\n *   - Transport signaling via /sessions/{id}/transport/webrtc/* (unchanged)\n *\n * No session polling is needed because the local runtime IS the model host —\n * capabilities are known the moment the session is created.\n */\n\nimport { CoordinatorClient } from \"./CoordinatorClient\";\nimport {\n  type CreateSessionResponse,\n  type SessionResponse,\n  CreateSessionResponseSchema,\n  SessionResponseSchema,\n  API_VERSION_HEADER,\n  API_ACCEPT_VERSION_HEADER,\n  REACTOR_API_VERSION,\n} from \"./types\";\n\nexport class LocalCoordinatorClient extends CoordinatorClient {\n  private cachedSessionResponse?: SessionResponse;\n\n  constructor(baseUrl: string, model: string) {\n    super({\n      baseUrl,\n      jwtToken: \"local\",\n      model,\n    });\n  }\n\n  // Local runtime endpoints are auth-free; skip the Authorization header.\n  protected override async getHeaders(): Promise<Record<string, string>> {\n    return {\n      [API_VERSION_HEADER]: String(REACTOR_API_VERSION),\n      [API_ACCEPT_VERSION_HEADER]: String(REACTOR_API_VERSION),\n    };\n  }\n\n  /**\n   * Starts a session on the local runtime.\n   *\n   * Unlike the production coordinator, the local runtime returns the full\n   * response (capabilities, selected_transport) immediately — no polling needed.\n   */\n  override async createSession(\n    extraArgs?: Record<string, any>\n  ): Promise<CreateSessionResponse> {\n    console.debug(\"[LocalCoordinatorClient] Starting session...\");\n\n    const response = await fetch(`${this.baseUrl}/start_session`, {\n      method: \"POST\",\n      headers: {\n        ...(await this.getHeaders()),\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        ...(extraArgs ? { extra_args: extraArgs } : {}),\n      }),\n      signal: this.signal,\n    });\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to start session: ${response.status} ${errorText}`\n      );\n    }\n\n    const data = await response.json();\n\n    const session = SessionResponseSchema.parse(data);\n    this.cachedSessionResponse = session;\n    this.currentSessionId = session.session_id;\n\n    console.debug(\n      \"[LocalCoordinatorClient] Session started:\",\n      this.currentSessionId,\n      \"transport:\",\n      session.selected_transport?.protocol,\n      \"tracks:\",\n      session.capabilities?.tracks.length\n    );\n\n    return CreateSessionResponseSchema.parse(data);\n  }\n\n  /**\n   * Adopts the local session for `connect({ sessionId })`: records the id and\n   * reads the session descriptor, caching it so {@link pollSessionReady}\n   * returns immediately. No `start_session` is issued.\n   *\n   * The local runtime hosts a single session (always id `\"local\"`), so the\n   * lookup ignores the specific id. This is a pure read of session info — it\n   * does not start or otherwise advance the session.\n   */\n  override async adoptSession(sessionId: string): Promise<void> {\n    this.currentSessionId = sessionId;\n    this.cachedSessionResponse = await this.getSession();\n    console.debug(\n      \"[LocalCoordinatorClient] Adopted existing session:\",\n      sessionId\n    );\n  }\n\n  /**\n   * Reads the local session descriptor (model, capabilities, transport,\n   * state) via the runtime's read-only `GET /session`.\n   *\n   * The local runtime exposes a single session, so — unlike the Coordinator's\n   * id-addressed `GET /sessions/{id}` — there is no id in the path. Read-only:\n   * it does not start or advance the session.\n   */\n  override async getSession(): Promise<SessionResponse> {\n    const response = await fetch(`${this.baseUrl}/session`, {\n      method: \"GET\",\n      headers: await this.getHeaders(),\n      signal: this.signal,\n    });\n\n    if (!response.ok) {\n      const errorText = await response.text();\n      throw new Error(`Failed to get session: ${response.status} ${errorText}`);\n    }\n\n    const data = await response.json();\n    return SessionResponseSchema.parse(data);\n  }\n\n  /**\n   * Returns the cached full session response immediately.\n   * The local runtime already provided everything in start_session.\n   */\n  override async pollSessionReady(): Promise<SessionResponse> {\n    if (!this.cachedSessionResponse) {\n      throw new Error(\n        \"No cached session response. Call createSession() first.\"\n      );\n    }\n    return this.cachedSessionResponse;\n  }\n\n  /**\n   * Stops the session on the local runtime.\n   */\n  override async terminateSession(): Promise<void> {\n    if (!this.currentSessionId) {\n      return;\n    }\n\n    console.debug(\n      \"[LocalCoordinatorClient] Stopping session:\",\n      this.currentSessionId\n    );\n\n    try {\n      await fetch(`${this.baseUrl}/stop_session`, {\n        method: \"POST\",\n        headers: await this.getHeaders(),\n        signal: this.signal,\n      });\n    } catch (error) {\n      console.error(\"[LocalCoordinatorClient] Error stopping session:\", error);\n    }\n\n    this.currentSessionId = undefined;\n    this.cachedSessionResponse = undefined;\n  }\n}\n","/**\n * WebRTC implementation of the TransportClient interface.\n *\n * Handles the full WebRTC lifecycle: signaling (ICE servers, SDP exchange\n * via the transport REST endpoints), RTCPeerConnection management, data\n * channel messaging, track publishing, and stats collection.\n */\n\nimport { AwaitQueue } from \"awaitqueue\";\nimport * as webrtc from \"../utils/webrtc\";\nimport type {\n  TransportClient,\n  TransportClientConfig,\n  TransportStatus,\n  TransportEvent,\n} from \"./TransportClient\";\nimport type { MessageScope, ConnectionStats } from \"../types\";\nimport { AbortError } from \"../types\";\nimport { type JwtResolver, normalizeJwtSource } from \"./auth\";\nimport {\n  type TrackCapability,\n  type TrackMappingEntry,\n  type WebRTCSdpOfferRequest,\n  type WebRTCSdpAnswerResponse,\n  type IceCandidate,\n  type IceCandidatesRequest,\n  IceServersResponseSchema,\n  WebRTCSdpOfferResponseSchema,\n  WebRTCSdpAnswerResponseSchema,\n  REACTOR_WEBRTC_VERSION,\n  REACTOR_SDK_VERSION,\n  REACTOR_SDK_TYPE,\n  WEBRTC_VERSION_HEADER,\n  VERSION_ERROR_CODES,\n} from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype EventHandler = (...args: any[]) => void;\n\nconst PING_INTERVAL_MS = 5_000;\nconst STATS_INTERVAL_MS = 2_000;\n\nconst INITIAL_BACKOFF_MS = 200;\nconst MAX_BACKOFF_MS = 15_000;\nconst BACKOFF_MULTIPLIER = 2;\nconst DEFAULT_MAX_POLL_ATTEMPTS = 6;\n\n// Accepted range for a server-minted connection id, mirroring the Coordinator's\n// validation (`connection_id > 1000 && <= 9999`). Used to fail fast on an\n// obviously invalid caller-supplied id before hitting the network.\nconst MIN_CONNECTION_ID = 1000;\nconst MAX_CONNECTION_ID = 9999;\n\n/**\n * Debounce window for coalescing trickle ICE candidates into a single\n * POST. Browsers fire {@link RTCPeerConnection.onicecandidate} in bursts\n * (host candidates land together, then srflx, then relay); buffering for\n * a few tens of milliseconds collapses each burst into one request\n * without adding noticeable latency to the connection. The\n * gathering-complete event ({@link RTCPeerConnectionIceEvent.candidate}\n * === ``null``) bypasses the debounce and flushes immediately.\n */\nconst ICE_CANDIDATE_BATCH_WINDOW_MS = 25;\n\nexport interface WebRTCTransportConfig extends TransportClientConfig {\n  webrtcVersion?: string;\n  maxPollAttempts?: number;\n}\n\n/**\n * WebRTC-specific timing breakdown of the transport connection.\n * Recorded once per connection and accessible via {@link WebRTCTransportClient.getTransportTimings}.\n */\nexport interface WebRTCTransportTimings {\n  protocol: \"webrtc\";\n  /** Time spent polling for the SDP answer (POST offer → GET answer 200) */\n  sdpPollingMs: number;\n  /** Number of SDP poll requests made (1 = answered on first try) */\n  sdpPollingAttempts: number;\n  /** setRemoteDescription → RTCPeerConnection connectionState \"connected\" */\n  iceNegotiationMs: number;\n  /** setRemoteDescription → RTCDataChannel \"open\" */\n  dataChannelMs: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Transceiver bookkeeping\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface TransceiverEntry {\n  name: string;\n  kind: \"audio\" | \"video\";\n  direction: RTCRtpTransceiverDirection;\n  transceiver?: RTCRtpTransceiver;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Implementation\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class WebRTCTransportClient implements TransportClient {\n  private eventListeners: Map<TransportEvent, Set<EventHandler>> = new Map();\n  private peerConnection: RTCPeerConnection | undefined;\n  private dataChannel: RTCDataChannel | undefined;\n  private controlChannel: RTCDataChannel | undefined;\n  private status: TransportStatus = \"disconnected\";\n  private pingInterval: ReturnType<typeof setInterval> | undefined;\n  private statsInterval: ReturnType<typeof setInterval> | undefined;\n  private stats: ConnectionStats | undefined;\n\n  private transceiverMap: Map<string, TransceiverEntry> = new Map();\n  private publishedTracks: Map<string, MediaStreamTrack> = new Map();\n  // Serializes all SDP renegotiations (pause/resume direction changes) and gates\n  // them behind the connection-ready task. Reset on each prepare() call.\n  private peerConnectionQueue = new AwaitQueue();\n  // Resolves the connection-ready gate task pushed in prepare(). Called by\n  // checkFullyConnected() once all three ready conditions are met.\n  private readyGateResolve: (() => void) | undefined;\n  private pendingControlRequests = new Map<\n    string,\n    {\n      resolve: () => void;\n      reject: (err: Error) => void;\n      timeout: ReturnType<typeof setTimeout>;\n    }\n  >();\n  private controlRequestCounter = 0;\n  private peerConnected = false;\n  private dataChannelOpen = false;\n  private controlChannelOpen = false;\n\n  private iceStartTime?: number;\n  private iceNegotiationMs?: number;\n  private dataChannelMs?: number;\n  private sdpPollingMs?: number;\n  private sdpPollingAttempts?: number;\n\n  private connectionId: number | undefined;\n  private pendingSdpOffer?: string;\n  private pendingTrackMapping?: TrackMappingEntry[];\n  private cachedIceServers?: Promise<RTCIceServer[]>;\n\n  // Trickle ICE batching\n  private pendingIceCandidates: IceCandidate[] = [];\n  private iceCandidateFlushTimer?: ReturnType<typeof setTimeout>;\n\n  private readonly baseUrl: string;\n  private readonly sessionId: string;\n  private readonly resolveJwt: JwtResolver;\n  webrtcVersion: string;\n  private readonly maxPollAttempts: number;\n  private abortController: AbortController;\n\n  constructor(config: WebRTCTransportConfig) {\n    this.baseUrl = config.baseUrl;\n    this.sessionId = config.sessionId;\n    this.resolveJwt = normalizeJwtSource(config.jwtToken);\n    this.webrtcVersion = config.webrtcVersion ?? REACTOR_WEBRTC_VERSION;\n    this.maxPollAttempts = config.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;\n    this.abortController = new AbortController();\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Event Emitter\n  // ─────────────────────────────────────────────────────────────────────────\n\n  on(event: TransportEvent, handler: EventHandler): void {\n    if (!this.eventListeners.has(event)) {\n      this.eventListeners.set(event, new Set());\n    }\n    this.eventListeners.get(event)!.add(handler);\n  }\n\n  off(event: TransportEvent, handler: EventHandler): void {\n    this.eventListeners.get(event)?.delete(handler);\n  }\n\n  private emit(event: TransportEvent, ...args: unknown[]): void {\n    this.eventListeners.get(event)?.forEach((handler) => handler(...args));\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // HTTP Helpers\n  // ─────────────────────────────────────────────────────────────────────────\n\n  private get signal(): AbortSignal {\n    return this.abortController.signal;\n  }\n\n  private get transportBaseUrl(): string {\n    return `${this.baseUrl}/sessions/${this.sessionId}/transport/webrtc`;\n  }\n\n  // Async so the JWT resolver can fetch a fresh token if needed; an\n  // empty token suppresses the `Authorization` header.\n  private async getHeaders(): Promise<Record<string, string>> {\n    const headers: Record<string, string> = {\n      [WEBRTC_VERSION_HEADER]: this.webrtcVersion,\n    };\n    const jwt = await this.resolveJwt();\n    if (jwt) {\n      headers.Authorization = `Bearer ${jwt}`;\n    }\n    return headers;\n  }\n\n  private async checkVersionMismatch(response: Response): Promise<void> {\n    if (response.status === 426) {\n      const msg =\n        `Client WebRTC version (${this.webrtcVersion}) is too old. ` +\n        `Server requires a newer version. Please upgrade @reactor-team/js-sdk.`;\n      console.error(`[WebRTCTransport]`, msg);\n      throw new Error(`${VERSION_ERROR_CODES[426]}: ${msg}`);\n    }\n\n    if (response.status === 501) {\n      const msg =\n        `Server does not support WebRTC version ${this.webrtcVersion}. ` +\n        `The server may need to be updated.`;\n      console.error(`[WebRTCTransport]`, msg);\n      throw new Error(`${VERSION_ERROR_CODES[501]}: ${msg}`);\n    }\n  }\n\n  private sleep(ms: number): Promise<void> {\n    return new Promise((resolve, reject) => {\n      const { signal } = this;\n      if (signal.aborted) {\n        reject(new AbortError(\"Sleep aborted\"));\n        return;\n      }\n      const timer = setTimeout(() => {\n        signal.removeEventListener(\"abort\", onAbort);\n        resolve();\n      }, ms);\n      const onAbort = () => {\n        clearTimeout(timer);\n        reject(new AbortError(\"Sleep aborted\"));\n      };\n      signal.addEventListener(\"abort\", onAbort, { once: true });\n    });\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Transport Signaling (HTTP)\n  // ─────────────────────────────────────────────────────────────────────────\n\n  private async fetchIceServers(): Promise<RTCIceServer[]> {\n    console.debug(\"[WebRTCTransport] Fetching ICE servers...\");\n\n    const response = await fetch(`${this.transportBaseUrl}/ice_servers`, {\n      method: \"GET\",\n      headers: await this.getHeaders(),\n      signal: this.signal,\n    });\n\n    await this.checkVersionMismatch(response);\n\n    if (!response.ok) {\n      throw new Error(`Failed to fetch ICE servers: ${response.status}`);\n    }\n\n    const data = await response.json();\n    const parsed = IceServersResponseSchema.parse(data);\n    const iceServers = webrtc.transformIceServers(parsed);\n\n    console.debug(\"[WebRTCTransport] Received ICE servers:\", iceServers.length);\n    return iceServers;\n  }\n\n  private async registerConnection(): Promise<number> {\n    console.debug(\"[WebRTCTransport] Registering connection...\");\n\n    const response = await fetch(`${this.transportBaseUrl}/connections`, {\n      method: \"POST\",\n      headers: await this.getHeaders(),\n      signal: this.signal,\n    });\n\n    await this.checkVersionMismatch(response);\n\n    if (response.status !== 201) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to register connection: ${response.status} ${errorText}`\n      );\n    }\n\n    const data = await response.json();\n    const connectionId: number = data.connection_id;\n    console.debug(\n      `[WebRTCTransport] Connection registered: id=${connectionId}`\n    );\n    return connectionId;\n  }\n\n  private async sendSdpOffer(\n    connectionId: number,\n    sdpOffer: string,\n    trackMapping: TrackMappingEntry[],\n    reconnect: boolean = false\n  ): Promise<void> {\n    const method = reconnect ? \"PUT\" : \"POST\";\n    console.debug(\n      `[WebRTCTransport] Sending SDP offer (${method}) connection=${connectionId}`\n    );\n\n    const requestBody: WebRTCSdpOfferRequest = {\n      sdp_offer: sdpOffer,\n      client_info: {\n        sdk_version: REACTOR_SDK_VERSION,\n        sdk_type: REACTOR_SDK_TYPE,\n      },\n      track_mapping: trackMapping,\n    };\n\n    const response = await fetch(\n      `${this.transportBaseUrl}/connections/${connectionId}/sdp_params`,\n      {\n        method,\n        headers: {\n          ...(await this.getHeaders()),\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify(requestBody),\n        signal: this.signal,\n      }\n    );\n\n    await this.checkVersionMismatch(response);\n\n    if (response.status !== 202) {\n      const errorText = await response.text();\n      // A caller-supplied connection id that the server doesn't recognise (or\n      // that has already been closed) surfaces here as a 404; an out-of-range\n      // id as a 400. Make those actionable rather than a bare status dump.\n      if (response.status === 404) {\n        throw new Error(\n          `[WebRTCTransport] Connection ${connectionId} not found or already ` +\n            `closed (404). A supplied connectionId must reference a connection ` +\n            `registered under this session (POST .../connections) that is still ` +\n            `open. ${errorText}`\n        );\n      }\n      if (response.status === 400) {\n        throw new Error(\n          `[WebRTCTransport] Connection ${connectionId} rejected (400): ${errorText}`\n        );\n      }\n      throw new Error(\n        `Failed to send SDP offer: ${response.status} ${errorText}`\n      );\n    }\n\n    console.debug(\"[WebRTCTransport] SDP offer accepted (202)\");\n  }\n\n  private async sendIceCandidates(\n    candidates: IceCandidate[],\n    is_final: boolean\n  ): Promise<void> {\n    if (this.connectionId === undefined) {\n      console.debug(\n        \"[WebRTCTransport] ICE candidates dropped: no active connection\"\n      );\n      return;\n    }\n\n    console.debug(\n      `[WebRTCTransport] Sending ICE candidates (count=${candidates.length}, is_final=${is_final})`\n    );\n\n    const requestBody: IceCandidatesRequest = {\n      candidates,\n      is_final,\n      client_info: {\n        sdk_version: REACTOR_SDK_VERSION,\n        sdk_type: REACTOR_SDK_TYPE,\n      },\n    };\n\n    const response = await fetch(\n      `${this.transportBaseUrl}/connections/${this.connectionId}/ice_candidates`,\n      {\n        method: \"POST\",\n        headers: {\n          ...(await this.getHeaders()),\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify(requestBody),\n        signal: this.signal,\n      }\n    );\n\n    await this.checkVersionMismatch(response);\n\n    if (response.status !== 202) {\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to send ICE candidates: ${response.status} ${errorText}`\n      );\n    }\n\n    console.debug(\"[WebRTCTransport] ICE candidates accepted (202)\");\n  }\n\n  /**\n   * Drain the pending ICE candidates buffer and POST them as a single\n   * batch. Called either by the debounce timer (collected candidates so\n   * far) or directly by the gathering-complete handler (with\n   * ``isFinal=true``).\n   *\n   * Errors are logged at debug level — trickle ICE is fire-and-forget,\n   * and the connection can still succeed even if some candidate batches\n   * are lost (host/srflx candidates from the other side are usually\n   * sufficient).\n   */\n  private flushPendingIceCandidates(isFinal: boolean): void {\n    if (this.iceCandidateFlushTimer !== undefined) {\n      clearTimeout(this.iceCandidateFlushTimer);\n      this.iceCandidateFlushTimer = undefined;\n    }\n\n    const batch = this.pendingIceCandidates;\n    this.pendingIceCandidates = [];\n\n    if (batch.length === 0 && !isFinal) {\n      return;\n    }\n\n    this.sendIceCandidates(batch, isFinal).catch((err) => {\n      console.debug(\"[WebRTCTransport] ICE candidate flush failed:\", err);\n    });\n  }\n\n  private cancelPendingIceCandidates(): void {\n    if (this.iceCandidateFlushTimer !== undefined) {\n      clearTimeout(this.iceCandidateFlushTimer);\n      this.iceCandidateFlushTimer = undefined;\n    }\n    this.pendingIceCandidates = [];\n  }\n\n  private async pollSdpAnswer(\n    connectionId: number\n  ): Promise<WebRTCSdpAnswerResponse> {\n    console.debug(\"[WebRTCTransport] Polling for SDP answer...\");\n\n    const pollUrl = `${this.transportBaseUrl}/connections/${connectionId}/sdp_params`;\n\n    const pollStart = performance.now();\n    let backoffMs = INITIAL_BACKOFF_MS;\n    let attempt = 0;\n\n    while (true) {\n      if (this.signal.aborted) {\n        throw new AbortError(\"SDP polling aborted\");\n      }\n\n      if (attempt >= this.maxPollAttempts) {\n        throw new Error(\n          `SDP polling exceeded maximum attempts (${this.maxPollAttempts})`\n        );\n      }\n\n      attempt++;\n      console.debug(\n        `[WebRTCTransport] SDP poll attempt ${attempt}/${this.maxPollAttempts}`\n      );\n\n      const response = await fetch(pollUrl, {\n        method: \"GET\",\n        headers: await this.getHeaders(),\n        signal: this.signal,\n      });\n\n      await this.checkVersionMismatch(response);\n\n      if (response.status === 200) {\n        const data = await response.json();\n        const parsed = WebRTCSdpAnswerResponseSchema.parse(data);\n        this.sdpPollingMs = performance.now() - pollStart;\n        this.sdpPollingAttempts = attempt;\n        console.debug(\n          `[WebRTCTransport] Received SDP answer via polling (${attempt} attempt(s), ${this.sdpPollingMs.toFixed(0)}ms)`\n        );\n        return parsed;\n      }\n\n      if (response.status === 202) {\n        console.debug(\n          `[WebRTCTransport] SDP answer pending, retrying in ${backoffMs}ms...`\n        );\n        await this.sleep(backoffMs);\n        backoffMs = Math.min(backoffMs * BACKOFF_MULTIPLIER, MAX_BACKOFF_MS);\n        continue;\n      }\n\n      const errorText = await response.text();\n      throw new Error(\n        `Failed to poll SDP answer: ${response.status} ${errorText}`\n      );\n    }\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Connection Lifecycle\n  // ─────────────────────────────────────────────────────────────────────────\n\n  async warmup(): Promise<void> {\n    if (!this.cachedIceServers) {\n      this.cachedIceServers = this.fetchIceServers();\n      this.cachedIceServers.catch(() => {});\n    }\n    await this.cachedIceServers;\n  }\n\n  async prepare(tracks: TrackCapability[]): Promise<void> {\n    this.setStatus(\"connecting\");\n    this.resetTransportTimings();\n\n    this.stopPing();\n    this.stopStatsPolling();\n    this.cancelPendingIceCandidates();\n\n    if (this.dataChannel) {\n      this.dataChannel.close();\n      this.dataChannel = undefined;\n    }\n    if (this.controlChannel) {\n      this.controlChannel.close();\n      this.controlChannel = undefined;\n    }\n    if (this.peerConnection) {\n      webrtc.closePeerConnection(this.peerConnection);\n      this.peerConnection = undefined;\n    }\n    this.peerConnected = false;\n    this.dataChannelOpen = false;\n    this.controlChannelOpen = false;\n\n    // Fresh queue for this connection. The first task is the connection-ready\n    // gate; every pause/resume renegotiation waits behind it.\n    this.peerConnectionQueue.stop();\n    this.peerConnectionQueue = new AwaitQueue();\n    this.peerConnectionQueue\n      .push(\n        () =>\n          new Promise<void>((resolve) => {\n            this.readyGateResolve = resolve;\n          })\n      )\n      .catch(() => {});\n\n    const iceServers = this.cachedIceServers\n      ? await this.cachedIceServers\n      : await this.fetchIceServers();\n    this.cachedIceServers = undefined;\n\n    this.peerConnection = webrtc.createPeerConnection({ iceServers });\n    this.setupPeerConnectionHandlers();\n\n    // Create the control channel BEFORE the main data channel. A\n    // pre-multi-connection runtime doesn't discriminate channels by label and\n    // collapses every inbound channel onto a single `self._data_channel`\n    // (last-write-wins), using it for both sending and binding its receive\n    // handler. Creating `control` first means that single channel converges on\n    // our main data channel, so both directions ride it natively. Newer\n    // runtimes key on the channel label, so the order is irrelevant to them.\n    this.controlChannel = webrtc.createDataChannel(\n      this.peerConnection,\n      \"control\"\n    );\n    this.setupControlChannelHandlers();\n\n    this.dataChannel = webrtc.createDataChannel(this.peerConnection);\n    this.setupDataChannelHandlers();\n\n    this.transceiverMap.clear();\n    for (const track of tracks) {\n      const transceiver = this.peerConnection.addTransceiver(track.kind, {\n        direction: track.direction,\n      });\n      this.transceiverMap.set(track.name, {\n        name: track.name,\n        kind: track.kind,\n        direction: track.direction,\n        transceiver,\n      });\n      console.debug(\n        `[WebRTCTransport] Transceiver added: \"${track.name}\" (${track.kind}, ${track.direction})`\n      );\n    }\n\n    this.pendingSdpOffer = await webrtc.createOffer(this.peerConnection);\n    this.pendingTrackMapping = this.buildTrackMapping(tracks);\n\n    console.debug(\"[WebRTCTransport] SDP offer prepared\");\n  }\n\n  async connect(\n    reconnect: boolean = false,\n    connectionId?: number\n  ): Promise<void> {\n    if (!this.pendingSdpOffer || !this.pendingTrackMapping) {\n      throw new Error(\n        \"[WebRTCTransport] No prepared connection. Call prepare() first.\"\n      );\n    }\n\n    const sdpOffer = this.pendingSdpOffer;\n    const trackMapping = this.pendingTrackMapping;\n    this.pendingSdpOffer = undefined;\n    this.pendingTrackMapping = undefined;\n\n    if (connectionId !== undefined) {\n      // Caller supplied a connection id (e.g. a backend pre-registered it via\n      // POST .../connections and handed it over). Adopt it and skip\n      // registration — the server validates existence when we send the offer.\n      if (\n        !Number.isInteger(connectionId) ||\n        connectionId <= MIN_CONNECTION_ID ||\n        connectionId > MAX_CONNECTION_ID\n      ) {\n        throw new Error(\n          `[WebRTCTransport] Invalid connectionId ${connectionId}: must be an ` +\n            `integer in (${MIN_CONNECTION_ID}, ${MAX_CONNECTION_ID}]`\n        );\n      }\n      this.connectionId = connectionId;\n    } else if (!reconnect || this.connectionId === undefined) {\n      // For a fresh connection, register to obtain an integer connection id.\n      // For a reconnect, reuse the existing id (PUT replaces the SDP on the same slot).\n      this.connectionId = await this.registerConnection();\n    }\n\n    await this.sendSdpOffer(\n      this.connectionId,\n      sdpOffer,\n      trackMapping,\n      reconnect\n    );\n\n    const answerResponse = await this.pollSdpAnswer(this.connectionId);\n\n    this.iceStartTime = performance.now();\n    await webrtc.setRemoteDescription(\n      this.peerConnection!,\n      answerResponse.sdp_answer\n    );\n    console.debug(\"[WebRTCTransport] Remote description set\");\n  }\n\n  async disconnect(): Promise<void> {\n    this.stopPing();\n    this.stopStatsPolling();\n    this.cancelPendingIceCandidates();\n\n    for (const name of Array.from(this.publishedTracks.keys())) {\n      await this.unpublishTrack(name);\n    }\n\n    if (this.dataChannel) {\n      this.dataChannel.close();\n      this.dataChannel = undefined;\n    }\n\n    if (this.controlChannel) {\n      this.controlChannel.close();\n      this.controlChannel = undefined;\n    }\n\n    if (this.peerConnection) {\n      webrtc.closePeerConnection(this.peerConnection);\n      this.peerConnection = undefined;\n    }\n\n    for (const [, pending] of this.pendingControlRequests) {\n      clearTimeout(pending.timeout);\n      pending.reject(\n        new Error(\"[WebRTCTransport] Disconnected while waiting for response\")\n      );\n    }\n    this.pendingControlRequests.clear();\n\n    // Abandon the ready gate and any queued direction changes.\n    this.readyGateResolve = undefined;\n    this.peerConnectionQueue.stop();\n\n    this.connectionId = undefined;\n    this.transceiverMap.clear();\n    this.peerConnected = false;\n    this.dataChannelOpen = false;\n    this.controlChannelOpen = false;\n    this.resetTransportTimings();\n    this.setStatus(\"disconnected\");\n    console.debug(\"[WebRTCTransport] Disconnected\");\n  }\n\n  abort(): void {\n    this.abortController.abort();\n    this.abortController = new AbortController();\n  }\n\n  getStatus(): TransportStatus {\n    return this.status;\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Track Mapping\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Builds the track_mapping array from capabilities + transceiver MIDs.\n   * Must be called after createOffer + setLocalDescription so that\n   * transceiver.mid is assigned.\n   */\n  private buildTrackMapping(tracks: TrackCapability[]): TrackMappingEntry[] {\n    return tracks.map((track) => {\n      const entry = this.transceiverMap.get(track.name);\n      const mid = entry?.transceiver?.mid;\n      if (mid == null) {\n        throw new Error(\n          `Cannot build track mapping: transceiver \"${track.name}\" has no MID. ` +\n            `Was createOffer() called?`\n        );\n      }\n      return {\n        mid,\n        name: track.name,\n        kind: track.kind,\n        direction: track.direction,\n      };\n    });\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Messaging\n  // ─────────────────────────────────────────────────────────────────────────\n\n  private get maxMessageBytes(): number | undefined {\n    return this.peerConnection?.sctp?.maxMessageSize ?? undefined;\n  }\n\n  sendCommand(\n    command: string,\n    data: any,\n    scope: MessageScope = \"application\",\n    uploads?: Record<string, object>\n  ): void {\n    if (!this.dataChannel) {\n      throw new Error(\"[WebRTCTransport] Data channel not available\");\n    }\n\n    try {\n      webrtc.sendMessage(\n        this.dataChannel,\n        command,\n        data,\n        scope,\n        this.maxMessageBytes,\n        uploads\n      );\n    } catch (error) {\n      console.warn(\"[WebRTCTransport] Failed to send message:\", error);\n    }\n  }\n\n  private sendControlMessage(command: string, data: any, reqId?: string): void {\n    if (!this.controlChannel) {\n      console.warn(\"[WebRTCTransport] Control channel not available\");\n      return;\n    }\n    try {\n      if (this.controlChannel.readyState !== \"open\") {\n        throw new Error(\n          `Control channel not open: ${this.controlChannel.readyState}`\n        );\n      }\n      this.controlChannel.send(\n        JSON.stringify({ type: \"notification\", event: command, data })\n      );\n    } catch (error) {\n      console.warn(\"[WebRTCTransport] Failed to send control message:\", error);\n    }\n  }\n\n  private sendControlRequest(\n    method: string,\n    data: any,\n    timeoutMs: number = 10_000\n  ): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n      const requestId = `ctrl_${++this.controlRequestCounter}`;\n      const timeout = setTimeout(() => {\n        this.pendingControlRequests.delete(requestId);\n        reject(new Error(`[WebRTCTransport] Request \"${method}\" timed out`));\n      }, timeoutMs);\n      this.pendingControlRequests.set(requestId, { resolve, reject, timeout });\n      try {\n        if (!this.controlChannel || this.controlChannel.readyState !== \"open\") {\n          throw new Error(\"Control channel not available\");\n        }\n        this.controlChannel.send(\n          JSON.stringify({\n            type: \"request\",\n            method,\n            request_id: requestId,\n            data,\n          })\n        );\n      } catch (error) {\n        clearTimeout(timeout);\n        this.pendingControlRequests.delete(requestId);\n        reject(error);\n      }\n    });\n  }\n\n  pauseTrack(name: string): void {\n    this.peerConnectionQueue\n      .push(async () => {\n        const entry = this.transceiverMap.get(name);\n        if (entry?.transceiver?.mid) {\n          entry.transceiver.direction = \"inactive\";\n          await this.applyDirectionLocally(entry.transceiver.mid, \"inactive\");\n        }\n        this.sendControlMessage(\"pause_track\", { name });\n      })\n      .catch((e) => {\n        console.warn(\"[WebRTCTransport] Failed to pause track:\", e);\n      });\n  }\n\n  resumeTrack(name: string): void {\n    this.peerConnectionQueue\n      .push(async () => {\n        const entry = this.transceiverMap.get(name);\n        if (entry?.transceiver?.mid) {\n          entry.transceiver.direction = entry.direction;\n          await this.applyDirectionLocally(\n            entry.transceiver.mid,\n            entry.direction\n          );\n        }\n        this.sendControlMessage(\"resume_track\", { name });\n      })\n      .catch((e) => {\n        console.warn(\"[WebRTCTransport] Failed to resume track:\", e);\n      });\n  }\n\n  private async applyDirectionLocally(\n    mid: string,\n    localDirection: RTCRtpTransceiverDirection\n  ): Promise<void> {\n    const pc = this.peerConnection;\n    if (!pc) return;\n\n    // Idempotent: if the transceiver already negotiated this direction there's\n    // nothing to do. This collapses the repeated auto-resume calls (one per\n    // track, re-fired on every `trackReceived`) into at most one real\n    // renegotiation instead of a storm of same-direction re-offers.\n    const tx = pc.getTransceivers().find((t) => t.mid === mid);\n    if (tx && tx.currentDirection === localDirection) return;\n\n    // The chain guarantees the previous change settled back to \"stable\"; if\n    // something left us mid-negotiation, bail rather than throw — the next\n    // queued change retries from a clean state.\n    if (pc.signalingState !== \"stable\") return;\n\n    const localSdp = pc.localDescription?.sdp;\n    const remoteSdp = pc.remoteDescription?.sdp;\n    if (!localSdp || !remoteSdp) return;\n\n    const modifiedLocal = webrtc.replaceSdpDirectionForMid(\n      localSdp,\n      mid,\n      localDirection\n    );\n    const modifiedRemote = webrtc.replaceSdpDirectionForMid(\n      remoteSdp,\n      mid,\n      webrtc.complementDirection(localDirection)\n    );\n\n    try {\n      await pc.setLocalDescription(\n        new RTCSessionDescription({ type: \"offer\", sdp: modifiedLocal })\n      );\n      await pc.setRemoteDescription(\n        new RTCSessionDescription({ type: \"answer\", sdp: modifiedRemote })\n      );\n    } catch (e) {\n      // Roll back a half-applied offer so the connection returns to \"stable\"\n      // and the next queued direction change can proceed cleanly.\n      if (pc.signalingState === \"have-local-offer\") {\n        try {\n          await pc.setLocalDescription({ type: \"rollback\" });\n        } catch {\n          // Best-effort; nothing more we can do here.\n        }\n      }\n      throw e;\n    }\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Track Publishing\n  // ─────────────────────────────────────────────────────────────────────────\n\n  async publishTrack(name: string, track: MediaStreamTrack): Promise<void> {\n    if (!this.peerConnection) {\n      throw new Error(\n        `[WebRTCTransport] Cannot publish track \"${name}\" - not initialized`\n      );\n    }\n\n    if (this.status !== \"connected\") {\n      throw new Error(\n        `[WebRTCTransport] Cannot publish track \"${name}\" - not connected`\n      );\n    }\n\n    const entry = this.transceiverMap.get(name);\n    if (!entry || !entry.transceiver) {\n      throw new Error(\n        `[WebRTCTransport] Cannot publish track \"${name}\" - no transceiver ` +\n          `(was it declared in capabilities?)`\n      );\n    }\n\n    if (entry.direction === \"recvonly\") {\n      throw new Error(\n        `[WebRTCTransport] Cannot publish track \"${name}\" - transceiver is recvonly`\n      );\n    }\n\n    await this.sendControlRequest(\"publish_track\", { name });\n    await entry.transceiver.sender.replaceTrack(track);\n    this.publishedTracks.set(name, track);\n    console.debug(`[WebRTCTransport] Track \"${name}\" published successfully`);\n  }\n\n  async unpublishTrack(name: string): Promise<void> {\n    const entry = this.transceiverMap.get(name);\n    if (!entry?.transceiver || !this.publishedTracks.has(name)) return;\n\n    try {\n      await entry.transceiver.sender.replaceTrack(null);\n      this.sendControlMessage(\"unpublish_track\", { name });\n      console.debug(\n        `[WebRTCTransport] Track \"${name}\" unpublished successfully`\n      );\n    } catch (error) {\n      console.error(\n        `[WebRTCTransport] Failed to unpublish track \"${name}\":`,\n        error\n      );\n      throw error;\n    } finally {\n      this.publishedTracks.delete(name);\n    }\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Stats\n  // ─────────────────────────────────────────────────────────────────────────\n\n  getStats(): ConnectionStats | undefined {\n    return this.stats;\n  }\n\n  getTransportTimings(): WebRTCTransportTimings | undefined {\n    if (this.iceNegotiationMs == null || this.dataChannelMs == null) {\n      return undefined;\n    }\n    return {\n      protocol: \"webrtc\",\n      sdpPollingMs: this.sdpPollingMs ?? 0,\n      sdpPollingAttempts: this.sdpPollingAttempts ?? 0,\n      iceNegotiationMs: this.iceNegotiationMs,\n      dataChannelMs: this.dataChannelMs,\n    };\n  }\n\n  private resetTransportTimings(): void {\n    this.iceStartTime = undefined;\n    this.iceNegotiationMs = undefined;\n    this.dataChannelMs = undefined;\n    this.sdpPollingMs = undefined;\n    this.sdpPollingAttempts = undefined;\n  }\n\n  private startStatsPolling(): void {\n    this.stopStatsPolling();\n    const statsExtractor = webrtc.createRTCStatsExtractor();\n    this.statsInterval = setInterval(async () => {\n      if (!this.peerConnection) return;\n      try {\n        const report = await this.peerConnection.getStats();\n        this.stats = statsExtractor(report);\n        this.emit(\"statsUpdate\", this.stats);\n      } catch {\n        // Connection may be closing\n      }\n    }, STATS_INTERVAL_MS);\n  }\n\n  private stopStatsPolling(): void {\n    if (this.statsInterval !== undefined) {\n      clearInterval(this.statsInterval);\n      this.statsInterval = undefined;\n    }\n    this.stats = undefined;\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Ping (Client Liveness)\n  // ─────────────────────────────────────────────────────────────────────────\n\n  private startPing(): void {\n    this.stopPing();\n    this.pingInterval = setInterval(() => {\n      if (this.dataChannel?.readyState === \"open\") {\n        try {\n          webrtc.sendMessage(this.dataChannel, \"ping\", {}, \"runtime\");\n        } catch {\n          // Data channel may be closing\n        }\n      }\n    }, PING_INTERVAL_MS);\n  }\n\n  private stopPing(): void {\n    if (this.pingInterval !== undefined) {\n      clearInterval(this.pingInterval);\n      this.pingInterval = undefined;\n    }\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Internal Helpers\n  // ─────────────────────────────────────────────────────────────────────────\n\n  private checkFullyConnected(): void {\n    if (this.peerConnected && this.dataChannelOpen && this.controlChannelOpen) {\n      this.setStatus(\"connected\");\n      this.startStatsPolling();\n      // Unblock all queued direction changes — the peer connection is now usable.\n      this.readyGateResolve!();\n      this.readyGateResolve = undefined;\n    }\n  }\n\n  private setStatus(newStatus: TransportStatus): void {\n    if (this.status !== newStatus) {\n      this.status = newStatus;\n      this.emit(\"statusChanged\", newStatus);\n    }\n  }\n\n  private setupPeerConnectionHandlers(): void {\n    if (!this.peerConnection) return;\n\n    this.peerConnection.onconnectionstatechange = () => {\n      const state = this.peerConnection?.connectionState;\n      console.debug(\"[WebRTCTransport] Connection state:\", state);\n\n      if (state) {\n        switch (state) {\n          case \"connected\":\n            if (this.iceStartTime != null && this.iceNegotiationMs == null) {\n              this.iceNegotiationMs = performance.now() - this.iceStartTime;\n            }\n            this.peerConnected = true;\n            this.checkFullyConnected();\n            break;\n          case \"disconnected\":\n          case \"closed\":\n            this.peerConnected = false;\n            this.setStatus(\"disconnected\");\n            break;\n          case \"failed\":\n            this.peerConnected = false;\n            this.setStatus(\"error\");\n            break;\n        }\n      }\n    };\n\n    this.peerConnection.ontrack = (event) => {\n      let trackName: string | undefined;\n      for (const [name, entry] of this.transceiverMap) {\n        if (entry.transceiver === event.transceiver) {\n          trackName = name;\n          break;\n        }\n      }\n      trackName ??= event.transceiver.mid ?? `unknown-${event.track.id}`;\n\n      console.debug(\n        `[WebRTCTransport] Track received: \"${trackName}\" (${event.track.kind}, mid=${event.transceiver.mid})`\n      );\n      const stream = event.streams[0] ?? new MediaStream([event.track]);\n      this.emit(\"trackReceived\", trackName, event.track, stream);\n    };\n\n    this.peerConnection.onicecandidate = (event) => {\n      // Browsers fire onicecandidate in tight bursts (host -> srflx ->\n      // relay). Buffer each candidate and flush after a small debounce\n      // window so we POST one batch per burst instead of one POST per\n      // candidate. The end-of-candidates marker (event.candidate ===\n      // null) bypasses the debounce and flushes immediately.\n      if (event.candidate) {\n        this.pendingIceCandidates.push({\n          candidate: event.candidate.candidate,\n          sdp_mid: event.candidate.sdpMid ?? undefined,\n          sdp_mline_index: event.candidate.sdpMLineIndex ?? undefined,\n        });\n        if (this.iceCandidateFlushTimer === undefined) {\n          this.iceCandidateFlushTimer = setTimeout(\n            () => this.flushPendingIceCandidates(false),\n            ICE_CANDIDATE_BATCH_WINDOW_MS\n          );\n        }\n      } else {\n        this.flushPendingIceCandidates(true);\n      }\n    };\n\n    this.peerConnection.onicecandidateerror = (event) => {\n      // ICE candidate errors are part of the normal WebRTC lifecycle:\n      // STUN/TURN servers frequently fail to allocate candidates\n      // (host blocked by NAT, server unreachable, auth not yet ready,\n      // etc.) without affecting the final connection.  Log at debug\n      // level so they don't drown out actionable warnings.\n      console.debug(\"[WebRTCTransport] ICE candidate error:\", event);\n    };\n\n    this.peerConnection.ondatachannel = (event) => {\n      console.debug(\"[WebRTCTransport] Data channel received from remote\");\n      this.dataChannel = event.channel;\n      this.setupDataChannelHandlers();\n    };\n  }\n\n  private setupDataChannelHandlers(): void {\n    if (!this.dataChannel) return;\n\n    this.dataChannel.onopen = () => {\n      console.debug(\"[WebRTCTransport] Data channel open\");\n      if (this.iceStartTime != null && this.dataChannelMs == null) {\n        this.dataChannelMs = performance.now() - this.iceStartTime;\n      }\n      this.dataChannelOpen = true;\n      this.startPing();\n      this.checkFullyConnected();\n    };\n\n    this.dataChannel.onclose = () => {\n      console.debug(\"[WebRTCTransport] Data channel closed\");\n      this.dataChannelOpen = false;\n      this.stopPing();\n    };\n\n    this.dataChannel.onerror = (error) => {\n      console.error(\"[WebRTCTransport] Data channel error:\", error);\n    };\n\n    this.dataChannel.onmessage = (event) => {\n      const rawData = webrtc.parseMessage(event.data) as any;\n      console.debug(\"[WebRTCTransport] Received message:\", rawData);\n\n      try {\n        if (rawData?.scope === \"application\" && rawData?.data !== undefined) {\n          this.emit(\"message\", rawData.data, \"application\" as MessageScope);\n        } else if (\n          rawData?.scope === \"runtime\" &&\n          rawData?.data !== undefined\n        ) {\n          this.emit(\"message\", rawData.data, \"runtime\" as MessageScope);\n        } else {\n          console.warn(\n            \"[WebRTCTransport] Received message without envelope, treating as application\"\n          );\n          this.emit(\"message\", rawData, \"application\" as MessageScope);\n        }\n      } catch (error) {\n        console.error(\n          \"[WebRTCTransport] Failed to parse/validate message:\",\n          error\n        );\n      }\n    };\n  }\n\n  private setupControlChannelHandlers(): void {\n    if (!this.controlChannel) return;\n\n    this.controlChannel.onopen = () => {\n      console.debug(\"[WebRTCTransport] Control channel open\");\n      this.controlChannelOpen = true;\n      this.checkFullyConnected();\n    };\n\n    this.controlChannel.onclose = () => {\n      console.debug(\"[WebRTCTransport] Control channel closed\");\n      this.controlChannelOpen = false;\n    };\n\n    this.controlChannel.onerror = (error) => {\n      console.error(\"[WebRTCTransport] Control channel error:\", error);\n    };\n\n    this.controlChannel.onmessage = (event) => {\n      const raw = webrtc.parseMessage(event.data) as any;\n      if (raw?.type === \"response\") {\n        const requestId = raw?.request_id as string | undefined;\n        if (requestId) {\n          const pending = this.pendingControlRequests.get(requestId);\n          if (pending) {\n            clearTimeout(pending.timeout);\n            this.pendingControlRequests.delete(requestId);\n            if (raw?.error) {\n              pending.reject(\n                new Error(\n                  `[WebRTCTransport] ${raw.method ?? \"request\"} failed: ${raw.error.message ?? \"unknown error\"}`\n                )\n              );\n            } else {\n              pending.resolve();\n            }\n          }\n        }\n        return;\n      }\n\n      console.debug(\"[WebRTCTransport] Received control message:\", raw);\n    };\n  }\n}\n","/**\n * SDP offer sanitization:\n * - Removes telephone-event codec entries from all m-sections.\n * - Remaps selected codec PTs that fall outside [96,127] — video: HEVC, VP8, VP9, H.264;\n *   audio: Opus. rtx entries stay at their original PTs; only apt= references are updated\n *   when a primary is remapped.\n * - After PT edits, RTP/SAVPF sections are normalized so rtpmap/fmtp/rtcp-fb (per PT) follow\n *   the payload-type order on the `m=` line (Chrome-style interleaving), not grouped by line type.\n * - Line breaks are preserved (`\\r\\n` vs `\\n`) using `\\r?\\n` splits.\n */\n\nimport type { MediaDescription } from \"sdp-transform\";\nimport { parse, parsePayloads } from \"sdp-transform\";\n\nfunction sdpLineSeparator(sdp: string): \"\\r\\n\" | \"\\n\" {\n  return sdp.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n}\n\nfunction splitSdpLines(sdp: string): string[] {\n  return sdp.split(/\\r?\\n/);\n}\n\nfunction joinSdpLines(lines: readonly string[], sep: \"\\r\\n\" | \"\\n\"): string {\n  return lines.join(sep);\n}\n\n/** RTP dynamic payload type range (RFC 3551). */\nconst DYNAMIC_PT_MIN = 96;\nconst DYNAMIC_PT_MAX = 127;\n\n/** Video codecs whose payload types are forced into [96,127] when outside that range. */\nfunction isSanitizedVideoCodecRtpmapEncoding(name: string): boolean {\n  const u = name.toUpperCase();\n  if (/^(H265|HEVC|HEV1|HVC1)/.test(u)) return true;\n  if (/^VP8(\\/|$)/.test(u)) return true;\n  if (/^VP9(\\/|$)/.test(u)) return true;\n  if (/^H264(\\/|$)/.test(u)) return true;\n  return false;\n}\n\nfunction isOpusRtpmapEncoding(name: string): boolean {\n  return /^OPUS(\\/|$)/i.test(name);\n}\n\nfunction parseFmtpApt(fmtpValue: string): number | null {\n  const apt = fmtpValue.match(/(?:^|;)apt=(\\d+)/i);\n  if (!apt) return null;\n  return Number.parseInt(apt[1]!, 10);\n}\n\nfunction mediaMLinePayloadTypes(media: MediaDescription): number[] {\n  return parsePayloads(String(media.payloads ?? \"\"));\n}\n\nfunction collectRtpmap(media: MediaDescription): Map<number, string> {\n  const rtpmap = new Map<number, string>();\n  for (const r of media.rtp ?? []) {\n    rtpmap.set(Number(r.payload), String(r.codec));\n  }\n  return rtpmap;\n}\n\n/**\n * Video: HEVC, VP8, VP9, H.264. Audio: Opus.\n * rtx entries are intentionally excluded — they keep their original PTs and only\n * have their apt= references updated when a primary is remapped.\n * Returns null when the section has no matching rtpmap.\n */\nfunction collectSanitizedCodecPayloadSet(\n  media: MediaDescription\n): Set<number> | null {\n  const rtpmap = collectRtpmap(media);\n  const tracked = new Set<number>();\n  if (media.type === \"video\") {\n    for (const [pt, enc] of rtpmap) {\n      if (isSanitizedVideoCodecRtpmapEncoding(enc)) tracked.add(pt);\n    }\n  } else if (media.type === \"audio\") {\n    for (const [pt, enc] of rtpmap) {\n      if (isOpusRtpmapEncoding(enc)) tracked.add(pt);\n    }\n  }\n  return tracked.size > 0 ? tracked : null;\n}\n\nfunction mediaSectionNeedsSanitizedPtRelocate(\n  media: MediaDescription\n): boolean {\n  const pts = collectSanitizedCodecPayloadSet(media);\n  if (!pts) {\n    return false;\n  }\n  return [...pts].some((p) => p < DYNAMIC_PT_MIN || p > DYNAMIC_PT_MAX);\n}\n\nfunction codecRelocationPriority(enc: string | undefined): number {\n  const u = (enc ?? \"\").toUpperCase();\n  if (/^(H265|HEVC|HEV1|HVC1)/.test(u)) return 0;\n  if (/^H264(\\/|$)/.test(u)) return 1;\n  if (/^VP9(\\/|$)/.test(u)) return 2;\n  if (/^VP8(\\/|$)/.test(u)) return 3;\n  if (/^OPUS(\\/|$)/.test(u)) return 4;\n  return 5;\n}\n\n/**\n * Returns `relocate` sorted so higher-priority codecs come first (H265 > H264 > VP9 > VP8 >\n * Opus). This ensures the most important codecs claim slots when the dynamic range is nearly full.\n */\nfunction sortedRelocate(\n  relocate: number[],\n  rtpmap: Map<number, string>\n): number[] {\n  return [...relocate].sort(\n    (a, b) =>\n      codecRelocationPriority(rtpmap.get(a)) -\n      codecRelocationPriority(rtpmap.get(b))\n  );\n}\n\nfunction remapFmtpConfigForPayloadMap(\n  config: string,\n  remap: Map<number, number>\n): string {\n  let s = config;\n  const pairs = [...remap.entries()].sort((a, b) => b[0] - a[0]);\n  for (const [oldP, newP] of pairs) {\n    if (oldP === newP) continue;\n    s = s.replace(new RegExp(`apt=${oldP}([^0-9]|$)`, \"g\"), `apt=${newP}$1`);\n  }\n  return s;\n}\n\n/**\n * Remaps sanitized codec payload types (and rtx bound to them) into [96, 127],\n * avoiding `forbidden` and PTs already present in this m-line. Allocation is\n * best-effort: when the dynamic range is nearly full, higher-priority codecs\n * (H265 > H264 > VP9 > VP8 > Opus) are relocated first; any that cannot fit\n * are left at their original payload type rather than throwing.\n */\nfunction computeCodecRemapForMedia(\n  media: MediaDescription,\n  forbidden: ReadonlySet<number>\n): Map<number, number> {\n  const remap = new Map<number, number>();\n  if (media.type !== \"video\" && media.type !== \"audio\") {\n    return remap;\n  }\n  const tracked = collectSanitizedCodecPayloadSet(media);\n  if (!tracked) {\n    return remap;\n  }\n  const relocate = [...tracked].filter(\n    (p) => p < DYNAMIC_PT_MIN || p > DYNAMIC_PT_MAX || forbidden.has(p)\n  );\n  if (relocate.length === 0) {\n    return remap;\n  }\n\n  const rtpmap = collectRtpmap(media);\n  const ordered = sortedRelocate(relocate, rtpmap);\n\n  const usedPts = new Set(forbidden);\n  for (const p of mediaMLinePayloadTypes(media)) {\n    if (p >= DYNAMIC_PT_MIN && p <= DYNAMIC_PT_MAX) usedPts.add(p);\n  }\n\n  let candidate = DYNAMIC_PT_MIN;\n  for (const oldPt of ordered) {\n    while (candidate <= DYNAMIC_PT_MAX && usedPts.has(candidate)) candidate++;\n    if (candidate > DYNAMIC_PT_MAX) break;\n    remap.set(oldPt, candidate);\n    usedPts.add(candidate);\n    candidate++;\n  }\n\n  return remap;\n}\n\nfunction rewriteRtpSavpfMPayloads(\n  mLine: string,\n  mapPt: (pt: number) => number\n): string {\n  if (!/\\bRTP\\/SAVPF\\b/.test(mLine)) {\n    return mLine;\n  }\n  const tokens = mLine.split(/\\s+/);\n  if (tokens.length < 4) {\n    return mLine;\n  }\n  const head = tokens.slice(0, 3).join(\" \");\n  const tail = tokens\n    .slice(3)\n    .map((t) => (/^\\d+$/.test(t) ? String(mapPt(Number(t))) : t));\n  return `${head} ${tail.join(\" \")}`;\n}\n\nfunction payloadTypesFromSavpfMLine(mLine: string): number[] {\n  const m = /\\bRTP\\/SAVPF\\s+(.+)$/i.exec(mLine);\n  return m ? parsePayloads(m[1]!.trim()) : [];\n}\n\nfunction payloadTypeFromPtBoundAttributeLine(line: string): number | null {\n  if (line.startsWith(\"a=rtpmap:\")) {\n    const x = /^a=rtpmap:(\\d+)/i.exec(line);\n    return x ? Number(x[1]) : null;\n  }\n  if (line.startsWith(\"a=fmtp:\")) {\n    const x = /^a=fmtp:(\\d+)/i.exec(line);\n    return x ? Number(x[1]) : null;\n  }\n  if (line.startsWith(\"a=rtcp-fb:\")) {\n    const x = /^a=rtcp-fb:(\\*|(\\d+))/i.exec(line);\n    if (!x || x[1] === \"*\") return null;\n    return Number(x[2]);\n  }\n  if (line.startsWith(\"a=rtcp-fb-trr-int:\")) {\n    const x = /^a=rtcp-fb-trr-int:(\\*|(\\d+))/i.exec(line);\n    if (!x || x[1] === \"*\") return null;\n    return Number(x[2]);\n  }\n  if (line.startsWith(\"a=imageattrs:\")) {\n    const x = /^a=imageattrs:(\\*|(\\d+))/i.exec(line);\n    if (!x || x[1] === \"*\") return null;\n    return Number(x[2]);\n  }\n  return null;\n}\n\nfunction isPtBoundCodecAttributeLine(line: string): boolean {\n  return payloadTypeFromPtBoundAttributeLine(line) !== null;\n}\n\n/**\n * Within one RTP/SAVPF m-section, reorder rtpmap/fmtp/rtcp-fb lines so each payload type's\n * attributes are contiguous and ordered by the `m=` payload list (same interleaving style as\n * Chrome). Non-codec lines before/after the codec block stay fixed.\n */\nfunction reorderSingleRtpSavpfSection(sec: string[]): string[] {\n  const mLine = sec[0]!;\n  if (!/\\bRTP\\/SAVPF\\b/.test(mLine)) {\n    return sec;\n  }\n  const body = sec.slice(1);\n  let first = -1;\n  let last = -1;\n  for (let j = 0; j < body.length; j++) {\n    if (isPtBoundCodecAttributeLine(body[j]!)) {\n      if (first === -1) first = j;\n      last = j;\n    }\n  }\n  if (first === -1) {\n    return sec;\n  }\n\n  const prefix = body.slice(0, first);\n  const codecLines = body.slice(first, last + 1);\n  const suffix = body.slice(last + 1);\n\n  const mPts = payloadTypesFromSavpfMLine(mLine);\n  const buckets = new Map<number, string[]>();\n  for (const line of codecLines) {\n    const pt = payloadTypeFromPtBoundAttributeLine(line);\n    if (pt === null) continue;\n    const arr = buckets.get(pt);\n    if (arr) arr.push(line);\n    else buckets.set(pt, [line]);\n  }\n\n  const seenOrder: number[] = [];\n  for (const line of codecLines) {\n    const pt = payloadTypeFromPtBoundAttributeLine(line);\n    if (pt !== null && !seenOrder.includes(pt)) seenOrder.push(pt);\n  }\n\n  const outCodec: string[] = [];\n  for (const pt of mPts) {\n    const arr = buckets.get(pt);\n    if (!arr) continue;\n    outCodec.push(...arr);\n    buckets.delete(pt);\n  }\n  for (const pt of seenOrder) {\n    const arr = buckets.get(pt);\n    if (!arr) continue;\n    outCodec.push(...arr);\n    buckets.delete(pt);\n  }\n  for (const arr of buckets.values()) {\n    outCodec.push(...arr);\n  }\n\n  return [mLine, ...prefix, ...outCodec, ...suffix];\n}\n\nfunction reorderCodecAttributes(sdp: string): string {\n  const sep = sdpLineSeparator(sdp);\n  const lines = splitSdpLines(sdp);\n  const out: string[] = [];\n  let i = 0;\n  while (i < lines.length) {\n    const line = lines[i]!;\n    if (!line.startsWith(\"m=\")) {\n      out.push(line);\n      i++;\n      continue;\n    }\n    const mLine = line;\n    i++;\n    const rest: string[] = [];\n    while (i < lines.length && !lines[i]!.startsWith(\"m=\")) {\n      rest.push(lines[i]!);\n      i++;\n    }\n    out.push(...reorderSingleRtpSavpfSection([mLine, ...rest]));\n  }\n  return joinSdpLines(out, sep);\n}\n\nfunction applyRemapToMediaAttributeLine(\n  line: string,\n  remap: Map<number, number>\n): string {\n  if (!line.startsWith(\"a=\")) {\n    return line;\n  }\n\n  if (line.startsWith(\"a=rtpmap:\")) {\n    const m = /^a=rtpmap:(\\d+)/.exec(line);\n    if (!m) return line;\n    const pt = Number(m[1]);\n    const np = remap.get(pt) ?? pt;\n    return np === pt ? line : line.replace(/^a=rtpmap:\\d+/, `a=rtpmap:${np}`);\n  }\n\n  if (line.startsWith(\"a=fmtp:\")) {\n    const m = /^a=fmtp:(\\d+)\\s(.*)$/.exec(line);\n    if (!m) return line;\n    const pt = Number(m[1]);\n    const np = remap.get(pt) ?? pt;\n    const cfg = remapFmtpConfigForPayloadMap(m[2], remap);\n    return `a=fmtp:${np} ${cfg}`;\n  }\n\n  if (line.startsWith(\"a=rtcp-fb:\")) {\n    const m = /^a=rtcp-fb:(\\*|\\d+)(\\s.*)?$/.exec(line);\n    if (!m || m[1] === \"*\") return line;\n    const pt = Number(m[1]);\n    const np = remap.get(pt) ?? pt;\n    return np === pt ? line : `a=rtcp-fb:${np}${m[2] ?? \"\"}`;\n  }\n\n  if (line.startsWith(\"a=rtcp-fb-trr-int:\")) {\n    const m = /^a=rtcp-fb-trr-int:(\\*|\\d+)(\\s.*)?$/.exec(line);\n    if (!m || m[1] === \"*\") return line;\n    const pt = Number(m[1]);\n    const np = remap.get(pt) ?? pt;\n    return np === pt ? line : `a=rtcp-fb-trr-int:${np}${m[2] ?? \"\"}`;\n  }\n\n  if (line.startsWith(\"a=imageattrs:\")) {\n    const m = /^a=imageattrs:(\\*|\\d+)(\\s.*)?$/.exec(line);\n    if (!m || m[1] === \"*\") return line;\n    const pt = Number(m[1]);\n    const np = remap.get(pt) ?? pt;\n    return np === pt ? line : `a=imageattrs:${np}${m[2] ?? \"\"}`;\n  }\n\n  return line;\n}\n\nfunction stripTelephoneEventsPreserveOrder(sdp: string): string {\n  const session = parse(sdp);\n  const telPerSection = session.media.map((media) => {\n    const tel = new Set<number>();\n    for (const r of media.rtp ?? []) {\n      if (/^telephone-event$/i.test(String(r.codec))) {\n        tel.add(Number(r.payload));\n      }\n    }\n    return tel;\n  });\n  if (!telPerSection.some((s) => s.size > 0)) {\n    return sdp;\n  }\n\n  const sep = sdpLineSeparator(sdp);\n  const lines = splitSdpLines(sdp);\n  let secIdx = -1;\n  const out: string[] = [];\n  for (const line of lines) {\n    if (line.startsWith(\"m=\")) {\n      secIdx++;\n      const drop = telPerSection[secIdx];\n      if (!drop || drop.size === 0 || !/\\bRTP\\/SAVPF\\b/.test(line)) {\n        out.push(line);\n        continue;\n      }\n      const tokens = line.split(/\\s+/);\n      const head = tokens.slice(0, 3).join(\" \");\n      const nums = tokens\n        .slice(3)\n        .filter((t) => /^\\d+$/.test(t))\n        .map(Number)\n        .filter((p) => !drop.has(p));\n      out.push(`${head} ${nums.join(\" \")}`);\n      continue;\n    }\n    if (secIdx >= 0) {\n      const drop = telPerSection[secIdx];\n      if (drop && drop.size > 0) {\n        const rtp = /^a=rtpmap:(\\d+)\\s/i.exec(line);\n        if (rtp && drop.has(Number(rtp[1]))) continue;\n        const fmtp = /^a=fmtp:(\\d+)\\s/i.exec(line);\n        if (fmtp && drop.has(Number(fmtp[1]))) continue;\n        const rfb = /^a=rtcp-fb:(\\d+)\\s/i.exec(line);\n        if (rfb && drop.has(Number(rfb[1]))) continue;\n        const trr = /^a=rtcp-fb-trr-int:(\\d+)\\s/i.exec(line);\n        if (trr && drop.has(Number(trr[1]))) continue;\n      }\n    }\n    out.push(line);\n  }\n  return joinSdpLines(out, sep);\n}\n\nfunction transformOfferSdpCodecDynamicPts(sdp: string): string {\n  const session = parse(sdp);\n  // Skip entirely only when no section carries any tracked codec at all.\n  // The weaker condition (vs. mediaSectionNeedsSanitizedPtRelocate) is intentional:\n  // a section whose codec PT is already in [96,127] still needs relocation when\n  // that PT conflicts with a prior section's PT in BUNDLE mode.\n  if (!session.media.some((m) => collectSanitizedCodecPayloadSet(m) !== null)) {\n    return sdp;\n  }\n\n  const remaps: Map<number, number>[] = [];\n  const forbidden = new Set<number>();\n  for (const media of session.media) {\n    const R = computeCodecRemapForMedia(media, forbidden);\n    remaps.push(R);\n    for (const p of mediaMLinePayloadTypes(media)) {\n      forbidden.add(R.get(p) ?? p);\n    }\n  }\n\n  const sep = sdpLineSeparator(sdp);\n  const lines = splitSdpLines(sdp);\n  let secIdx = -1;\n  const out: string[] = [];\n  for (let line of lines) {\n    if (line.startsWith(\"m=\")) {\n      secIdx++;\n      const R = remaps[secIdx]!;\n      if (R.size > 0 && /\\bRTP\\/SAVPF\\b/.test(line)) {\n        line = rewriteRtpSavpfMPayloads(line, (p) => R.get(p) ?? p);\n      }\n      out.push(line);\n      continue;\n    }\n    const R = secIdx >= 0 ? remaps[secIdx]! : null;\n    if (!R || R.size === 0) {\n      out.push(line);\n      continue;\n    }\n    out.push(applyRemapToMediaAttributeLine(line, R));\n  }\n  return joinSdpLines(out, sep);\n}\n\nexport function sanitize(sdp: string): string {\n  const stripped = stripTelephoneEventsPreserveOrder(sdp);\n  const remapped = transformOfferSdpCodecDynamicPts(stripped);\n  return reorderCodecAttributes(remapped);\n}\n","/**\n * Stateless WebRTC utility functions for SDP exchange and peer connection management.\n */\n\nimport type { IceServer, IceServersResponse } from \"../core/types\";\nimport type { MessageScope, ConnectionStats } from \"../types\";\nimport { sanitize } from \"./sdp\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface WebRTCConfig {\n  iceServers: RTCIceServer[];\n  dataChannelLabel?: string;\n}\n\nconst DEFAULT_DATA_CHANNEL_LABEL = \"data\";\n\nconst FORCE_RELAY_MODE = false;\n\n/**\n * Safe cross-browser default for the maximum data channel message size (bytes).\n * Most browsers negotiate 256 KiB via SCTP; we use a slightly lower value to\n * leave room for framing overhead.\n */\nconst DEFAULT_MAX_MESSAGE_BYTES = 256 * 1024; // 256 KiB\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Peer Connection Creation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a new RTCPeerConnection with the specified configuration.\n * @param config WebRTC configuration with required iceServers\n */\nexport function createPeerConnection(config: WebRTCConfig): RTCPeerConnection {\n  return new RTCPeerConnection({\n    iceServers: config.iceServers,\n    iceTransportPolicy: FORCE_RELAY_MODE ? \"relay\" : \"all\",\n  });\n}\n\n/**\n * Creates a data channel on the peer connection.\n */\nexport function createDataChannel(\n  pc: RTCPeerConnection,\n  label?: string\n): RTCDataChannel {\n  return pc.createDataChannel(label ?? DEFAULT_DATA_CHANNEL_LABEL);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SDP Offer/Answer\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates an SDP offer on the peer connection.\n * No MID munging — track identity is conveyed via track_mapping metadata.\n * @returns The SDP offer string with gathered ICE candidates.\n */\nexport async function createOffer(pc: RTCPeerConnection): Promise<string> {\n  const initial = await pc.createOffer();\n  const sdp = sanitize(initial.sdp ?? \"\");\n  await pc.setLocalDescription(\n    new RTCSessionDescription({ type: initial.type, sdp })\n  );\n\n  const localDescription = pc.localDescription;\n  if (!localDescription) {\n    throw new Error(\"Failed to create local description\");\n  }\n\n  return localDescription.sdp;\n}\n\n/**\n * Creates an SDP answer in response to a received offer.\n * Waits for ICE gathering to complete before returning.\n */\nexport async function createAnswer(\n  pc: RTCPeerConnection,\n  offer: string\n): Promise<string> {\n  await setRemoteDescription(pc, offer);\n\n  const answer = await pc.createAnswer();\n  await pc.setLocalDescription(answer);\n\n  await waitForIceGathering(pc);\n\n  const localDescription = pc.localDescription;\n  if (!localDescription) {\n    throw new Error(\"Failed to create local description\");\n  }\n\n  return localDescription.sdp;\n}\n\n/**\n * Sets the remote description on the peer connection.\n */\nexport async function setRemoteDescription(\n  pc: RTCPeerConnection,\n  sdp: string\n): Promise<void> {\n  const sessionDescription = new RTCSessionDescription({\n    sdp: sdp,\n    type: \"answer\",\n  });\n  await pc.setRemoteDescription(sessionDescription);\n}\n\n/**\n * Gets the local SDP description from the peer connection.\n */\nexport function getLocalDescription(pc: RTCPeerConnection): string | undefined {\n  const desc = pc.localDescription;\n  if (!desc) return undefined;\n  return desc.sdp;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ICE Handling\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Transforms ICE servers from the API response to RTCIceServer format.\n * The API uses `uris` and a nested `credentials` object; the browser\n * RTCIceServer interface expects `urls`, `username`, and `credential`.\n */\nexport function transformIceServers(\n  response: IceServersResponse\n): RTCIceServer[] {\n  return response.ice_servers.map((server: IceServer) => {\n    const rtcServer: RTCIceServer = {\n      urls: server.uris,\n    };\n    if (server.credentials) {\n      rtcServer.username = server.credentials.username;\n      rtcServer.credential = server.credentials.password;\n    }\n    return rtcServer;\n  });\n}\n\n/**\n * Adds an ICE candidate to the peer connection.\n */\nexport async function addIceCandidate(\n  pc: RTCPeerConnection,\n  candidate: RTCIceCandidateInit\n): Promise<void> {\n  await pc.addIceCandidate(new RTCIceCandidate(candidate));\n}\n\n/**\n * Waits for ICE gathering to complete with a timeout.\n */\nexport function waitForIceGathering(\n  pc: RTCPeerConnection,\n  timeoutMs: number = 5000\n): Promise<void> {\n  return new Promise((resolve) => {\n    if (pc.iceGatheringState === \"complete\") {\n      resolve();\n      return;\n    }\n\n    const onGatheringStateChange = () => {\n      if (pc.iceGatheringState === \"complete\") {\n        pc.removeEventListener(\n          \"icegatheringstatechange\",\n          onGatheringStateChange\n        );\n        resolve();\n      }\n    };\n\n    pc.addEventListener(\"icegatheringstatechange\", onGatheringStateChange);\n\n    setTimeout(() => {\n      pc.removeEventListener(\"icegatheringstatechange\", onGatheringStateChange);\n      resolve();\n    }, timeoutMs);\n  });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Track Management\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Adds a track to the peer connection.\n */\nexport function addTrack(\n  pc: RTCPeerConnection,\n  track: MediaStreamTrack,\n  stream?: MediaStream\n): RTCRtpSender {\n  const mediaStream = stream ?? new MediaStream([track]);\n  return pc.addTrack(track, mediaStream);\n}\n\n/**\n * Removes a track from the peer connection by its sender.\n */\nexport function removeTrack(pc: RTCPeerConnection, sender: RTCRtpSender): void {\n  pc.removeTrack(sender);\n}\n\n/**\n * Finds the sender for a specific track.\n */\nexport function findSenderForTrack(\n  pc: RTCPeerConnection,\n  track: MediaStreamTrack\n): RTCRtpSender | undefined {\n  return pc.getSenders().find((sender) => sender.track === track);\n}\n\n/**\n * Removes all tracks from the peer connection.\n */\nexport function removeAllTracks(pc: RTCPeerConnection): void {\n  for (const sender of pc.getSenders()) {\n    pc.removeTrack(sender);\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Data Channel Messaging\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sends a message through a data channel wrapped in the two-level envelope.\n *\n * Wire format:\n *   { scope: \"application\"|\"runtime\", data: { type: <command>, data: <payload> } }\n *\n * @param channel      The RTCDataChannel to send on.\n * @param command      Inner command/message type (e.g. \"set_prompt\", \"requestCapabilities\").\n * @param data         Payload for the command.\n * @param scope        Outer envelope scope – defaults to \"application\".\n * @param maxBytes     Max allowed serialized message size in bytes.\n *                     Defaults to {@link DEFAULT_MAX_MESSAGE_BYTES} (256 KiB).\n *                     Pass the negotiated SCTP limit when available.\n */\nexport function sendMessage(\n  channel: RTCDataChannel,\n  command: string,\n  data: any,\n  scope: MessageScope = \"application\",\n  maxBytes: number = DEFAULT_MAX_MESSAGE_BYTES,\n  uploads?: Record<string, object>\n): void {\n  if (channel.readyState !== \"open\") {\n    throw new Error(`Data channel not open: ${channel.readyState}`);\n  }\n  const jsonData = typeof data === \"string\" ? JSON.parse(data) : data;\n  const inner: Record<string, any> = { type: command, data: jsonData };\n  if (uploads && Object.keys(uploads).length > 0) {\n    inner.uploads = uploads;\n  }\n  const payload = { scope, data: inner };\n  const serialized = JSON.stringify(payload);\n\n  const byteLength = new TextEncoder().encode(serialized).byteLength;\n  if (byteLength > maxBytes) {\n    throw new Error(\n      `Data channel message too large: ${byteLength} bytes exceeds ` +\n        `limit of ${maxBytes} bytes (command: \"${command}\")`\n    );\n  }\n\n  channel.send(serialized);\n}\n\n/**\n * Parses a received data channel message, attempting JSON parse.\n */\nexport function parseMessage(data: unknown): unknown {\n  if (typeof data === \"string\") {\n    try {\n      return JSON.parse(data);\n    } catch {\n      return data;\n    }\n  }\n  return data;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SDP Direction\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Returns the complementary SDP direction from the remote peer's perspective.\n * sendonly ↔ recvonly; sendrecv and inactive are symmetric.\n */\nexport function complementDirection(\n  direction: RTCRtpTransceiverDirection\n): RTCRtpTransceiverDirection {\n  if (direction === \"sendonly\") return \"recvonly\";\n  if (direction === \"recvonly\") return \"sendonly\";\n  return direction;\n}\n\n/**\n * Replaces the direction attribute inside the m= section identified by `mid`.\n *\n * Splits the SDP on m= section boundaries, locates the section containing\n * `a=mid:<mid>`, and substitutes its direction line. All other sections and\n * the session block are returned unchanged.\n */\nexport function replaceSdpDirectionForMid(\n  sdp: string,\n  mid: string,\n  direction: RTCRtpTransceiverDirection\n): string {\n  // Split on the lookahead so each media section retains its leading \\r\\n.\n  const [session, ...mediaSections] = sdp.split(/(?=\\r\\nm=)/);\n\n  const updated = mediaSections.map((section) => {\n    if (!section.includes(`\\r\\na=mid:${mid}\\r\\n`)) return section;\n    return section.replace(\n      /\\r\\na=(sendonly|recvonly|sendrecv|inactive)\\r\\n/,\n      `\\r\\na=${direction}\\r\\n`\n    );\n  });\n\n  return session + updated.join(\"\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Connection State\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Checks if the peer connection is in a connected state.\n */\nexport function isConnected(pc: RTCPeerConnection): boolean {\n  return pc.connectionState === \"connected\";\n}\n\n/**\n * Checks if the peer connection is closed or failed.\n */\nexport function isClosed(pc: RTCPeerConnection): boolean {\n  return pc.connectionState === \"closed\" || pc.connectionState === \"failed\";\n}\n\n/**\n * Closes the peer connection and cleans up.\n */\nexport function closePeerConnection(pc: RTCPeerConnection): void {\n  pc.close();\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stats Extraction\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a function with a captured closure over a set of working variables which are\n * used to compute connection stats from aggregate counters over the sample interval.\n *\n * @returns A function that extracts connection stats from an RTCStatsReport.\n */\n\ntype RTCStatsExtractor = (report: RTCStatsReport) => ConnectionStats;\n\nexport function createRTCStatsExtractor(): RTCStatsExtractor {\n  let lastBytesReceived: number | undefined;\n  let lastBytesSent: number | undefined;\n  let lastCandPairTimestamp: number | undefined;\n\n  return (report: RTCStatsReport) => {\n    let candPairId: string | undefined;\n    let rtt: number | undefined;\n    let availableOutgoingBitrate: number | undefined;\n    let availableIncomingBitrate: number | undefined;\n    let incomingBitrate: number | undefined;\n    let outgoingBitrate: number | undefined;\n    let videoInboundRtpId: string | undefined;\n    let framesPerSecond: number | undefined;\n    let jitter: number | undefined;\n    let packetLossRatio: number | undefined;\n    let candidateType: string | undefined;\n\n    report.forEach((stat) => {\n      if (\n        candPairId === undefined &&\n        stat.type === \"candidate-pair\" &&\n        stat.state === \"succeeded\" &&\n        stat.nominated\n      ) {\n        // Extract stats from the first successful candidate-pair found.\n        candPairId = stat.id;\n        if (stat.currentRoundTripTime !== undefined) {\n          rtt = stat.currentRoundTripTime * 1000;\n        }\n        if (stat.availableOutgoingBitrate !== undefined) {\n          availableOutgoingBitrate = stat.availableOutgoingBitrate;\n        }\n        if (stat.availableIncomingBitrate != undefined) {\n          availableIncomingBitrate = stat.availableIncomingBitrate;\n        }\n        const localCandidate = report.get(stat.localCandidateId);\n        if (localCandidate?.candidateType) {\n          candidateType = localCandidate.candidateType;\n        }\n        const timeDiff: number =\n          lastCandPairTimestamp !== undefined\n            ? stat.timestamp - lastCandPairTimestamp\n            : 0;\n        if (stat.bytesReceived !== undefined) {\n          if (lastBytesReceived !== undefined && timeDiff > 0) {\n            incomingBitrate =\n              (((stat.bytesReceived - lastBytesReceived) * 8) / timeDiff) *\n              1000; /* Bits/Second */\n          }\n          lastBytesReceived = stat.bytesReceived;\n        }\n        if (stat.bytesSent !== undefined) {\n          if (lastBytesSent !== undefined && timeDiff > 0) {\n            outgoingBitrate =\n              (((stat.bytesSent - lastBytesSent) * 8) / timeDiff) *\n              1000; /* Bits/Second */\n          }\n          lastBytesSent = stat.bytesSent;\n        }\n        lastCandPairTimestamp = stat.timestamp;\n      }\n\n      // If there is more than one video stream the stats will be from the first one encountered.\n      if (\n        videoInboundRtpId === undefined &&\n        stat.type === \"inbound-rtp\" &&\n        stat.kind === \"video\"\n      ) {\n        videoInboundRtpId = stat.id;\n        if (stat.framesPerSecond !== undefined) {\n          framesPerSecond = stat.framesPerSecond;\n        }\n        if (stat.jitter !== undefined) {\n          jitter = stat.jitter;\n        }\n        if (\n          stat.packetsReceived !== undefined &&\n          stat.packetsLost !== undefined &&\n          stat.packetsReceived + stat.packetsLost > 0\n        ) {\n          packetLossRatio =\n            stat.packetsLost / (stat.packetsReceived + stat.packetsLost);\n        }\n      }\n    });\n\n    return {\n      rtt,\n      candidateType,\n      availableIncomingBitrate,\n      availableOutgoingBitrate,\n      incomingBitrate,\n      outgoingBitrate,\n      framesPerSecond,\n      packetLossRatio,\n      jitter,\n      timestamp: Date.now(),\n    };\n  };\n}\n","import {\n  type ReactorEvent,\n  type ReactorStatus,\n  type ReactorState,\n  type ReactorError,\n  type MessageScope,\n  type ConnectOptions,\n  type ConnectionStats,\n  type ConnectionTimings,\n  isAbortError,\n} from \"../types\";\nimport { type JwtResolver, type JwtSource, normalizeJwtSource } from \"./auth\";\nimport { CoordinatorClient } from \"./CoordinatorClient\";\nimport { LocalCoordinatorClient } from \"./LocalCoordinatorClient\";\nimport { type TransportClient, type TransportStatus } from \"./TransportClient\";\nimport { WebRTCTransportClient } from \"./WebRTCTransportClient\";\nimport {\n  type Capabilities,\n  type SessionResponse,\n  type TrackCapability,\n  REACTOR_WEBRTC_VERSION,\n} from \"./types\";\nimport { z } from \"zod\";\n\nconst LOCAL_COORDINATOR_URL = \"http://localhost:8080\";\nexport const DEFAULT_BASE_URL = \"https://api.reactor.inc\";\n\nconst TrackHintSchema = z.object({\n  name: z.string(),\n  kind: z.enum([\"video\", \"audio\"]),\n  direction: z.enum([\"recvonly\", \"sendonly\"]),\n});\n\nconst OptionsSchema = z.object({\n  apiUrl: z.string().default(DEFAULT_BASE_URL),\n  modelName: z.string(),\n  local: z.boolean().default(false),\n  modelTracks: z.array(TrackHintSchema).optional(),\n});\nexport type Options = z.input<typeof OptionsSchema>;\n\nexport { FileRef } from \"./FileRef\";\nimport { FileRef } from \"./FileRef\";\nimport { RecordingClient } from \"./RecordingClient\";\nimport type { Clip, DownloadClipOptions } from \"../utils/recording\";\n\ntype EventHandler = (...args: any[]) => void;\n\nexport class Reactor {\n  private coordinatorClient: CoordinatorClient | undefined;\n  private transportClient: TransportClient | undefined;\n  private status: ReactorStatus = \"disconnected\";\n  private coordinatorUrl: string;\n  private lastError?: ReactorError;\n  private model: string;\n  private sessionExpiration?: number;\n  private local: boolean;\n  private sessionId?: string;\n  /**\n   * True only when the active session was created by THIS connect flow\n   * (via `createSession()`). False when the session was adopted via\n   * `connect({ sessionId })`. We must never DELETE a session we did not\n   * create — another client (or the backend that created it) owns its\n   * lifecycle, so on disconnect/teardown/error an adopting client tears\n   * down its own transport but leaves the session alive.\n   */\n  private createdSession = false;\n  private connectStartTime?: number;\n  private connectionTimings?: ConnectionTimings;\n\n  private capabilities?: Capabilities;\n  private tracks: TrackCapability[] = [];\n  private presetTracks?: TrackCapability[];\n  private autoResumeTracks = false;\n  private sessionResponse?: SessionResponse;\n  // Cached so clip surfaces (player, download button, hook) can\n  // reach the same token source without re-threading `getJwt`.\n  // Outlives `disconnect()` because captured clips can still be\n  // downloaded after the session has ended.\n  private jwtResolver?: JwtResolver;\n\n  /** Per-Reactor recording client. See {@link RecordingClient}. */\n  readonly recording: RecordingClient;\n\n  constructor(options: Options) {\n    const validatedOptions = OptionsSchema.parse(options);\n    this.coordinatorUrl = validatedOptions.apiUrl;\n    this.model = validatedOptions.modelName;\n    this.local = validatedOptions.local;\n    if (this.local && options.apiUrl === undefined) {\n      this.coordinatorUrl = LOCAL_COORDINATOR_URL;\n    }\n    if (validatedOptions.modelTracks) {\n      this.presetTracks = validatedOptions.modelTracks;\n    }\n\n    this.recording = new RecordingClient({\n      onRuntimeMessage: (handler) => {\n        this.on(\"runtimeMessage\", handler);\n        return () => this.off(\"runtimeMessage\", handler);\n      },\n      onStatusChanged: (handler) => {\n        this.on(\"statusChanged\", handler);\n        return () => this.off(\"statusChanged\", handler);\n      },\n      sendRuntimeCommand: (command, data) =>\n        this.sendCommand(command, data, \"runtime\"),\n      getStatus: () => this.status,\n      getCoordinatorBaseUrl: () => this.coordinatorUrl,\n    });\n  }\n\n  private eventListeners: Map<ReactorEvent, Set<EventHandler>> = new Map();\n\n  /**\n   * Returns the JWT resolver supplied to the most recent\n   * {@link connect} call, or `undefined` if none was set (pre-connect\n   * or local mode). Used by clip surfaces as a fallback when their\n   * own `getJwt` prop is omitted.\n   */\n  getJwtResolver(): JwtResolver | undefined {\n    return this.jwtResolver;\n  }\n\n  on(event: ReactorEvent, handler: EventHandler) {\n    if (!this.eventListeners.has(event)) {\n      this.eventListeners.set(event, new Set());\n    }\n    this.eventListeners.get(event)!.add(handler);\n  }\n\n  off(event: ReactorEvent, handler: EventHandler) {\n    this.eventListeners.get(event)?.delete(handler);\n  }\n\n  emit(event: ReactorEvent, ...args: any[]) {\n    this.eventListeners.get(event)?.forEach((handler) => handler(...args));\n  }\n\n  /**\n   * Sends a command to the model via the data channel.\n   *\n   * When any value in `data` is a {@link FileRef}, it is automatically\n   * extracted and serialized into a separate `uploads` section on the\n   * wire, keyed by the parameter name.  Scalar values remain in `data`.\n   */\n  async sendCommand(\n    command: string,\n    data: any,\n    scope: MessageScope = \"application\"\n  ): Promise<void> {\n    // Pre-flight failure: reported through `lastError` so unawaited\n    // callers observe it without a `try/catch`.\n    if (this.status !== \"ready\") {\n      this.createError(\n        \"NOT_READY\",\n        `Cannot send command \"${command}\" while status is \"${this.status}\". Must be \"ready\".`,\n        \"api\",\n        true\n      );\n      return;\n    }\n\n    try {\n      let uploads: Record<string, object> | undefined;\n\n      if (data && typeof data === \"object\" && !Array.isArray(data)) {\n        const scalarData: Record<string, unknown> = {};\n        const extractedUploads: Record<string, object> = {};\n\n        for (const [key, value] of Object.entries(data)) {\n          if (value instanceof FileRef) {\n            extractedUploads[key] = {\n              upload_id: value.uploadId,\n              name: value.name,\n              mime_type: value.mimeType,\n              size: value.size,\n            };\n          } else {\n            scalarData[key] = value;\n          }\n        }\n\n        if (Object.keys(extractedUploads).length > 0) {\n          uploads = extractedUploads;\n          data = scalarData;\n        }\n      }\n\n      this.transportClient?.sendCommand(command, data, scope, uploads);\n    } catch (error) {\n      console.error(\"[Reactor] Failed to send message:\", error);\n      this.createError(\n        \"MESSAGE_SEND_FAILED\",\n        `Failed to send message: ${error}`,\n        \"gpu\",\n        true\n      );\n    }\n  }\n\n  /**\n   * Uploads a file to the session's object store and returns a {@link FileRef}.\n   *\n   * Flow:\n   *  1. POST /sessions/{id}/uploads → allocate presigned PUT URL\n   *  2. PUT file bytes to the presigned URL\n   *  3. Send `fileUploaded` notification on the runtime data channel\n   *     (fires `@file_uploaded` on the model if registered)\n   *  4. Return a `FileRef` to pass into {@link sendCommand}\n   *\n   * In local mode, the presigned URL returned by the runtime is rewritten\n   * to use the SDK-configured base URL (scheme + host), so port-forwarded\n   * setups work correctly (REA-1573).\n   */\n  async uploadFile(\n    file: File | Blob,\n    options?: { name?: string }\n  ): Promise<FileRef> {\n    if (this.status !== \"ready\") {\n      throw new Error(\n        `Cannot upload file, status is \"${this.status}\". Must be \"ready\".`\n      );\n    }\n    if (!this.coordinatorClient || !this.sessionId) {\n      throw new Error(\"No active session. Call connect() first.\");\n    }\n\n    const name = options?.name ?? (file instanceof File ? file.name : \"upload\");\n    const mimeType = file.type || \"application/octet-stream\";\n    const size = file.size;\n\n    if (size <= 0) {\n      throw new Error(\"File is empty\");\n    }\n\n    const slot = await this.coordinatorClient.createUpload(this.sessionId, {\n      name,\n      size,\n      mime_type: mimeType,\n    });\n\n    let presignedUrl = slot.presigned_url;\n    if (this.local) {\n      const target = new URL(this.coordinatorUrl);\n      const parsed = new URL(presignedUrl);\n      parsed.protocol = target.protocol;\n      parsed.hostname = target.hostname;\n      parsed.port = target.port;\n      presignedUrl = parsed.toString();\n    }\n\n    const putResponse = await fetch(presignedUrl, {\n      method: \"PUT\",\n      body: file,\n      headers: {\n        \"Content-Length\": String(size),\n      },\n    });\n    if (!putResponse.ok) {\n      throw new Error(\n        `File upload failed: ${putResponse.status} ${putResponse.statusText}`\n      );\n    }\n\n    await this.sendCommand(\n      \"fileUploaded\",\n      {\n        upload_id: slot.presigned_id,\n        name,\n        mime_type: mimeType,\n        size,\n      },\n      \"runtime\"\n    );\n\n    console.debug(\"[Reactor] File uploaded:\", {\n      uploadId: slot.presigned_id,\n      name,\n      mimeType,\n      size,\n    });\n\n    return new FileRef(slot.presigned_id, name, mimeType, size);\n  }\n\n  /**\n   * Request a clip covering the last `durationSeconds` of the live\n   * session. Capped server-side at `recording.clip_max_seconds`\n   * (default 5 minutes). Resolves with a {@link Clip} whose\n   * `playlistUrl` can be handed to any HLS-capable player.\n   *\n   * @throws {RecordingError} on invalid input, transport failure,\n   *   timeout, runtime-side `clipFailed`, or disconnect mid-request.\n   */\n  async requestClip(durationSeconds: number): Promise<Clip> {\n    return this.recording.requestClip(durationSeconds);\n  }\n\n  /**\n   * Request a clip covering the entire session up to \"now\". Same\n   * mechanics as {@link requestClip} with `start = 0`; only the\n   * resolved {@link Clip.kind} discriminator differs.\n   */\n  async requestRecording(): Promise<Clip> {\n    return this.recording.requestRecording();\n  }\n\n  /**\n   * Stream the chunks referenced by `clip.playlistUrl` and trigger a\n   * native browser download of the assembled fragmented-MP4 Blob.\n   * Pass `filename: null` to skip the download trigger and receive\n   * the Blob (useful for non-DOM consumers and tests).\n   */\n  async downloadClipAsFile(\n    clip: Clip,\n    filename: string | null = \"reactor-clip.mp4\",\n    options?: DownloadClipOptions\n  ): Promise<Blob> {\n    return this.recording.downloadClipAsFile(clip, filename, options);\n  }\n\n  /**\n   * Publishes a MediaStreamTrack to a named sendonly track.\n   * The transceiver is already set up from capabilities — this just\n   * calls replaceTrack() on the sender.\n   */\n  async publishTrack(name: string, track: MediaStreamTrack): Promise<void> {\n    if (process.env.NODE_ENV !== \"development\" && this.status !== \"ready\") {\n      console.warn(\n        `[Reactor] Cannot publish track \"${name}\", status is ${this.status}`\n      );\n      return;\n    }\n\n    try {\n      await this.transportClient?.publishTrack(name, track);\n    } catch (error) {\n      console.error(`[Reactor] Failed to publish track \"${name}\":`, error);\n      this.createError(\n        \"TRACK_PUBLISH_FAILED\",\n        `Failed to publish track \"${name}\": ${error}`,\n        \"gpu\",\n        true\n      );\n      throw error;\n    }\n  }\n\n  async unpublishTrack(name: string): Promise<void> {\n    try {\n      await this.transportClient?.unpublishTrack(name);\n    } catch (error) {\n      console.error(`[Reactor] Failed to unpublish track \"${name}\":`, error);\n      this.createError(\n        \"TRACK_UNPUBLISH_FAILED\",\n        `Failed to unpublish track \"${name}\": ${error}`,\n        \"gpu\",\n        true\n      );\n    }\n  }\n\n  pauseTrack(name: string): void {\n    this.transportClient?.pauseTrack(name);\n  }\n\n  resumeTrack(name: string): void {\n    this.transportClient?.resumeTrack(name);\n  }\n\n  /**\n   * Reconnects to an existing session with a fresh transport.\n   */\n  async reconnect(options?: ConnectOptions): Promise<void> {\n    if (!this.sessionId || !this.coordinatorClient) {\n      console.warn(\"[Reactor] No active session to reconnect to.\");\n      return;\n    }\n\n    if (this.status === \"ready\") {\n      console.warn(\"[Reactor] Already connected, no need to reconnect.\");\n      return;\n    }\n\n    if (this.tracks.length === 0) {\n      console.warn(\"[Reactor] No tracks available for reconnect.\");\n      return;\n    }\n\n    this.setStatus(\"connecting\");\n\n    try {\n      if (!this.transportClient) {\n        this.transportClient = new WebRTCTransportClient({\n          baseUrl: this.coordinatorUrl,\n          sessionId: this.sessionId,\n          jwtToken: this.local ? \"local\" : \"\",\n          maxPollAttempts: options?.maxAttempts,\n        });\n        this.setupTransportHandlers();\n      }\n\n      await this.transportClient.prepare(this.tracks);\n      await this.transportClient.connect(true);\n    } catch (error) {\n      if (isAbortError(error)) return;\n\n      console.error(\"[Reactor] Failed to reconnect:\", error);\n      this.disconnect(true);\n      this.createError(\n        \"RECONNECTION_FAILED\",\n        `Failed to reconnect: ${error}`,\n        \"api\",\n        true\n      );\n    }\n  }\n\n  /**\n   * Connects to the coordinator, creates a session, then establishes\n   * the transport using server-declared capabilities. `jwtToken` may\n   * be a static string or a {@link JwtSource} resolver; pass a\n   * resolver when the token is short-lived so each Coordinator HTTP\n   * hop sees a fresh value.\n   *\n   * Pass `options.sessionId` to attach to a session that already exists\n   * (e.g. one created by a backend) instead of creating a new one — session\n   * creation is skipped and the transport is brought up against that id. The\n   * `jwtToken` must be valid for the account that owns the session. Works in\n   * local mode too (it looks the session up by id rather than starting one).\n   */\n  async connect(jwtToken?: JwtSource, options?: ConnectOptions): Promise<void> {\n    console.debug(\"[Reactor] Connecting, status:\", this.status);\n\n    if (jwtToken == undefined && !this.local) {\n      throw new Error(\"No authentication provided and not in local mode\");\n    }\n\n    if (this.status !== \"disconnected\") {\n      throw new Error(\"Already connected or connecting\");\n    }\n    this.setStatus(\"connecting\");\n\n    this.connectStartTime = performance.now();\n\n    // Cache the resolver so clip surfaces can reuse it via\n    // `getJwtResolver()`. Local mode is auth-free, leave it unset.\n    if (!this.local && jwtToken !== undefined) {\n      this.jwtResolver = normalizeJwtSource(jwtToken);\n    }\n\n    try {\n      this.coordinatorClient = this.local\n        ? new LocalCoordinatorClient(this.coordinatorUrl, this.model)\n        : new CoordinatorClient({\n            baseUrl: this.coordinatorUrl,\n            jwtToken: jwtToken!,\n            model: this.model,\n          });\n\n      // 1. Resolve the session — either attach to a caller-supplied session\n      //    (created elsewhere, e.g. by a backend) or create a fresh one. In\n      //    the attach path there's no POST /sessions, so `sessionCreationMs`\n      //    stays 0.\n      let sessionCreationMs = 0;\n      let sessionId: string;\n      if (options?.sessionId) {\n        await this.coordinatorClient.adoptSession(options.sessionId);\n        sessionId = options.sessionId;\n        // Adopted, not created — we don't own this session's lifecycle.\n        this.createdSession = false;\n        console.debug(\"[Reactor] Attaching to existing session:\", sessionId);\n      } else {\n        const tSession = performance.now();\n        const initialResponse = await this.coordinatorClient.createSession();\n        sessionCreationMs = performance.now() - tSession;\n        sessionId = initialResponse.session_id;\n        // We created it, so we own teardown (DELETE) of this session.\n        this.createdSession = true;\n        console.debug(\n          \"[Reactor] Session created:\",\n          sessionId,\n          \"state:\",\n          initialResponse.state\n        );\n      }\n\n      this.setSessionId(sessionId);\n\n      this.setStatus(\"waiting\");\n\n      this.transportClient = new WebRTCTransportClient({\n        baseUrl: this.coordinatorUrl,\n        sessionId,\n        jwtToken: this.local ? \"local\" : jwtToken!,\n        webrtcVersion: REACTOR_WEBRTC_VERSION,\n        maxPollAttempts: options?.maxAttempts,\n      });\n      this.setupTransportHandlers();\n\n      let sessionPollingMs: number;\n      let transportConnectingMs: number;\n\n      this.autoResumeTracks = options?.autoResumeTracks ?? true;\n\n      if (this.presetTracks) {\n        // 2a. Parallel path: tracks are known at build time, so we can\n        //     prepare the transport while waiting for the Runtime.\n        this.tracks = this.presetTracks;\n\n        const tParallel = performance.now();\n        const [sessionResponse] = await Promise.all([\n          this.coordinatorClient.pollSessionReady(),\n          this.transportClient.prepare(this.tracks),\n        ]);\n        sessionPollingMs = performance.now() - tParallel;\n\n        this.sessionResponse = sessionResponse;\n        this.capabilities = {\n          ...sessionResponse.capabilities!,\n          tracks: this.tracks,\n        };\n        this.emit(\"capabilitiesReceived\", this.capabilities);\n\n        const tConnect = performance.now();\n        await this.transportClient.connect(false, options?.connectionId);\n        transportConnectingMs = performance.now() - tConnect;\n      } else {\n        // 2b. Sequential path: tracks come from the poll response, but\n        //     we can still warm up the transport (ICE fetch) in parallel.\n        this.transportClient.warmup();\n\n        const tPoll = performance.now();\n        const sessionResponse = await this.coordinatorClient.pollSessionReady();\n        sessionPollingMs = performance.now() - tPoll;\n\n        this.sessionResponse = sessionResponse;\n        this.tracks = sessionResponse.capabilities!.tracks;\n        this.capabilities = {\n          ...sessionResponse.capabilities!,\n          tracks: this.tracks,\n        };\n        this.emit(\"capabilitiesReceived\", this.capabilities);\n\n        const protocol = sessionResponse.selected_transport!.protocol;\n        if (protocol !== \"webrtc\") {\n          throw new Error(`Unsupported transport protocol: ${protocol}`);\n        }\n\n        const tTransport = performance.now();\n        await this.transportClient.prepare(this.tracks);\n        await this.transportClient.connect(false, options?.connectionId);\n        transportConnectingMs = performance.now() - tTransport;\n      }\n\n      console.debug(\"[Reactor] Session ready, tracks:\", this.tracks.length);\n\n      this.connectionTimings = {\n        sessionCreationMs: sessionCreationMs + sessionPollingMs,\n        transportConnectingMs,\n        totalMs: 0,\n      };\n    } catch (error) {\n      if (isAbortError(error)) return;\n\n      console.error(\"[Reactor] Connection failed:\", error);\n      this.createError(\n        \"CONNECTION_FAILED\",\n        `Connection failed: ${error}`,\n        \"api\",\n        true\n      );\n      try {\n        await this.disconnect(false);\n      } catch (disconnectError) {\n        console.error(\n          \"[Reactor] Failed to clean up after connection failure:\",\n          disconnectError\n        );\n      }\n      throw error;\n    }\n  }\n\n  /**\n   * Sets up event handlers for the transport client.\n   * Each handler captures the client reference to ignore stale events.\n   */\n  private setupTransportHandlers(): void {\n    if (!this.transportClient) return;\n    const client = this.transportClient;\n\n    client.on(\"message\", (message: any, scope: MessageScope) => {\n      if (this.transportClient !== client) return;\n      if (scope === \"application\") {\n        this.emit(\"message\", message);\n      } else if (scope === \"runtime\") {\n        // Just emit — `RecordingClient` and any app-level\n        // `runtimeMessage` listeners are subscribers via `on()`.\n        // Content moderation events arrive as `{ type: \"moderation\",\n        // data: ModerationEvent }`; apps filter on `type` rather than\n        // subscribing to a dedicated SDK event.\n        this.emit(\"runtimeMessage\", message);\n      }\n    });\n\n    client.on(\"statusChanged\", (status: TransportStatus) => {\n      if (this.transportClient !== client) return;\n      switch (status) {\n        case \"connected\":\n          this.finalizeConnectionTimings();\n\n          if (this.autoResumeTracks) {\n            for (const track of this.tracks) {\n              if (track.direction === \"recvonly\") {\n                this.resumeTrack(track.name);\n              }\n            }\n          }\n\n          this.setStatus(\"ready\");\n          break;\n        case \"disconnected\":\n          this.disconnect(true);\n          break;\n        case \"error\":\n          this.createError(\n            \"GPU_CONNECTION_ERROR\",\n            \"Transport connection failed\",\n            \"gpu\",\n            true\n          );\n          this.disconnect();\n          break;\n      }\n    });\n\n    client.on(\n      \"trackReceived\",\n      (name: string, track: MediaStreamTrack, stream: MediaStream) => {\n        if (this.transportClient !== client) return;\n        this.emit(\"trackReceived\", name, track, stream);\n\n        if (this.autoResumeTracks) {\n          for (const track of this.tracks) {\n            if (track.direction === \"recvonly\") {\n              this.resumeTrack(track.name);\n            }\n          }\n        }\n      }\n    );\n\n    client.on(\"statsUpdate\", (stats: ConnectionStats) => {\n      if (this.transportClient !== client) return;\n      this.emit(\"statsUpdate\", {\n        ...stats,\n        connectionTimings: this.connectionTimings,\n      });\n    });\n  }\n\n  /**\n   * Disconnects from both the transport and the coordinator.\n   */\n  async disconnect(recoverable: boolean = false) {\n    if (this.status === \"disconnected\" && !this.sessionId) {\n      console.warn(\"[Reactor] Already disconnected\");\n      return;\n    }\n\n    this.coordinatorClient?.abort();\n    this.transportClient?.abort();\n\n    if (this.coordinatorClient && !recoverable) {\n      // Only the session's creator may DELETE it. A client that adopted an\n      // existing session (connect({ sessionId })) tears down its own\n      // transport but must leave the session running for its real owner —\n      // this guard applies to every non-recoverable path (explicit\n      // disconnect, transport error, connect-failure cleanup, unmount).\n      if (this.createdSession) {\n        try {\n          await this.coordinatorClient.terminateSession();\n        } catch (error) {\n          console.error(\"[Reactor] Error terminating session:\", error);\n        }\n      } else {\n        console.debug(\n          \"[Reactor] Adopted session (not the creator) — skipping DELETE, leaving it alive\"\n        );\n      }\n      this.coordinatorClient = undefined;\n    }\n\n    if (this.transportClient) {\n      try {\n        await this.transportClient.disconnect();\n      } catch (error) {\n        console.error(\"[Reactor] Error disconnecting transport:\", error);\n      }\n      if (!recoverable) {\n        this.transportClient = undefined;\n      }\n    }\n\n    this.setStatus(\"disconnected\");\n    this.resetConnectionTimings();\n    if (!recoverable) {\n      this.setSessionExpiration(undefined);\n      this.setSessionId(undefined);\n      this.capabilities = undefined;\n      this.tracks = [];\n      this.sessionResponse = undefined;\n      this.createdSession = false;\n    }\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Getters\n  // ─────────────────────────────────────────────────────────────────────────\n\n  getSessionId(): string | undefined {\n    return this.sessionId;\n  }\n\n  getStatus(): ReactorStatus {\n    return this.status;\n  }\n\n  getState(): ReactorState {\n    return {\n      status: this.status,\n      lastError: this.lastError,\n    };\n  }\n\n  getLastError(): ReactorError | undefined {\n    return this.lastError;\n  }\n\n  getCapabilities(): Capabilities | undefined {\n    return this.capabilities;\n  }\n\n  getSessionInfo(): SessionResponse | undefined {\n    return this.sessionResponse;\n  }\n\n  getStats(): ConnectionStats | undefined {\n    const stats = this.transportClient?.getStats();\n    if (!stats) return undefined;\n    return { ...stats, connectionTimings: this.connectionTimings };\n  }\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Private State Management\n  // ─────────────────────────────────────────────────────────────────────────\n\n  private setSessionId(newSessionId: string | undefined) {\n    console.debug(\n      \"[Reactor] Setting session ID:\",\n      newSessionId,\n      \"from\",\n      this.sessionId\n    );\n    if (this.sessionId !== newSessionId) {\n      this.sessionId = newSessionId;\n      this.emit(\"sessionIdChanged\", newSessionId);\n    }\n  }\n\n  private setStatus(newStatus: ReactorStatus) {\n    console.debug(\"[Reactor] Setting status:\", newStatus, \"from\", this.status);\n    if (this.status !== newStatus) {\n      this.status = newStatus;\n      this.emit(\"statusChanged\", newStatus);\n    }\n  }\n\n  private setSessionExpiration(newSessionExpiration: number | undefined) {\n    console.debug(\n      \"[Reactor] Setting session expiration:\",\n      newSessionExpiration\n    );\n    if (this.sessionExpiration !== newSessionExpiration) {\n      this.sessionExpiration = newSessionExpiration;\n      this.emit(\"sessionExpirationChanged\", newSessionExpiration);\n    }\n  }\n\n  private resetConnectionTimings(): void {\n    this.connectStartTime = undefined;\n    this.connectionTimings = undefined;\n  }\n\n  private finalizeConnectionTimings(): void {\n    if (!this.connectionTimings || this.connectStartTime == null) return;\n\n    this.connectionTimings.totalMs = performance.now() - this.connectStartTime;\n    this.connectStartTime = undefined;\n\n    console.debug(\"[Reactor] Connection timings:\", this.connectionTimings);\n  }\n\n  private createError(\n    code: string,\n    message: string,\n    component: \"api\" | \"gpu\",\n    recoverable: boolean,\n    retryAfter?: number\n  ) {\n    this.lastError = {\n      code,\n      message,\n      timestamp: Date.now(),\n      recoverable,\n      component,\n      retryAfter,\n    };\n    this.emit(\"error\", this.lastError);\n  }\n}\n","/**\n * Stateless primitives for the Reactor recording feature.\n *\n * This module owns:\n * - Public-facing {@link Clip} type, {@link RecordingError} class, and\n *   the `RuntimeRecordingMessageType` enum.\n * - Wire schemas (`ClipReadyPayloadSchema`, `ClipFailedPayloadSchema`)\n *   plus `clipFromPayload` to convert snake_case wire payloads into\n *   camelCase `Clip` objects.\n * - HTTP helpers `fetchPlaylist` (deadline-driven polling) and\n *   `parsePlaylist` (HLS `.m3u8` parser).\n * - The `downloadClipAsFile` helper that streams the referenced\n *   fMP4 chunks, remuxes them into a flat social-media-ready MP4\n *   via `mp4box` (PTS=0, faststart, `major_brand=isom`), and\n *   returns a Blob for browser download.\n *\n * Stateful pieces (FIFO promise correlator, in-flight request\n * lifecycle, integration with Reactor's event bus) live in the\n * `RecordingClient` class in `core/RecordingClient.ts`.\n *\n * Wire contract is documented end-to-end in the *Video Recording\n * Feature* Notion page; the runtime side ships in\n * `reactor_runtime/recording/`.\n */\n\nimport { z } from \"zod\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Runtime message types (mirrors RuntimeMessageType in Python)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Names of the runtime-scoped messages used by the recording feature.\n * Outbound names are sent via `Reactor.sendCommand(name, …, \"runtime\")`;\n * inbound names appear as `message.type` on `runtimeMessage` events.\n */\nexport const RuntimeRecordingMessageType = {\n  REQUEST_CLIP: \"requestClip\",\n  REQUEST_RECORDING: \"requestRecording\",\n  CLIP_READY: \"clipReady\",\n  CLIP_FAILED: \"clipFailed\",\n} as const;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Discriminator on a {@link Clip}. `\"snap\"` from `requestClip`, `\"recording\"` from `requestRecording`. */\nexport type ClipKind = \"snap\" | \"recording\";\n\n/**\n * Resolved outcome of a {@link Reactor.requestClip} / {@link Reactor.requestRecording} call.\n *\n * The runtime returns immediately on every request — it does *not*\n * block until the in-progress chunk has been finalised. The response\n * is a *promise*: `endMarker` reflects wall-clock at request time\n * and may point inside a chunk ffmpeg is still writing. The\n * `/clips` manifest endpoint returns `202 Retry-After` while that\n * chunk is in flight; the SDK polls until `200`.\n *\n * - `startMarker` / `endMarker` / `nowMarker` are session-relative\n *   seconds since recorder start.\n * - `predictedReadyAtMs` is a **Unix epoch in milliseconds** — the\n *   runtime's estimate of when the boundary chunk will be servable\n *   by `/clips`. Compare against `Date.now()` to drive a \"ready in\n *   Ns\" indicator. The SDK uses this as the polling deadline: past\n *   `predictedReadyAtMs + slackMs` and still 202, the SDK fails with\n *   `CLIP_NOT_READY` on the assumption the runtime crashed.\n * - `playlistUrl` is short-lived in production (the embedded chunk\n *   URLs are presigned for a few minutes). Re-issuing the request\n *   produces a fresh URL with fresh presigning.\n */\nexport interface Clip {\n  sessionId: string;\n  kind: ClipKind;\n  startMarker: number;\n  endMarker: number;\n  nowMarker: number;\n  predictedReadyAtMs: number;\n  playlistUrl: string;\n}\n\n/**\n * Error thrown when a recording request cannot be served.\n *\n * `code` is a stable, machine-readable identifier; `reason` is the\n * raw string the runtime / manifest endpoint returned (passed through\n * verbatim from `clipFailed.reason`, the `/clips` HTTP body, or the\n * SDK's pre-flight check).\n */\nexport class RecordingError extends Error {\n  constructor(\n    public readonly code:\n      | \"RECORDER_DISABLED\"\n      | \"INVALID_DURATION\"\n      | \"REQUEST_TIMEOUT\"\n      | \"DISCONNECTED\"\n      | \"CLIP_GONE\"\n      | \"CLIP_NOT_READY\"\n      | \"PLAYLIST_FETCH_FAILED\"\n      | \"CHUNK_FETCH_FAILED\"\n      | \"INVALID_PLAYLIST\"\n      | \"DOWNLOAD_UNSUPPORTED\"\n      | \"INTERNAL_ERROR\",\n    public readonly reason: string\n  ) {\n    super(`${code}: ${reason}`);\n    this.name = \"RecordingError\";\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire schemas\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** `clipReady.data` payload as serialised by `ClipResult.to_dict()`. */\nexport const ClipReadyPayloadSchema = z\n  .object({\n    session_id: z.string(),\n    kind: z.enum([\"snap\", \"recording\"]),\n    start_marker: z.number(),\n    end_marker: z.number(),\n    now_marker: z.number(),\n    predicted_ready_at_ms: z.number(),\n    playlist_url: z.string(),\n  })\n  .passthrough();\n\nexport type ClipReadyPayload = z.infer<typeof ClipReadyPayloadSchema>;\n\n/** `clipFailed.data` payload — short reason string from the runtime. */\nexport const ClipFailedPayloadSchema = z\n  .object({\n    reason: z.string().default(\"unknown\"),\n  })\n  .passthrough();\n\nexport type ClipFailedPayload = z.infer<typeof ClipFailedPayloadSchema>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire → public type conversion\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Options for {@link clipFromPayload}. */\nexport interface ParseClipOptions {\n  /**\n   * Coordinator base URL.  When set, resolves `payload.playlist_url`\n   * against this base — used to convert the runtime's path-only\n   * playlist URL (``/clips?session_id=…``) into an absolute URL the\n   * browser/SDK can fetch.  Also handles the legacy case where an\n   * older runtime emits an absolute URL bound to its own\n   * ``0.0.0.0`` listener: the host/port are rewritten onto the\n   * coordinator.\n   */\n  coordinatorBaseUrl?: string;\n}\n\n/**\n * Convert a validated {@link ClipReadyPayload} into the public {@link Clip}.\n * Pure: no IO, no side effects.\n */\nexport function clipFromPayload(\n  payload: ClipReadyPayload,\n  options: ParseClipOptions = {}\n): Clip {\n  const playlistUrl = options.coordinatorBaseUrl\n    ? rewriteUrlHost(payload.playlist_url, options.coordinatorBaseUrl)\n    : payload.playlist_url;\n  return {\n    sessionId: payload.session_id,\n    kind: payload.kind,\n    startMarker: payload.start_marker,\n    endMarker: payload.end_marker,\n    nowMarker: payload.now_marker,\n    predictedReadyAtMs: payload.predicted_ready_at_ms,\n    playlistUrl,\n  };\n}\n\n/**\n * Resolve `target` against `base`, returning an absolute URL.\n *\n * Two cases:\n *\n * * **Path-only / relative target** (no scheme — e.g. the\n *   runtime's current ``/clips?session_id=…`` shape): joined\n *   against ``base`` via ``new URL(target, base)``, preserving\n *   path, query, and fragment.\n * * **Absolute target** (``http://…`` / ``https://…``, e.g. a legacy\n *   runtime emitting a URL bound to its own ``0.0.0.0`` listener):\n *   scheme, hostname, and port are replaced with ``base``'s,\n *   preserving path, query, and fragment.\n *\n * Returns the original ``target`` on any parse failure rather than\n * throwing — the SDK consumes the URL eagerly and shouldn't break\n * a session over a malformed clip envelope.\n */\nexport function rewriteUrlHost(target: string, base: string): string {\n  try {\n    // Path-only / scheme-less → resolve against base.\n    if (!/^[a-z][a-z0-9+.-]*:/i.test(target)) {\n      return new URL(target, base).toString();\n    }\n    // Absolute → rewrite scheme/host/port, keep path/query/fragment.\n    const baseUrl = new URL(base);\n    const parsed = new URL(target);\n    parsed.protocol = baseUrl.protocol;\n    parsed.hostname = baseUrl.hostname;\n    parsed.port = baseUrl.port;\n    return parsed.toString();\n  } catch {\n    return target;\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// HLS playlist fetching + parsing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Internal — segments referenced by an HLS manifest, in playback order. */\ninterface ParsedPlaylist {\n  initUrl: string;\n  segmentUrls: string[];\n}\n\n/**\n * Default grace period after `predictedReadyAtMs` before\n * {@link fetchPlaylist} gives up.  Applied from\n * `max(predictedReadyAtMs, pollStart)` so late clicks still get the\n * full window.\n */\nexport const DEFAULT_PLAYLIST_POLL_SLACK_MS = 15_000;\n\nexport interface FetchPlaylistOptions {\n  /**\n   * Unix epoch (ms) when the runtime predicts the boundary chunk will\n   * be servable. Pass `clip.predictedReadyAtMs` here. When set, polling\n   * continues until `predictedReadyAtMs + slackMs`; once past, a\n   * stuck `202` produces `CLIP_NOT_READY` (assume runtime crashed).\n   */\n  predictedReadyAtMs?: number;\n  /** Grace period after `predictedReadyAtMs`. Default {@link DEFAULT_PLAYLIST_POLL_SLACK_MS}. */\n  slackMs?: number;\n  /**\n   * Hard cap on the per-poll wait. The server's `Retry-After` header is\n   * honored but clamped. Default 2000 ms keeps pending UI snappy.\n   */\n  maxRetryDelayMs?: number;\n  /** Floor on the per-poll wait so we don't hot-loop on cheap networks. Default 200 ms. */\n  minRetryDelayMs?: number;\n  /**\n   * Fallback retry count used when `predictedReadyAtMs` is omitted\n   * (e.g. someone calls `fetchPlaylist` directly with a saved URL,\n   * outside of a fresh `Clip`). Default 5.\n   */\n  maxRetries?: number;\n  /** Aborts in-flight fetches and the inter-poll sleep. */\n  signal?: AbortSignal;\n  /**\n   * Coordinator JWT.  When set, attached as `Authorization: Bearer\n   * <jwt>` on the manifest GET — the Coordinator hop.  In local mode\n   * (HttpRuntime), leave undefined.\n   */\n  jwt?: string;\n}\n\n/**\n * Fetch the playlist URL, polling on `202 Accepted` (which the manifest\n * endpoint returns while the boundary chunk is still uploading).\n *\n * Polling deadline is driven by `predictedReadyAtMs + slackMs` when a\n * `Clip` was just minted by the runtime; without that field the\n * function falls back to `maxRetries` attempts. Either way:\n * - `200` → returns the manifest body.\n * - `410` / `404` → throws `CLIP_GONE`.\n * - `5xx`/other → throws `PLAYLIST_FETCH_FAILED` (no retry).\n * - Past the deadline / retries with stuck `202` → throws\n *   `CLIP_NOT_READY` (the runtime probably crashed mid-chunk).\n */\nexport async function fetchPlaylist(\n  playlistUrl: string,\n  options: FetchPlaylistOptions = {}\n): Promise<string> {\n  const slackMs = options.slackMs ?? DEFAULT_PLAYLIST_POLL_SLACK_MS;\n  const minDelay = Math.max(0, options.minRetryDelayMs ?? 200);\n  const maxDelay = Math.max(minDelay, options.maxRetryDelayMs ?? 2_000);\n  const fallbackMaxRetries = options.maxRetries ?? 5;\n\n  const hasDeadline = typeof options.predictedReadyAtMs === \"number\";\n  // Slack window starts from max(predictedReadyAtMs, now), so late\n  // clicks still get the full grace window.\n  const startedPollingAt = Date.now();\n  const deadlineMs = hasDeadline\n    ? Math.max(options.predictedReadyAtMs as number, startedPollingAt) + slackMs\n    : undefined;\n\n  const headers = options.jwt\n    ? { Authorization: `Bearer ${options.jwt}` }\n    : undefined;\n\n  let attempt = 0;\n  let lastStatus = 0;\n\n  while (true) {\n    let response: Response;\n    try {\n      response = await fetch(playlistUrl, {\n        signal: options.signal,\n        headers,\n      });\n    } catch (error) {\n      throw new RecordingError(\n        \"PLAYLIST_FETCH_FAILED\",\n        `Network error fetching playlist: ${(error as Error).message}`\n      );\n    }\n    lastStatus = response.status;\n\n    // Order matters: 202 is in the 2xx range so it would match\n    // ``response.ok`` if we didn't branch on it first.\n    if (response.status === 202) {\n      // Decide whether to keep polling.\n      if (hasDeadline) {\n        if (Date.now() >= (deadlineMs as number)) {\n          throw new RecordingError(\n            \"CLIP_NOT_READY\",\n            `Boundary chunk still pending after ${slackMs}ms grace (predicted ready ${new Date(\n              options.predictedReadyAtMs as number\n            ).toISOString()}). Runtime may have crashed mid-clip.`\n          );\n        }\n      } else if (attempt >= fallbackMaxRetries) {\n        throw new RecordingError(\n          \"CLIP_NOT_READY\",\n          `Manifest still pending after ${attempt + 1} attempts (last status ${lastStatus})`\n        );\n      }\n\n      const headerDelay = parseRetryAfter(\n        response.headers.get(\"Retry-After\"),\n        minDelay\n      );\n      const delay = Math.min(maxDelay, Math.max(minDelay, headerDelay));\n      // Don't sleep past the deadline; clamp so the next loop sees it.\n      const clampedDelay = hasDeadline\n        ? Math.min(delay, Math.max(0, (deadlineMs as number) - Date.now()))\n        : delay;\n      await sleep(clampedDelay, options.signal);\n      attempt++;\n      continue;\n    }\n\n    if (response.status === 200) {\n      return await response.text();\n    }\n    if (response.status === 410) {\n      throw new RecordingError(\n        \"CLIP_GONE\",\n        \"Clip is no longer available (chunks aged out or session unknown)\"\n      );\n    }\n    if (response.status === 404) {\n      throw new RecordingError(\n        \"CLIP_GONE\",\n        \"Session not found for clip playlist\"\n      );\n    }\n    throw new RecordingError(\n      \"PLAYLIST_FETCH_FAILED\",\n      `Manifest endpoint returned HTTP ${response.status}`\n    );\n  }\n}\n\n/**\n * Parse an HLS `.m3u8` body into the init segment URL plus the ordered\n * list of media segment URLs. Resolves relative URLs against the\n * playlist URL itself.\n */\nexport function parsePlaylist(\n  manifestBody: string,\n  playlistUrl: string\n): ParsedPlaylist {\n  let initUrl: string | undefined;\n  const segments: string[] = [];\n\n  const lines = manifestBody.split(/\\r?\\n/);\n  for (const line of lines) {\n    const trimmed = line.trim();\n    if (!trimmed) continue;\n\n    if (trimmed.startsWith(\"#EXT-X-MAP\")) {\n      const match = trimmed.match(/URI=\"([^\"]+)\"/);\n      if (match) {\n        initUrl = resolveAgainst(match[1], playlistUrl);\n      }\n      continue;\n    }\n    if (trimmed.startsWith(\"#\")) {\n      continue;\n    }\n    segments.push(resolveAgainst(trimmed, playlistUrl));\n  }\n\n  if (!initUrl) {\n    throw new RecordingError(\n      \"INVALID_PLAYLIST\",\n      \"Playlist is missing an #EXT-X-MAP init segment URI\"\n    );\n  }\n  if (segments.length === 0) {\n    throw new RecordingError(\n      \"INVALID_PLAYLIST\",\n      \"Playlist contains no media segments\"\n    );\n  }\n\n  return { initUrl, segmentUrls: segments };\n}\n\n/**\n * Wrap an HLS manifest body in a `blob:` URL suitable for `<video src>`\n * or `hls.js`.  Bypasses the \"player can't set Authorization headers\"\n * problem: the manifest body is served from browser memory, and the\n * chunk URLs inside are already-signed S3 GETs.\n *\n * Path-only / relative chunk URLs in the body are absolutized against\n * ``playlistUrl`` first.  Without this rewrite the browser would\n * resolve them against the ``blob:`` URL's effective base — the page's\n * origin — and a local-dev frontend on (e.g.) port 3000 would issue\n * chunk fetches against its own dev server instead of the\n * HttpRuntime's ``/clips/chunks/...`` endpoint on port 8080.  In\n * production / kind-cluster mode the manifest already carries absolute\n * presigned S3 URLs and `resolveAgainst` is a no-op on them.\n *\n * Caller owns the returned URL — revoke it via `URL.revokeObjectURL`\n * when playback tears down.  Browser-only; throws `INVALID_PLAYLIST`\n * outside a DOM environment.\n */\nexport function createPlayableManifestUrl(\n  manifestBody: string,\n  playlistUrl: string\n): string {\n  if (\n    typeof Blob === \"undefined\" ||\n    typeof URL === \"undefined\" ||\n    typeof URL.createObjectURL !== \"function\"\n  ) {\n    throw new RecordingError(\n      \"INVALID_PLAYLIST\",\n      \"createPlayableManifestUrl requires a browser environment with URL.createObjectURL\"\n    );\n  }\n  const rewritten = absolutizeManifestUrls(manifestBody, playlistUrl);\n  const blob = new Blob([rewritten], {\n    type: \"application/vnd.apple.mpegurl\",\n  });\n  return URL.createObjectURL(blob);\n}\n\n/**\n * Rewrite an HLS manifest body so every chunk URL is absolute.\n *\n * - ``#EXT-X-MAP:URI=\"<rel>\"`` → ``URI=\"<abs>\"`` via ``resolveAgainst``.\n * - Each non-comment, non-empty line is treated as a media segment URL\n *   and absolutized.\n * - Lines that are already absolute (any URL with a scheme — e.g. an S3\n *   presigned GET) pass through unchanged because ``new URL(target,\n *   base)`` returns ``target`` verbatim when ``target`` is absolute.\n * - All other directives (``#EXTM3U``, ``#EXTINF:...``,\n *   ``#EXT-X-VERSION:...``, etc.) and blank lines are preserved as-is.\n * - Original line endings are preserved (``\\r\\n`` vs ``\\n``) so the\n *   playable blob byte-matches the source on non-rewriting platforms.\n */\nfunction absolutizeManifestUrls(\n  manifestBody: string,\n  playlistUrl: string\n): string {\n  // Preserve the source's line-ending style so the blob is byte-stable\n  // for absolute-only manifests (production path).\n  const eol = manifestBody.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n  const lines = manifestBody.split(/\\r?\\n/);\n  const out: string[] = [];\n  for (const line of lines) {\n    if (line.startsWith(\"#EXT-X-MAP\")) {\n      out.push(\n        line.replace(\n          /URI=\"([^\"]+)\"/,\n          (_, uri) => `URI=\"${resolveAgainst(uri, playlistUrl)}\"`\n        )\n      );\n      continue;\n    }\n    const trimmed = line.trim();\n    if (!trimmed || trimmed.startsWith(\"#\")) {\n      out.push(line);\n      continue;\n    }\n    out.push(resolveAgainst(trimmed, playlistUrl));\n  }\n  return out.join(eol);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// downloadClipAsFile\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DownloadClipOptions {\n  /**\n   * Coordinator JWT.  Attached on the manifest GET only; the chunks\n   * referenced inside the manifest are S3 presigned URLs and are\n   * fetched unauthenticated.\n   */\n  jwt?: string;\n  /** Forwarded to every request. Cancels both the playlist poll and any in-flight chunk fetches. */\n  signal?: AbortSignal;\n  /** Called after each chunk completes — useful for progress UI. */\n  onProgress?: (info: {\n    fetched: number;\n    total: number;\n    bytes: number;\n  }) => void;\n}\n\n/**\n * Stream the chunks referenced by `clip.playlistUrl`, remux the\n * assembled bytes into a flat social-media-ready MP4, and (when\n * `filename` is non-null) trigger a browser-native `<a download>`.\n * Pass `filename: null` to skip the download trigger and just\n * receive the Blob.\n *\n * The remux step rewrites the container only — H.264 NAL units and\n * AAC packets pass through unchanged — so `start_time=0`,\n * `+faststart`, and `major_brand=isom` come for free without any\n * re-encode cost.  See {@link maybeRemux} for the fallback\n * semantics on the rare parse failure.\n */\nexport async function downloadClipAsFile(\n  clip: Clip,\n  filename: string | null = \"reactor-clip.mp4\",\n  options: DownloadClipOptions = {}\n): Promise<Blob> {\n  // Two distinct hops: the manifest GET hits the Coordinator (JWT\n  // required) and goes through `fetchPlaylist`; the chunks are S3\n  // presigned URLs carrying their own SigV4 query auth and are\n  // fetched directly with plain `fetch`.\n  const manifestBody = await fetchPlaylist(clip.playlistUrl, {\n    predictedReadyAtMs: clip.predictedReadyAtMs,\n    signal: options.signal,\n    jwt: options.jwt,\n  });\n  const { initUrl, segmentUrls } = parsePlaylist(\n    manifestBody,\n    clip.playlistUrl\n  );\n\n  const orderedUrls = [initUrl, ...segmentUrls];\n  const parts: Uint8Array[] = [];\n  let bytes = 0;\n\n  for (let i = 0; i < orderedUrls.length; i++) {\n    const url = orderedUrls[i];\n    let response: Response;\n    try {\n      response = await fetch(url, { signal: options.signal });\n    } catch (error) {\n      throw new RecordingError(\n        \"CHUNK_FETCH_FAILED\",\n        `Network error fetching chunk ${i}: ${(error as Error).message}`\n      );\n    }\n    if (!response.ok) {\n      throw new RecordingError(\n        \"CHUNK_FETCH_FAILED\",\n        `Chunk ${i} returned HTTP ${response.status}`\n      );\n    }\n    const data = new Uint8Array(await response.arrayBuffer());\n    parts.push(data);\n    bytes += data.byteLength;\n    options.onProgress?.({\n      fetched: i + 1,\n      total: orderedUrls.length,\n      bytes,\n    });\n  }\n\n  const finalBytes = await maybeRemux(parts);\n  const blob = new Blob([finalBytes as BlobPart], { type: \"video/mp4\" });\n\n  if (filename === null) {\n    return blob;\n  }\n  if (\n    typeof document === \"undefined\" ||\n    typeof URL.createObjectURL !== \"function\"\n  ) {\n    throw new RecordingError(\n      \"DOWNLOAD_UNSUPPORTED\",\n      \"downloadClipAsFile requires a DOM environment; pass filename=null to skip the download trigger\"\n    );\n  }\n\n  triggerBrowserDownload(blob, filename);\n  return blob;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Fragmented MP4 → flat MP4 remux\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Concatenate `parts` and pipe the result through\n * {@link remuxFragmentedToFlat}.  Remux failures (`mp4box` import\n * blocked by exotic bundler / CSP, corrupt input, unsupported\n * codec, internal assertion) are funneled through a single path:\n * a `console.warn` and a return of the unmodified concatenation,\n * so the download still succeeds end-to-end with the (worse, but\n * still playable) fragmented MP4 the runtime emitted.\n */\nasync function maybeRemux(parts: Uint8Array[]): Promise<Uint8Array> {\n  const input = concatUint8Arrays(parts);\n  try {\n    const MP4Box = await loadMp4Box();\n    return await remuxFragmentedToFlat(input, MP4Box);\n  } catch (error) {\n    console.warn(\n      \"[Reactor] Clip remux failed, returning fragmented MP4 instead.\",\n      error\n    );\n    return input;\n  }\n}\n\n/**\n * Indirection so tests can stub the dynamic `mp4box` import without\n * pulling the real bytes through every test that calls\n * `downloadClipAsFile`.\n *\n * @internal\n */\nexport const __remuxInternals = {\n  loadMp4Box: (): Promise<typeof import(\"mp4box\")> => import(\"mp4box\"),\n};\n\nfunction loadMp4Box(): Promise<typeof import(\"mp4box\")> {\n  return __remuxInternals.loadMp4Box();\n}\n\n/**\n * The actual fMP4 → flat MP4 conversion.  No re-encode: every NAL\n * unit and AAC packet passes through unchanged.  Only the container\n * framing (boxes) and decode timestamps get rewritten.\n *\n * The output file is initialised with `[\"isom\", \"mp42\", \"avc1\", \"iso2\"]`\n * as compatible brands so the major brand is `isom` (not `iso5`,\n * which is what byte-concatenated fragments default to) and the\n * sample tables are written ahead of `mdat` — i.e. the file is\n * implicitly faststart.  Per-track decode times are shifted by the\n * first sample's DTS so the output starts at `0.000000` regardless\n * of where the clip sits on the session timeline.\n */\nasync function remuxFragmentedToFlat(\n  input: Uint8Array,\n  MP4Box: typeof import(\"mp4box\")\n): Promise<Uint8Array> {\n  return new Promise<Uint8Array>((resolve, reject) => {\n    const inFile = MP4Box.createFile();\n    const outFile = MP4Box.createFile();\n    outFile.init({ brands: [\"isom\", \"mp42\", \"avc1\", \"iso2\"] });\n\n    const inToOutTrack = new Map<number, number>();\n    const trackBaselineDts = new Map<number, number>();\n    let settled = false;\n\n    const fail = (err: Error) => {\n      if (settled) return;\n      settled = true;\n      reject(err);\n    };\n\n    inFile.onError = (_module: string, message: string) => {\n      fail(new Error(message));\n    };\n\n    inFile.onReady = (info) => {\n      if (!info.tracks.length) {\n        fail(new Error(\"no tracks in input\"));\n        return;\n      }\n      for (const track of info.tracks) {\n        // setExtractionOptions delivers samples in batches via\n        // onSamples; nbSamples controls batch size only, not total.\n        inFile.setExtractionOptions(track.id, null, { nbSamples: 1000 });\n      }\n      inFile.start();\n    };\n\n    inFile.onSamples = (id, _user, samples) => {\n      if (settled || samples.length === 0) return;\n\n      let outId = inToOutTrack.get(id);\n      let baseline = trackBaselineDts.get(id);\n      if (outId === undefined) {\n        // The sample carries the parsed input SampleEntry (avc1 /\n        // mp4a / …) whose child boxes are the actual codec config\n        // (avcC, esds, …).  `addTrack` creates a fresh empty\n        // SampleEntry of the requested `type`, so we have to pass\n        // the input's child boxes via `description_boxes`: they\n        // become direct children of the new SampleEntry, where\n        // decoders expect them.  Passing the whole input\n        // SampleEntry as `description` instead would nest it\n        // (`stsd > avc1 > avc1 > avcC`) and decoders would fail to\n        // find avcC and render black.\n        const firstSample = samples[0];\n        const sampleEntry =\n          firstSample.description as import(\"mp4box\").SampleEntry;\n        const inputTrack = trackInfoById(inFile, id);\n        outId = outFile.addTrack({\n          type: sampleEntry.type as import(\"mp4box\").SampleEntryFourCC,\n          timescale: firstSample.timescale,\n          width: inputTrack?.track_width,\n          height: inputTrack?.track_height,\n          language: inputTrack?.language,\n          hdlr: inputTrack?.video\n            ? \"vide\"\n            : inputTrack?.audio\n              ? \"soun\"\n              : undefined,\n          // `boxes` is typed as `Box[]` but `description_boxes`\n          // wants the narrower `BoxKind[]` union — at runtime they\n          // are the same concrete instances, just typed loosely.\n          description_boxes:\n            sampleEntry.boxes as unknown as import(\"mp4box\").IsoFileOptions[\"description_boxes\"],\n        });\n        baseline = firstSample.dts;\n        inToOutTrack.set(id, outId);\n        trackBaselineDts.set(id, baseline);\n      }\n      const dtsShift = baseline as number;\n\n      for (const sample of samples) {\n        if (!sample.data) continue;\n        outFile.addSample(outId, sample.data, {\n          duration: sample.duration,\n          dts: sample.dts - dtsShift,\n          cts: sample.cts - dtsShift,\n          is_sync: sample.is_sync,\n        });\n      }\n    };\n\n    let buf: import(\"mp4box\").MP4BoxBuffer;\n    try {\n      buf = MP4Box.MP4BoxBuffer.fromArrayBuffer(\n        input.buffer.slice(\n          input.byteOffset,\n          input.byteOffset + input.byteLength\n        ),\n        0\n      );\n    } catch (error) {\n      fail(error as Error);\n      return;\n    }\n\n    try {\n      inFile.appendBuffer(buf);\n      inFile.flush();\n    } catch (error) {\n      fail(error as Error);\n      return;\n    }\n\n    if (settled) return;\n    if (inToOutTrack.size === 0) {\n      fail(new Error(\"no samples extracted from input\"));\n      return;\n    }\n\n    let output: Uint8Array;\n    try {\n      const stream = outFile.getBuffer();\n      output = new Uint8Array(\n        stream.buffer.slice(0, stream.byteLength) as ArrayBuffer\n      );\n    } catch (error) {\n      fail(error as Error);\n      return;\n    }\n\n    settled = true;\n    resolve(output);\n  });\n}\n\nfunction trackInfoById(\n  file: import(\"mp4box\").ISOFile,\n  id: number\n): import(\"mp4box\").Track | undefined {\n  const info = file.getInfo();\n  return info.tracks.find((t) => t.id === id);\n}\n\nfunction concatUint8Arrays(parts: Uint8Array[]): Uint8Array {\n  if (parts.length === 1) return parts[0];\n  let total = 0;\n  for (const p of parts) total += p.byteLength;\n  const out = new Uint8Array(total);\n  let offset = 0;\n  for (const p of parts) {\n    out.set(p, offset);\n    offset += p.byteLength;\n  }\n  return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction resolveAgainst(target: string, base: string): string {\n  try {\n    return new URL(target, base).toString();\n  } catch {\n    return target;\n  }\n}\n\nfunction parseRetryAfter(header: string | null, fallbackMs: number): number {\n  if (!header) return fallbackMs;\n  const seconds = Number(header);\n  if (Number.isFinite(seconds) && seconds >= 0) {\n    return seconds * 1000;\n  }\n  const dateMs = Date.parse(header);\n  if (!Number.isNaN(dateMs)) {\n    return Math.max(0, dateMs - Date.now());\n  }\n  return fallbackMs;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n  return new Promise((resolve, reject) => {\n    if (signal?.aborted) {\n      reject(new DOMException(\"Aborted\", \"AbortError\"));\n      return;\n    }\n    const timer = setTimeout(() => {\n      signal?.removeEventListener(\"abort\", onAbort);\n      resolve();\n    }, ms);\n    const onAbort = () => {\n      clearTimeout(timer);\n      reject(new DOMException(\"Aborted\", \"AbortError\"));\n    };\n    signal?.addEventListener(\"abort\", onAbort, { once: true });\n  });\n}\n\nfunction triggerBrowserDownload(blob: Blob, filename: string): void {\n  const objectUrl = URL.createObjectURL(blob);\n  try {\n    const a = document.createElement(\"a\");\n    a.href = objectUrl;\n    a.download = filename;\n    a.style.display = \"none\";\n    document.body.appendChild(a);\n    a.click();\n    a.remove();\n  } finally {\n    URL.revokeObjectURL(objectUrl);\n  }\n}\n","/**\n * Per-Reactor recording client.  Owns the FIFO promise correlator for\n * inbound `clipReady` / `clipFailed` runtime messages and the\n * lifecycle hooks (subscribe on construct, reject pending requests\n * on disconnect).  Reactor exposes thin delegations for the common\n * case; this class is exported for advanced consumers and isolated\n * testing.\n */\n\nimport type { ReactorStatus } from \"../types\";\nimport {\n  ClipFailedPayloadSchema,\n  ClipReadyPayloadSchema,\n  RecordingError,\n  RuntimeRecordingMessageType,\n  clipFromPayload,\n  createPlayableManifestUrl,\n  downloadClipAsFile as downloadClipAsFileFn,\n  fetchPlaylist as fetchPlaylistFn,\n  type Clip,\n  type DownloadClipOptions,\n  type FetchPlaylistOptions,\n} from \"../utils/recording\";\n\n/** Default per-request timeout for `clipReady` / `clipFailed`. */\nexport const DEFAULT_CLIP_REQUEST_TIMEOUT_MS = 10_000;\n\n/** Slim adapter the {@link RecordingClient} requires from its host. */\nexport interface RecordingClientHost {\n  /**\n   * Subscribe to runtime-scoped messages on the data channel.\n   * Returns an unsubscribe function. Mirrors the\n   * `reactor.on(\"runtimeMessage\", …)` pattern.\n   */\n  onRuntimeMessage: (handler: (message: unknown) => void) => () => void;\n  /**\n   * Subscribe to status changes (so the client can reject pending\n   * requests on disconnect). Returns an unsubscribe function.\n   */\n  onStatusChanged: (handler: (status: ReactorStatus) => void) => () => void;\n  /**\n   * Send a runtime-scoped command on the data channel. The\n   * `RecordingClient` uses this to fire `requestClip` /\n   * `requestRecording`.\n   */\n  sendRuntimeCommand: (\n    command: string,\n    data: Record<string, unknown>\n  ) => Promise<void>;\n  /** Current connection status — guards `requestClip` before it goes on the wire. */\n  getStatus: () => ReactorStatus;\n  /**\n   * Coordinator base URL used to resolve `clip.playlistUrl` against.\n   *\n   * The runtime now emits a path-only ``playlist_url`` (e.g.\n   * ``/clips?session_id=…``) so the SDK is the absolute-origin owner\n   * for both local-dev (HttpRuntime on the SDK's ``apiUrl``) and\n   * production (Coordinator on the SDK's ``apiUrl``).  We always\n   * resolve against the ``Reactor``'s coordinator URL — there is no\n   * remote-vs-local divergence on the read path.\n   *\n   * For backwards-compat with older runtimes that emitted absolute\n   * URLs (scheme + host bound to the runtime's own listener,\n   * e.g. ``http://0.0.0.0:8080/clips?…``), the same URL is also used\n   * to rewrite the host onto the coordinator — see\n   * ``rewriteUrlHost`` in ``utils/recording.ts``.\n   */\n  getCoordinatorBaseUrl: () => string | undefined;\n}\n\n/** Internal — one entry per in-flight `requestClip` / `requestRecording`. */\ntype PendingClipRequest = {\n  resolve: (clip: Clip) => void;\n  reject: (error: RecordingError) => void;\n  timeoutHandle?: ReturnType<typeof setTimeout>;\n};\n\nexport class RecordingClient {\n  // FIFO queue: the runtime processes requests in receipt order and\n  // `clipFailed` carries no discriminator, so FIFO matching is the\n  // simplest correct correlator.\n  private pendingClipRequests: PendingClipRequest[] = [];\n\n  /** Timeout applied per request; configurable for tests. */\n  private readonly requestTimeoutMs: number;\n\n  /** Cleanup handles for the host event subscriptions. */\n  private readonly unsubscribers: Array<() => void> = [];\n\n  constructor(\n    private readonly host: RecordingClientHost,\n    options: { requestTimeoutMs?: number } = {}\n  ) {\n    this.requestTimeoutMs =\n      options.requestTimeoutMs ?? DEFAULT_CLIP_REQUEST_TIMEOUT_MS;\n\n    this.unsubscribers.push(\n      this.host.onRuntimeMessage((message) => this.onRuntimeMessage(message))\n    );\n    this.unsubscribers.push(\n      this.host.onStatusChanged((status) => this.onStatusChanged(status))\n    );\n  }\n\n  /**\n   * Request a clip covering the last `durationSeconds` of the live\n   * session.  See {@link Reactor.requestClip}.\n   */\n  async requestClip(durationSeconds: number): Promise<Clip> {\n    if (\n      typeof durationSeconds !== \"number\" ||\n      !Number.isFinite(durationSeconds) ||\n      durationSeconds <= 0\n    ) {\n      throw new RecordingError(\n        \"INVALID_DURATION\",\n        \"durationSeconds must be a positive finite number\"\n      );\n    }\n    const status = this.host.getStatus();\n    if (status !== \"ready\") {\n      throw new RecordingError(\n        \"DISCONNECTED\",\n        `Cannot request clip while status is \"${status}\"`\n      );\n    }\n    return this.dispatchClipRequest(RuntimeRecordingMessageType.REQUEST_CLIP, {\n      duration_seconds: durationSeconds,\n    });\n  }\n\n  /**\n   * Request a clip covering the entire session up to \"now\".  See\n   * {@link Reactor.requestRecording}.\n   */\n  async requestRecording(): Promise<Clip> {\n    const status = this.host.getStatus();\n    if (status !== \"ready\") {\n      throw new RecordingError(\n        \"DISCONNECTED\",\n        `Cannot request recording while status is \"${status}\"`\n      );\n    }\n    return this.dispatchClipRequest(\n      RuntimeRecordingMessageType.REQUEST_RECORDING,\n      {}\n    );\n  }\n\n  /**\n   * Poll `/clips` and return the raw manifest body.  Same polling\n   * semantics as the bare {@link fetchPlaylist} helper; the caller is\n   * responsible for supplying the Coordinator JWT via `options.jwt`\n   * (origin-scoped to `clip.playlistUrl`).  In local mode against the\n   * HttpRuntime, `options.jwt` should be omitted.\n   */\n  async fetchPlaylist(\n    clip: Clip,\n    options: FetchPlaylistOptions = {}\n  ): Promise<string> {\n    return fetchPlaylistFn(clip.playlistUrl, {\n      predictedReadyAtMs: clip.predictedReadyAtMs,\n      ...options,\n    });\n  }\n\n  /**\n   * Fetch the manifest and return a `blob:` URL suitable for\n   * `<video src>` / `hls.js`.  The player reads the manifest from\n   * browser memory; chunk URLs inside are presigned S3 GETs.\n   *\n   * Caller owns the returned URL — revoke it via `URL.revokeObjectURL`\n   * when playback tears down.  Pass `options.jwt` for production\n   * `/clips` (the Coordinator hop) — see {@link FetchPlaylistOptions}.\n   */\n  async getPlayableManifestUrl(\n    clip: Clip,\n    options: FetchPlaylistOptions = {}\n  ): Promise<string> {\n    const body = await this.fetchPlaylist(clip, options);\n    // clip.playlistUrl is the absolute base needed to resolve any\n    // path-only chunk URLs in the manifest body (local HttpRuntime\n    // emits relative chunks; production S3 presigned URLs are\n    // already absolute and pass through untouched).\n    return createPlayableManifestUrl(body, clip.playlistUrl);\n  }\n\n  /**\n   * Thin delegation to {@link downloadClipAsFile}.  Caller must pass\n   * `options.jwt` in production — see {@link DownloadClipOptions}.\n   */\n  async downloadClipAsFile(\n    clip: Clip,\n    filename: string | null = \"reactor-clip.mp4\",\n    options?: DownloadClipOptions\n  ): Promise<Blob> {\n    return downloadClipAsFileFn(clip, filename, options);\n  }\n\n  /**\n   * Drop every pending request and unsubscribe from host events.\n   * Safe to call multiple times; further `requestClip` /\n   * `requestRecording` calls after `destroy()` will hang waiting for\n   * a response that will never arrive — host should not call into\n   * the client after destroy.\n   */\n  destroy(): void {\n    this.rejectPending(\"RecordingClient destroyed\");\n    while (this.unsubscribers.length > 0) {\n      const off = this.unsubscribers.pop();\n      try {\n        off?.();\n      } catch {\n        // Best-effort cleanup — ignore handler removal errors.\n      }\n    }\n  }\n\n  /**\n   * Register a pending FIFO entry, send the runtime message via\n   * the host, and resolve when the next `clipReady` / `clipFailed`\n   * arrives.\n   */\n  private dispatchClipRequest(\n    messageType:\n      | typeof RuntimeRecordingMessageType.REQUEST_CLIP\n      | typeof RuntimeRecordingMessageType.REQUEST_RECORDING,\n    payload: Record<string, unknown>\n  ): Promise<Clip> {\n    return new Promise<Clip>((resolve, reject) => {\n      const pending: PendingClipRequest = { resolve, reject };\n      pending.timeoutHandle = setTimeout(() => {\n        const idx = this.pendingClipRequests.indexOf(pending);\n        if (idx === -1) return;\n        this.pendingClipRequests.splice(idx, 1);\n        reject(\n          new RecordingError(\n            \"REQUEST_TIMEOUT\",\n            `No clipReady/clipFailed within ${this.requestTimeoutMs}ms`\n          )\n        );\n      }, this.requestTimeoutMs);\n\n      this.pendingClipRequests.push(pending);\n\n      try {\n        void this.host.sendRuntimeCommand(messageType, payload);\n      } catch (error) {\n        const idx = this.pendingClipRequests.indexOf(pending);\n        if (idx !== -1) this.pendingClipRequests.splice(idx, 1);\n        if (pending.timeoutHandle) clearTimeout(pending.timeoutHandle);\n        reject(\n          new RecordingError(\n            \"INTERNAL_ERROR\",\n            `Failed to send ${messageType}: ${(error as Error).message}`\n          )\n        );\n      }\n    });\n  }\n\n  /**\n   * Match inbound `clipReady` / `clipFailed` to the next pending\n   * request FIFO; ignore every other runtime message type.\n   */\n  private onRuntimeMessage(message: unknown): void {\n    if (!message || typeof message !== \"object\") return;\n    const msg = message as { type?: unknown; data?: unknown };\n    const type = msg.type;\n    if (\n      type !== RuntimeRecordingMessageType.CLIP_READY &&\n      type !== RuntimeRecordingMessageType.CLIP_FAILED\n    ) {\n      return;\n    }\n    const pending = this.pendingClipRequests.shift();\n    if (!pending) {\n      console.warn(\"[Reactor] Received\", type, \"with no pending request\");\n      return;\n    }\n    if (pending.timeoutHandle) clearTimeout(pending.timeoutHandle);\n\n    if (type === RuntimeRecordingMessageType.CLIP_READY) {\n      const parsed = ClipReadyPayloadSchema.safeParse(msg.data);\n      if (!parsed.success) {\n        pending.reject(\n          new RecordingError(\n            \"INTERNAL_ERROR\",\n            `Malformed clipReady payload: ${parsed.error.message}`\n          )\n        );\n        return;\n      }\n      pending.resolve(\n        clipFromPayload(parsed.data, {\n          coordinatorBaseUrl: this.host.getCoordinatorBaseUrl(),\n        })\n      );\n      return;\n    }\n\n    const parsedFail = ClipFailedPayloadSchema.safeParse(msg.data ?? {});\n    const reason = parsedFail.success ? parsedFail.data.reason : \"unknown\";\n    pending.reject(\n      new RecordingError(\n        reason === \"recorder disabled\" ||\n          reason === \"recorder disabled or encoder crashed\"\n          ? \"RECORDER_DISABLED\"\n          : \"INTERNAL_ERROR\",\n        reason\n      )\n    );\n  }\n\n  private onStatusChanged(status: ReactorStatus): void {\n    if (status === \"disconnected\") {\n      this.rejectPending(\"Reactor disconnected\");\n    }\n  }\n\n  /** Reject every in-flight request with a typed disconnect error. */\n  private rejectPending(reason: string): void {\n    if (this.pendingClipRequests.length === 0) return;\n    const pending = this.pendingClipRequests;\n    this.pendingClipRequests = [];\n    for (const entry of pending) {\n      if (entry.timeoutHandle) clearTimeout(entry.timeoutHandle);\n      entry.reject(new RecordingError(\"DISCONNECTED\", reason));\n    }\n  }\n}\n","\"use client\";\n\nimport {\n  ReactNode,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport {\n  createReactorStore,\n  initReactorStore,\n  ReactorContext,\n  ReactorStore,\n  ReactorStoreApi,\n  type ReactorInitializationProps,\n} from \"../core/store\";\nimport { useStore } from \"zustand\";\nimport type { ConnectOptions } from \"../types\";\nimport type { JwtResolver, JwtSource } from \"../core/auth\";\n\n/**\n * Options for the React provider's connect behavior.\n * Extends the core ConnectOptions with autoConnect for the React lifecycle.\n */\nexport interface ReactorConnectOptions extends ConnectOptions {\n  /** Whether to automatically connect when the provider mounts. Default: false. */\n  autoConnect?: boolean;\n}\n\n// Provider props\n//\n// `children` is declared optional even though a render-less Provider\n// makes no sense at runtime — React always supplies the slot via JSX\n// (`<ReactorProvider>…</ReactorProvider>`) or the third positional\n// argument to `createElement`, but @types/react's `createElement`\n// overloads gate the *second* argument on the props type alone:\n// when `children` is required, callers can't use the third-arg form\n// even though it works fine at runtime. Relaxing to optional lets\n// generated codegen output (and any other programmatic caller) pass\n// children positionally without falling back to an\n// `eslint-disable-next-line react/no-children-prop` workaround in\n// downstream consumer projects.\ninterface ReactorProviderProps extends Omit<\n  ReactorInitializationProps,\n  \"jwtToken\"\n> {\n  connectOptions?: ReactorConnectOptions;\n  /**\n   * Static JWT token. Use when you hold a long-lived SDK JWT (e.g.\n   * minted via `/tokens`). For short-lived tokens use {@link getJwt}.\n   */\n  jwtToken?: string;\n  /**\n   * Lazy JWT resolver invoked immediately before each Coordinator\n   * HTTP request. Preferred for short-lived tokens. Wins over\n   * {@link jwtToken} when both are provided.\n   */\n  getJwt?: JwtResolver;\n  children?: ReactNode;\n}\n\n// tsx component\nexport function ReactorProvider({\n  children,\n  connectOptions,\n  jwtToken,\n  getJwt,\n  ...props\n}: ReactorProviderProps) {\n  // Auto-stabilize the `getJwt` resolver via ref. Without this, an\n  // inline `getJwt={async () => …}` on every parent render gives the\n  // provider a new function identity, the effect below treats it\n  // as \"auth source changed\", tears the store down, and disconnects\n  // the live session — a footgun we'd otherwise be asking every\n  // consumer to defuse with `useCallback`. Same idiom React's\n  // `useEffectEvent` codifies, and what we already use internally\n  // in `useClipDownload` / `ClipPlayer` to absorb resolver identity\n  // churn there. The ref is read at request time so the latest\n  // resolver — and any state it closes over (Clerk session, account\n  // ID, etc.) — is on the wire for every Coordinator HTTP hop.\n  const getJwtRef = useRef<JwtResolver | undefined>(getJwt);\n  useEffect(() => {\n    getJwtRef.current = getJwt;\n  });\n  // Only re-memoize when the resolver transitions between defined\n  // and undefined (sign-in / sign-out). Pure identity changes are\n  // absorbed by `getJwtRef`.\n  const hasGetJwt = getJwt !== undefined;\n  const stableGetJwt = useMemo<JwtResolver | undefined>(() => {\n    if (!hasGetJwt) return undefined;\n    return async () => {\n      const r = getJwtRef.current;\n      return r ? await r() : \"\";\n    };\n  }, [hasGetJwt]);\n\n  // Static-string `jwtToken` keeps its legacy semantics: changing\n  // the string identity means \"different auth source\" (e.g. tenant\n  // switch) and intentionally tears the session down. Consumers who\n  // need to rotate a short-lived token should use `getJwt` — which\n  // is now both the recommended path and the foolproof one.\n  const jwtSource: JwtSource | undefined = stableGetJwt ?? jwtToken;\n\n  // Stable Reactor instance\n  const storeRef = useRef<ReactorStoreApi | undefined>(undefined);\n  const firstRender = useRef(true);\n  // State to trigger re-renders when store changes\n  const [_storeVersion, setStoreVersion] = useState(0);\n\n  // Destructure connectOptions with defaults\n  const { autoConnect = false, ...pollingOptions } = connectOptions ?? {};\n\n  if (storeRef.current === undefined) {\n    console.debug(\"[ReactorProvider] Creating new reactor store\");\n    // We create the store without autoconnecting, to avoid duplicate connections.\n    // We actually connect when the component is mounted, to be on sync with the react component lifecycle.\n    storeRef.current = createReactorStore(\n      initReactorStore({\n        ...props,\n        jwtToken: jwtSource,\n        connectOptions: pollingOptions,\n      })\n    );\n    console.debug(\"[ReactorProvider] Reactor store created successfully\");\n  }\n\n  const { apiUrl, modelName, local } = props;\n  const maxAttempts = pollingOptions.maxAttempts;\n  const { autoResumeTracks } = pollingOptions;\n\n  // Keep connectOptions in the store in sync when provider props change without\n  // tearing the store down (e.g. toggling autoResumeTracks while disconnected).\n  useEffect(() => {\n    storeRef.current?.setState({ connectOptions: pollingOptions });\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [autoResumeTracks, maxAttempts]);\n\n  // On page unload, perform a non-recoverable disconnect. The session's\n  // creator tears the session down (DELETE), matching the pre-multi-connection\n  // behaviour; a client that adopted an existing session (connect({ sessionId }))\n  // only closes its own transport and leaves the session alive for its owner.\n  // This gating lives in Reactor.disconnect() via the `createdSession` flag, so\n  // passing `false` here is safe for both roles. We can't await during unload,\n  // so the DELETE is best-effort (same as before).\n  useEffect(() => {\n    const handleBeforeUnload = () => {\n      storeRef.current?.getState().internal.reactor.disconnect(false);\n    };\n\n    window.addEventListener(\"beforeunload\", handleBeforeUnload);\n    return () => window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n  }, []);\n\n  useEffect(() => {\n    if (firstRender.current) {\n      firstRender.current = false;\n\n      // We know as a fact that the store is not undefined at this point\n      const current = storeRef.current!;\n      if (\n        autoConnect &&\n        current.getState().status === \"disconnected\" &&\n        jwtSource\n      ) {\n        console.debug(\n          \"[ReactorProvider] Starting autoconnect in first render...\"\n        );\n        current\n          .getState()\n          .connect(jwtSource, pollingOptions)\n          .then(() => {\n            console.debug(\n              \"[ReactorProvider] Autoconnect successful in first render\"\n            );\n          })\n          .catch((error) => {\n            console.error(\n              \"[ReactorProvider] Failed to autoconnect in first render:\",\n              error\n            );\n          });\n      }\n      return () => {\n        console.debug(\n          \"[ReactorProvider] Disconnecting in cleanup for first render\"\n        );\n        current\n          .getState()\n          .disconnect()\n          .then(() => {\n            console.debug(\n              \"[ReactorProvider] Disconnect completed successfully in cleanup for first render\"\n            );\n          })\n          .catch((error) => {\n            console.error(\n              \"[ReactorProvider] Failed to disconnect in cleanup for first render:\",\n              error\n            );\n          });\n      };\n    }\n\n    console.debug(\"[ReactorProvider] Updating reactor store\");\n    storeRef.current = createReactorStore(\n      initReactorStore({\n        apiUrl,\n        modelName,\n        local,\n        jwtToken: jwtSource,\n        connectOptions: pollingOptions,\n      } satisfies ReactorInitializationProps)\n    );\n\n    // Store current reference to the store in the return\n    const current = storeRef.current!;\n\n    // Increment version to trigger re-render and propagate new store to Provider\n    setStoreVersion((v) => v + 1);\n    console.debug(\n      \"[ReactorProvider] Reactor store updated successfully, and increased version\"\n    );\n\n    if (\n      autoConnect &&\n      current.getState().status === \"disconnected\" &&\n      jwtSource\n    ) {\n      console.debug(\"[ReactorProvider] Starting autoconnect...\");\n      current\n        .getState()\n        .connect(jwtSource, pollingOptions)\n        .then(() => {\n          console.debug(\"[ReactorProvider] Autoconnect successful\");\n        })\n        .catch((error) => {\n          console.error(\"[ReactorProvider] Failed to autoconnect:\", error);\n        });\n    }\n\n    return () => {\n      console.debug(\"[ReactorProvider] Disconnecting in cleanup\");\n      current\n        .getState()\n        .disconnect()\n        .then(() => {\n          console.debug(\n            \"[ReactorProvider] Disconnect completed successfully in cleanup\"\n          );\n        })\n        .catch((error) => {\n          console.error(\"[ReactorProvider] Failed to disconnect:\", error);\n        });\n    };\n    // `autoConnect` is a first-mount-only signal and `maxAttempts`\n    // is initial-handshake polling config; neither identifies the\n    // session, so they're intentionally out of the deps to stop\n    // mid-mount prop changes from tearing the live session down.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [apiUrl, modelName, local, jwtSource]);\n\n  return (\n    <ReactorContext.Provider value={storeRef.current}>\n      {children}\n    </ReactorContext.Provider>\n  );\n}\n\nexport function useReactorStore<T = ReactorStore>(\n  selector: (state: ReactorStore) => T\n): T {\n  const ctx = useContext(ReactorContext);\n  if (!ctx) {\n    throw new Error(\"useReactor must be used within a ReactorProvider\");\n  }\n\n  return useStore(ctx, selector);\n}\n","import { StoreApi } from \"zustand\";\nimport type {\n  ReactorStatus,\n  ReactorError,\n  MessageScope,\n  ConnectOptions,\n} from \"../types\";\nimport { type JwtSource } from \"./auth\";\nimport { Reactor, type Options as ReactorOptions } from \"./Reactor\";\nimport { FileRef } from \"./FileRef\";\nimport type { Clip, DownloadClipOptions } from \"../utils/recording\";\nimport { create } from \"zustand/react\";\nimport { createContext } from \"react\";\n\nexport type ReactorStoreApi = ReturnType<typeof createReactorStore>;\n\nexport interface ReactorState {\n  status: ReactorStatus;\n  /**\n   * Media tracks received from the model, keyed by track name.\n   *\n   * Each entry maps a recvonly track name (e.g. `\"main_video\"`,\n   * `\"main_audio\"`) to the live `MediaStreamTrack` delivered by the model.\n   */\n  tracks: Record<string, MediaStreamTrack>;\n  lastError?: ReactorError;\n  sessionId?: string;\n  sessionExpiration?: number;\n  /** Token source for Coordinator HTTP calls — see {@link JwtSource}. */\n  jwtToken?: JwtSource;\n  /**\n   * Default connect options set by the provider. Applied as base values\n   * whenever `connect()` is called without explicit options, so that\n   * provider-level settings like `autoResumeTracks` are honoured for\n   * both autoConnect and manual connect calls.\n   */\n  connectOptions?: ConnectOptions;\n}\n\nexport interface ReactorActions {\n  sendCommand(command: string, data: any, scope?: MessageScope): Promise<void>;\n  /**\n   * Connect the underlying {@link Reactor}. `options` is forwarded verbatim —\n   * pass `options.sessionId` to attach to an existing session instead of\n   * creating one (see {@link ConnectOptions}).\n   */\n  connect(jwtToken?: JwtSource, options?: ConnectOptions): Promise<void>;\n  disconnect(recoverable?: boolean): Promise<void>;\n  publish(name: string, track: MediaStreamTrack): Promise<void>;\n  unpublish(name: string): Promise<void>;\n  reconnect(options?: ConnectOptions): Promise<void>;\n  uploadFile(file: File | Blob, options?: { name?: string }): Promise<FileRef>;\n  /**\n   * Capture the trailing `durationSeconds` of the live session as a\n   * clip.  Top-level alias for `internal.reactor.requestClip(…)` —\n   * lifted onto the store so consumers don't have to reach through\n   * `internal.reactor` for the common case.  See\n   * {@link Reactor.requestClip}.\n   */\n  requestClip(durationSeconds: number): Promise<Clip>;\n  /**\n   * Capture the entire session up to \"now\" as a clip.  Top-level\n   * alias for `internal.reactor.requestRecording()`.  See\n   * {@link Reactor.requestRecording}.\n   */\n  requestRecording(): Promise<Clip>;\n  /**\n   * Stream the chunks referenced by `clip.playlistUrl` and trigger a\n   * native browser download of the assembled MP4.  Top-level alias\n   * for `internal.reactor.downloadClipAsFile(…)`.  See\n   * {@link Reactor.downloadClipAsFile}.\n   */\n  downloadClipAsFile(\n    clip: Clip,\n    filename?: string | null,\n    options?: DownloadClipOptions\n  ): Promise<Blob>;\n}\n\n// Internal state not exposed to components\ninterface ReactorInternalState {\n  reactor: Reactor;\n}\n\nexport const ReactorContext = createContext<ReactorStoreApi | undefined>(\n  undefined\n);\n\nexport type ReactorStore = ReactorState &\n  ReactorActions & {\n    internal: ReactorInternalState;\n  };\n\n// We introduce two methods to perform authentication:\n//  - putting the auth information inside of the ReactorProvider props, and then calling connect() without arguments.\n//  - not putting anything in the props, and then calling connect() passing as arguments the auth information.\n// When in the first case, the auth information is saved in the STATE. Then, when you call connect() without arguments,\n// the actual auth information is fetched from that STATE.\n// In the second case, you pass the auth information directly into the function in the Reactor core.\nexport const defaultInitState: ReactorState = {\n  status: \"disconnected\",\n  tracks: {},\n  lastError: undefined,\n  sessionExpiration: undefined,\n  jwtToken: undefined,\n  sessionId: undefined,\n  connectOptions: undefined,\n};\n\nexport interface ReactorInitializationProps extends ReactorOptions {\n  /** Token source for the underlying {@link Reactor} — see {@link JwtSource}. */\n  jwtToken?: JwtSource;\n  /** Default connect options applied when `connect()` is called without explicit options. */\n  connectOptions?: ConnectOptions;\n}\n\nexport const initReactorStore = (\n  props: ReactorInitializationProps\n): ReactorState & ReactorInitializationProps => {\n  return {\n    ...defaultInitState,\n    // These are only used for dev initialization, not exposed in the store\n    ...props,\n  };\n};\n\nexport const createReactorStore = (\n  initProps: ReactorInitializationProps,\n  publicState: ReactorState = defaultInitState\n): StoreApi<ReactorStore> => {\n  console.debug(\"[ReactorStore] Creating store\", {\n    apiUrl: initProps.apiUrl,\n    jwtToken: initProps.jwtToken,\n    initialState: publicState,\n  });\n\n  return create<ReactorStore>()((set, get) => {\n    const reactor = new Reactor(initProps);\n\n    console.debug(\"[ReactorStore] Setting up event listeners\");\n\n    reactor.on(\"statusChanged\", (newStatus: ReactorStatus) => {\n      console.debug(\"[ReactorStore] Status changed\", {\n        oldStatus: get().status,\n        newStatus,\n      });\n      if (newStatus === \"disconnected\") {\n        set({ status: newStatus, tracks: {} });\n      } else {\n        set({ status: newStatus });\n      }\n    });\n\n    reactor.on(\n      \"sessionExpirationChanged\",\n      (newSessionExpiration: number | undefined) => {\n        console.debug(\"[ReactorStore] Session expiration changed\", {\n          oldSessionExpiration: get().sessionExpiration,\n          newSessionExpiration: newSessionExpiration,\n        });\n        set({ sessionExpiration: newSessionExpiration });\n      }\n    );\n\n    reactor.on(\"trackReceived\", (name: string, track: MediaStreamTrack) => {\n      console.debug(\"[ReactorStore] Track received\", {\n        name,\n        kind: track.kind,\n        id: track.id,\n      });\n      set({ tracks: { ...get().tracks, [name]: track } });\n    });\n\n    reactor.on(\"error\", (error: ReactorError) => {\n      console.debug(\"[ReactorStore] Error occurred\", error);\n      set({ lastError: error });\n    });\n\n    reactor.on(\"sessionIdChanged\", (newSessionId: string | undefined) => {\n      console.debug(\"[ReactorStore] Session ID changed\", {\n        oldSessionId: get().sessionId,\n        newSessionId: newSessionId,\n      });\n      set({ sessionId: newSessionId });\n    });\n\n    return {\n      ...publicState,\n      jwtToken: initProps.jwtToken,\n      connectOptions: initProps.connectOptions,\n      internal: { reactor },\n\n      // actions\n      onMessage: (handler: (message: any) => void) => {\n        console.debug(\"[ReactorStore] Registering message handler\");\n\n        get().internal.reactor.on(\"message\", handler);\n\n        return () => {\n          console.debug(\"[ReactorStore] Cleaning up message handler\");\n          get().internal.reactor.off(\"message\", handler);\n        };\n      },\n      sendCommand: async (command: string, data: any, scope?: MessageScope) => {\n        console.debug(\"[ReactorStore] Sending command\", {\n          command,\n          data,\n          scope,\n        });\n        try {\n          await get().internal.reactor.sendCommand(command, data, scope);\n          console.debug(\"[ReactorStore] Command sent successfully\");\n        } catch (error) {\n          console.error(\"[ReactorStore] Failed to send command:\", error);\n          throw error;\n        }\n      },\n      connect: async (jwtToken?: JwtSource, options?: ConnectOptions) => {\n        if (jwtToken === undefined) {\n          // If no JWT Token, it might have been passed in the constructor props. So read from it.\n          jwtToken = get().jwtToken;\n        }\n\n        // Merge provider-level defaults with call-time overrides (call-time wins).\n        const resolvedOptions: ConnectOptions = {\n          ...get().connectOptions,\n          ...options,\n        };\n\n        console.debug(\"[ReactorStore] Connect called.\");\n\n        try {\n          await get().internal.reactor.connect(jwtToken, resolvedOptions);\n          console.debug(\"[ReactorStore] Connect completed successfully\");\n        } catch (error) {\n          console.error(\"[ReactorStore] Connect failed:\", error);\n          throw error;\n        }\n      },\n      disconnect: async (recoverable: boolean = false) => {\n        console.debug(\"[ReactorStore] Disconnect called\", {\n          currentStatus: get().status,\n        });\n\n        try {\n          await get().internal.reactor.disconnect(recoverable);\n          console.debug(\"[ReactorStore] Disconnect completed successfully\");\n        } catch (error) {\n          console.error(\"[ReactorStore] Disconnect failed:\", error);\n          throw error;\n        }\n      },\n      publish: async (name: string, track: MediaStreamTrack) => {\n        console.debug(`[ReactorStore] Publishing track \"${name}\"`);\n\n        try {\n          await get().internal.reactor.publishTrack(name, track);\n          console.debug(\n            `[ReactorStore] Track \"${name}\" published successfully`\n          );\n        } catch (error) {\n          console.error(\n            `[ReactorStore] Failed to publish track \"${name}\":`,\n            error\n          );\n          throw error;\n        }\n      },\n      unpublish: async (name: string) => {\n        console.debug(`[ReactorStore] Unpublishing track \"${name}\"`);\n\n        try {\n          await get().internal.reactor.unpublishTrack(name);\n          console.debug(\n            `[ReactorStore] Track \"${name}\" unpublished successfully`\n          );\n        } catch (error) {\n          console.error(\n            `[ReactorStore] Failed to unpublish track \"${name}\":`,\n            error\n          );\n          throw error;\n        }\n      },\n      reconnect: async (options?: ConnectOptions) => {\n        console.debug(\"[ReactorStore] Reconnecting\");\n        try {\n          await get().internal.reactor.reconnect(options);\n          console.debug(\"[ReactorStore] Reconnect completed successfully\");\n        } catch (error) {\n          console.error(\"[ReactorStore] Failed to reconnect:\", error);\n          throw error;\n        }\n      },\n      uploadFile: async (file: File | Blob, options?: { name?: string }) => {\n        console.debug(\"[ReactorStore] Uploading file\");\n        try {\n          const result = await get().internal.reactor.uploadFile(file, options);\n          console.debug(\"[ReactorStore] File uploaded successfully\", result);\n          return result;\n        } catch (error) {\n          console.error(\"[ReactorStore] Failed to upload file:\", error);\n          throw error;\n        }\n      },\n      requestClip: (durationSeconds: number) =>\n        get().internal.reactor.requestClip(durationSeconds),\n      requestRecording: () => get().internal.reactor.requestRecording(),\n      downloadClipAsFile: (\n        clip: Clip,\n        filename?: string | null,\n        options?: DownloadClipOptions\n      ) => get().internal.reactor.downloadClipAsFile(clip, filename, options),\n    };\n  });\n};\n","import { useReactorStore } from \"./ReactorProvider\";\nimport type { ReactorStore } from \"../core/store\";\nimport type { ConnectionStats } from \"../types\";\nimport { useShallow } from \"zustand/shallow\";\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Generic hook for accessing selected parts of the Reactor store.\n *\n * @param selector - A function that selects part of the store state.\n * @returns The selected slice from the store.\n */\nexport function useReactor<T>(selector: (state: ReactorStore) => T): T {\n  return useReactorStore(useShallow(selector));\n}\n\n/**\n * Hook for receiving model application messages.\n *\n * Only fires for messages sent by the model via `get_ctx().send()`.\n * Internal platform-level messages (e.g. capabilities) are NOT delivered here.\n *\n * @param handler - Callback invoked with each application message payload.\n */\nexport function useReactorMessage(handler: (message: any) => void): void {\n  const reactor = useReactor((state) => state.internal.reactor);\n  const handlerRef = useRef(handler);\n\n  useEffect(() => {\n    handlerRef.current = handler;\n  }, [handler]);\n\n  useEffect(() => {\n    const stableHandler = (message: any) => {\n      handlerRef.current(message);\n    };\n\n    reactor.on(\"message\", stableHandler);\n\n    return () => {\n      reactor.off(\"message\", stableHandler);\n    };\n  }, [reactor]);\n}\n\n/**\n * Hook for receiving internal platform-level (runtime) messages.\n *\n * This is intended for advanced use cases that need access to the runtime\n * control layer, such as capabilities negotiation. Model application messages\n * sent via `get_ctx().send()` are NOT delivered through this hook — use\n * {@link useReactorMessage} for those.\n *\n * @param handler - Callback invoked with each runtime message payload.\n */\nexport function useReactorInternalMessage(\n  handler: (message: any) => void\n): void {\n  const reactor = useReactor((state) => state.internal.reactor);\n  const handlerRef = useRef(handler);\n\n  useEffect(() => {\n    handlerRef.current = handler;\n  }, [handler]);\n\n  useEffect(() => {\n    const stableHandler = (message: any) => {\n      handlerRef.current(message);\n    };\n\n    reactor.on(\"runtimeMessage\", stableHandler);\n\n    return () => {\n      reactor.off(\"runtimeMessage\", stableHandler);\n    };\n  }, [reactor]);\n}\n\n/**\n * Hook that returns the current connection stats (RTT, etc.).\n * Updates every ~2s while connected. Returns undefined when disconnected.\n */\nexport function useStats(): ConnectionStats | undefined {\n  const reactor = useReactor((state) => state.internal.reactor);\n  const [stats, setStats] = useState<ConnectionStats | undefined>(undefined);\n\n  useEffect(() => {\n    const handler = (newStats: ConnectionStats) => {\n      setStats(newStats);\n    };\n\n    reactor.on(\"statsUpdate\", handler);\n\n    return () => {\n      reactor.off(\"statsUpdate\", handler);\n      setStats(undefined);\n    };\n  }, [reactor]);\n\n  return stats;\n}\n","\"use client\";\n\nimport { useReactor } from \"./hooks\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport React from \"react\";\n\nexport interface ReactorViewProps {\n  /**\n   * The name of the recvonly track to render.\n   * Must match a track name declared in the server capabilities.\n   * Check the model's documentation for available track names.\n   * Defaults to `\"main_video\"`.\n   */\n  track?: string;\n  /**\n   * Optional name of a recvonly audio track to play alongside the video\n   * (e.g. `\"main_audio\"`). The audio is mixed into the same `<video>` element.\n   * Check the model's documentation for available track names.\n   */\n  audioTrack?: string;\n  width?: number;\n  height?: number;\n  className?: string;\n  style?: React.CSSProperties;\n  videoObjectFit?: NonNullable<\n    React.VideoHTMLAttributes<HTMLVideoElement>[\"style\"]\n  >[\"objectFit\"];\n  /**\n   * Controls whether inbound audio plays.  Default is `true`\n   * (muted) when no `audioTrack` is set — keeps the underlying\n   * `<video>` element within browser autoplay policies; `false`\n   * when an `audioTrack` is set.  Pass an explicit value to\n   * override either default.\n   */\n  muted?: boolean;\n}\n\nexport function ReactorView({\n  track = \"main_video\",\n  audioTrack,\n  width,\n  height,\n  className,\n  style,\n  videoObjectFit = \"contain\",\n  muted = audioTrack === undefined,\n}: ReactorViewProps) {\n  const { videoMediaTrack, audioMediaTrack } = useReactor((state) => ({\n    videoMediaTrack: state.tracks[track] ?? null,\n    audioMediaTrack: audioTrack ? (state.tracks[audioTrack] ?? null) : null,\n  }));\n\n  const videoRef = useRef<HTMLVideoElement>(null);\n\n  const mediaStream = useMemo(() => {\n    const tracks: MediaStreamTrack[] = [];\n    if (videoMediaTrack) tracks.push(videoMediaTrack);\n    if (audioMediaTrack) tracks.push(audioMediaTrack);\n    if (tracks.length === 0) return null;\n    return new MediaStream(tracks);\n  }, [videoMediaTrack, audioMediaTrack]);\n\n  useEffect(() => {\n    console.debug(\"[ReactorView] Media track effect triggered\", {\n      track,\n      hasVideoElement: !!videoRef.current,\n      hasVideoTrack: !!videoMediaTrack,\n      hasAudioTrack: !!audioMediaTrack,\n    });\n\n    const el = videoRef.current;\n    if (!el || !mediaStream) {\n      console.debug(\"[ReactorView] No tracks or element to attach\");\n      return;\n    }\n\n    // (Re-)bind the stream to the element and start playback. Setting\n    // `srcObject = null` first forces the element to re-initialise its decode\n    // pipeline so it re-requests a keyframe — needed when re-attaching a track\n    // that started rendering black (see the unmute handler below).\n    const attach = (reset: boolean) => {\n      try {\n        if (reset) el.srcObject = null;\n        el.srcObject = mediaStream;\n        el.play().catch((e) => {\n          console.warn(\"[ReactorView] Auto-play failed:\", e);\n        });\n      } catch (error) {\n        console.error(\"[ReactorView] Failed to attach media stream:\", error);\n      }\n    };\n\n    console.debug(\"[ReactorView] Attaching media stream to element\");\n    attach(false);\n\n    // A recvonly track negotiated while the server has it paused arrives\n    // `muted` (no RTP). When the server starts sending — e.g. on auto-resume's\n    // `resume_track` — the track fires `unmute`, but the element was attached\n    // and play()'d while the track was empty, so some browsers keep showing\n    // black on the existing srcObject until a fresh attach. Without this, the\n    // only way to render an auto-resumed track was a manual pause/resume (which\n    // renegotiated a brand-new track for us to attach). Re-attach on `unmute`\n    // so auto-resumed tracks render on their own.\n    const onUnmute = () => {\n      console.debug(\n        \"[ReactorView] Track unmuted — re-attaching to render incoming media\"\n      );\n      attach(true);\n    };\n    const tracks = mediaStream.getTracks();\n    for (const t of tracks) t.addEventListener(\"unmute\", onUnmute);\n\n    return () => {\n      console.debug(\"[ReactorView] Detaching media stream from element\");\n      for (const t of tracks) t.removeEventListener(\"unmute\", onUnmute);\n      if (videoRef.current) {\n        videoRef.current.srcObject = null;\n      }\n    };\n  }, [mediaStream]);\n\n  return (\n    <div\n      style={{\n        position: \"relative\",\n        background: \"#000\",\n        ...(width && { width }),\n        ...(height && { height }),\n        ...style,\n      }}\n      className={className}\n    >\n      <video\n        ref={videoRef}\n        style={{\n          width: \"100%\",\n          height: \"100%\",\n          objectFit: videoObjectFit,\n          display: videoMediaTrack ? \"block\" : \"none\",\n        }}\n        muted={muted}\n        playsInline\n      />\n    </div>\n  );\n}\n","\"use client\";\n\nimport { useReactor, useReactorInternalMessage } from \"./hooks\";\nimport { FileRef } from \"../core/FileRef\";\nimport React, { useState, useCallback, useRef } from \"react\";\n\nexport interface ReactorControllerProps {\n  className?: string;\n  style?: React.CSSProperties;\n}\n\ninterface CommandParamSchema {\n  description?: string;\n  type: string;\n  format?: string;\n  minimum?: number;\n  maximum?: number;\n  required?: boolean;\n  enum?: string[];\n  default?: unknown;\n}\n\nfunction isFileParam(ps: CommandParamSchema): boolean {\n  return (\n    ps.type === \"file\" ||\n    (ps.type === \"object\" && ps.format === \"file-reference\")\n  );\n}\n\ninterface CommandSchema {\n  description: string;\n  schema: Record<string, CommandParamSchema>;\n}\n\n// The runtime sends commands as a list of {name, description, schema}\n// matching the proto Command message.\ninterface CommandCapabilityMessage {\n  name: string;\n  description: string;\n  schema?: Record<string, CommandParamSchema>;\n}\n\ninterface CommandsMessage {\n  commands: CommandCapabilityMessage[];\n}\n\nexport function ReactorController({\n  className,\n  style,\n}: ReactorControllerProps) {\n  const { sendCommand, uploadFile, status } = useReactor((state) => ({\n    sendCommand: state.sendCommand,\n    uploadFile: state.uploadFile,\n    status: state.status,\n  }));\n  const [commands, setCommands] = useState<Record<string, CommandSchema>>({});\n  const [formValues, setFormValues] = useState<\n    Record<string, Record<string, any>>\n  >({});\n  const formRef = useRef(formValues);\n  formRef.current = formValues;\n  const [expandedCommands, setExpandedCommands] = useState<\n    Record<string, boolean>\n  >({});\n\n  // Reset commands when disconnected\n  React.useEffect(() => {\n    if (status === \"disconnected\") {\n      setCommands({});\n      setFormValues({});\n      setExpandedCommands({});\n    }\n  }, [status]);\n\n  // Function to request capabilities (sent on the \"runtime\" channel)\n  const requestCapabilities = useCallback(() => {\n    if (status === \"ready\") {\n      sendCommand(\"requestCapabilities\", {}, \"runtime\");\n    }\n  }, [status, sendCommand]);\n\n  // Send requestCapabilities when ready\n  React.useEffect(() => {\n    if (status === \"ready\") {\n      requestCapabilities();\n    }\n  }, [status, requestCapabilities]);\n\n  // Retry every 5 seconds if capabilities not set\n  React.useEffect(() => {\n    // Only set up interval if status is ready and commands are empty\n    if (status !== \"ready\" || Object.keys(commands).length > 0) {\n      return;\n    }\n\n    const interval = setInterval(() => {\n      requestCapabilities();\n    }, 5000);\n\n    return () => clearInterval(interval);\n  }, [status, commands, requestCapabilities]);\n\n  useReactorInternalMessage((message) => {\n    if (\n      message &&\n      typeof message === \"object\" &&\n      message.type === \"modelCapabilities\" &&\n      message.data &&\n      \"commands\" in message.data\n    ) {\n      const commandsMessage = message.data as CommandsMessage;\n\n      // Convert the list of command descriptors (proto Command shape)\n      // into a Record keyed by name for the controller's internal state.\n      const commandsRecord: Record<string, CommandSchema> = {};\n      for (const cmd of commandsMessage.commands) {\n        commandsRecord[cmd.name] = {\n          description: cmd.description,\n          schema: cmd.schema ?? {},\n        };\n      }\n      setCommands(commandsRecord);\n\n      // Initialize form values for each command\n      const initialValues: Record<string, Record<string, any>> = {};\n      const initialExpanded: Record<string, boolean> = {};\n\n      Object.entries(commandsRecord).forEach(([commandName, commandSchema]) => {\n        initialValues[commandName] = {};\n        initialExpanded[commandName] = false;\n\n        Object.entries(commandSchema.schema).forEach(\n          ([paramName, paramSchema]) => {\n            if (isFileParam(paramSchema)) {\n              initialValues[commandName][paramName] = null;\n            } else if (paramSchema.default !== undefined) {\n              initialValues[commandName][paramName] = paramSchema.default;\n            } else if (\n              paramSchema.type === \"number\" ||\n              paramSchema.type === \"integer\"\n            ) {\n              initialValues[commandName][paramName] = paramSchema.minimum ?? 0;\n            } else if (paramSchema.type === \"string\") {\n              initialValues[commandName][paramName] = \"\";\n            } else if (paramSchema.type === \"boolean\") {\n              initialValues[commandName][paramName] = false;\n            }\n          }\n        );\n      });\n      setFormValues(initialValues);\n      setExpandedCommands(initialExpanded);\n    }\n  });\n\n  const handleInputChange = useCallback(\n    (commandName: string, paramName: string, value: any) => {\n      setFormValues((prev) => ({\n        ...prev,\n        [commandName]: {\n          ...prev[commandName],\n          [paramName]: value,\n        },\n      }));\n    },\n    []\n  );\n\n  const toggleCommandExpanded = useCallback((commandName: string) => {\n    setExpandedCommands((prev) => ({\n      ...prev,\n      [commandName]: !prev[commandName],\n    }));\n  }, []);\n\n  const handleCommandSubmit = useCallback(\n    async (commandName: string) => {\n      const commandSchema = commands[commandName];\n      const formData = formRef.current[commandName] || {};\n\n      const data: Record<string, any> = {};\n\n      Object.keys(commandSchema.schema).forEach((paramName) => {\n        const paramSchema = commandSchema.schema[paramName];\n        let value = formData[paramName];\n\n        if (value instanceof FileRef) {\n          data[paramName] = value;\n          return;\n        }\n\n        if (paramSchema.type === \"number\" && typeof value === \"string\") {\n          value = parseFloat(value) || 0;\n        } else if (\n          paramSchema.type === \"integer\" &&\n          typeof value === \"string\"\n        ) {\n          value = parseInt(value) || 0;\n        } else if (\n          paramSchema.type === \"boolean\" &&\n          typeof value !== \"boolean\"\n        ) {\n          value = Boolean(value);\n        }\n\n        if (value !== undefined && value !== \"\" && value !== null) {\n          data[paramName] = value;\n        } else if (paramSchema.required) {\n          if (paramSchema.type === \"number\" || paramSchema.type === \"integer\") {\n            data[paramName] = paramSchema.minimum ?? 0;\n          } else if (paramSchema.type === \"string\") {\n            data[paramName] = \"\";\n          } else if (paramSchema.type === \"boolean\") {\n            data[paramName] = false;\n          }\n        }\n      });\n\n      await sendCommand(commandName, data);\n    },\n    [sendCommand, commands]\n  );\n\n  const renderInput = (\n    commandName: string,\n    paramName: string,\n    paramSchema: CommandParamSchema\n  ) => {\n    const value = formValues[commandName]?.[paramName] ?? \"\";\n\n    if (isFileParam(paramSchema)) {\n      return (\n        <FileParamInput\n          key={`${commandName}-${paramName}`}\n          commandName={commandName}\n          paramName={paramName}\n          description={paramSchema.description}\n          value={value}\n          onChange={handleInputChange}\n          disabled={status !== \"ready\"}\n          uploadFile={uploadFile}\n        />\n      );\n    }\n\n    if (paramSchema.type === \"number\" || paramSchema.type === \"integer\") {\n      const isInteger = paramSchema.type === \"integer\";\n      const step = isInteger ? 1 : 0.1;\n      const parseValue = isInteger ? parseInt : parseFloat;\n\n      // Use slider if min/max are defined, otherwise use number input\n      if (\n        typeof paramSchema.minimum === \"number\" &&\n        typeof paramSchema.maximum === \"number\"\n      ) {\n        return (\n          <div style={{ marginBottom: \"8px\" }}>\n            <label\n              style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n            >\n              {paramName} ({paramSchema.minimum} - {paramSchema.maximum})\n              {paramSchema.description && ` - ${paramSchema.description}`}\n              {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n            </label>\n            <input\n              type=\"range\"\n              min={paramSchema.minimum}\n              max={paramSchema.maximum}\n              step={step}\n              value={value}\n              onChange={(e) => {\n                const newValue = parseValue(e.target.value) || 0;\n                handleInputChange(commandName, paramName, newValue);\n                // Execute command immediately for sliders\n                handleCommandSubmit(commandName);\n              }}\n              style={{ width: \"100%\", marginBottom: \"4px\" }}\n            />\n            <div style={{ fontSize: \"11px\", color: \"#888\" }}>\n              Value: {value}\n            </div>\n          </div>\n        );\n      } else {\n        return (\n          <div style={{ marginBottom: \"8px\" }}>\n            <label\n              style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n            >\n              {paramName}\n              {paramSchema.description && ` - ${paramSchema.description}`}\n              {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n            </label>\n            <input\n              type=\"number\"\n              value={value}\n              min={paramSchema.minimum}\n              max={paramSchema.maximum}\n              step={step}\n              inputMode=\"numeric\"\n              onChange={(e) => {\n                const val = e.target.value;\n                if (val === \"\" || val === \"-\") {\n                  // Allow empty or just minus sign while typing\n                  handleInputChange(commandName, paramName, val);\n                } else {\n                  const parsed = parseValue(val);\n                  if (!isNaN(parsed)) {\n                    handleInputChange(commandName, paramName, parsed);\n                  }\n                }\n              }}\n              onBlur={(e) => {\n                // On blur, ensure we have a valid number\n                const val = e.target.value;\n                if (val === \"\" || val === \"-\") {\n                  handleInputChange(commandName, paramName, 0);\n                }\n              }}\n              style={{\n                width: \"100%\",\n                padding: \"4px\",\n                fontSize: \"12px\",\n                border: \"1px solid #ccc\",\n                borderRadius: \"2px\",\n              }}\n            />\n          </div>\n        );\n      }\n    } else if (paramSchema.type === \"string\") {\n      if (paramSchema.enum) {\n        // Dropdown for enum values\n        return (\n          <div style={{ marginBottom: \"8px\" }}>\n            <label\n              style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n            >\n              {paramName}\n              {paramSchema.description && ` - ${paramSchema.description}`}\n              {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n            </label>\n            <select\n              value={value}\n              onChange={(e) =>\n                handleInputChange(commandName, paramName, e.target.value)\n              }\n              style={{\n                width: \"100%\",\n                padding: \"4px\",\n                fontSize: \"12px\",\n                border: \"1px solid #ccc\",\n                borderRadius: \"2px\",\n              }}\n            >\n              <option value=\"\">Select...</option>\n              {paramSchema.enum.map((option: string) => (\n                <option key={option} value={option}>\n                  {option}\n                </option>\n              ))}\n            </select>\n          </div>\n        );\n      } else {\n        // Text input\n        return (\n          <div style={{ marginBottom: \"8px\" }}>\n            <label\n              style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}\n            >\n              {paramName}\n              {paramSchema.description && ` - ${paramSchema.description}`}\n              {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n            </label>\n            <input\n              type=\"text\"\n              value={value}\n              onChange={(e) =>\n                handleInputChange(commandName, paramName, e.target.value)\n              }\n              style={{\n                width: \"100%\",\n                padding: \"4px\",\n                fontSize: \"12px\",\n                border: \"1px solid #ccc\",\n                borderRadius: \"2px\",\n              }}\n            />\n          </div>\n        );\n      }\n    } else if (paramSchema.type === \"boolean\") {\n      return (\n        <div style={{ marginBottom: \"8px\" }}>\n          <label\n            style={{\n              fontSize: \"12px\",\n              color: \"#666\",\n              display: \"flex\",\n              alignItems: \"center\",\n            }}\n          >\n            <input\n              type=\"checkbox\"\n              checked={value}\n              onChange={(e) =>\n                handleInputChange(commandName, paramName, e.target.checked)\n              }\n              style={{ marginRight: \"6px\" }}\n            />\n            {paramName}\n            {paramSchema.description && ` - ${paramSchema.description}`}\n            {paramSchema.required && <span style={{ color: \"red\" }}> *</span>}\n          </label>\n        </div>\n      );\n    }\n\n    return null;\n  };\n\n  const renderCommand = (commandName: string, commandSchema: CommandSchema) => {\n    const hasParams = Object.keys(commandSchema.schema).length > 0;\n    const isExpanded = expandedCommands[commandName];\n\n    const params = Object.values(commandSchema.schema);\n    const allSliders =\n      params.length > 0 &&\n      params.every(\n        (ps) =>\n          (ps.type === \"number\" || ps.type === \"integer\") &&\n          typeof ps.minimum === \"number\" &&\n          typeof ps.maximum === \"number\"\n      );\n\n    const showExecuteButton = !allSliders;\n\n    return (\n      <div\n        key={commandName}\n        style={{\n          border: \"1px solid #ddd\",\n          borderRadius: \"4px\",\n          marginBottom: \"8px\",\n          backgroundColor: \"#fafafa\",\n        }}\n      >\n        {/* Command Header - Always Visible */}\n        <div\n          onClick={() => toggleCommandExpanded(commandName)}\n          style={{\n            padding: \"8px 12px\",\n            cursor: \"pointer\",\n            borderBottom: isExpanded ? \"1px solid #ddd\" : \"none\",\n            display: \"flex\",\n            justifyContent: \"space-between\",\n            alignItems: \"center\",\n          }}\n        >\n          <div>\n            <h4\n              style={{\n                margin: \"0\",\n                fontSize: \"13px\",\n                fontWeight: \"bold\",\n              }}\n            >\n              {commandName}\n            </h4>\n            {isExpanded && commandSchema.description && (\n              <p\n                style={{ margin: \"4px 0 0 0\", fontSize: \"11px\", color: \"#666\" }}\n              >\n                {commandSchema.description}\n              </p>\n            )}\n          </div>\n          <div\n            style={{\n              fontSize: \"10px\",\n              color: \"#999\",\n              transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n              transition: \"transform 0.2s\",\n            }}\n          >\n            ▼\n          </div>\n        </div>\n\n        {/* Command Content - Collapsible */}\n        {isExpanded && (\n          <div style={{ padding: \"12px\", paddingTop: \"0\" }}>\n            {hasParams && (\n              <div style={{ marginBottom: showExecuteButton ? \"12px\" : \"0\" }}>\n                <div\n                  style={{\n                    marginBottom: \"8px\",\n                    fontSize: \"12px\",\n                    fontWeight: \"bold\",\n                    color: \"#555\",\n                  }}\n                >\n                  Parameters:\n                </div>\n                {Object.entries(commandSchema.schema).map(\n                  ([paramName, paramSchema]) => (\n                    <div\n                      key={`${commandName}-${paramName}`}\n                      style={{ marginLeft: \"8px\" }}\n                    >\n                      {renderInput(commandName, paramName, paramSchema)}\n                    </div>\n                  )\n                )}\n              </div>\n            )}\n\n            {!hasParams && (\n              <div\n                style={{\n                  marginBottom: showExecuteButton ? \"12px\" : \"0\",\n                  marginTop: \"2px\",\n                  fontSize: \"11px\",\n                  color: \"#666\",\n                  fontStyle: \"italic\",\n                }}\n              >\n                No parameters required\n              </div>\n            )}\n\n            {showExecuteButton && (\n              <button\n                onClick={() => handleCommandSubmit(commandName)}\n                style={{\n                  padding: \"8px 16px\",\n                  fontSize: \"12px\",\n                  backgroundColor: \"#007bff\",\n                  color: \"white\",\n                  border: \"none\",\n                  borderRadius: \"4px\",\n                  cursor: \"pointer\",\n                  fontWeight: \"bold\",\n                }}\n              >\n                Execute {commandName}\n              </button>\n            )}\n          </div>\n        )}\n      </div>\n    );\n  };\n\n  return (\n    <div className={className} style={style}>\n      <div style={{ fontFamily: \"monospace\", fontSize: \"12px\" }}>\n        {Object.keys(commands).length === 0 ? (\n          <div style={{ padding: \"12px\", color: \"#666\", fontStyle: \"italic\" }}>\n            Waiting for commands schema...\n          </div>\n        ) : (\n          <div>\n            <h3\n              style={{\n                margin: \"0 0 16px 0\",\n                fontSize: \"16px\",\n                fontWeight: \"bold\",\n              }}\n            >\n              Reactor Commands\n            </h3>\n            {Object.entries(commands).map(([commandName, commandSchema]) => (\n              <div key={commandName}>\n                {renderCommand(commandName, commandSchema)}\n              </div>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\nfunction FileParamInput({\n  commandName,\n  paramName,\n  description,\n  value,\n  onChange,\n  disabled,\n  uploadFile,\n}: {\n  commandName: string;\n  paramName: string;\n  description?: string;\n  value: any;\n  onChange: (cmd: string, param: string, value: any) => void;\n  disabled: boolean;\n  uploadFile: (\n    file: File | Blob,\n    options?: { name?: string }\n  ) => Promise<FileRef>;\n}) {\n  const inputRef = useRef<HTMLInputElement | null>(null);\n  const [uploading, setUploading] = useState(false);\n  const fileRef = value instanceof FileRef ? value : null;\n\n  const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n    const file = e.target.files?.[0];\n    if (!file) return;\n    e.target.value = \"\";\n    setUploading(true);\n    try {\n      const ref = await uploadFile(file);\n      onChange(commandName, paramName, ref);\n    } catch (err) {\n      console.error(\"[FileParamInput] Upload failed:\", err);\n    } finally {\n      setUploading(false);\n    }\n  };\n\n  return (\n    <div style={{ marginBottom: \"8px\" }}>\n      <label style={{ fontSize: \"12px\", color: \"#666\", display: \"block\" }}>\n        {paramName}\n        <span style={{ color: \"#888\", fontSize: \"11px\" }}> (file)</span>\n        {description && ` - ${description}`}\n      </label>\n      <div\n        style={{\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: \"8px\",\n          marginTop: \"4px\",\n        }}\n      >\n        <input\n          ref={inputRef}\n          type=\"file\"\n          accept=\"image/*\"\n          onChange={handleFile}\n          style={{ display: \"none\" }}\n        />\n        <button\n          disabled={disabled || uploading}\n          onClick={() => inputRef.current?.click()}\n          style={{\n            padding: \"4px 12px\",\n            fontSize: \"12px\",\n            backgroundColor: \"#007bff\",\n            color: \"white\",\n            border: \"none\",\n            borderRadius: \"4px\",\n            cursor: disabled || uploading ? \"default\" : \"pointer\",\n            opacity: disabled || uploading ? 0.5 : 1,\n          }}\n        >\n          {uploading ? \"Uploading\\u2026\" : fileRef ? \"Change\" : \"Choose File\"}\n        </button>\n        {fileRef && (\n          <span style={{ fontSize: \"11px\", color: \"#28a745\" }}>\n            \\u2713 {fileRef.name}\n          </span>\n        )}\n      </div>\n    </div>\n  );\n}\n","\"use client\";\n\nimport { useReactor } from \"./hooks\";\nimport { useEffect, useRef, useState } from \"react\";\nimport React from \"react\";\n\nexport interface WebcamStreamProps {\n  /**\n   * The name of the sendonly **video** track to publish the webcam to.\n   * Must match a track name declared in the server capabilities.\n   * Check the model's documentation for available track names.\n   */\n  track: string;\n  /**\n   * Capture and publish the microphone alongside the webcam.  Pass\n   * `true` for default constraints, or an explicit\n   * `MediaTrackConstraints` to control sample rate / device / echo\n   * cancellation.  Requires {@link audioTrack} so the SDK knows\n   * which sendonly track to publish the mic to.  Default `false`.\n   */\n  audio?: boolean | MediaTrackConstraints;\n  /**\n   * The name of the sendonly **audio** track to publish the mic to.\n   * Ignored when {@link audio} is `false` (the default); required\n   * otherwise.\n   */\n  audioTrack?: string;\n  className?: string;\n  style?: React.CSSProperties;\n  videoConstraints?: MediaTrackConstraints;\n  showWebcam?: boolean;\n  videoObjectFit?: NonNullable<\n    React.VideoHTMLAttributes<HTMLVideoElement>[\"style\"]\n  >[\"objectFit\"];\n  /**\n   * Fires once `getUserMedia` is rejected with `NotAllowedError`\n   * or `PermissionDeniedError`.\n   */\n  onPermissionDenied?: () => void;\n  /**\n   * Fires after the local media stream has been published (both\n   * video and audio when {@link audio} is enabled).  Re-fires\n   * after a reconnect.\n   */\n  onPublished?: () => void;\n  /**\n   * Fires on non-permission `getUserMedia` failures and on\n   * publish / unpublish rejections.  Permission denials route to\n   * {@link onPermissionDenied} instead.\n   */\n  onError?: (error: Error) => void;\n}\n\nexport function WebcamStream({\n  track,\n  audio = false,\n  audioTrack,\n  className,\n  style,\n  videoConstraints = {\n    width: { ideal: 1280 },\n    height: { ideal: 720 },\n  },\n  showWebcam = true,\n  videoObjectFit = \"contain\",\n  onPermissionDenied,\n  onPublished,\n  onError,\n}: WebcamStreamProps) {\n  const [stream, setStream] = useState<MediaStream | null>(null);\n  const [isPublishing, setIsPublishing] = useState(false);\n  const [permissionDenied, setPermissionDenied] = useState(false);\n\n  const { status, publish, unpublish, reactor } = useReactor((state) => ({\n    status: state.status,\n    publish: state.publish,\n    unpublish: state.unpublish,\n    reactor: state.internal.reactor,\n  }));\n\n  const videoRef = useRef<HTMLVideoElement>(null);\n\n  // Held in refs so inline callback identity doesn't churn the\n  // publish/unpublish effect on every parent render.\n  const onPermissionDeniedRef = useRef(onPermissionDenied);\n  const onPublishedRef = useRef(onPublished);\n  const onErrorRef = useRef(onError);\n  useEffect(() => {\n    onPermissionDeniedRef.current = onPermissionDenied;\n    onPublishedRef.current = onPublished;\n    onErrorRef.current = onError;\n  });\n\n  // Without an `audioTrack` the captured mic has nowhere to publish;\n  // warn rather than silently capturing video-only.\n  const audioRequested = audio !== false && audio !== undefined;\n  const audioEnabled = audioRequested && !!audioTrack;\n  if (audioRequested && !audioTrack) {\n    console.warn(\n      \"[WebcamStream] `audio` was set but `audioTrack` is missing; capturing video-only.\"\n    );\n  }\n\n  const startWebcam = async () => {\n    console.debug(\"[WebcamPublisher] Starting webcam\");\n\n    try {\n      const mediaStream = await navigator.mediaDevices.getUserMedia({\n        video: videoConstraints,\n        audio: audioEnabled ? (audio === true ? true : audio) : false,\n      });\n\n      console.debug(\"[WebcamPublisher] Webcam started successfully\");\n      setStream(mediaStream);\n      setPermissionDenied(false);\n    } catch (err) {\n      console.error(\"[WebcamPublisher] Failed to start webcam:\", err);\n\n      if (\n        err instanceof DOMException &&\n        (err.name === \"NotAllowedError\" || err.name === \"PermissionDeniedError\")\n      ) {\n        console.debug(\"[WebcamPublisher] Camera permission denied\");\n        setPermissionDenied(true);\n        onPermissionDeniedRef.current?.();\n      } else {\n        onErrorRef.current?.(\n          err instanceof Error ? err : new Error(String(err))\n        );\n      }\n    }\n  };\n\n  const stopWebcam = async () => {\n    console.debug(\"[WebcamPublisher] Stopping webcam\");\n\n    // Unpublish failures are logged but don't block local-track\n    // teardown — leaving tracks running keeps the camera/mic\n    // indicator on after unmount.\n    const unpublishTasks: Array<Promise<void>> = [unpublish(track)];\n    if (audioEnabled && audioTrack) {\n      unpublishTasks.push(unpublish(audioTrack));\n    }\n    const results = await Promise.allSettled(unpublishTasks);\n    for (const r of results) {\n      if (r.status === \"rejected\") {\n        console.error(\n          \"[WebcamPublisher] Error unpublishing before stop:\",\n          r.reason\n        );\n        onErrorRef.current?.(\n          r.reason instanceof Error ? r.reason : new Error(String(r.reason))\n        );\n      }\n    }\n\n    setIsPublishing(false);\n\n    stream?.getTracks().forEach((t) => {\n      t.stop();\n      console.debug(\"[WebcamPublisher] Stopped track:\", t.kind);\n    });\n    setStream(null);\n\n    console.debug(\"[WebcamPublisher] Webcam stopped\");\n  };\n\n  // Attach stream to video element\n  useEffect(() => {\n    console.debug(\"[WebcamPublisher] Stream effect triggered\", {\n      hasVideoElement: !!videoRef.current,\n      hasStream: !!stream,\n    });\n\n    if (!videoRef.current) {\n      return;\n    }\n\n    if (stream) {\n      console.debug(\"[WebcamPublisher] Attaching stream to video element\");\n      videoRef.current.srcObject = stream;\n      console.debug(\"[WebcamPublisher] Stream attached successfully\");\n    } else {\n      console.debug(\"[WebcamPublisher] Clearing video element\");\n      videoRef.current.srcObject = null;\n    }\n  }, [stream]);\n\n  // Auto-publish when reactor is ready and webcam is active.\n  useEffect(() => {\n    if (!stream) {\n      return;\n    }\n\n    if (status === \"ready\" && !isPublishing) {\n      console.debug(\n        \"[WebcamPublisher] Reactor ready, auto-publishing webcam stream\"\n      );\n      const videoMediaTrack = stream.getVideoTracks()[0];\n      const audioMediaTrack =\n        audioEnabled && audioTrack ? stream.getAudioTracks()[0] : null;\n      const tasks: Array<Promise<void>> = [];\n      if (videoMediaTrack) tasks.push(publish(track, videoMediaTrack));\n      if (audioMediaTrack && audioTrack)\n        tasks.push(publish(audioTrack, audioMediaTrack));\n      if (tasks.length === 0) return;\n      Promise.all(tasks)\n        .then(() => {\n          console.debug(\"[WebcamPublisher] Auto-publish successful\");\n          setIsPublishing(true);\n          onPublishedRef.current?.();\n        })\n        .catch((err) => {\n          console.error(\"[WebcamPublisher] Auto-publish failed:\", err);\n          onErrorRef.current?.(\n            err instanceof Error ? err : new Error(String(err))\n          );\n        });\n    } else if (status !== \"ready\" && isPublishing) {\n      console.debug(\"[WebcamPublisher] Reactor not ready, auto-unpublishing\");\n      const tasks: Array<Promise<void>> = [unpublish(track)];\n      if (audioEnabled && audioTrack) tasks.push(unpublish(audioTrack));\n      Promise.allSettled(tasks).then((results) => {\n        for (const r of results) {\n          if (r.status === \"rejected\") {\n            console.error(\"[WebcamPublisher] Auto-unpublish failed:\", r.reason);\n            onErrorRef.current?.(\n              r.reason instanceof Error ? r.reason : new Error(String(r.reason))\n            );\n          }\n        }\n        setIsPublishing(false);\n      });\n    }\n  }, [\n    status,\n    stream,\n    isPublishing,\n    publish,\n    unpublish,\n    track,\n    audioEnabled,\n    audioTrack,\n  ]);\n\n  // Listen for error events from Reactor\n  useEffect(() => {\n    const handleError = (error: any) => {\n      console.debug(\"[WebcamPublisher] Received error event:\", error);\n\n      // Handle track publish failures by resetting state\n      if (error.code === \"TRACK_PUBLISH_FAILED\") {\n        console.debug(\n          \"[WebcamPublisher] Track publish failed, resetting isPublishing state\"\n        );\n        setIsPublishing(false);\n      }\n    };\n\n    reactor.on(\"error\", handleError);\n\n    return () => {\n      reactor.off(\"error\", handleError);\n    };\n  }, [reactor]);\n\n  // Reset publishing state when status changes away from ready\n  useEffect(() => {\n    if (status !== \"ready\") {\n      console.debug(\n        \"[WebcamPublisher] Status changed to\",\n        status,\n        \"- resetting isPublishing state\"\n      );\n      setIsPublishing(false);\n    }\n  }, [status, isPublishing]);\n\n  // Auto-start webcam on mount and cleanup on unmount\n  useEffect(() => {\n    console.debug(\"[WebcamPublisher] Auto-starting webcam\");\n    startWebcam();\n\n    return () => {\n      console.debug(\"[WebcamPublisher] Cleanup on unmount\");\n      stopWebcam();\n    };\n  }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n  const showPlaceholder = !stream;\n\n  return (\n    <div\n      style={{\n        display: showWebcam ? \"block\" : \"none\",\n        position: \"relative\",\n        background: \"#000\",\n        ...style,\n      }}\n      className={className}\n    >\n      <video\n        ref={videoRef}\n        style={{\n          width: \"100%\",\n          height: \"100%\",\n          objectFit: videoObjectFit,\n          display: showPlaceholder ? \"none\" : \"block\",\n        }}\n        muted\n        playsInline\n        autoPlay\n      />\n      {showPlaceholder && (\n        <div\n          style={{\n            position: \"absolute\",\n            top: 0,\n            left: 0,\n            width: \"100%\",\n            height: \"100%\",\n            color: \"#fff\",\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            fontSize: \"16px\",\n            fontFamily: \"monospace\",\n            textAlign: \"center\",\n            padding: \"20px\",\n            boxSizing: \"border-box\",\n            flexDirection: \"column\",\n            gap: \"12px\",\n          }}\n        >\n          {permissionDenied ? (\n            <div style={{ fontSize: \"12px\", fontFamily: \"monospace\" }}>\n              Camera access denied.\n              <br />\n              Please allow access in your browser settings.\n            </div>\n          ) : (\n            <div style={{ fontSize: \"12px\", fontFamily: \"monospace\" }}>\n              Starting camera...\n            </div>\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\nimport { useContext, useEffect, useRef, useState } from \"react\";\nimport React from \"react\";\nimport { ReactorContext } from \"../core/store\";\nimport {\n  RecordingError,\n  createPlayableManifestUrl,\n  fetchPlaylist,\n  type Clip,\n} from \"../utils/recording\";\n\n/**\n * Video preview for a captured {@link Clip}.\n *\n * Renders an HLS manifest through the browser's native\n * ``<video controls>`` element.  On Safari / iOS the manifest is\n * attached directly (native HLS support).  On Chrome / Firefox / Edge\n * ``hls.js`` is dynamically imported — declared as an **optional peer\n * dependency** so consumers who don't use this component aren't billed\n * the ~80 KB.  If ``hls.js`` isn't installed and native HLS isn't\n * available the player surfaces an inline error overlay; the\n * underlying chunks remain downloadable via {@link useClipDownload} /\n * {@link ClipDownloadButton} (which don't depend on hls.js at all).\n *\n * **Preview only.**  This component intentionally does *not* render a\n * download UI.  Compose it with {@link ClipDownloadButton} or build\n * your own download surface around {@link useClipDownload} — the\n * separation keeps both pieces independently extendible and avoids\n * baking layout decisions into the SDK.\n *\n * Unlike {@link ReactorView} / {@link WebcamStream} this component\n * **does not require a ``ReactorProvider``** in the tree.  It\n * operates on the ``Clip`` value alone, so it stays usable after\n * ``reactor.disconnect()`` and works with clips loaded from\n * fixtures, server logs, or any other source. When a\n * ``ReactorProvider`` is mounted above, an omitted ``getJwt``\n * inherits the provider's resolver.\n *\n * @example Compose with the download button (explicit `getJwt`):\n * ```tsx\n * <div>\n *   <ClipPlayer clip={clip} getJwt={() => jwt} />\n *   <ClipDownloadButton clip={clip} getJwt={() => jwt} />\n * </div>\n * ```\n *\n * @example Local dev (HttpRuntime, no auth on /clips):\n * ```tsx\n * <ClipPlayer clip={clip} />\n * ```\n */\nexport interface ClipPlayerProps {\n  /**\n   * The captured clip to play.  When this prop changes by reference\n   * the player re-fetches the manifest and re-attaches the player.\n   */\n  clip: Clip;\n  /**\n   * Lazy resolver for the Coordinator JWT used on the ``/clips``\n   * manifest GET. Called at request time so token refreshes are\n   * picked up automatically.\n   *\n   * - **Production:** required outside a ``<ReactorProvider>``;\n   *   optional inside one (inherits the provider's resolver).\n   * - **Local-dev (HttpRuntime):** omit — ``/clips`` is auth-free.\n   *\n   * Chunk URLs inside the manifest are presigned (prod) or\n   * unauthenticated (local) and are fetched without\n   * ``Authorization`` in either case.\n   */\n  getJwt?: () => string | Promise<string>;\n  /**\n   * Override the grace period beyond ``clip.predictedReadyAtMs``\n   * before the SDK gives up polling the ``/clips`` manifest endpoint\n   * with ``CLIP_NOT_READY``.  Defaults to\n   * {@link DEFAULT_PLAYLIST_POLL_SLACK_MS} (15 s) — enough for snap\n   * clips, but long-running session recordings may legitimately need\n   * a minute or more for the final boundary chunk to finish encoding\n   * + uploading, in which case pass a larger value (e.g. ``120_000``\n   * for two-minute grace).  Forwarded directly to\n   * {@link fetchPlaylist}'s ``slackMs`` option — see that helper for\n   * the polling semantics.\n   */\n  slackMs?: number;\n  /** Play automatically once the manifest is attached. Default ``true``. */\n  autoPlay?: boolean;\n  /**\n   * Start muted.  Default ``true`` because browser autoplay policies\n   * block audio-bearing video from playing without a user gesture;\n   * the user can unmute via the native ``<video controls>``.\n   */\n  muted?: boolean;\n  className?: string;\n  style?: React.CSSProperties;\n  /**\n   * Fires when the player enters its inline error state.  Receives\n   * the originating error: a ``RecordingError`` for manifest-fetch\n   * failures, a plain ``Error`` for hls.js / native playback or\n   * missing-peer-dep cases.\n   */\n  onError?: (error: Error) => void;\n}\n\ntype Phase =\n  | { kind: \"waiting\" }\n  | { kind: \"loading\" }\n  | { kind: \"ready\" }\n  | { kind: \"error\"; message: string; error: Error };\n\nexport function ClipPlayer({\n  clip,\n  getJwt,\n  slackMs,\n  autoPlay = true,\n  muted = true,\n  className,\n  style,\n  onError,\n}: ClipPlayerProps) {\n  const videoRef = useRef<HTMLVideoElement | null>(null);\n  const [phase, setPhase] = useState<Phase>({ kind: \"waiting\" });\n\n  // `undefined` outside a `<ReactorProvider>`; used to inherit the\n  // provider's JWT resolver when `getJwt` is omitted.\n  const store = useContext(ReactorContext);\n\n  // The playback effect intentionally depends only on `clip`.  Callers\n  // typically pass an inline `getJwt={() => token}` that changes\n  // identity on every render — using it directly in the effect deps\n  // would tear down the player and re-fetch the manifest on every\n  // parent re-render (visible to the user as \"keeps fetching, never\n  // shows\").  Same reasoning for `autoPlay`: it's only read at the\n  // moment of attach.  Refs keep the latest values reachable from\n  // inside the effect without forcing it to re-run.\n  const getJwtRef = useRef(getJwt);\n  const autoPlayRef = useRef(autoPlay);\n  const slackMsRef = useRef(slackMs);\n  const onErrorRef = useRef(onError);\n  // Same ref pattern: the resolver is looked up inside `setup` at\n  // request time, so a provider swap is picked up on next attach\n  // without tearing the player down.\n  const storeRef = useRef(store);\n  useEffect(() => {\n    getJwtRef.current = getJwt;\n    autoPlayRef.current = autoPlay;\n    slackMsRef.current = slackMs;\n    onErrorRef.current = onError;\n    storeRef.current = store;\n  });\n\n  // Re-fires per error transition: each new `clip` resets through\n  // `waiting` / `loading` before potentially re-entering `error`.\n  useEffect(() => {\n    if (phase.kind === \"error\") {\n      onErrorRef.current?.(phase.error);\n    }\n  }, [phase]);\n\n  // Playback pipeline: fetch manifest (with optional JWT) → wrap in\n  // blob URL → attach via native HLS (Safari) or hls.js (everyone\n  // else).  Re-runs only when `clip` changes by reference.  The\n  // cleanup closure tears every piece down deterministically.\n  useEffect(() => {\n    const video = videoRef.current;\n    if (!video) return;\n\n    const abort = new AbortController();\n    let cancelled = false;\n    let hlsInstance: { destroy: () => void } | null = null;\n    let manifestBlobUrl: string | null = null;\n\n    const fail = (error: Error) => {\n      if (cancelled) return;\n      const message =\n        error instanceof RecordingError\n          ? `${error.code}: ${error.reason}`\n          : error.message;\n      setPhase({ kind: \"error\", message, error });\n    };\n\n    const attachPlayer = async (manifestUrl: string) => {\n      const canPlayNative =\n        video.canPlayType(\"application/vnd.apple.mpegurl\") !== \"\";\n      if (canPlayNative) {\n        // Safari / iOS path — attach the blob: URL and let the\n        // browser parse HLS natively.\n        video.src = manifestUrl;\n        if (autoPlayRef.current) {\n          video.play().catch(() => {\n            // Autoplay may be blocked by the browser; native controls still work.\n          });\n        }\n        if (!cancelled) setPhase({ kind: \"ready\" });\n        return;\n      }\n\n      // Non-Safari path — dynamic import of the optional peer\n      // dependency.  Bundlers preserve `import()` so the chunk is\n      // only fetched when this branch executes.\n      let HlsCtor: HlsConstructor;\n      try {\n        const mod = (await import(\"hls.js\")) as { default: HlsConstructor };\n        HlsCtor = mod.default;\n      } catch {\n        fail(\n          new Error(\n            \"HLS playback unavailable in this browser. Install `hls.js` as a peer dependency, or use Download.\"\n          )\n        );\n        return;\n      }\n      if (cancelled) return;\n      if (!HlsCtor.isSupported()) {\n        fail(\n          new Error(\"This browser cannot play HLS clips. Use Download instead.\")\n        );\n        return;\n      }\n      const hls = new HlsCtor();\n      hls.loadSource(manifestUrl);\n      hls.attachMedia(video);\n      hls.on(HlsCtor.Events.MANIFEST_PARSED, () => {\n        if (cancelled) return;\n        setPhase({ kind: \"ready\" });\n        if (autoPlayRef.current) {\n          video.play().catch(() => {\n            // Autoplay may be blocked.\n          });\n        }\n      });\n      hls.on(HlsCtor.Events.ERROR, (_evt: unknown, data: HlsErrorData) => {\n        if (cancelled) return;\n        // Surface non-fatal errors via `console.warn` — they often\n        // explain \"fetches but nothing renders\" symptoms (e.g.\n        // `bufferAppendingError`, `fragParsingError`,\n        // `levelLoadError`) that the user-facing overlay would\n        // otherwise hide.  Fatal errors still hard-fail the player.\n        if (data.fatal) {\n          fail(new Error(`Playback error: ${data.details ?? \"unknown\"}`));\n          return;\n        }\n        console.warn(\"[Reactor.ClipPlayer] hls.js non-fatal error\", data);\n      });\n      hlsInstance = hls;\n    };\n\n    const setup = async () => {\n      try {\n        setPhase({ kind: \"waiting\" });\n        // Explicit `getJwt` wins; fall back to the provider's resolver.\n        const explicit = getJwtRef.current;\n        const fallback = storeRef.current\n          ?.getState()\n          .internal.reactor.getJwtResolver();\n        const resolver = explicit ?? fallback;\n        const jwt = resolver ? await resolver() : undefined;\n        if (cancelled) return;\n        const body = await fetchPlaylist(clip.playlistUrl, {\n          predictedReadyAtMs: clip.predictedReadyAtMs,\n          slackMs: slackMsRef.current,\n          jwt,\n          signal: abort.signal,\n        });\n        if (cancelled) return;\n        setPhase({ kind: \"loading\" });\n        manifestBlobUrl = createPlayableManifestUrl(body, clip.playlistUrl);\n        await attachPlayer(manifestBlobUrl);\n      } catch (err) {\n        if (cancelled) return;\n        // `AbortError` from teardown is expected — don't paint it as\n        // a failure to the user.\n        if (err instanceof DOMException && err.name === \"AbortError\") {\n          return;\n        }\n        fail(err instanceof Error ? err : new Error(String(err)));\n      }\n    };\n\n    setup();\n\n    return () => {\n      cancelled = true;\n      abort.abort();\n      hlsInstance?.destroy();\n      video.pause();\n      video.removeAttribute(\"src\");\n      video.load();\n      if (manifestBlobUrl) URL.revokeObjectURL(manifestBlobUrl);\n    };\n  }, [clip]);\n\n  const overlayText =\n    phase.kind === \"waiting\"\n      ? \"Waiting for clip…\"\n      : phase.kind === \"loading\"\n        ? \"Loading player…\"\n        : null;\n\n  return (\n    <div\n      className={className}\n      style={{\n        position: \"relative\",\n        background: \"#000\",\n        width: \"100%\",\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        ...style,\n      }}\n    >\n      {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n      <video\n        ref={videoRef}\n        controls\n        playsInline\n        muted={muted}\n        style={{\n          display: \"block\",\n          width: \"100%\",\n          height: \"auto\",\n          maxHeight: \"100%\",\n        }}\n      />\n\n      {overlayText && (\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            color: \"rgba(255,255,255,0.6)\",\n            font: \"11px ui-monospace, SFMono-Regular, Menlo, monospace\",\n            letterSpacing: \"0.1em\",\n            textTransform: \"uppercase\",\n            pointerEvents: \"none\",\n          }}\n        >\n          {overlayText}\n        </div>\n      )}\n\n      {phase.kind === \"error\" && (\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            padding: 24,\n            background: \"rgba(0,0,0,0.8)\",\n            color: \"#ef4444\",\n            font: \"11px ui-monospace, SFMono-Regular, Menlo, monospace\",\n            textAlign: \"center\",\n          }}\n        >\n          {phase.message}\n        </div>\n      )}\n    </div>\n  );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Minimal local typings for the optional `hls.js` peer dep.  We\n// can't `import type { Hls } from \"hls.js\"` because the dep is\n// optional — that import would fail in environments where it\n// isn't installed.  The structural type below covers exactly the\n// surface this component uses.\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface HlsInstance {\n  loadSource: (url: string) => void;\n  attachMedia: (el: HTMLMediaElement) => void;\n  on: (event: string, cb: (evt: unknown, data: HlsErrorData) => void) => void;\n  destroy: () => void;\n}\n\ninterface HlsConstructor {\n  new (): HlsInstance;\n  isSupported: () => boolean;\n  readonly Events: {\n    readonly MANIFEST_PARSED: string;\n    readonly ERROR: string;\n  };\n}\n\ninterface HlsErrorData {\n  fatal?: boolean;\n  details?: string;\n}\n","\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport type { Clip } from \"../utils/recording\";\nimport { useClipDownload, type ClipDownloadState } from \"./useClipDownload\";\n\n/**\n * Standalone download button for a captured {@link Clip}.\n *\n * Drops anywhere in your UI — modal headers, list rows, hover menus,\n * floating action buttons — and is responsible for nothing more than\n * triggering a download and reflecting its state.  Internally wraps\n * {@link useClipDownload}; for completely custom UIs (progress bars,\n * menu items, post-download blob handling) call that hook directly.\n *\n * Styling is intentionally minimal.  Override via ``className``,\n * ``style``, or replace the inner content with the ``children``\n * render-prop.  No CSS file is shipped — every default style is\n * inline so it loses to anything the consumer provides.\n *\n * @example Default label, state-aware:\n * ```tsx\n * <ClipDownloadButton clip={clip} getJwt={() => jwt} />\n * ```\n *\n * @example Custom label that follows the state:\n * ```tsx\n * <ClipDownloadButton clip={clip} getJwt={() => jwt}>\n *   {(s) => (s.kind === \"downloading\" ? `${s.fetched}/${s.total}` : \"Save MP4\")}\n * </ClipDownloadButton>\n * ```\n *\n * @example Static label:\n * ```tsx\n * <ClipDownloadButton clip={clip} getJwt={() => jwt}>Save</ClipDownloadButton>\n * ```\n */\nexport interface ClipDownloadButtonProps {\n  /** The clip to download. */\n  clip: Clip;\n  /**\n   * Lazy JWT resolver. Optional inside a ``<ReactorProvider>``\n   * (inherits the provider's resolver) and in local-dev mode. See\n   * {@link ClipPlayerProps.getJwt}.\n   */\n  getJwt?: () => string | Promise<string>;\n  /** Filename for the saved MP4.  Default ``\"reactor-clip.mp4\"``. */\n  filename?: string;\n  /**\n   * Inner content of the button.  Three forms:\n   *\n   * - **Omitted** — renders a sensible default label that follows\n   *   the state (``\"Download\"`` / ``\"Downloading 3/8…\"`` / etc.).\n   * - **`ReactNode`** — static label.  No state-driven text.\n   * - **`(state) => ReactNode`** — state-aware render function;\n   *   use for custom progress strings, spinners, etc.\n   */\n  children?: React.ReactNode | ((state: ClipDownloadState) => React.ReactNode);\n  /** Forwarded to the underlying ``<button>``. */\n  className?: string;\n  /** Forwarded to the underlying ``<button>`` — merges *after* the defaults so each property overrides. */\n  style?: React.CSSProperties;\n  /** Forwarded to the underlying ``<button>``.  ORed with the internal \"downloading\" state. */\n  disabled?: boolean;\n  /**\n   * Fires when the download completes with the assembled MP4 ``Blob``.\n   */\n  onSuccess?: (blob: Blob) => void;\n  /**\n   * Fires when the download fails.  Message mirrors the in-button\n   * state — ``\"<CODE>: <reason>\"`` for ``RecordingError``s, the\n   * plain error message otherwise.\n   */\n  onError?: (error: Error) => void;\n}\n\nexport function ClipDownloadButton({\n  clip,\n  getJwt,\n  filename = \"reactor-clip.mp4\",\n  children,\n  className,\n  style,\n  disabled,\n  onSuccess,\n  onError,\n}: ClipDownloadButtonProps) {\n  const { state, download } = useClipDownload(clip, { filename, getJwt });\n  const downloading = state.kind === \"downloading\";\n  const isDisabled = downloading || !!disabled;\n\n  // Held in refs so inline callback identity doesn't churn the\n  // error-emit effect or the click handler on every parent render.\n  const onSuccessRef = useRef(onSuccess);\n  const onErrorRef = useRef(onError);\n  useEffect(() => {\n    onSuccessRef.current = onSuccess;\n    onErrorRef.current = onError;\n  });\n\n  // `useClipDownload.download()` resolves to `undefined` on failure\n  // (the hook surfaces errors through state, not by rejecting).\n  // Each retry that lands back in `\"error\"` re-fires `onError`.\n  useEffect(() => {\n    if (state.kind === \"error\") {\n      onErrorRef.current?.(new Error(state.message));\n    }\n  }, [state]);\n\n  const content =\n    typeof children === \"function\"\n      ? children(state)\n      : children !== undefined\n        ? children\n        : defaultLabel(state);\n\n  return (\n    <button\n      type=\"button\"\n      onClick={() => {\n        void download().then((blob) => {\n          if (blob) onSuccessRef.current?.(blob);\n        });\n      }}\n      disabled={isDisabled}\n      title={state.kind === \"error\" ? state.message : undefined}\n      className={className}\n      style={{\n        padding: \"5px 12px\",\n        borderRadius: 4,\n        border: \"1px solid rgba(255,255,255,0.15)\",\n        background: \"rgba(255,255,255,0.05)\",\n        color: \"#fff\",\n        font: \"11px ui-monospace, SFMono-Regular, Menlo, monospace\",\n        cursor: isDisabled ? \"default\" : \"pointer\",\n        opacity: isDisabled ? 0.6 : 1,\n        transition: \"background-color 120ms ease\",\n        ...style,\n      }}\n    >\n      {content}\n    </button>\n  );\n}\n\nfunction defaultLabel(state: ClipDownloadState): React.ReactNode {\n  if (state.kind === \"downloading\") {\n    if (state.total > 0) {\n      return `Downloading ${state.fetched}/${state.total}…`;\n    }\n    return \"Downloading…\";\n  }\n  return \"Download\";\n}\n","\"use client\";\n\nimport { useCallback, useContext, useEffect, useRef, useState } from \"react\";\nimport { ReactorContext } from \"../core/store\";\nimport {\n  RecordingError,\n  downloadClipAsFile,\n  type Clip,\n} from \"../utils/recording\";\n\n/**\n * State machine for an in-progress clip download.\n *\n * - ``idle``: no download in flight (initial state, and what the hook\n *   returns to after a successful save).\n * - ``downloading``: chunks are being fetched.  ``fetched`` and\n *   ``total`` count the number of chunks (init segment + media\n *   segments) — useful for a progress bar.  ``total`` is 0 until the\n *   manifest is parsed and the chunk count is known.\n * - ``error``: most recent attempt failed; the ``message`` is suitable\n *   for surfacing inline.  ``RecordingError`` instances are formatted\n *   as ``\"<CODE>: <reason>\"`` for grep-ability.\n */\nexport type ClipDownloadState =\n  | { kind: \"idle\" }\n  | { kind: \"downloading\"; fetched: number; total: number }\n  | { kind: \"error\"; message: string };\n\nexport interface UseClipDownloadOptions {\n  /**\n   * Filename used when the browser save dialog opens.  Pass ``null``\n   * to skip the ``<a download>`` trigger entirely — the returned\n   * Blob is still resolved so the caller can ``URL.createObjectURL``\n   * it or re-upload it.  Default ``\"reactor-clip.mp4\"``.\n   */\n  filename?: string | null;\n  /**\n   * Lazy resolver for the Coordinator JWT used on the ``/clips``\n   * manifest GET.  Called on every {@link download} invocation, so\n   * token refreshes are picked up automatically.\n   *\n   * Optional inside a ``<ReactorProvider>`` (inherits the provider's\n   * resolver) and in local-dev mode. See\n   * {@link ClipPlayerProps.getJwt}.\n   */\n  getJwt?: () => string | Promise<string>;\n}\n\nexport interface UseClipDownloadResult {\n  /** Current state of the most recent download attempt. */\n  state: ClipDownloadState;\n  /**\n   * Trigger a download.  Resolves with the assembled fragmented-MP4\n   * Blob, or ``undefined`` if a download was already in flight (the\n   * call is a no-op in that case — guarding against double-click is\n   * the hook's responsibility, not the caller's).\n   *\n   * Errors are caught and surfaced via {@link state} — the returned\n   * Promise still resolves to ``undefined`` rather than rejecting,\n   * because the typical caller is a click handler that doesn't await.\n   * Use ``state.kind === \"error\"`` to drive failure UI.\n   */\n  download: () => Promise<Blob | undefined>;\n  /** Reset to ``idle``.  Does *not* cancel an in-flight download. */\n  reset: () => void;\n}\n\n/**\n * Headless download primitive for a {@link Clip}.\n *\n * Wraps {@link downloadClipAsFile} in a React state machine so the\n * consumer can render any button they want, anywhere they want, and\n * still get progress + error feedback.  Used internally by\n * {@link ClipPlayer}'s built-in download button — when you need\n * custom placement or styling, set ``showDownloadButton={false}`` on\n * the player and call this hook directly.\n *\n * Stable callback identity: ``download`` and ``reset`` are the same\n * function reference across renders, so they're safe to pass through\n * memoized children without forcing re-renders.\n *\n * @example Render a download button anywhere in your tree:\n * ```tsx\n * function ClipCard({ clip }: { clip: Clip }) {\n *   const jwt = useReactor((s) => s.jwtToken);\n *   const { state, download } = useClipDownload(clip, {\n *     filename: `snap-${clip.sessionId}.mp4`,\n *     getJwt: jwt ? () => jwt : undefined,\n *   });\n *   return (\n *     <button onClick={download} disabled={state.kind === \"downloading\"}>\n *       {state.kind === \"downloading\"\n *         ? `${state.fetched}/${state.total}`\n *         : state.kind === \"error\"\n *           ? \"Retry\"\n *           : \"Download\"}\n *     </button>\n *   );\n * }\n * ```\n */\nexport function useClipDownload(\n  clip: Clip,\n  options: UseClipDownloadOptions = {}\n): UseClipDownloadResult {\n  const [state, setState] = useState<ClipDownloadState>({ kind: \"idle\" });\n\n  // `undefined` outside a `<ReactorProvider>`; used to inherit the\n  // provider's JWT resolver when `options.getJwt` is omitted.\n  const store = useContext(ReactorContext);\n\n  // Latest-value refs so `download` can be a stable callback (empty\n  // deps) without forcing the caller to memoize `clip` / `options`.\n  // This is the same pattern the SDK uses elsewhere (see\n  // useReactorMessage in hooks.ts).\n  const clipRef = useRef(clip);\n  const filenameRef = useRef<string | null>(\n    options.filename ?? \"reactor-clip.mp4\"\n  );\n  const getJwtRef = useRef(options.getJwt);\n  useEffect(() => {\n    clipRef.current = clip;\n    filenameRef.current =\n      options.filename === undefined ? \"reactor-clip.mp4\" : options.filename;\n    getJwtRef.current = options.getJwt;\n  });\n\n  // Re-entrancy guard.  Lives in a ref (not state) so back-to-back\n  // synchronous clicks before the first `setState` flushes are still\n  // handled correctly.\n  const inFlightRef = useRef(false);\n\n  const download = useCallback(async (): Promise<Blob | undefined> => {\n    if (inFlightRef.current) return undefined;\n    inFlightRef.current = true;\n    setState({ kind: \"downloading\", fetched: 0, total: 0 });\n    try {\n      // Explicit `options.getJwt` wins; fall back to the provider's\n      // resolver. Reading at click time picks up provider swaps\n      // without re-running this callback.\n      const explicit = getJwtRef.current;\n      const fallback = store?.getState().internal.reactor.getJwtResolver();\n      const resolver = explicit ?? fallback;\n      const jwt = resolver ? await resolver() : undefined;\n      const blob = await downloadClipAsFile(\n        clipRef.current,\n        filenameRef.current,\n        {\n          jwt,\n          onProgress: ({ fetched, total }) =>\n            setState({ kind: \"downloading\", fetched, total }),\n        }\n      );\n      setState({ kind: \"idle\" });\n      return blob;\n    } catch (err) {\n      const message =\n        err instanceof RecordingError\n          ? `${err.code}: ${err.reason}`\n          : err instanceof Error\n            ? err.message\n            : String(err);\n      setState({ kind: \"error\", message });\n      return undefined;\n    } finally {\n      inFlightRef.current = false;\n    }\n  }, [store]);\n\n  const reset = useCallback(() => setState({ kind: \"idle\" }), []);\n\n  return { state, download, reset };\n}\n"],"mappings":"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","names":["R","_a","e","z","z","z","z","track","_a","current","useEffect","useRef","useState","useEffect","useMemo","useRef","jsx","useRef","useMemo","useEffect","useState","useRef","jsx","useState","useRef","_a","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useContext","useEffect","useRef","useState","jsx","jsxs","useRef","useState","useContext","useEffect","useEffect","useRef","useCallback","useContext","useEffect","useRef","useState","useState","useContext","useRef","useEffect","useCallback","jsx","useRef","useEffect"]}