openrtc 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +26 -3
  2. package/dist/DelegatingRuntimeAdapter-CjHzBEBA.d.ts +180 -0
  3. package/dist/{IpcRuntimeAdapter-CT-W0Mtp.d.ts → IpcRuntimeAdapter-CtckyMol.d.ts} +2 -1
  4. package/dist/auth/index.js +1 -1
  5. package/dist/auth/internal.js +1 -1
  6. package/dist/chunk-3VTF5RKW.js +1 -0
  7. package/dist/chunk-JXGKO7SX.js +1 -0
  8. package/dist/chunk-MFT6SOZ6.js +3 -0
  9. package/dist/chunk-SGZWR63E.js +1 -0
  10. package/dist/chunk-VARALO7G.js +1 -0
  11. package/dist/chunk-WOHFM6DI.js +2 -0
  12. package/dist/chunk-YHJOXD46.js +2 -0
  13. package/dist/{factories-0TxDPMeu.d.ts → factories-CwZiIRg1.d.ts} +412 -226
  14. package/dist/index.d.ts +100 -6
  15. package/dist/index.js +2 -1
  16. package/dist/openrtc_bg.wasm +0 -0
  17. package/dist/runtime/WasmRuntimeAdapter.d.ts +2 -1
  18. package/dist/runtime/WasmRuntimeAdapter.js +1 -1
  19. package/dist/runtime/index.d.ts +205 -391
  20. package/dist/runtime/index.js +1 -1
  21. package/dist/runtime/tauri.d.ts +5 -6
  22. package/dist/runtime/tauri.js +1 -1
  23. package/dist/transport/index.d.ts +11 -0
  24. package/dist/transport/index.js +1 -0
  25. package/dist/{DelegatingRuntimeAdapter-CW8d78dZ.d.ts → types-DyCGvj9l.d.ts} +200 -170
  26. package/package.json +12 -10
  27. package/dist/chunk-4MH42GUI.js +0 -2
  28. package/dist/chunk-CQAKCS2S.js +0 -4
  29. package/dist/chunk-D54VGRRH.js +0 -1
  30. package/dist/chunk-DIQYFAGL.js +0 -2
  31. package/dist/chunk-PZ25OCVG.js +0 -2
  32. package/dist/chunk-UVASANPG.js +0 -1
  33. package/wasm/README.md +0 -64
  34. package/wasm/openrtc.d.ts +0 -320
  35. package/wasm/openrtc.js +0 -2290
  36. package/wasm/openrtc_bg.wasm +0 -0
  37. package/wasm/openrtc_bg.wasm.d.ts +0 -113
  38. package/wasm/package.json +0 -16
@@ -1,145 +1,8 @@
1
- import { l as ChannelDescriptor$1, i as ProtocolName, L as LocalDeviceInfo, I as IncomingStreamChannelMetadata, k as ClientOptions, m as Device, n as SignalingSession, o as RuntimeIdentity, p as DeviceStatusSnapshot, M as ManagedDeviceConnectOptions, q as PeerState, r as PeerHealth, J as JoinRoomOptions, s as RoomMember, t as PeerScope, B as BackendConnectionState, A as AuthContext, u as PeerLifecycleStatus, P as PeerSessionSnapshot, v as ConnectionOptions, G as GrantScope, w as ISignalingBackend, c as RuntimeCapabilities, a as ConnectDeviceHooks, b as ConnectDeviceOptions, C as ConnectDeviceResult, x as ManagedConnectionRecord, R as ResolvedPeerIdentity, d as AppLimits, N as NativeConnectResult, E as ExplicitFileDataSendParams } from './DelegatingRuntimeAdapter-CW8d78dZ.js';
2
- import { P as PlutoIpcBridge, I as IpcRuntimeAdapter } from './IpcRuntimeAdapter-CT-W0Mtp.js';
1
+ import { A as AuthContext, R as RuntimeCapabilities, a as RuntimeCapabilityProfile } from './DelegatingRuntimeAdapter-CjHzBEBA.js';
2
+ import { J as ConnectionOptions, q as ApplicationPayloadCrypto, v as ApplicationPayloadReadinessOptions, w as ApplicationPayloadRouteOptions, n as ProtocolName, x as ApplicationPayloadSendOptions, K as ChannelDescriptor$1, L as LocalDeviceInfo, O as IncomingStreamChannelMetadata, C as ClientOptions, a as Device, f as SignalingSession, R as RuntimeIdentity, b as DeviceStatusSnapshot, Q as ManagedDeviceConnectOptions, T as PeerState, U as PeerHealth, V as JoinRoomOptions, W as RoomMember, X as PeerScope, j as BackendConnectionState, Y as PeerLifecycleStatus, P as PeerSessionSnapshot, G as GrantScope, I as ISignalingBackend, l as ConnectDeviceHooks, m as ConnectDeviceOptions, k as ConnectDeviceResult, M as ManagedConnectionRecord, c as ResolvedPeerIdentity, t as AppLimits, i as NativeConnectResult, E as ExplicitFileDataSendParams } from './types-DyCGvj9l.js';
3
+ import { P as PlutoIpcBridge, I as IpcRuntimeAdapter } from './IpcRuntimeAdapter-CtckyMol.js';
3
4
  import { WasmRuntimeAdapter } from './runtime/WasmRuntimeAdapter.js';
4
5
 
5
- /**
6
- * App-facing options intentionally hide transport node identifiers from callbacks.
7
- */
8
- type RuntimeClientOptions = Omit<ClientOptions, 'onDevices' | 'onAutoConnect' | 'onIncomingSession'> & {
9
- onDevices?: (devices: RuntimeDevice[]) => void;
10
- onAutoConnect?: (devices: RuntimeDevice[]) => void;
11
- onIncomingSession?: (session: RuntimeSession) => Promise<void>;
12
- };
13
- /**
14
- * App-facing device model. Runtime transport identifiers stay internal.
15
- */
16
- type RuntimeDevice = Omit<Device, 'nodeId'>;
17
- /**
18
- * App-facing session model. Node identifiers are runtime-internal metadata.
19
- */
20
- type RuntimeSession = Omit<SignalingSession, 'initiatorNodeId' | 'targetNodeId' | 'appTag'>;
21
- type RuntimeAuthIdentity = RuntimeIdentity;
22
- type RuntimeAuthContext = AuthContext;
23
- /**
24
- * App-facing peer state model. Runtime transport identifiers stay internal.
25
- */
26
- type RuntimePeerState = Omit<PeerState, 'nodeId'>;
27
- type RuntimePeerHealth = PeerHealth;
28
- type RuntimePeerLifecycleStatus = PeerLifecycleStatus;
29
- type RuntimePeerScope = PeerScope;
30
- /**
31
- * App-facing peer session snapshot model. Runtime transport identifiers stay internal.
32
- */
33
- type RuntimePeerSessionSnapshot = Omit<PeerSessionSnapshot, 'nodeId'>;
34
- type RuntimeLocalDeviceInfo = LocalDeviceInfo;
35
- type RuntimeRoomMember = RoomMember;
36
- type RuntimeDeviceConnectOptions = ManagedDeviceConnectOptions;
37
- type RuntimeJoinRoomOptions = JoinRoomOptions;
38
- type RuntimeChannelDescriptor = ChannelDescriptor$1;
39
- interface RuntimeConnectionTarget {
40
- ticket: string;
41
- timeoutMs?: number;
42
- }
43
- interface RuntimePeerConnectOptions {
44
- deviceId?: string;
45
- ticket?: string;
46
- scope?: RuntimePeerScope;
47
- channelId?: string;
48
- }
49
- interface RuntimePeerStreamOptions {
50
- ticket?: string;
51
- scope?: RuntimePeerScope;
52
- channelId?: string;
53
- timeoutMs?: number;
54
- }
55
- /**
56
- * App-facing device status snapshot model. Runtime transport identifiers stay internal.
57
- */
58
- type RuntimeDeviceStatusSnapshot = Omit<DeviceStatusSnapshot, 'nodeId'>;
59
- type RuntimeDeviceStatus = RuntimeDeviceStatusSnapshot;
60
- type RuntimeIncomingStream = {
61
- type: 'bi' | 'uni';
62
- stream: any;
63
- remoteNodeId: string;
64
- protocolHint?: 'control' | 'explicit' | 'native-main' | 'unknown';
65
- channel?: IncomingStreamChannelMetadata | null;
66
- };
67
- type RuntimeConnectionEvent = {
68
- type: 'connected' | 'disconnected';
69
- connectionId: string;
70
- localNodeId: string;
71
- remoteNodeId: string;
72
- transport: ProtocolName;
73
- };
74
- type RuntimeRawConnection = {
75
- connectionId: string;
76
- localNodeId: string;
77
- remoteNodeId: string;
78
- };
79
- type RuntimePresenceLoopOptions = {
80
- deviceName?: string;
81
- ticket?: string;
82
- metadata?: string;
83
- };
84
- type RuntimeManagedSessionPlatform = 'native' | 'web';
85
- type RuntimeManagedSessionKind = 'app-user-device' | 'share-anonymous' | 'drive-grant-guest' | 'scoped-guest' | (string & {});
86
- type RuntimeAutoConnectPolicy = 'enabled' | 'disabled' | 'scoped-only';
87
- type RuntimePresencePolicy = 'online' | 'disabled';
88
- type RuntimeConnectionStateSnapshot = BackendConnectionState;
89
- type RuntimeUiConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'failed';
90
- interface RuntimePeerReadinessSnapshot {
91
- requestedId: string;
92
- connected: boolean;
93
- settledReady: boolean;
94
- status: RuntimeUiConnectionStatus;
95
- health: RuntimePeerHealth | 'unknown';
96
- deviceId?: string | null;
97
- deviceIdHint?: string | null;
98
- activeConnectionId?: string | null;
99
- readinessState?: string;
100
- readinessReason?: string;
101
- scopes: string[];
102
- }
103
- interface RuntimeLocalDeviceProfile {
104
- deviceId: string | null;
105
- deviceName: string;
106
- platformType: string;
107
- userId: string | null;
108
- capabilities: NonNullable<RuntimeLocalDeviceInfo['capabilities']>;
109
- lastSeenAt: string;
110
- localNodeId?: string | null;
111
- }
112
- interface RuntimeManagedSessionOptions {
113
- platform: RuntimeManagedSessionPlatform;
114
- sessionKind?: RuntimeManagedSessionKind;
115
- scope?: string | null;
116
- autoConnectPolicy?: RuntimeAutoConnectPolicy;
117
- presencePolicy?: RuntimePresencePolicy;
118
- userId?: string | null;
119
- deviceName?: string;
120
- localDeviceId?: string | null;
121
- metadata?: string;
122
- autoConnect?: boolean;
123
- presence?: boolean;
124
- }
125
- interface RuntimeManagedSession {
126
- mode: RuntimeManagedSessionPlatform;
127
- device: RuntimeLocalDeviceProfile;
128
- stop: () => Promise<void>;
129
- }
130
- type ConnectionTarget = RuntimeConnectionTarget;
131
- type PeerConnectOptions = RuntimePeerConnectOptions;
132
- type PeerStreamOptions = RuntimePeerStreamOptions;
133
- type IncomingStream = RuntimeIncomingStream;
134
- type ConnectionEvent = RuntimeConnectionEvent;
135
- type RawConnection = RuntimeRawConnection;
136
- type PresenceLoopOptions = RuntimePresenceLoopOptions;
137
- type PeerReadiness = RuntimePeerReadinessSnapshot;
138
- type DeviceProfile = RuntimeLocalDeviceProfile;
139
- type SessionOptions$1 = RuntimeManagedSessionOptions;
140
- type Session$1 = RuntimeManagedSession;
141
- type ChannelDescriptor = RuntimeChannelDescriptor;
142
-
143
6
  interface ITransport {
144
7
  readonly name: string;
145
8
  readonly isReady: boolean;
@@ -163,16 +26,21 @@ declare class Connection {
163
26
  private static readonly textEncoder;
164
27
  private static readonly UPGRADE_RETRY_DELAY_MS;
165
28
  private static readonly MAX_UPGRADE_RETRIES;
29
+ private static readonly MAX_UPGRADE_REDRIVES;
30
+ private static readonly UPGRADE_REDRIVE_DELAY_MS;
166
31
  private static readonly WEBRTC_DISCONNECTED_GRACE_MS;
167
32
  private static readonly MAX_PENDING_MESSAGES;
33
+ private static readonly DEFAULT_APPLICATION_CRYPTO_WAIT_MS;
168
34
  /**
169
35
  * Must exceed `WEBRTC_ICE_CONNECT_TIMEOUT_MS` in `WebRTCTransport`: the transport
170
36
  * waits that long for ICE + DataChannel before `failed`. A shorter watchdog here
171
37
  * tears the PC down early, burns `MAX_UPGRADE_RETRIES`, and stretches upgrades to ~60s+ under load.
172
38
  */
173
39
  private static readonly WEBRTC_UPGRADE_ATTEMPT_TIMEOUT_MS;
40
+ private static readonly MAX_PENDING_ENCRYPTED_APPLICATION_PAYLOADS;
174
41
  private listeners;
175
42
  private pendingMessages;
43
+ private pendingEncryptedApplicationPayloads;
176
44
  private webrtcBinaryHandlers;
177
45
  private closeListeners;
178
46
  private upgradeStateListeners;
@@ -188,34 +56,61 @@ declare class Connection {
188
56
  readonly localNodeId: string;
189
57
  private options;
190
58
  private writeMutex;
59
+ private applicationCryptoWaiters;
191
60
  private nativeSignalSender;
192
61
  private webrtcTransport;
62
+ private webRtcApplicationRouteReadyTransport;
63
+ private webRtcApplicationRouteReadyNegotiationId;
193
64
  private upgradeState;
194
65
  private upgradeRequested;
66
+ private negotiationSwitchCount;
195
67
  private upgradeAttemptActive;
196
68
  private upgradeRetryCount;
69
+ private upgradeReDriveCount;
70
+ private upgradeReDriveTimer;
197
71
  private upgradeRetryTimer;
198
72
  private upgradeAttemptTimeoutTimer;
199
73
  private lastReportedTransportStatus;
74
+ private applicationRouteMissingNotified;
200
75
  private upgradeTerminalFailure;
201
76
  private pendingSignals;
202
77
  private activeNegotiationId;
203
78
  private webrtcHeartbeat;
204
79
  private webRtcHeartbeatProbeInFlight;
80
+ private webRtcApplicationRouteProbeInFlight;
205
81
  private webRtcHeartbeatConsecutiveProbeFailures;
206
82
  private irohHealthListeners;
207
83
  private webrtcHealthListeners;
208
84
  private lastWebRtcHeartbeatOkAt;
209
85
  private lastWebRtcDataChannelOpenAt;
86
+ private static readonly WEBRTC_RENEGOTIATE_LIVENESS_WINDOW_MS;
87
+ private static readonly MAX_NEGOTIATION_SWITCHES;
210
88
  private lastForegroundAt;
211
89
  private webRtcDisconnectedGraceTimer;
212
90
  private preferredWebRtcRole;
213
91
  constructor(id: string, localNodeId: string, remoteNodeId: string, writer: WritableStreamDefaultWriter<Uint8Array>, reader: ReadableStreamDefaultReader<Uint8Array>, options?: ConnectionOptions);
92
+ setApplicationCrypto(applicationCrypto: ApplicationPayloadCrypto | undefined): void;
93
+ requireApplicationCrypto(): void;
94
+ isApplicationCryptoRequired(): boolean;
95
+ isReadyForApplicationPayload(options?: ApplicationPayloadReadinessOptions): boolean;
96
+ getApplicationPayloadTransports(options?: ApplicationPayloadRouteOptions): ProtocolName[];
97
+ private isTransportAvailableForApplicationPayload;
98
+ private resolveApplicationCryptoWaiters;
99
+ private rejectApplicationCryptoWaiters;
100
+ private waitForRequiredApplicationCrypto;
214
101
  /**
215
102
  * Starts WebRTC upgrade only when peer capability negotiation allows it.
216
103
  * No-op when WebRTC is not configured or an upgrade already started.
217
104
  */
218
105
  requestWebRTCUpgrade(reason?: string): void;
106
+ /**
107
+ * Whether the current WebRTC route is provably alive: an open data channel
108
+ * plus either a recent heartbeat round trip or a freshly opened channel. An
109
+ * `open` readyState alone is not proof — it lingers for many seconds after the
110
+ * remote vanishes (e.g. a page reload), so a dead-but-`open` channel reports
111
+ * not-live and can be replaced rather than trusted.
112
+ */
113
+ private isWebRtcRouteHeartbeatLive;
219
114
  requestTransportRecovery(reason: string, options?: {
220
115
  forceRenegotiate?: boolean;
221
116
  replaceExisting?: boolean;
@@ -248,6 +143,8 @@ declare class Connection {
248
143
  candidateTypeSummary: string | null;
249
144
  } | null;
250
145
  }>;
146
+ private lastWebRtcLocalCandidateType;
147
+ private lastWebRtcRemoteCandidateType;
251
148
  get transportType(): ProtocolName;
252
149
  private _peerMoQReady;
253
150
  get peerMoQReady(): boolean;
@@ -260,7 +157,7 @@ declare class Connection {
260
157
  * Send a message to the other peer.
261
158
  * Messages can be strings or objects (JSON stringified).
262
159
  */
263
- send(message: string | object | ArrayBuffer | Uint8Array): Promise<void>;
160
+ send(message: string | object | ArrayBuffer | Uint8Array, options?: ApplicationPayloadSendOptions): Promise<void>;
264
161
  /**
265
162
  * Send media data (video/audio header + payload)
266
163
  * Internal use for now.
@@ -271,24 +168,41 @@ declare class Connection {
271
168
  * Throws when the requested transport is unavailable.
272
169
  */
273
170
  sendOnTransport(transport: ProtocolName, message: string | object | ArrayBuffer | Uint8Array): Promise<void>;
171
+ /**
172
+ * Send plaintext control traffic that is required to bootstrap application
173
+ * key agreement. This is intentionally separate from public app-data sends.
174
+ */
175
+ sendControl(message: string | object | ArrayBuffer | Uint8Array): Promise<void>;
274
176
  /**
275
177
  * Write an already-framed payload directly on the underlying iroh control stream.
276
178
  * Used by native-main signaling paths that must preserve protocol framing.
277
179
  */
278
180
  sendRawIrohFrame(frame: Uint8Array): Promise<void>;
279
181
  /**
280
- * Return currently usable transports for this connection.
182
+ * Compatibility/diagnostic view of currently usable transports.
183
+ *
184
+ * Lifecycle projection and route selection must use getTransportStatus().
185
+ * This legacy list intentionally remains for older callers and tests that
186
+ * need a human-readable inventory, but it is not an authority for active
187
+ * versus fallback transport state.
281
188
  */
282
189
  getAvailableTransports(): ProtocolName[];
283
190
  onWebRTCBinaryMessage(handler: (data: Uint8Array) => boolean): void;
191
+ private dispatchWebRtcBinaryHandlers;
284
192
  getWebRTCTransport(): ITransport | null;
285
193
  sendOnWebRTC(data: Uint8Array): Promise<void>;
286
194
  private encodePayload;
195
+ private protectApplicationPayload;
196
+ private openApplicationPayload;
197
+ private openUntypedApplicationPayload;
198
+ private queueEncryptedApplicationPayload;
199
+ private flushPendingEncryptedApplicationPayloads;
287
200
  private buildFrame;
288
201
  private sendOverIroh;
289
202
  private sendUsingTransport;
290
203
  private sendTypedOnTransport;
291
204
  private sendTyped;
205
+ private getSendTransportPriorities;
292
206
  /**
293
207
  * Disconnect the connection
294
208
  */
@@ -310,6 +224,7 @@ declare class Connection {
310
224
  private readLoop;
311
225
  injectData(chunk: Uint8Array): void;
312
226
  private handleData;
227
+ private tryHandlePlaintextNativeSignal;
313
228
  private static isUntypedDataFrame;
314
229
  private processDataPayload;
315
230
  private isLikelyTextPayload;
@@ -318,6 +233,8 @@ declare class Connection {
318
233
  receiveMessage(msg: any): void;
319
234
  receiveDataPayload(messageData: Uint8Array): void;
320
235
  private trackListeners;
236
+ private warnedSrtpApplicationCryptoBoundary;
237
+ private warnSrtpApplicationCryptoBoundary;
321
238
  addTrack(track: MediaStreamTrack): RTCRtpSender | null;
322
239
  addTransceiver(trackOrKind: MediaStreamTrack | string, init?: RTCRtpTransceiverInit): RTCRtpTransceiver | null;
323
240
  onTrack(callback: (event: RTCTrackEvent) => void): void;
@@ -325,10 +242,12 @@ declare class Connection {
325
242
  private isWebRtcDataChannelOpen;
326
243
  private clearWebRtcDisconnectedGraceTimer;
327
244
  private transportStillLikelyAlive;
328
- private handleDisconnectedTransportStateWithGrace;
245
+ private handleUnhealthyWebRtcTransportStateWithGrace;
329
246
  private attemptUpgrade;
330
247
  private setUpgradeState;
331
248
  private handleUpgradeFailure;
249
+ private scheduleUpgradeReDrive;
250
+ private clearUpgradeReDriveTimer;
332
251
  private shouldRetryUpgrade;
333
252
  private scheduleUpgradeRetry;
334
253
  private clearUpgradeRetryTimer;
@@ -337,14 +256,74 @@ declare class Connection {
337
256
  private teardownWebRTCTransport;
338
257
  private startWebRtcHeartbeat;
339
258
  private stopWebRtcHeartbeat;
259
+ private notifyWebRtcApplicationRouteMissing;
260
+ private startWebRtcApplicationRouteProbe;
340
261
  private sendIrohHeartbeatPong;
341
262
  /** Register a listener for WebRTC heartbeat health changes. */
342
263
  onWebRtcHeartbeatHealth(cb: (health: ConnectionHealth) => void): void;
343
264
  /** Register a listener for iroh heartbeat health changes (WASM path). */
344
265
  onIrohHeartbeatHealth(cb: (health: ConnectionHealth) => void): void;
345
266
  reportTransportStatusIfChanged(): void;
267
+ /**
268
+ * Single connection-owned transport snapshot for status reporting,
269
+ * lifecycle projection, and diagnostics. The active route is application
270
+ * ready; the parallel route is its usable fallback or candidate.
271
+ */
272
+ getTransportStatus(): {
273
+ activeTransport: ProtocolName;
274
+ parallelTransport: ProtocolName | null;
275
+ };
346
276
  private resolveParallelTransport;
347
277
  setNativeSignalSender(sender: (envelope: any) => Promise<void>): void;
278
+ /**
279
+ * Structured connection-lifecycle log. One event per meaningful transition
280
+ * (upgrade requested/started/failed, route promoted, app route ready, …) with
281
+ * the connection's current lifecycle snapshot, so the whole connection flow
282
+ * can be reconstructed from logs. Emits both a human line and a JSON line.
283
+ */
284
+ private traceLifecycle;
285
+ getControlFrameMode(): NonNullable<ConnectionOptions['controlFrameMode']>;
286
+ /**
287
+ * Send a control envelope over the native-main signal route. Returns false
288
+ * (without sending) unless this connection is in `native-main` control-frame
289
+ * mode and has a native signal sender, so a typed connection never
290
+ * accidentally routes control through the native sender.
291
+ */
292
+ sendNativeControlEnvelope(envelope: any): Promise<boolean>;
293
+ /**
294
+ * Promote a connection to the native-main control plane: switch its
295
+ * control-frame mode to `native-main` and attach the native signal sender.
296
+ * Used when native-main evidence arrives for a connection that bootstrapped
297
+ * on a typed stream, so subsequent control traffic flows over the durable
298
+ * native-main route even if the bootstrap stream later closes.
299
+ */
300
+ promoteToNativeMainControlRoute(sender: (envelope: any) => Promise<void>): void;
301
+ private hasRequiredApplicationCrypto;
302
+ private clearWebRtcApplicationRouteReady;
303
+ private isCurrentWebRtcTransportApplicationRouteReady;
304
+ private isWebRtcApplicationRouteReadyForTransport;
305
+ /**
306
+ * Mark the upgraded WebRTC data plane as carrying a proven application route
307
+ * (data channel open + required crypto present). Idempotent. Promotes the
308
+ * active transport projection to `webrtc` via reportTransportStatusIfChanged.
309
+ */
310
+ private markWebRtcApplicationRouteReady;
311
+ /**
312
+ * True only when the WebRTC transport is upgraded AND its application route
313
+ * is proven (crypto present). The active route must not advertise `webrtc`
314
+ * until this holds, so a half-open data channel never looks routable.
315
+ */
316
+ isWebRtcApplicationRouteReady(): boolean;
317
+ /**
318
+ * MoQ has a different data-plane shape than WebRTC: it is a shared relay
319
+ * session on the Client plus a peer capability/subscription bit on this
320
+ * Connection. Even so, it follows the same lifecycle invariant as every
321
+ * custom transport: do not advertise it as the active application route until
322
+ * the local transport is ready, the peer route is enabled, and the required
323
+ * application crypto is available. Future MoQ route probes should tighten
324
+ * this predicate, not bypass it.
325
+ */
326
+ isMoQApplicationRouteReady(): boolean;
348
327
  private sendInternalSignal;
349
328
  receiveInternalSignal(content: any): void;
350
329
  private handleInternalSignal;
@@ -353,6 +332,202 @@ declare class Connection {
353
332
  private normalizeNegotiationId;
354
333
  }
355
334
 
335
+ /**
336
+ * Coalesced peer reconciliation scheduler and full reconcile orchestration.
337
+ *
338
+ * Internal `Client` triggers schedule through `schedulePeerReconciliation()` so
339
+ * concurrent refresh requests share one in-flight `runPeerReconciliation` pass.
340
+ */
341
+
342
+ type PeerReconciliationStats = {
343
+ scheduled: number;
344
+ coalesced: number;
345
+ ran: number;
346
+ orphanTsConnection: number;
347
+ orphanRustRecord: number;
348
+ orphanTsConnectionHealed: number;
349
+ };
350
+
351
+ /**
352
+ * App-facing options intentionally hide transport node identifiers from callbacks.
353
+ */
354
+ type RuntimeClientOptions = Omit<ClientOptions, 'onDevices' | 'onAutoConnect' | 'onIncomingSession'> & {
355
+ onDevices?: (devices: RuntimeDevice[]) => void;
356
+ onAutoConnect?: (devices: RuntimeDevice[]) => void;
357
+ onIncomingSession?: (session: RuntimeSession) => Promise<void>;
358
+ };
359
+ /**
360
+ * App-facing device model. Runtime transport identifiers stay internal.
361
+ */
362
+ type RuntimeDevice = Omit<Device, 'nodeId'>;
363
+ /**
364
+ * App-facing session model. Node identifiers are runtime-internal metadata.
365
+ */
366
+ type RuntimeSession = Omit<SignalingSession, 'initiatorNodeId' | 'targetNodeId' | 'appTag'>;
367
+ type RuntimeAuthIdentity = RuntimeIdentity;
368
+ type RuntimeAuthContext = AuthContext;
369
+ /**
370
+ * App-facing peer state model. Runtime transport identifiers stay internal.
371
+ */
372
+ type RuntimePeerState = Omit<PeerState, 'nodeId'>;
373
+ type RuntimePeerHealth = PeerHealth;
374
+ type RuntimePeerLifecycleStatus = PeerLifecycleStatus;
375
+ type RuntimePeerScope = PeerScope;
376
+ /**
377
+ * App-facing peer session snapshot model. Runtime transport identifiers stay internal.
378
+ */
379
+ type RuntimePeerSessionSnapshot = Omit<PeerSessionSnapshot, 'nodeId'>;
380
+ type RuntimeLocalDeviceInfo = LocalDeviceInfo;
381
+ type RuntimeRoomMember = RoomMember;
382
+ type RuntimeDeviceConnectOptions = ManagedDeviceConnectOptions;
383
+ type RuntimeJoinRoomOptions = JoinRoomOptions;
384
+ type RuntimeChannelDescriptor = ChannelDescriptor$1;
385
+ interface RuntimeConnectionTarget {
386
+ ticket: string;
387
+ timeoutMs?: number;
388
+ }
389
+ interface RuntimePeerConnectOptions {
390
+ deviceId?: string;
391
+ ticket?: string;
392
+ scope?: RuntimePeerScope;
393
+ channelId?: string;
394
+ /**
395
+ * When true, automatic/observer dials fail fast if the peer has a sticky
396
+ * manual-disconnect projection for this app session. User-initiated manual
397
+ * reconnects should leave this unset and clear the exclusion explicitly.
398
+ */
399
+ respectManualDisconnect?: boolean;
400
+ }
401
+ interface RuntimePeerStreamOptions {
402
+ ticket?: string;
403
+ scope?: RuntimePeerScope;
404
+ channelId?: string;
405
+ timeoutMs?: number;
406
+ }
407
+ type RuntimeLogicalChannelFraming = 'raw' | 'u32be';
408
+ interface RuntimeScopedChannelOptions {
409
+ peerId?: string;
410
+ peerNodeId?: string;
411
+ deviceId?: string | null;
412
+ ticket?: string;
413
+ scope: RuntimePeerScope;
414
+ channelId: string;
415
+ timeoutMs?: number;
416
+ respectManualDisconnect?: boolean;
417
+ /**
418
+ * Native product protocols such as Plutonium drive-view expect a plaintext
419
+ * stream label before payload frames. OpenRTC owns that handshake here so
420
+ * apps do not need to choose raw transport APIs.
421
+ */
422
+ nativeLabel?: boolean;
423
+ framing?: RuntimeLogicalChannelFraming;
424
+ }
425
+ interface RuntimeScopedLogicalChannel {
426
+ channelId: string;
427
+ peerId: string;
428
+ connection: Connection;
429
+ send(payload: Uint8Array): Promise<void>;
430
+ onMessage(callback: (payload: Uint8Array) => void): () => void;
431
+ close(): Promise<void>;
432
+ diagnostics(): {
433
+ connectionId: string | null;
434
+ remoteNodeId: string | null;
435
+ transports: string[];
436
+ framing: RuntimeLogicalChannelFraming;
437
+ nativeLabel: boolean;
438
+ };
439
+ }
440
+ /**
441
+ * App-facing device status snapshot model. Runtime transport identifiers stay internal.
442
+ */
443
+ type RuntimeDeviceStatusSnapshot = Omit<DeviceStatusSnapshot, 'nodeId'>;
444
+ type RuntimeDeviceStatus = RuntimeDeviceStatusSnapshot;
445
+ type RuntimeIncomingStream = {
446
+ type: 'bi' | 'uni';
447
+ stream: any;
448
+ remoteNodeId: string;
449
+ protocolHint?: 'control' | 'explicit' | 'native-main' | 'unknown';
450
+ channel?: IncomingStreamChannelMetadata | null;
451
+ };
452
+ type RuntimeConnectionEvent = {
453
+ type: 'connected' | 'disconnected';
454
+ connectionId: string;
455
+ localNodeId: string;
456
+ remoteNodeId: string;
457
+ transport: ProtocolName;
458
+ };
459
+ type RuntimeRawConnection = {
460
+ connectionId: string;
461
+ localNodeId: string;
462
+ remoteNodeId: string;
463
+ };
464
+ type RuntimePresenceLoopOptions = {
465
+ deviceName?: string;
466
+ ticket?: string;
467
+ metadata?: string;
468
+ };
469
+ type RuntimeManagedSessionPlatform = 'native' | 'web';
470
+ type RuntimeManagedSessionKind = 'app-user-device' | 'share-anonymous' | 'drive-grant-guest' | 'scoped-guest' | (string & {});
471
+ type RuntimeAutoConnectPolicy = 'enabled' | 'disabled' | 'scoped-only';
472
+ type RuntimePresencePolicy = 'online' | 'disabled';
473
+ type RuntimeConnectionStateSnapshot = BackendConnectionState;
474
+ type RuntimeApplicationPayloadReadinessOptions = ApplicationPayloadReadinessOptions;
475
+ type RuntimeUiConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'failed';
476
+ interface RuntimePeerReadinessSnapshot {
477
+ requestedId: string;
478
+ connected: boolean;
479
+ settledReady: boolean;
480
+ status: RuntimeUiConnectionStatus;
481
+ health: RuntimePeerHealth | 'unknown';
482
+ deviceId?: string | null;
483
+ deviceIdHint?: string | null;
484
+ activeConnectionId?: string | null;
485
+ readinessState?: string;
486
+ readinessReason?: string;
487
+ scopes: string[];
488
+ }
489
+ interface RuntimeLocalDeviceProfile {
490
+ deviceId: string | null;
491
+ deviceName: string;
492
+ platformType: string;
493
+ userId: string | null;
494
+ capabilities: NonNullable<RuntimeLocalDeviceInfo['capabilities']>;
495
+ lastSeenAt: string;
496
+ localNodeId?: string | null;
497
+ }
498
+ interface RuntimeManagedSessionOptions {
499
+ platform: RuntimeManagedSessionPlatform;
500
+ sessionKind?: RuntimeManagedSessionKind;
501
+ scope?: string | null;
502
+ autoConnectPolicy?: RuntimeAutoConnectPolicy;
503
+ presencePolicy?: RuntimePresencePolicy;
504
+ userId?: string | null;
505
+ deviceName?: string;
506
+ localDeviceId?: string | null;
507
+ metadata?: string;
508
+ autoConnect?: boolean;
509
+ presence?: boolean;
510
+ }
511
+ interface RuntimeManagedSession {
512
+ mode: RuntimeManagedSessionPlatform;
513
+ device: RuntimeLocalDeviceProfile;
514
+ stop: () => Promise<void>;
515
+ }
516
+ type ConnectionTarget = RuntimeConnectionTarget;
517
+ type PeerConnectOptions = RuntimePeerConnectOptions;
518
+ type PeerStreamOptions = RuntimePeerStreamOptions;
519
+ type ScopedChannelOptions = RuntimeScopedChannelOptions;
520
+ type ScopedLogicalChannel = RuntimeScopedLogicalChannel;
521
+ type IncomingStream = RuntimeIncomingStream;
522
+ type ConnectionEvent = RuntimeConnectionEvent;
523
+ type RawConnection = RuntimeRawConnection;
524
+ type PresenceLoopOptions = RuntimePresenceLoopOptions;
525
+ type PeerReadiness = RuntimePeerReadinessSnapshot;
526
+ type DeviceProfile = RuntimeLocalDeviceProfile;
527
+ type SessionOptions$1 = RuntimeManagedSessionOptions;
528
+ type Session$1 = RuntimeManagedSession;
529
+ type ChannelDescriptor = RuntimeChannelDescriptor;
530
+
356
531
  interface SessionOptions {
357
532
  /**
358
533
  * Logical purpose of this session. Used as a peer-scope tag on all
@@ -430,6 +605,7 @@ declare class ScopedSession implements Session {
430
605
  disconnectPeer(peerId: string): Promise<void>;
431
606
  onIncomingStream(callback: (stream: RuntimeIncomingStream) => void): () => void;
432
607
  dispose(): void;
608
+ private releaseScopedPeersOnDispose;
433
609
  private assertNotDisposed;
434
610
  }
435
611
 
@@ -437,29 +613,29 @@ declare class ScopedSession implements Session {
437
613
  * Phase 4 of the OpenRTC auth + connection remediation plan
438
614
  * (`docs/plans/openrtc-auth-connection-remediation-plan.md`).
439
615
  *
440
- * `DriveGrantActorHandle` is the TS mirror of the Rust
441
- * `DriveGrantConnectionActor` (see `openrtc/crates/openrtc/src/client/drive_grant_actor.rs`).
442
- * It is the single observer-facing API for drive-grant guest connections:
443
- * `DriveViewClient`, `DriveShareRedeemPage`, and friends call this handle
444
- * instead of dialing/retrying directly. With one handle per
445
- * `(scope, peerNodeId)` key, multiple requesters that share a connection
446
- * coalesce their `ensureReady()` calls onto a single underlying dial
447
- * removing the lifecycle-replacement race documented in `logs-desktop-A.md`.
616
+ * `DriveGrantActorHandle` is the historical implementation behind the public
617
+ * `ScopedConnectionActorHandle` alias. It is a TypeScript observer-side dial
618
+ * coalescer for scoped protocol routes such as drive-grant guests and
619
+ * trusted-device drive-view. It mirrors the intended Rust actor shape but is
620
+ * not backed by the Rust actor today. With one handle per `(scope, peerNodeId)`
621
+ * key, participating callers coalesce `ensureReady()` onto one underlying
622
+ * `connectPeer` call. It is not a general peer lifecycle authority.
448
623
  *
449
624
  * ## Scope
450
625
  *
451
- * Phase 4 lands the handle + `RuntimeClient.getDriveGrantActor` accessor
452
- * + observer-side rewiring. The actor is **not yet** wired into the
453
- * underlying Rust dial path the legacy `connectPeer` codepath continues
454
- * to run (Phase 7 flips that). What changes today:
626
+ * The handle is not wired into the underlying Rust dial path; it delegates to
627
+ * the existing `connectPeer` path. It remains a transitional compatibility
628
+ * layer until the runtime chooses one actor implementation and wires it
629
+ * end-to-end. What it does today:
455
630
  *
456
- * - `DriveViewClient.ensureGrantConnected` calls
457
- * `runtimeClient.getDriveGrantActor(scope, peerNodeId).ensureReady()`,
458
- * then performs its existing `connectPeer` call inside the
459
- * handle-owned `dial` function. Multiple concurrent observers for the
460
- * same key thus coalesce to one `connectPeer` call.
461
- * - `DeviceContext` for `app-user-device` is bit-for-bit unchanged —
462
- * this file only loads when `sessionKind === 'drive-grant-guest'`.
631
+ * - `DriveGrantConnectionPipeline` and trusted-device drive-view acquisition call
632
+ * `runtimeClient.getScopedConnectionActor(scope, peerNodeId).ensureReady()`,
633
+ * then perform their existing `connectPeer` call inside the handle-owned
634
+ * `dial` function. Multiple concurrent observers for the same key thus
635
+ * coalesce to one `connectPeer` call.
636
+ * - `DeviceContext` for the managed `app-user-device` session remains
637
+ * separate. It owns app presence/auto-connect and must not call this
638
+ * observer coalescer directly.
463
639
  *
464
640
  * ## Coalescing semantics
465
641
  *
@@ -479,16 +655,22 @@ declare class ScopedSession implements Session {
479
655
  * | --(failure)--> `idle`
480
656
  * `<any>` --(shutdown)--> `closed`
481
657
  *
482
- * The Rust actor enforces the same shape; the two are intentionally kept
483
- * isomorphic so Phase 7 can collapse the TS handle into a thin observer
484
- * over the Rust actor without changing call sites.
658
+ * WebRTC upgrade/readiness is intentionally absent from this actor. The
659
+ * runtime `Connection`/peer lifecycle owns transport promotion. Actor callers
660
+ * may observe that lifecycle separately, but this handle must not become a
661
+ * second WebRTC-ready emitter.
662
+ *
663
+ * The Rust actor has a similar shape but is inactive scaffolding in the current
664
+ * workspace. Treat the two implementations as migration debt, not redundant
665
+ * production authorities.
485
666
  */
486
667
  type DriveGrantActorErrorKind = 'shutdown' | 'dial-failed' | 'not-ready' | 'logical-channel-not-implemented';
487
668
  declare class DriveGrantActorError extends Error {
488
669
  readonly kind: DriveGrantActorErrorKind;
489
670
  constructor(kind: DriveGrantActorErrorKind, message: string);
490
671
  }
491
- type DriveGrantWebRtcState = 'unknown' | 'disabled' | 'connecting' | 'connected' | 'failed';
672
+ type DriveGrantActorState = 'idle' | 'dialing' | 'base-ready' | 'protocol-ready' | 'degraded' | 'closed';
673
+ type DriveGrantPreferredTransport = 'webrtc' | 'base' | 'none';
492
674
  interface DriveGrantConnectionKey {
493
675
  scope: string;
494
676
  peerNodeId: string;
@@ -514,22 +696,23 @@ interface DriveGrantActorHandleOptions {
514
696
  interface DriveGrantLogicalChannelHandle {
515
697
  label: string;
516
698
  }
517
- type WebRtcListener = (state: DriveGrantWebRtcState) => void;
518
699
  declare class DriveGrantActorHandle {
519
700
  readonly key: DriveGrantConnectionKey;
520
701
  private readonly dial;
521
702
  private readonly close;
522
703
  private state;
523
704
  private inFlight;
524
- private webrtcState;
525
- private webrtcListeners;
526
705
  constructor(options: DriveGrantActorHandleOptions);
527
706
  /** Returns true if the actor has resolved a successful dial and is not shut down. */
528
707
  isReady(): boolean;
529
708
  /** Returns true after `shutdown()` has been called. */
530
709
  isClosed(): boolean;
710
+ getState(): DriveGrantActorState;
711
+ getPreferredTransport(): DriveGrantPreferredTransport;
712
+ markProtocolReady(): void;
713
+ markDegraded(): void;
531
714
  /**
532
- * Phase 7 correlation context. Always populated with
715
+ * Correlation context. Always populated with
533
716
  * `sessionKind=drive-grant-guest`, `scope`, `peerNodeId`, and
534
717
  * (when the scope encodes one) `grantId`.
535
718
  */
@@ -540,15 +723,6 @@ declare class DriveGrantActorHandle {
540
723
  * `shutdown()` flips state to `closed`.
541
724
  */
542
725
  ensureReady(): Promise<void>;
543
- /** Subscribe to WebRTC adoption state. Returns an unsubscribe fn. */
544
- observeWebRtc(listener: WebRtcListener): () => void;
545
- /** Current WebRTC state without subscribing. */
546
- getWebRtcState(): DriveGrantWebRtcState;
547
- /**
548
- * Test/Phase-7 hook: lifecycle code that learns of a WebRTC state
549
- * transition pushes it via this method so observers see the change.
550
- */
551
- setWebRtcState(state: DriveGrantWebRtcState): void;
552
726
  /**
553
727
  * Phase 6: returns a logical channel handle backed by the base iroh
554
728
  * transport. The host-side `DriveViewBaseIrohHandler` dispatches
@@ -565,8 +739,7 @@ declare class DriveGrantActorHandle {
565
739
  openLogicalChannelTestStub(label: string): Promise<DriveGrantLogicalChannelHandle>;
566
740
  /**
567
741
  * Idempotent shutdown. Subsequent `ensureReady()` rejects with
568
- * `shutdown`; subsequent `shutdown()` is a no-op. Observers receive
569
- * a final `'failed'` state so they can wire teardown side-effects.
742
+ * `shutdown`; subsequent `shutdown()` is a no-op.
570
743
  */
571
744
  shutdown(): Promise<void>;
572
745
  }
@@ -657,15 +830,17 @@ declare class RuntimeClient {
657
830
  private managedSessionTransition;
658
831
  private managedSessionStartInFlight;
659
832
  private readonly managedReconciliationTasks;
833
+ private managedReconciliationTimer;
834
+ private readonly managedReconciliationFollowupTimers;
660
835
  /**
661
- * Phase 4: per-`{scope, peerNodeId}` scoped-connection actor registry.
662
- * Lazily created; `null` until an observer first calls `getDriveGrantActor`
663
- * (legacy name) or `getScopedConnectionActor` (generic alias).
836
+ * The one per-`{scope, peerNodeId}` observer-side dial coalescer. It is
837
+ * intentionally separate from the peer lifecycle read model: it requests a
838
+ * scoped route through `connectPeer`, while `Client` owns connection state.
664
839
  *
665
- * Note: persistent same-account auto-connect (`app-user-device`) and
666
- * anonymous share flows (`share-anonymous`) do not use this coalescer.
840
+ * Persistent same-account auto-connect (`app-user-device`) and anonymous
841
+ * share flows (`share-anonymous`) do not use this grant-guest coalescer.
667
842
  */
668
- private driveGrantActorRegistry;
843
+ private scopedConnectionActorRegistry;
669
844
  private static describeTicketForLogs;
670
845
  private serializeManagedSessionOperation;
671
846
  constructor(options: RuntimeClientOptions, backend?: ISignalingBackend | null);
@@ -674,7 +849,6 @@ declare class RuntimeClient {
674
849
  private resolveManagedAutoConnectEnabled;
675
850
  private resolveManagedPresenceEnabled;
676
851
  private getEffectiveRuntimeUserId;
677
- private sleep;
678
852
  private fetchAndApplyAppLimits;
679
853
  private resolvePeerStreamTarget;
680
854
  private compatPeerConnectionFromTarget;
@@ -682,11 +856,14 @@ declare class RuntimeClient {
682
856
  private hasManagedSession;
683
857
  private usesNativeIpcBackend;
684
858
  private startManagedReconciliation;
859
+ private scheduleManagedReconciliationFollowups;
860
+ private stopManagedReconciliation;
685
861
  private notifyManagedReconciliation;
686
862
  reconcileNativeManagedSession(reason?: string): Promise<void>;
687
863
  private connectionStateSnapshotKey;
688
864
  private runNativeStartLoop;
689
865
  private isUnsupportedRuntimeAdapterError;
866
+ private isRecoverableNativeManagedSessionStartError;
690
867
  onIdentityChange(callback: (identity: RuntimeAuthIdentity | null) => void): () => void;
691
868
  signInAnonymously(): Promise<void>;
692
869
  signInWithPluto(): Promise<void>;
@@ -699,8 +876,10 @@ declare class RuntimeClient {
699
876
  localNodeId?: string;
700
877
  userId?: string;
701
878
  }>;
879
+ getPeerReconciliationStats(): Readonly<PeerReconciliationStats>;
702
880
  getRuntimePolicy(): RuntimePolicySnapshot;
703
881
  getRuntimeCapabilities(): RuntimeCapabilities;
882
+ getCapabilityProfile(): RuntimeCapabilityProfile;
704
883
  start(options?: {
705
884
  clearSessionTokens?: boolean;
706
885
  }): Promise<void>;
@@ -790,6 +969,7 @@ declare class RuntimeClient {
790
969
  */
791
970
  connectDevice(device: RuntimeDevice, options: RuntimeDeviceConnectOptions): Promise<ConnectDeviceResult>;
792
971
  watchPeerStates(callback: (peers: RuntimePeerState[]) => void): () => void;
972
+ watchPeerLifecycle(callback: (peers: RuntimePeerState[]) => void): () => void;
793
973
  getPeerState(id: string): RuntimePeerState | undefined;
794
974
  resolvePeerConnectionRecords(id: string): Promise<ManagedConnectionRecord[]>;
795
975
  getPeerSession(id: string): Promise<PeerSessionSnapshot | null>;
@@ -804,8 +984,9 @@ declare class RuntimeClient {
804
984
  getPeerScopes(id: string): string[];
805
985
  isSamePeer(a: string, b: string): boolean;
806
986
  refreshPeerSnapshot(): Promise<RuntimePeerState[]>;
807
- subscribeSessions(localDeviceId: string, callback: (session: RuntimeSession) => void): Promise<() => void>;
987
+ subscribeSessions(localDeviceId: string, callback: (session: RuntimeSession) => void | Promise<void>): Promise<() => void>;
808
988
  getAppLimits(): AppLimits;
989
+ private syncRoomRtdbPresenceFromBackend;
809
990
  createRoom(roomId?: string): Promise<string>;
810
991
  joinRoom(roomId: string, options?: RuntimeJoinRoomOptions): Promise<Connection[]>;
811
992
  leaveRoom(roomId: string): Promise<void>;
@@ -814,26 +995,24 @@ declare class RuntimeClient {
814
995
  /** @deprecated Use connect() (no-arg) for lifecycle, or connectTo() for peer connections */
815
996
  connectByTicket(target: RuntimeConnectionTarget): Promise<Connection>;
816
997
  connectPeer(options: RuntimePeerConnectOptions): Promise<Connection>;
998
+ connectScopedChannel(options: RuntimeScopedChannelOptions): Promise<RuntimeScopedLogicalChannel>;
817
999
  /**
818
- * Phase 4 of the OpenRTC auth + connection remediation plan:
819
- * returns the per-`{scope, peerNodeId}` actor handle that
820
- * drive-grant guest observers (DriveViewClient,
821
- * DriveShareRedeemPage) coalesce through. The first call for a
822
- * given key spawns the handle; subsequent calls return the same
1000
+ * Returns the per-`{scope, peerNodeId}` observer-side coalescer that
1001
+ * participating scoped guest callers (such as DriveViewClient) use. The
1002
+ * first call for a given key spawns the handle; subsequent calls return the same
823
1003
  * handle so concurrent observers share a single underlying dial.
824
1004
  *
825
- * The handle's `dial` function delegates to this RuntimeClient's
826
- * existing `connectPeer` API the actor is purely an observer
827
- * coalescer in Phase 4. Phase 7 collapses this onto the Rust
828
- * actor in `Client::drive_grant_actor_registry`.
1005
+ * The handle's `dial` function delegates to this RuntimeClient's existing
1006
+ * `connectPeer` API. It is a compatibility coalescer, not an independent
1007
+ * transport owner, and the Rust registry is not enabled by workspace
1008
+ * production startup.
829
1009
  *
830
- * Calling this from `app-user-device` paths is a programming bug
831
- * the registry is scoped to drive-grant-guest connections by
832
- * convention, and same-account auto-connect must not coalesce
833
- * onto a single shared dial (each device-pair has its own
834
- * lifecycle).
1010
+ * Managed `app-user-device` startup/presence/auto-connect must not call
1011
+ * this observer coalescer directly. Scoped protocol observers may use it
1012
+ * only with a narrow `dial` callback that delegates back through
1013
+ * `connectPeer`; the runtime `Client` remains the peer lifecycle owner.
835
1014
  */
836
- getDriveGrantActor(key: DriveGrantConnectionKey | {
1015
+ getScopedConnectionActor(key: DriveGrantConnectionKey | {
837
1016
  scope: string;
838
1017
  peerNodeId: string;
839
1018
  }, options?: {
@@ -851,37 +1030,26 @@ declare class RuntimeClient {
851
1030
  * router/disconnect handler to the connection object) can
852
1031
  * supply their own `dial` here. The actor's coalescer still
853
1032
  * ensures only one `dial(key)` runs even with N concurrent
854
- * `getDriveGrantActor(...).ensureReady()` callers — this just
1033
+ * `getScopedConnectionActor(...).ensureReady()` callers — this just
855
1034
  * lets the caller decide what "dial" means.
856
1035
  */
857
1036
  dial?: (key: DriveGrantConnectionKey) => Promise<void>;
858
1037
  }): DriveGrantActorHandle;
859
- /**
860
- * Domain-agnostic alias for `getDriveGrantActor`.
861
- *
862
- * OpenRTC’s per-`{scope, peerNodeId}` actor is a generic “scoped connection”
863
- * coalescer. Plutonium’s drive-grant flows were the first adopter, but other
864
- * app protocols (magic-link, user-device, etc.) may also want the same shape.
865
- *
866
- * Backward-compat: this is a thin wrapper with identical behavior.
867
- */
868
- getScopedConnectionActor(key: DriveGrantConnectionKey | {
869
- scope: string;
870
- peerNodeId: string;
871
- }, options?: Parameters<RuntimeClient['getDriveGrantActor']>[1]): DriveGrantActorHandle;
872
- /** Domain-agnostic alias for `shutdownDriveGrantActors`. */
1038
+ private assertScopedDialAllowed;
1039
+ private scopedDialCandidateIds;
1040
+ private hasManualDisconnectSuppression;
1041
+ /** Shuts down every scoped observer handle during managed-session teardown. */
873
1042
  shutdownScopedConnectionActors(): Promise<void>;
874
- /**
875
- * Phase 4: tear down all live drive-grant actors. Called from
876
- * managed-session teardown so a refresh between drive-grant guest
877
- * routes does not leak observers across navigations.
878
- */
879
- shutdownDriveGrantActors(): Promise<void>;
880
1043
  openPeerBi(id: string, options?: RuntimePeerStreamOptions): Promise<{
881
1044
  connection: Connection;
882
1045
  readable: ReadableStream<Uint8Array>;
883
1046
  writable: WritableStream<Uint8Array>;
884
1047
  }>;
1048
+ openPeerNativeBi(id: string, label: string, options?: RuntimePeerStreamOptions): Promise<{
1049
+ connection: Connection;
1050
+ readable: ReadableStream<Uint8Array>;
1051
+ writable: WritableStream<Uint8Array>;
1052
+ }>;
885
1053
  /** Like openPeerBi but skips the settled_ready requirement — usable for latency probes. */
886
1054
  openPeerBiTransportOnly(id: string, options?: RuntimePeerStreamOptions): Promise<{
887
1055
  connection: Connection;
@@ -929,8 +1097,15 @@ declare class RuntimeClient {
929
1097
  forceDisconnectPeer(id: string): Promise<void>;
930
1098
  getPeerHealth(id: string): Promise<RuntimePeerHealth>;
931
1099
  getConnectionStates(): Promise<RuntimeConnectionStateSnapshot[]>;
1100
+ /**
1101
+ * Subscribe to raw backend connection diagnostics.
1102
+ *
1103
+ * This compatibility surface intentionally does not drive peer lifecycle or
1104
+ * UI readiness; use watchPeerStates() for the reducer-owned lifecycle view.
1105
+ */
932
1106
  onConnectionStateChange(callback: (state: RuntimeConnectionStateSnapshot) => void): Promise<() => void> | (() => void);
933
1107
  getConnections(): Connection[];
1108
+ getApplicationReadyConnections(options?: RuntimeApplicationPayloadReadinessOptions): Connection[];
934
1109
  onConnection(callback: (connection: Connection) => void): () => void;
935
1110
  onDisconnection(callback: (connection: Connection) => void): () => void;
936
1111
  onConnectionEvent(callback: (event: RuntimeConnectionEvent) => void): () => void;
@@ -939,6 +1114,17 @@ declare class RuntimeClient {
939
1114
  connect(): Promise<void>;
940
1115
  /** Disconnect from the runtime (alias for stop()) */
941
1116
  disconnect(): void;
1117
+ /**
1118
+ * Terminal teardown of the runtime instance. Unlike stop()/disconnect()
1119
+ * (which only stop the listen loop and are used during normal operation),
1120
+ * destroy() tears down the underlying Client's coordinated dispose path —
1121
+ * document/window listeners, the periodic auth-refresh interval, and signaling
1122
+ * subscriptions (audit V2). Call this only when the runtime singleton is truly
1123
+ * going away (app shutdown, HMR dispose), never on a routine disconnect.
1124
+ */
1125
+ destroy(): void;
1126
+ /** Alias for destroy(); supports callers that expect a `dispose()` contract. */
1127
+ dispose(): void;
942
1128
  /** Alias for onIdentityChange */
943
1129
  onAuthChange(callback: (identity: RuntimeAuthIdentity | null) => void): () => void;
944
1130
  /** Alias for setAuthContext */
@@ -1038,4 +1224,4 @@ declare function createWasmClient(options: RuntimeFactoryOptions): PlutoRuntimeA
1038
1224
  */
1039
1225
  declare function createIpcClient(options: RuntimeFactoryOptions, bridge: PlutoIpcBridge): PlutoRuntimeAssembly<IpcRuntimeAdapter>;
1040
1226
 
1041
- export { DriveGrantActorError as $, type RuntimeAuthIdentity as A, type RuntimeAutoConnectPolicy as B, Connection as C, type DeviceProfile as D, type RuntimeConnectionTarget as E, type RuntimeDeviceStatus as F, type RuntimeJoinRoomOptions as G, type RuntimeManagedSession as H, type IncomingStream as I, type RuntimeManagedSessionKind as J, type RuntimeManagedSessionOptions as K, type RuntimeManagedSessionPlatform as L, type RuntimePeerLifecycleStatus as M, type RuntimePeerScope as N, OPENRTC_RUNTIME_POLICY as O, type PlutoRuntimeAssembly as P, type RuntimePeerSessionSnapshot as Q, type RuntimeClientOptions as R, type RuntimePresenceLoopOptions as S, type RuntimePresencePolicy as T, type RuntimeRoomMember as U, type RuntimeSession as V, type RuntimeUiConnectionStatus as W, ScopedSession as X, type Session as Y, type SessionOptions as Z, getRuntimePolicy as _, RuntimeClient as a, type DriveGrantActorErrorKind as a0, DriveGrantActorHandle as a1, type DriveGrantActorHandleOptions as a2, DriveGrantActorRegistry as a3, type DriveGrantClose as a4, type DriveGrantConnectionKey as a5, type DriveGrantDial as a6, type DriveGrantLogicalChannelHandle as a7, type DriveGrantWebRtcState as a8, type RuntimeFactoryOptions as a9, type Session$1 as aa, type SessionOptions$1 as ab, createIpcClient as ac, createWasmClient as ad, driveGrantConnectionKey as ae, driveGrantConnectionKeyToString as af, type RuntimeDevice as b, type RuntimeDeviceStatusSnapshot as c, type RuntimeDeviceConnectOptions as d, type RuntimeLocalDeviceInfo as e, type RuntimeLocalDeviceProfile as f, type RuntimePeerState as g, type RuntimePeerConnectOptions as h, type RuntimePeerReadinessSnapshot as i, type RuntimePeerHealth as j, type RuntimeChannelDescriptor as k, type RuntimePeerStreamOptions as l, type RuntimeIncomingStream as m, type RuntimePolicySnapshot as n, type RuntimeConnectionStateSnapshot as o, type RuntimeConnectionEvent as p, type RuntimeRawConnection as q, type ChannelDescriptor as r, type ConnectionEvent as s, type ConnectionTarget as t, type PeerConnectOptions as u, type PeerReadiness as v, type PeerStreamOptions as w, type PresenceLoopOptions as x, type RawConnection as y, type RuntimeAuthContext as z };
1227
+ export { type RuntimeSession as $, type PeerReadiness as A, type PeerStreamOptions as B, Connection as C, type DeviceProfile as D, type PresenceLoopOptions as E, type RawConnection as F, type RuntimeAuthContext as G, type RuntimeAuthIdentity as H, type ITransport as I, type RuntimeAutoConnectPolicy as J, type RuntimeConnectionTarget as K, type RuntimeDeviceStatus as L, type RuntimeJoinRoomOptions as M, type RuntimeLogicalChannelFraming as N, OPENRTC_RUNTIME_POLICY as O, type PeerReconciliationStats as P, type RuntimeManagedSession as Q, type RuntimeClientOptions as R, type RuntimeManagedSessionKind as S, type RuntimeManagedSessionOptions as T, type RuntimeManagedSessionPlatform as U, type RuntimePeerLifecycleStatus as V, type RuntimePeerScope as W, type RuntimePeerSessionSnapshot as X, type RuntimePresenceLoopOptions as Y, type RuntimePresencePolicy as Z, type RuntimeRoomMember as _, RuntimeClient as a, type RuntimeUiConnectionStatus as a0, type ScopedChannelOptions as a1, type ScopedLogicalChannel as a2, ScopedSession as a3, type Session as a4, type SessionOptions as a5, getRuntimePolicy as a6, type RuntimeFactoryOptions as a7, DriveGrantActorError as a8, type DriveGrantActorErrorKind as a9, DriveGrantActorHandle as aa, type DriveGrantActorHandleOptions as ab, DriveGrantActorRegistry as ac, type DriveGrantActorState as ad, type DriveGrantClose as ae, type DriveGrantDial as af, type DriveGrantConnectionKey as ag, type DriveGrantLogicalChannelHandle as ah, type DriveGrantPreferredTransport as ai, type Session$1 as aj, type SessionOptions$1 as ak, createIpcClient as al, createWasmClient as am, driveGrantConnectionKey as an, driveGrantConnectionKeyToString as ao, type RuntimeDevice as b, type RuntimeDeviceStatusSnapshot as c, type RuntimeDeviceConnectOptions as d, type RuntimeLocalDeviceInfo as e, type RuntimeLocalDeviceProfile as f, type RuntimePeerState as g, type RuntimePeerConnectOptions as h, type RuntimePeerReadinessSnapshot as i, type RuntimePeerHealth as j, type RuntimeChannelDescriptor as k, type RuntimePeerStreamOptions as l, type RuntimeIncomingStream as m, type RuntimeScopedChannelOptions as n, type RuntimeScopedLogicalChannel as o, type RuntimePolicySnapshot as p, type RuntimeConnectionStateSnapshot as q, type RuntimeApplicationPayloadReadinessOptions as r, type RuntimeConnectionEvent as s, type PlutoRuntimeAssembly as t, type RuntimeRawConnection as u, type ChannelDescriptor as v, type ConnectionEvent as w, type ConnectionTarget as x, type IncomingStream as y, type PeerConnectOptions as z };