@ikonai/sdk 1.0.44 → 1.0.46

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.
@@ -55,6 +55,15 @@ export interface ChannelManagerConfig {
55
55
  * WebRTC-type entrypoints are filtered out (they are signal-only, not real channels).
56
56
  */
57
57
  webRtcEnabled?: boolean;
58
+ /**
59
+ * Called during transport fallback when a partial connection occurred.
60
+ * Returns fresh entrypoints from a new /connect call so the next transport
61
+ * type uses a clean server session with no stale channel state.
62
+ */
63
+ onRefreshEntrypoints?: () => Promise<{
64
+ entrypoints: Entrypoint[];
65
+ sessionId: number;
66
+ }>;
58
67
  }
59
68
  /**
60
69
  * ChannelManager orchestrates multiple channels to an Ikon server.
@@ -75,8 +84,6 @@ export declare class ChannelManager {
75
84
  private shouldReconnect;
76
85
  private entrypoints;
77
86
  private abortController;
78
- private demotedTypes;
79
- private connectionStable;
80
87
  private connectionTimeoutMs;
81
88
  private readonly config;
82
89
  constructor(config: ChannelManagerConfig);
@@ -114,6 +121,13 @@ export declare class ChannelManager {
114
121
  * stopping) clear the channels and transition to idle/stopped before this runs.
115
122
  */
116
123
  checkHealth(): void;
124
+ /**
125
+ * Force every active channel to drop its transport and notify the manager,
126
+ * kicking off the normal reconnect flow. Used when a higher layer has
127
+ * evidence that the connection is dead (e.g. no ACK received for a user
128
+ * action within the ack-timeout window).
129
+ */
130
+ triggerReconnect(reason: string): void;
117
131
  /**
118
132
  * Internal connect implementation.
119
133
  * @param isReconnect - If true, don't set offline state on failure (let attemptReconnect handle it)
@@ -137,6 +151,11 @@ export declare class ChannelManager {
137
151
  private handleChannelStateChange;
138
152
  /**
139
153
  * Handle channel close.
154
+ *
155
+ * Only `CORE_ON_SERVER_STOPPING` (handled separately via channel state →
156
+ * `handleChannelStateChange`) means the server is gone. Any transport-level
157
+ * close — including a clean WebSocket code 1000 — just means this transport
158
+ * dropped, and we reconnect.
140
159
  */
141
160
  private handleChannelClose;
142
161
  /**
@@ -36,9 +36,8 @@ export interface ChannelConfig {
36
36
  * Callback when channel is closed.
37
37
  * @param reason - Optional reason for the close
38
38
  * @param wasClean - Whether the close was clean (normal)
39
- * @param isServerClose - Whether the close code was 1000 or 1001, indicating server-initiated close
40
39
  */
41
- onClose?: (reason?: string, wasClean?: boolean, isServerClose?: boolean) => void;
40
+ onClose?: (reason?: string, wasClean?: boolean) => void;
42
41
  /**
43
42
  * Callback when an error occurs.
44
43
  */
@@ -83,6 +82,13 @@ export declare class Channel {
83
82
  * Disconnect the channel.
84
83
  */
85
84
  disconnect(): void;
85
+ /**
86
+ * Force the channel to drop its transport and notify the manager so the
87
+ * normal reconnect flow kicks in. Used when an upper layer (e.g. action-ack
88
+ * timeout) has evidence the connection is dead even though the transport
89
+ * still believes it is open.
90
+ */
91
+ triggerReconnect(reason: string): void;
86
92
  /**
87
93
  * Check transport health and trigger close if the connection is dead.
88
94
  * Used after tab resume to detect WebSockets closed by the OS while suspended.
@@ -177,7 +177,7 @@ export interface TimeoutConfig {
177
177
  slowConnectionThresholdMs?: number;
178
178
  /**
179
179
  * Duration in milliseconds before giving up and showing offline.
180
- * Default: 60000 (60 seconds)
180
+ * Default: 180000 (3 minutes)
181
181
  */
182
182
  connectionTimeoutMs?: number;
183
183
  /**
@@ -197,6 +197,14 @@ export interface TimeoutConfig {
197
197
  * Default: 2
198
198
  */
199
199
  maxReconnectAttempts?: number;
200
+ /**
201
+ * Timeout in ms for server ACK of a user action call.
202
+ * If the server does not acknowledge an action call within this window, the SDK
203
+ * assumes the connection is dead and triggers reconnection. Kept well under
204
+ * keepaliveTimeoutMs so we detect network-down faster than the keepalive watchdog.
205
+ * Default: 5000 (5 seconds)
206
+ */
207
+ actionAckTimeoutMs?: number;
200
208
  }
201
209
  /**
202
210
  * Media Session metadata for OS-level media controls (lock screen, notifications).
@@ -338,9 +346,15 @@ export interface IkonClientConfig {
338
346
  */
339
347
  websocket?: boolean;
340
348
  /**
341
- * Force WebTransport transport only. When true, only WebTransport (and WebTransportProxy)
342
- * endpoint types are used. When false, WebTransport types are excluded.
343
- * Can be overridden by the `?ikon-webtransport=` query parameter.
349
+ * Enable WebTransport.
350
+ *
351
+ * WebTransport is disabled by default because it has proven unreliable on bad
352
+ * networks. Apps opt in by setting this to `true`, which makes the SDK include
353
+ * WebTransport (and WebTransportProxy) endpoint types and prefer them over
354
+ * WebSocket. When left unset or set to `false`, WebTransport types are excluded
355
+ * and the SDK uses WebSocket / WebSocketProxy only.
356
+ *
357
+ * Can be overridden by the `?ikon-webtransport=true|false` query parameter.
344
358
  */
345
359
  webtransport?: boolean;
346
360
  /**
@@ -351,10 +365,11 @@ export interface IkonClientConfig {
351
365
  proxy?: boolean;
352
366
  }
353
367
  export declare const DEFAULT_SLOW_CONNECTION_THRESHOLD_MS = 5000;
354
- export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 60000;
368
+ export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 180000;
355
369
  export declare const DEFAULT_KEEPALIVE_TIMEOUT_MS = 15000;
356
370
  export declare const DEFAULT_RECONNECT_BACKOFF_MS = 2000;
357
371
  export declare const DEFAULT_MAX_RECONNECT_ATTEMPTS = 2;
372
+ export declare const DEFAULT_ACTION_ACK_TIMEOUT_MS = 5000;
358
373
  export declare const DEFAULT_PROVISIONING_TIMEOUT_MS = 60000;
359
374
  export declare const OFFLINE_SOFT_RETRY_THRESHOLD_MS: number;
360
375
  export declare const RECONNECT_RETRY_INTERVAL_MS: number;
@@ -90,8 +90,10 @@ export type StateHandler = (state: ConnectionState) => void;
90
90
  export declare class IkonClient {
91
91
  private channelManager;
92
92
  private protocolWorker;
93
+ private preloadedWorker;
93
94
  private workerManagerState;
94
95
  private authResponse;
96
+ private _connectUrl;
95
97
  private currentState;
96
98
  private slowConnectionTimer;
97
99
  private connectionTimer;
@@ -104,12 +106,15 @@ export declare class IkonClient {
104
106
  private readonly config;
105
107
  private readonly slowConnectionThresholdMs;
106
108
  private readonly connectionTimeoutMs;
109
+ private readonly actionAckTimeoutMs;
110
+ private readonly _pendingActionAcks;
107
111
  private readonly endpointSelector;
108
112
  private boundBeforeUnload;
109
113
  private boundPageHide;
110
114
  private boundVisibilityChange;
111
115
  private boundPopstate;
112
116
  private boundOnline;
117
+ private boundOffline;
113
118
  private _globalState;
114
119
  private _globalStateReceived;
115
120
  private _channelsConnected;
@@ -119,6 +124,7 @@ export declare class IkonClient {
119
124
  private unregisterBrowserFunctions;
120
125
  private unregisterMediaCaptureFunctions;
121
126
  private unregisterSoundFunctions;
127
+ private unregisterLoginInterceptor;
122
128
  private nextProtocolPortId;
123
129
  private nextProtocolSendPortId;
124
130
  private readonly _audioEnabled;
@@ -130,6 +136,7 @@ export declare class IkonClient {
130
136
  private _webRtcAudioStreams;
131
137
  private _webRtcVideoStreams;
132
138
  private _webRtcTrackMap;
139
+ private _pendingActiveAudioTracks;
133
140
  private _keepaliveLockAbort;
134
141
  private _fullReauthAttempted;
135
142
  private _softReconnectAttempted;
@@ -137,6 +144,7 @@ export declare class IkonClient {
137
144
  private _disconnectedAt;
138
145
  private _reconnectTimer;
139
146
  private _serverStopped;
147
+ private _hasBeenConnected;
140
148
  private _waitForExternalConnectUrl;
141
149
  private _externalConnectUrlCleanup;
142
150
  /**
@@ -179,7 +187,9 @@ export declare class IkonClient {
179
187
  onWebRtcTrackMapChanged?: (info: WebRTCTrackMapInfo) => void;
180
188
  /**
181
189
  * Look up the WebRTC video MediaStream for a given app-level stream ID.
182
- * Returns null if no track is mapped to this stream ID or if the stream is inactive.
190
+ * Checks remote tracks first, then falls back to local capture streams so single-user
191
+ * self-preview works (e.g. a security-camera app showing its own webcam).
192
+ * Returns null if no match or if the stream is inactive.
183
193
  */
184
194
  getWebRtcVideoStreamByStreamId(streamId: string): MediaStream | null;
185
195
  /**
@@ -227,6 +237,14 @@ export declare class IkonClient {
227
237
  */
228
238
  disconnect(): void;
229
239
  sendActionCall(actionId: string, payload?: string): void;
240
+ /**
241
+ * Register an outstanding action call and start its ack-timeout timer. If the
242
+ * server does not echo back an ActionCallAck for this CallId within
243
+ * `actionAckTimeoutMs`, we assume the connection is dead and force reconnect.
244
+ */
245
+ private trackPendingActionAck;
246
+ private onActionAckTimeout;
247
+ private clearPendingActionAcks;
230
248
  /**
231
249
  * Send a protocol message to the server.
232
250
  * Routes to appropriate channel based on the message's opcode group.
@@ -299,6 +317,13 @@ export declare class IkonClient {
299
317
  private handleChannelManagerStateChange;
300
318
  /**
301
319
  * Update the connection state and notify subscribers.
320
+ *
321
+ * Principle: once connected, the only allowed path to 'offline' is an
322
+ * explicit `CORE_ON_SERVER_STOPPING` from the server, which sets
323
+ * `_serverStopped = true` first. Any other attempt is a bug — redirect to
324
+ * the infinite reconnect loop and log so we notice in dev. Exception: when
325
+ * retry is disabled via ikon-retry=false the caller has opted out of
326
+ * reconnection, so let the offline transition through.
302
327
  */
303
328
  private setState;
304
329
  /**
@@ -310,10 +335,9 @@ export declare class IkonClient {
310
335
  private setupWebRtcSignaling;
311
336
  private _webRtcAudioUnlockHandler;
312
337
  private readonly _webRtcMediaSession;
313
- private ensureWebRtcAudioElement;
314
- private playWebRtcAudioTrack;
338
+ private createWebRtcAudioElement;
339
+ private destroyWebRtcAudioElement;
315
340
  private ensureWebRtcAudioUnlockHandler;
316
- private stopWebRtcAudioTrack;
317
341
  private setupWebRtcMediaSession;
318
342
  private removeWebRtcAudioUnlockHandler;
319
343
  private connectProtocolOnMainThread;
@@ -335,6 +359,7 @@ export declare class IkonClient {
335
359
  private scheduleReconnectRetry;
336
360
  private clearReconnectTimer;
337
361
  private attemptReconnectOnVisible;
362
+ private handleBrowserOffline;
338
363
  /**
339
364
  * Clear all timers.
340
365
  */
@@ -15,6 +15,7 @@ export declare const IKON_AUTH_URL_DEV = "https://auth.dev.ikonai.com";
15
15
  export declare const IKON_AUTH_BASE_URL = "https://auth.ikonai.com";
16
16
  /**
17
17
  * Get backend URL from BackendType.
18
+ * Defaults to same-origin /ikon/api on deployed apps; honors ?ikon-api=false to opt out.
18
19
  */
19
20
  export declare function getBackendUrl(backendType?: BackendType): string;
20
21
  /**
@@ -22,9 +23,10 @@ export declare function getBackendUrl(backendType?: BackendType): string;
22
23
  * Priority:
23
24
  * 1. Explicit override (e.g. from config)
24
25
  * 2. VITE_IKON_BACKEND_URL env var (set by .NET server during local dev)
25
- * 3. Local environment (localhost, LAN IPs)IKON_BACKEND_URL_DEV
26
- * 4. Hostname contains '.dev.' → IKON_BACKEND_URL_DEV
27
- * 5. DefaultIKON_BACKEND_URL_PROD
26
+ * 3. Cloud environment, not opted out via ?ikon-api=false same-origin /ikon/api (default)
27
+ * 4. Local environment (localhost, LAN IPs) → IKON_BACKEND_URL_DEV
28
+ * 5. Hostname contains '.dev.' IKON_BACKEND_URL_DEV
29
+ * 6. Default → IKON_BACKEND_URL_PROD
28
30
  */
29
31
  export declare function deriveBackendUrl(override?: string): string;
30
32
  /**
@@ -1,5 +1,5 @@
1
1
  import { ProtocolMessage, ActionFunctionRegister, GlobalState } from '../../../../shared/protocol/src/index.ts';
2
- import { FunctionDefinition, FunctionHandler, NormalizedFunction } from './types';
2
+ import { FunctionCallError, FunctionDefinition, FunctionHandler, NormalizedFunction } from './types';
3
3
  export interface RemoteFunction {
4
4
  id: string;
5
5
  name: string;
@@ -11,6 +11,15 @@ export interface RemoteFunction {
11
11
  llmCallOnlyOnce: boolean;
12
12
  requiresInstance: boolean;
13
13
  }
14
+ /**
15
+ * An error interceptor can observe and optionally handle function call errors.
16
+ * Return a Promise to take over resolution of the original call (e.g. retry after login).
17
+ * Return null to let the error propagate normally.
18
+ */
19
+ export type ErrorInterceptor = (error: FunctionCallError, callInfo: {
20
+ functionName: string;
21
+ args: unknown[];
22
+ }) => Promise<unknown> | null;
14
23
  export interface FunctionRegistryConfig {
15
24
  sendProtocolMessage: (message: ProtocolMessage) => void;
16
25
  sessionId: number;
@@ -19,6 +28,7 @@ export declare class FunctionRegistry {
19
28
  private readonly functions;
20
29
  private readonly remoteFunctions;
21
30
  private readonly pendingCalls;
31
+ private readonly errorInterceptors;
22
32
  private config;
23
33
  private isConnected;
24
34
  /**
@@ -30,6 +40,11 @@ export declare class FunctionRegistry {
30
40
  * Detach from the client (on disconnect).
31
41
  */
32
42
  detach(): void;
43
+ /**
44
+ * Add an error interceptor that can handle or transform function call errors.
45
+ * Returns an unregister function.
46
+ */
47
+ addErrorInterceptor(interceptor: ErrorInterceptor): () => void;
33
48
  /**
34
49
  * Register a function with the registry.
35
50
  * If already connected, sends registration to server immediately.
@@ -1,5 +1,5 @@
1
1
  export type { ValueDescriptor, NormalizedValueDescriptor, PrimitiveValueKind, FunctionParameterDefinition, FunctionDefinition, FunctionHandler, FunctionResultWithData } from './types';
2
2
  export { withResultData, FunctionCallError } from './types';
3
- export { FunctionRegistry, type FunctionRegistryConfig } from './function-registry';
3
+ export { FunctionRegistry, type FunctionRegistryConfig, type ErrorInterceptor } from './function-registry';
4
4
  export { registerBrowserFunctions, isBrowserEnvironment } from './browser-functions';
5
5
  export { registerSoundFunctions, SoundFunctionNames } from './sound-functions';
@@ -67,6 +67,7 @@ export interface NormalizedFunction {
67
67
  export declare class FunctionCallError extends Error {
68
68
  readonly remoteErrorType: string;
69
69
  readonly remoteStackTrace: string;
70
- constructor(message: string, remoteErrorType: string, remoteStackTrace: string);
70
+ readonly code: string;
71
+ constructor(message: string, remoteErrorType: string, remoteStackTrace: string, code?: string);
71
72
  }
72
73
  export {};
package/index.d.ts CHANGED
@@ -14,14 +14,16 @@ export { setLogLevel, setLogSink, getLogLevel, getLogSink, createLogger, LogLeve
14
14
  export type { LogEntry, LogSink, Logger, LogEventListener, LogEventSubscriptionOptions } from './utils/logger';
15
15
  export { initializeDebugMode, isDebugModeEnabled } from './utils/debug-mode';
16
16
  export { initializeInspectMode, isInspectModeEnabled, enableInspectMode } from './utils/inspect-mode';
17
- export { getLangParam, getProxyParam, getWebSocketParam, getWebTransportParam, getDebugParam, getServerUrlParam, getAudioParam, getVideoParam, getWebRtcParam, getInspectParam, setSdkUrlParam, IKON_PARAM_PROXY, IKON_PARAM_WEBSOCKET, IKON_PARAM_WEBTRANSPORT, IKON_PARAM_DEBUG, IKON_PARAM_LANG, IKON_PARAM_SERVER_URL, IKON_PARAM_GIT_SOURCE, IKON_PARAM_GIT_BRANCH, IKON_PARAM_AUDIO, IKON_PARAM_VIDEO, IKON_PARAM_WEBRTC, IKON_PARAM_INSPECT } from './utils/query-params';
17
+ export { getLangParam, getProxyParam, getWebSocketParam, getWebTransportParam, getDebugParam, getServerUrlParam, getAudioParam, getVideoParam, getWebRtcParam, getInspectParam, getRetryParam, setSdkUrlParam, IKON_PARAM_PROXY, IKON_PARAM_WEBSOCKET, IKON_PARAM_WEBTRANSPORT, IKON_PARAM_DEBUG, IKON_PARAM_LANG, IKON_PARAM_SERVER_URL, IKON_PARAM_GIT_SOURCE, IKON_PARAM_GIT_BRANCH, IKON_PARAM_AUDIO, IKON_PARAM_VIDEO, IKON_PARAM_WEBRTC, IKON_PARAM_INSPECT, IKON_PARAM_RETRY } from './utils/query-params';
18
18
  export { getOpcodeName } from './utils/opcode-names';
19
19
  export { initializeLogSink, setSendLogsCallback, getBufferedLogs, takeBufferedLogs, flushLogs, clearLogBuffer, getLogBufferSize } from './utils/log-sink';
20
20
  export type { LogSinkConfig } from './utils/log-sink';
21
21
  export { startLogSender, stopLogSender } from './utils/log-sender';
22
- export { FunctionRegistry, registerBrowserFunctions, isBrowserEnvironment, FunctionCallError, type FunctionRegistryConfig } from './functions';
22
+ export { FunctionRegistry, registerBrowserFunctions, isBrowserEnvironment, FunctionCallError, type FunctionRegistryConfig, type ErrorInterceptor } from './functions';
23
23
  export { withResultData } from './functions';
24
24
  export type { ValueDescriptor, NormalizedValueDescriptor, PrimitiveValueKind, FunctionParameterDefinition, FunctionDefinition, FunctionHandler, FunctionResultWithData } from './functions';
25
+ export { loginErrorInterceptor, reprovisionAfterLogin, consumeLoginHandoff, getLoginPromptState, clearLoginPrompt, subscribe as subscribeLoginPrompt, consumeLoginPendingCall } from './login';
26
+ export type { LoginPromptState, LoginPendingCall } from './login';
25
27
  export { IKON_AUTH_BASE_URL, IKON_AUTH_URL_PROD, IKON_AUTH_URL_DEV, IKON_BACKEND_URL_PROD, IKON_BACKEND_URL_DEV, deriveAuthUrl, deriveBackendUrl, deriveBackendType, parseUrlParams, clearSessionParamsFromUrl, isCloudEnvironment, type UrlParams } from './connection/urls';
26
28
  export { getOrCreateDeviceId, clearDeviceId, extractUserIdFromToken } from './storage';
27
29
  export { IkonMedia, IkonAudioPlayback, IkonVideoPlayback, isSharedArrayBufferSupported, isAudioWorkletSupported, IkonMediaCapture, IkonVideoCapture, IkonAudioCapture, IkonImageCapture } from './media';