@opengeni/sdk 0.52.1 → 1.0.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 (300) hide show
  1. package/README.md +1078 -33
  2. package/dist/accounts.d.ts +153 -0
  3. package/dist/accounts.js +307 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/adapters/express.d.ts +31 -0
  6. package/dist/adapters/express.js +87 -0
  7. package/dist/adapters/express.js.map +1 -0
  8. package/dist/adapters/hono.d.ts +16 -0
  9. package/dist/adapters/hono.js +8 -0
  10. package/dist/adapters/hono.js.map +1 -0
  11. package/dist/adapters/next.d.ts +29 -0
  12. package/dist/adapters/next.js +22 -0
  13. package/dist/adapters/next.js.map +1 -0
  14. package/dist/allowance-refusal.d.ts +2 -0
  15. package/dist/allowance-refusal.js +50 -0
  16. package/dist/allowance-refusal.js.map +1 -0
  17. package/dist/artifact-catalog.d.ts +31 -0
  18. package/dist/artifact-client.d.ts +23 -11
  19. package/dist/artifacts.d.ts +1 -1
  20. package/dist/artifacts.js +7 -5
  21. package/dist/automations-client.d.ts +17 -0
  22. package/dist/automations.d.ts +3 -0
  23. package/dist/automations.js +84 -0
  24. package/dist/automations.js.map +1 -0
  25. package/dist/browser.d.ts +11 -0
  26. package/dist/browser.js +47 -0
  27. package/dist/browser.js.map +1 -0
  28. package/dist/chat/fold.d.ts +75 -0
  29. package/dist/chat/handler.d.ts +41 -0
  30. package/dist/chat/http.d.ts +67 -0
  31. package/dist/chat/ids.d.ts +34 -0
  32. package/dist/chat/index.d.ts +9 -0
  33. package/dist/chat/index.js +1694 -0
  34. package/dist/chat/index.js.map +1 -0
  35. package/dist/chat/openai.d.ts +30 -0
  36. package/dist/chat/opengeni.d.ts +128 -0
  37. package/dist/chat/types.d.ts +207 -0
  38. package/dist/chat/vercel.d.ts +49 -0
  39. package/dist/chats.d.ts +8 -0
  40. package/dist/{chunk-KIHGPM7H.js → chunk-2UXZUCEZ.js} +309 -46
  41. package/dist/chunk-2UXZUCEZ.js.map +1 -0
  42. package/dist/chunk-4SMEOV3P.js +246 -0
  43. package/dist/chunk-4SMEOV3P.js.map +1 -0
  44. package/dist/{chunk-FHI4DFIG.js → chunk-AH5D5Q5V.js} +31 -35
  45. package/dist/chunk-AH5D5Q5V.js.map +1 -0
  46. package/dist/chunk-APXVSSSY.js +301 -0
  47. package/dist/chunk-APXVSSSY.js.map +1 -0
  48. package/dist/chunk-DUVIYER4.js +639 -0
  49. package/dist/chunk-DUVIYER4.js.map +1 -0
  50. package/dist/{chunk-ILQZR5N6.js → chunk-FG3OO5LM.js} +3484 -750
  51. package/dist/chunk-FG3OO5LM.js.map +1 -0
  52. package/dist/chunk-GZYZT22T.js +260 -0
  53. package/dist/chunk-GZYZT22T.js.map +1 -0
  54. package/dist/chunk-HMGADJGZ.js +150 -0
  55. package/dist/chunk-HMGADJGZ.js.map +1 -0
  56. package/dist/{chunk-DXDL7EEW.js → chunk-KWADQ227.js} +198 -114
  57. package/dist/chunk-KWADQ227.js.map +1 -0
  58. package/dist/chunk-LY2K6ELJ.js +126 -0
  59. package/dist/chunk-LY2K6ELJ.js.map +1 -0
  60. package/dist/chunk-MOTJYIYL.js +340 -0
  61. package/dist/chunk-MOTJYIYL.js.map +1 -0
  62. package/dist/{chunk-MZWTWOX6.js → chunk-NC57YMUO.js} +447 -3
  63. package/dist/chunk-NC57YMUO.js.map +1 -0
  64. package/dist/chunk-RWKB6YZG.js +1270 -0
  65. package/dist/chunk-RWKB6YZG.js.map +1 -0
  66. package/dist/chunk-U6CV2SXO.js +195 -0
  67. package/dist/chunk-U6CV2SXO.js.map +1 -0
  68. package/dist/chunk-UM4ZXXJR.js +9 -0
  69. package/dist/chunk-UM4ZXXJR.js.map +1 -0
  70. package/dist/chunk-V6GIY62B.js +229 -0
  71. package/dist/chunk-V6GIY62B.js.map +1 -0
  72. package/dist/chunk-WU6GQG25.js +268 -0
  73. package/dist/chunk-WU6GQG25.js.map +1 -0
  74. package/dist/{chunk-VWE2QIVO.js → chunk-XNG7YGR3.js} +23 -6
  75. package/dist/chunk-XNG7YGR3.js.map +1 -0
  76. package/dist/{chunk-FRJFNDIR.js → chunk-YIKA2QT4.js} +37 -2
  77. package/dist/chunk-YIKA2QT4.js.map +1 -0
  78. package/dist/chunk-ZKPY6YAJ.js +135 -0
  79. package/dist/chunk-ZKPY6YAJ.js.map +1 -0
  80. package/dist/client.d.ts +843 -107
  81. package/dist/codex-realtime-controller.d.ts +6 -0
  82. package/dist/codex-realtime-controller.js +2 -2
  83. package/dist/codex-realtime-v3.d.ts +13 -2
  84. package/dist/company-brain.d.ts +22 -0
  85. package/dist/company-profile.d.ts +14 -1
  86. package/dist/core.d.ts +9 -3
  87. package/dist/core.js +28 -9
  88. package/dist/deprecation.d.ts +47 -0
  89. package/dist/desktop.d.ts +8 -0
  90. package/dist/document-authority-client.d.ts +51 -0
  91. package/dist/document-authority.d.ts +2 -0
  92. package/dist/document-authority.js +12 -0
  93. package/dist/document-authority.js.map +1 -0
  94. package/dist/editable-artifacts/browser-session.d.ts +7 -3
  95. package/dist/editable-artifacts/controller.d.ts +4 -1
  96. package/dist/editable-artifacts/errors.d.ts +1 -1
  97. package/dist/editable-artifacts/http-live-transport.d.ts +6 -2
  98. package/dist/editable-artifacts/index.d.ts +2 -0
  99. package/dist/editable-artifacts/open-failure.d.ts +9 -0
  100. package/dist/editable-artifacts/types.d.ts +6 -3
  101. package/dist/editable-artifacts/worker/kernel-adapter.d.ts +1 -1
  102. package/dist/editable-artifacts-worker.js +44 -17
  103. package/dist/editable-artifacts-worker.js.map +1 -1
  104. package/dist/editable-artifacts.js +243 -124
  105. package/dist/editable-artifacts.js.map +1 -1
  106. package/dist/embedding-client.d.ts +129 -0
  107. package/dist/errors.d.ts +36 -1
  108. package/dist/feedback.d.ts +23 -0
  109. package/dist/gateway-realtime-transport.d.ts +1 -0
  110. package/dist/gateway-realtime-transport.js +5 -3
  111. package/dist/github-repositories.d.ts +10 -0
  112. package/dist/github-repositories.js +36 -0
  113. package/dist/github-repositories.js.map +1 -0
  114. package/dist/index.d.ts +61 -12
  115. package/dist/index.js +277 -126
  116. package/dist/index.js.map +1 -1
  117. package/dist/interaction-revision-stream.d.ts +11 -0
  118. package/dist/interaction.d.ts +687 -5
  119. package/dist/interaction.js +32 -1
  120. package/dist/knowledge.d.ts +7 -0
  121. package/dist/knowledge.js +20 -0
  122. package/dist/knowledge.js.map +1 -0
  123. package/dist/message-links.d.ts +68 -0
  124. package/dist/model-context.d.ts +68 -0
  125. package/dist/model-display.d.ts +2 -0
  126. package/dist/model-display.js +15 -0
  127. package/dist/model-display.js.map +1 -0
  128. package/dist/model-picker-order.d.ts +1 -0
  129. package/dist/model-picker-order.js +10 -0
  130. package/dist/model-picker-order.js.map +1 -0
  131. package/dist/organization-integration-policy.d.ts +26 -0
  132. package/dist/organization-integration-policy.js +26 -0
  133. package/dist/organization-integration-policy.js.map +1 -0
  134. package/dist/organization-private-session-settings.d.ts +6 -0
  135. package/dist/organization-private-session-settings.js +22 -0
  136. package/dist/organization-private-session-settings.js.map +1 -0
  137. package/dist/organization-user-setup.d.ts +8 -0
  138. package/dist/organization-user-setup.js +16 -0
  139. package/dist/organization-user-setup.js.map +1 -0
  140. package/dist/pr-review-client.d.ts +16 -0
  141. package/dist/pr-review.d.ts +4 -0
  142. package/dist/pr-review.js +62 -0
  143. package/dist/pr-review.js.map +1 -0
  144. package/dist/preference-registry.d.ts +9 -1
  145. package/dist/proxy.d.ts +7 -5
  146. package/dist/realtime.d.ts +6 -4
  147. package/dist/realtime.js +10 -6
  148. package/dist/realtime.js.map +1 -1
  149. package/dist/retained-artifacts.d.ts +5 -1
  150. package/dist/session-history-import.d.ts +22 -0
  151. package/dist/session-history-import.js +36 -0
  152. package/dist/session-history-import.js.map +1 -0
  153. package/dist/session-list-entries.d.ts +1 -0
  154. package/dist/session-list-entries.js +7 -0
  155. package/dist/session-list-entries.js.map +1 -0
  156. package/dist/session-message-search.d.ts +53 -0
  157. package/dist/session-proxy-client.d.ts +24 -0
  158. package/dist/session-proxy.d.ts +245 -0
  159. package/dist/session-proxy.js +24 -0
  160. package/dist/session-proxy.js.map +1 -0
  161. package/dist/session-titles.d.ts +2 -0
  162. package/dist/site-browser-entry.d.ts +1 -0
  163. package/dist/site-browser-runtime.gen.d.ts +1 -0
  164. package/dist/site-document.d.ts +5 -0
  165. package/dist/site-document.js +20 -0
  166. package/dist/site-document.js.map +1 -0
  167. package/dist/site-document.test.d.ts +1 -0
  168. package/dist/site-http.d.ts +16 -0
  169. package/dist/site-tool-bridge.d.ts +44 -0
  170. package/dist/site.d.ts +101 -0
  171. package/dist/site.js +49 -0
  172. package/dist/site.js.map +1 -0
  173. package/dist/skills.d.ts +75 -0
  174. package/dist/stream.d.ts +2 -0
  175. package/dist/tenant-workspaces.d.ts +47 -0
  176. package/dist/tenant-workspaces.js +8 -0
  177. package/dist/tenant-workspaces.js.map +1 -0
  178. package/dist/tool-auth.d.ts +87 -0
  179. package/dist/tool-auth.js +23 -0
  180. package/dist/tool-auth.js.map +1 -0
  181. package/dist/tool-display-metadata.d.ts +3 -0
  182. package/dist/tools-FXBLUWAE.js +12 -0
  183. package/dist/tools-FXBLUWAE.js.map +1 -0
  184. package/dist/tools.d.ts +61 -0
  185. package/dist/types.d.ts +3048 -406
  186. package/dist/usage-allowances.d.ts +36 -0
  187. package/dist/usage-allowances.js +80 -0
  188. package/dist/usage-allowances.js.map +1 -0
  189. package/dist/workspace-artifacts.d.ts +33 -5
  190. package/dist/workspace-instruction-policies.d.ts +24 -2
  191. package/dist/workspace-integrations.d.ts +74 -0
  192. package/dist/workspace-integrations.js +65 -0
  193. package/dist/workspace-integrations.js.map +1 -0
  194. package/dist/workspace-learning.d.ts +124 -0
  195. package/dist/workspace-live-stream.d.ts +14 -0
  196. package/dist/workspace-state.d.ts +8 -71
  197. package/package.json +108 -3
  198. package/src/accounts.ts +443 -0
  199. package/src/adapters/express.ts +136 -0
  200. package/src/adapters/hono.ts +18 -0
  201. package/src/adapters/next.ts +42 -0
  202. package/src/allowance-refusal.ts +5 -0
  203. package/src/artifact-catalog.ts +41 -0
  204. package/src/artifact-client.ts +43 -52
  205. package/src/artifacts.ts +7 -1
  206. package/src/automations-client.ts +124 -0
  207. package/src/automations.ts +15 -0
  208. package/src/browser.ts +54 -0
  209. package/src/chat/fold.ts +431 -0
  210. package/src/chat/handler.ts +240 -0
  211. package/src/chat/http.ts +257 -0
  212. package/src/chat/ids.ts +85 -0
  213. package/src/chat/index.ts +87 -0
  214. package/src/chat/openai.ts +439 -0
  215. package/src/chat/opengeni.ts +722 -0
  216. package/src/chat/types.ts +219 -0
  217. package/src/chat/vercel.ts +204 -0
  218. package/src/chats.ts +16 -0
  219. package/src/client.ts +5162 -792
  220. package/src/codex-realtime-controller.ts +260 -12
  221. package/src/codex-realtime-lifecycle.ts +1 -0
  222. package/src/codex-realtime-v3.ts +162 -31
  223. package/src/codex-realtime.ts +1 -1
  224. package/src/company-brain.ts +59 -0
  225. package/src/company-profile.ts +17 -1
  226. package/src/core.ts +23 -1
  227. package/src/deprecation.ts +149 -0
  228. package/src/desktop.ts +11 -1
  229. package/src/document-authority-client.ts +219 -0
  230. package/src/document-authority.ts +17 -0
  231. package/src/editable-artifacts/browser-session.ts +36 -22
  232. package/src/editable-artifacts/controller.ts +102 -47
  233. package/src/editable-artifacts/errors.ts +2 -0
  234. package/src/editable-artifacts/http-live-transport.ts +62 -37
  235. package/src/editable-artifacts/index.ts +6 -0
  236. package/src/editable-artifacts/open-failure.ts +79 -0
  237. package/src/editable-artifacts/storage.ts +31 -40
  238. package/src/editable-artifacts/types.ts +6 -3
  239. package/src/editable-artifacts/worker/kernel-adapter.ts +23 -7
  240. package/src/editable-artifacts/worker/runtime.ts +26 -13
  241. package/src/editable-artifacts/worker/wire-codec.ts +23 -5
  242. package/src/embedding-client.ts +519 -0
  243. package/src/errors.ts +185 -4
  244. package/src/feedback.ts +20 -0
  245. package/src/gateway-realtime-transport.ts +253 -110
  246. package/src/github-repositories.ts +60 -0
  247. package/src/index.ts +603 -54
  248. package/src/interaction-revision-stream.ts +117 -0
  249. package/src/interaction.ts +1367 -51
  250. package/src/knowledge.ts +72 -0
  251. package/src/message-links.ts +178 -0
  252. package/src/model-context.ts +112 -0
  253. package/src/model-display.ts +10 -0
  254. package/src/model-picker-order.ts +6 -0
  255. package/src/organization-integration-policy.ts +54 -0
  256. package/src/organization-private-session-settings.ts +33 -0
  257. package/src/organization-user-setup.ts +31 -0
  258. package/src/pr-review-client.ts +92 -0
  259. package/src/pr-review.ts +17 -0
  260. package/src/preference-registry.ts +11 -0
  261. package/src/proxy.ts +17 -6
  262. package/src/realtime.ts +15 -4
  263. package/src/retained-artifacts.ts +101 -0
  264. package/src/session-history-import.ts +84 -0
  265. package/src/session-list-entries.ts +1 -0
  266. package/src/session-message-search.ts +47 -0
  267. package/src/session-proxy-client.ts +64 -0
  268. package/src/session-proxy.ts +1473 -0
  269. package/src/session-titles.ts +10 -0
  270. package/src/site-browser-entry.ts +6 -0
  271. package/src/site-browser-runtime.gen.ts +2 -0
  272. package/src/site-document.test.ts +64 -0
  273. package/src/site-document.ts +18 -0
  274. package/src/site-http.ts +162 -0
  275. package/src/site-tool-bridge.ts +146 -0
  276. package/src/site.ts +585 -0
  277. package/src/skills.ts +69 -0
  278. package/src/sse.ts +8 -2
  279. package/src/stream.ts +38 -4
  280. package/src/tenant-workspaces.ts +148 -0
  281. package/src/tool-auth.ts +301 -0
  282. package/src/tool-display-metadata.ts +19 -0
  283. package/src/tools.ts +396 -0
  284. package/src/types.ts +3607 -493
  285. package/src/usage-allowances.ts +189 -0
  286. package/src/workspace-artifacts.ts +40 -6
  287. package/src/workspace-instruction-policies.ts +26 -1
  288. package/src/workspace-integrations.ts +402 -0
  289. package/src/workspace-learning.ts +138 -0
  290. package/src/workspace-live-stream.ts +137 -0
  291. package/src/workspace-state.ts +8 -82
  292. package/dist/chunk-DXDL7EEW.js.map +0 -1
  293. package/dist/chunk-FHI4DFIG.js.map +0 -1
  294. package/dist/chunk-FRJFNDIR.js.map +0 -1
  295. package/dist/chunk-ILQZR5N6.js.map +0 -1
  296. package/dist/chunk-KIHGPM7H.js.map +0 -1
  297. package/dist/chunk-MZWTWOX6.js.map +0 -1
  298. package/dist/chunk-V5F253OG.js +0 -116
  299. package/dist/chunk-V5F253OG.js.map +0 -1
  300. package/dist/chunk-VWE2QIVO.js.map +0 -1
@@ -33,6 +33,7 @@ import type {
33
33
  GatewayRealtimeConnectResponse,
34
34
  EndSessionRealtimeRequest,
35
35
  RenewSessionRealtimeRequest,
36
+ SessionRealtimeInboundEntry,
36
37
  SessionRealtimeMode,
37
38
  SessionRealtimeModel,
38
39
  SessionRealtimeMutationResponse,
@@ -49,7 +50,29 @@ export const CODEX_REALTIME_NEGOTIATION_TIMEOUT_MS = 20_000;
49
50
  // OpenGeni policy: rotate conservatively without asserting an upstream lifetime.
50
51
  const DEFAULT_CONNECTION_ROTATION_INTERVAL_MS = 15 * 60_000;
51
52
  const DEFAULT_RECONNECT_BACKOFF_MS = [250, 1_000, 2_000, 5_000] as const;
53
+ const MAX_BROWSER_TIMEOUT_MS = 2_147_483_647;
52
54
  const OWNER_RECORD_VERSION = 1;
55
+ const OWNER_DELEGATION_REPLAY_VERSION = 1;
56
+ const OWNER_DELEGATION_REPLAY_MAX_CALLS = 4_096;
57
+ const OWNER_DELEGATION_REPLAY_MAX_BYTES = 4 * 1024 * 1024;
58
+ const SESSION_REALTIME_INBOUND_ENTRY_KEYS = new Set([
59
+ "operationId",
60
+ "kind",
61
+ "role",
62
+ "providerEventId",
63
+ "delegationItemId",
64
+ "text",
65
+ "payload",
66
+ "modelContext",
67
+ ]);
68
+ const SESSION_REALTIME_INBOUND_KINDS = new Set([
69
+ "user_transcript",
70
+ "assistant_transcript",
71
+ "delegation_call",
72
+ "interruption",
73
+ "error",
74
+ ]);
75
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
53
76
 
54
77
  export type CodexRealtimeControllerStatus =
55
78
  | "idle"
@@ -123,6 +146,12 @@ export type CodexRealtimeControllerClient = {
123
146
  request: GatewayRealtimeConnectRequest,
124
147
  options?: { signal?: AbortSignal | undefined },
125
148
  ): Promise<GatewayRealtimeConnectResponse>;
149
+ negotiateXaiSubscriptionRealtime?(
150
+ workspaceId: string,
151
+ sessionId: string,
152
+ request: GatewayRealtimeConnectRequest,
153
+ options?: { signal?: AbortSignal | undefined },
154
+ ): Promise<GatewayRealtimeConnectResponse>;
126
155
  activateCodexRealtimeConnection(
127
156
  workspaceId: string,
128
157
  sessionId: string,
@@ -149,7 +178,9 @@ export type CodexRealtimeOwnerStorage = Pick<Storage, "getItem" | "setItem" | "r
149
178
 
150
179
  /** Canonical browser-owner storage namespace for a public realtime model. */
151
180
  export function sessionRealtimeOwnerStorageNamespace(model: SessionRealtimeModel): string {
152
- return model === "gpt-live-1-boulder-alpha" ? "codex-realtime-owner" : "gateway-realtime-owner";
181
+ if (model === "gpt-live-1-boulder-alpha") return "codex-realtime-owner";
182
+ if (model === "supergrok/grok-voice-think-fast-2.0") return "xai-realtime-owner";
183
+ return "gateway-realtime-owner";
153
184
  }
154
185
 
155
186
  /** Canonical browser-owner storage key shared by the SDK controller and React facade. */
@@ -194,6 +225,7 @@ export type CreateCodexRealtimeControllerOptions = {
194
225
  createPeerConnection?: (() => RTCPeerConnection) | undefined;
195
226
  getUserMedia?: ((constraints: MediaStreamConstraints) => Promise<MediaStream>) | undefined;
196
227
  randomUUID?: (() => string) | undefined;
228
+ now?: (() => Date) | undefined;
197
229
  setInterval?: ((callback: () => void, delayMs: number) => unknown) | undefined;
198
230
  clearInterval?: ((handle: unknown) => void) | undefined;
199
231
  setTimeout?: ((callback: () => void, delayMs: number) => unknown) | undefined;
@@ -205,6 +237,8 @@ export type CreateCodexRealtimeControllerOptions = {
205
237
  model?: SessionRealtimeModel | undefined;
206
238
  ownerStorageNamespace?: string | undefined;
207
239
  startTransport?: RealtimeControllerTransportStarter | undefined;
240
+ /** Model-visible application context captured with each durable realtime message. */
241
+ getModelContext?: (() => string | undefined) | undefined;
208
242
  };
209
243
 
210
244
  export type RealtimeControllerTransportStarter = (input: {
@@ -250,6 +284,13 @@ type OwnerRecord = {
250
284
  operationId: string;
251
285
  browserInstanceId: string;
252
286
  ownerKey: string;
287
+ delegationReplay?: OwnerDelegationReplay | undefined;
288
+ };
289
+
290
+ type OwnerDelegationReplay = {
291
+ version: typeof OWNER_DELEGATION_REPLAY_VERSION;
292
+ acceptedDelegationItemIds: string[];
293
+ pendingDelegations: SessionRealtimeInboundEntry[];
253
294
  };
254
295
 
255
296
  type ConnectionRuntime = {
@@ -277,6 +318,7 @@ export function createCodexRealtimeController(
277
318
  );
278
319
  const model = options.model ?? "gpt-live-1-boulder-alpha";
279
320
  const randomUUID = options.randomUUID ?? defaultRandomUUID;
321
+ const now = options.now ?? (() => new Date());
280
322
  const scheduleInterval =
281
323
  options.setInterval ?? ((callback, delay) => globalThis.setInterval(callback, delay));
282
324
  const unscheduleInterval =
@@ -323,6 +365,7 @@ export function createCodexRealtimeController(
323
365
  let rotationTimer: unknown = null;
324
366
  let reconnectTimer: unknown = null;
325
367
  let negotiationTimer: unknown = null;
368
+ let lostOwnerExpiryTimer: unknown = null;
326
369
  let closed = false;
327
370
  let stopping = false;
328
371
  let generation = 0;
@@ -330,6 +373,59 @@ export function createCodexRealtimeController(
330
373
  let recoveryTerminal = false;
331
374
  let mutationTail = Promise.resolve();
332
375
  let connectionTask: Promise<void> | null = null;
376
+ const acceptedDelegationItemIds = new Set(
377
+ owner?.delegationReplay?.acceptedDelegationItemIds ?? [],
378
+ );
379
+ const pendingDelegations = new Map(
380
+ (owner?.delegationReplay?.pendingDelegations ?? []).flatMap((entry) =>
381
+ entry.delegationItemId ? [[entry.delegationItemId, entry] as const] : [],
382
+ ),
383
+ );
384
+
385
+ const invalidateStoredOwner = (): void => {
386
+ try {
387
+ storage?.removeItem(storageKey);
388
+ } catch {
389
+ // Best effort only. The bridge still fails closed and retains the exact
390
+ // in-memory snapshot for this controller's bounded recovery attempts.
391
+ }
392
+ };
393
+
394
+ const persistDelegationReplay = (input: {
395
+ acceptedDelegationItemIds: ReadonlySet<string>;
396
+ pendingDelegations: ReadonlyMap<string, SessionRealtimeInboundEntry>;
397
+ }): void => {
398
+ if (!owner || !storage) return;
399
+ const delegationReplay: OwnerDelegationReplay = {
400
+ version: OWNER_DELEGATION_REPLAY_VERSION,
401
+ acceptedDelegationItemIds: [...input.acceptedDelegationItemIds],
402
+ pendingDelegations: [...input.pendingDelegations.values()],
403
+ };
404
+ if (
405
+ delegationReplay.pendingDelegations.length > CODEX_REALTIME_V3_PENDING_MAX_ENTRIES ||
406
+ delegationReplay.acceptedDelegationItemIds.length +
407
+ delegationReplay.pendingDelegations.length >
408
+ OWNER_DELEGATION_REPLAY_MAX_CALLS
409
+ ) {
410
+ invalidateStoredOwner();
411
+ throw new Error("Realtime delegation replay journal exceeded its call limit");
412
+ }
413
+ const next: OwnerRecord = { ...owner, delegationReplay };
414
+ const serialized = JSON.stringify(next);
415
+ if (new TextEncoder().encode(serialized).byteLength > OWNER_DELEGATION_REPLAY_MAX_BYTES) {
416
+ invalidateStoredOwner();
417
+ throw new Error("Realtime delegation replay journal exceeded its byte limit");
418
+ }
419
+ try {
420
+ storage.setItem(storageKey, serialized);
421
+ } catch (error) {
422
+ // Never leave older ownership proof reloadable without the delegation
423
+ // snapshot that the active bridge just froze.
424
+ invalidateStoredOwner();
425
+ throw error;
426
+ }
427
+ owner = next;
428
+ };
333
429
 
334
430
  const publish = (patch: Partial<CodexRealtimeControllerSnapshot>): void => {
335
431
  state = { ...state, ...patch };
@@ -364,11 +460,13 @@ export function createCodexRealtimeController(
364
460
  if (rotationTimer !== null) unscheduleTimeout(rotationTimer);
365
461
  if (reconnectTimer !== null) unscheduleTimeout(reconnectTimer);
366
462
  if (negotiationTimer !== null) unscheduleTimeout(negotiationTimer);
463
+ if (lostOwnerExpiryTimer !== null) unscheduleTimeout(lostOwnerExpiryTimer);
367
464
  heartbeatTimer = null;
368
465
  syncTimer = null;
369
466
  rotationTimer = null;
370
467
  reconnectTimer = null;
371
468
  negotiationTimer = null;
469
+ lostOwnerExpiryTimer = null;
372
470
  };
373
471
 
374
472
  const stopNegotiationTimers = (): void => {
@@ -414,6 +512,8 @@ export function createCodexRealtimeController(
414
512
  const clearOwner = (): void => {
415
513
  owner = null;
416
514
  storage?.removeItem(storageKey);
515
+ acceptedDelegationItemIds.clear();
516
+ pendingDelegations.clear();
417
517
  };
418
518
 
419
519
  const transitionEnded = (message = "Realtime mode ended"): void => {
@@ -454,16 +554,26 @@ export function createCodexRealtimeController(
454
554
  publish({ microphone: "active" });
455
555
  return acquired;
456
556
  } catch (error) {
457
- if (error instanceof CodexRealtimeMicrophoneError) {
458
- const kind = error.code === "permission_denied" ? "permission_failure" : "device_failure";
557
+ const microphoneError =
558
+ error instanceof CodexRealtimeMicrophoneError
559
+ ? error
560
+ : signal.aborted && state.microphone === "acquiring"
561
+ ? new CodexRealtimeMicrophoneError(
562
+ "acquisition_failed",
563
+ "Microphone did not become available before voice startup timed out",
564
+ )
565
+ : null;
566
+ if (microphoneError) {
567
+ const kind =
568
+ microphoneError.code === "permission_denied" ? "permission_failure" : "device_failure";
459
569
  publish({
460
- microphone: error.code,
570
+ microphone: microphoneError.code,
461
571
  status: state.mode?.state === "active" ? "recovering" : "error",
462
- diagnostic: diagnostic(kind, error.message, true),
463
- error: error.message,
572
+ diagnostic: diagnostic(kind, microphoneError.message, true),
573
+ error: microphoneError.message,
464
574
  });
465
575
  }
466
- throw error;
576
+ throw microphoneError ?? error;
467
577
  }
468
578
  };
469
579
 
@@ -506,7 +616,10 @@ export function createCodexRealtimeController(
506
616
  });
507
617
  return;
508
618
  }
509
- publish({ audibleOutput: next, ...(next === "audible" ? { error: null } : {}) });
619
+ publish({
620
+ audibleOutput: next,
621
+ ...(next === "audible" ? { error: null } : {}),
622
+ });
510
623
  };
511
624
 
512
625
  const startActiveIntervals = (): void => {
@@ -893,6 +1006,10 @@ export function createCodexRealtimeController(
893
1006
  sync: async (request) =>
894
1007
  await syncForGeneration(targetGeneration, activated.mode.id, request),
895
1008
  randomUUID,
1009
+ ...(options.getModelContext ? { getModelContext: options.getModelContext } : {}),
1010
+ acceptedDelegationItemIds,
1011
+ pendingDelegations,
1012
+ onDelegationReplayStateChange: persistDelegationReplay,
896
1013
  onSnapshot: (nextBridge) => {
897
1014
  if (active?.generation === targetGeneration) publish({ bridge: nextBridge });
898
1015
  },
@@ -1079,7 +1196,12 @@ export function createCodexRealtimeController(
1079
1196
  await handleConnectionFailure(error, "reconnect", false);
1080
1197
  } else {
1081
1198
  clearOwner();
1082
- publish({ status: "error", realtimeId: null, mode: null, error: safeError(error) });
1199
+ publish({
1200
+ status: "error",
1201
+ realtimeId: null,
1202
+ mode: null,
1203
+ error: safeError(error),
1204
+ });
1083
1205
  }
1084
1206
  throw error;
1085
1207
  }
@@ -1139,6 +1261,12 @@ export function createCodexRealtimeController(
1139
1261
  if (!record || record.operationId !== lifecycle.operationId) {
1140
1262
  closeBrowserResources();
1141
1263
  owner = null;
1264
+ const leaseExpiresAt = Date.parse(lifecycle.leaseExpiresAt);
1265
+ const remainingLeaseMs = leaseExpiresAt - now().getTime();
1266
+ if (Number.isFinite(remainingLeaseMs) && remainingLeaseMs <= 0) {
1267
+ transitionEnded("Realtime lease expired");
1268
+ return;
1269
+ }
1142
1270
  const message =
1143
1271
  "Realtime is active in another browser owner. It can resume after that owner stops or its lease expires.";
1144
1272
  publish({
@@ -1149,13 +1277,26 @@ export function createCodexRealtimeController(
1149
1277
  diagnostic: diagnostic("lost_owner", message, false),
1150
1278
  error: message,
1151
1279
  });
1280
+ if (Number.isFinite(remainingLeaseMs) && remainingLeaseMs <= MAX_BROWSER_TIMEOUT_MS) {
1281
+ const observedRealtimeId = lifecycle.realtimeId;
1282
+ lostOwnerExpiryTimer = scheduleTimeout(() => {
1283
+ lostOwnerExpiryTimer = null;
1284
+ if (state.status === "lost_owner" && state.realtimeId === observedRealtimeId) {
1285
+ transitionEnded("Realtime lease expired");
1286
+ }
1287
+ }, remainingLeaseMs);
1288
+ }
1152
1289
  return;
1153
1290
  }
1154
1291
  if (connectionTask || state.status === "starting") return;
1155
1292
  closed = false;
1156
1293
  stopping = false;
1157
1294
  owner = record;
1158
- publish({ status: "recovering", realtimeId: lifecycle.realtimeId, error: null });
1295
+ publish({
1296
+ status: "recovering",
1297
+ realtimeId: lifecycle.realtimeId,
1298
+ error: null,
1299
+ });
1159
1300
  connectionTask = begin(record, true)
1160
1301
  .then(() => undefined)
1161
1302
  .catch(async (error) => {
@@ -1359,8 +1500,13 @@ function readOwnerRecord(
1359
1500
  ): OwnerRecord | null {
1360
1501
  const raw = storage?.getItem(key);
1361
1502
  if (!raw) return null;
1503
+ if (new TextEncoder().encode(raw).byteLength > OWNER_DELEGATION_REPLAY_MAX_BYTES) {
1504
+ storage?.removeItem(key);
1505
+ return null;
1506
+ }
1362
1507
  try {
1363
1508
  const parsed = recordValue(JSON.parse(raw));
1509
+ const delegationReplay = readOwnerDelegationReplay(parsed?.delegationReplay);
1364
1510
  if (
1365
1511
  parsed?.version !== OWNER_RECORD_VERSION ||
1366
1512
  parsed.workspaceId !== scope.workspaceId ||
@@ -1368,18 +1514,120 @@ function readOwnerRecord(
1368
1514
  !stringValue(parsed.operationId) ||
1369
1515
  !stringValue(parsed.browserInstanceId) ||
1370
1516
  !stringValue(parsed.ownerKey) ||
1371
- String(parsed.ownerKey).length < 32
1517
+ String(parsed.ownerKey).length < 32 ||
1518
+ delegationReplay === null
1372
1519
  ) {
1373
1520
  storage?.removeItem(key);
1374
1521
  return null;
1375
1522
  }
1376
- return parsed as OwnerRecord;
1523
+ return {
1524
+ ...(parsed as Omit<OwnerRecord, "delegationReplay">),
1525
+ ...(delegationReplay ? { delegationReplay } : {}),
1526
+ };
1377
1527
  } catch {
1378
1528
  storage?.removeItem(key);
1379
1529
  return null;
1380
1530
  }
1381
1531
  }
1382
1532
 
1533
+ function readOwnerDelegationReplay(value: unknown): OwnerDelegationReplay | undefined | null {
1534
+ if (value === undefined) return undefined;
1535
+ const record = recordValue(value);
1536
+ if (
1537
+ record?.version !== OWNER_DELEGATION_REPLAY_VERSION ||
1538
+ !Array.isArray(record.acceptedDelegationItemIds) ||
1539
+ !Array.isArray(record.pendingDelegations) ||
1540
+ record.acceptedDelegationItemIds.length + record.pendingDelegations.length >
1541
+ OWNER_DELEGATION_REPLAY_MAX_CALLS ||
1542
+ record.pendingDelegations.length > CODEX_REALTIME_V3_PENDING_MAX_ENTRIES
1543
+ ) {
1544
+ return null;
1545
+ }
1546
+ const acceptedDelegationItemIds: string[] = [];
1547
+ const accepted = new Set<string>();
1548
+ for (const candidate of record.acceptedDelegationItemIds) {
1549
+ if (typeof candidate !== "string" || candidate.length < 1 || candidate.length > 1_024) {
1550
+ return null;
1551
+ }
1552
+ if (!accepted.has(candidate)) {
1553
+ accepted.add(candidate);
1554
+ acceptedDelegationItemIds.push(candidate);
1555
+ }
1556
+ }
1557
+ const pendingDelegations: SessionRealtimeInboundEntry[] = [];
1558
+ const pendingIds = new Set<string>();
1559
+ for (const candidate of record.pendingDelegations) {
1560
+ const parsed = readSessionRealtimeInboundEntry(candidate);
1561
+ if (
1562
+ !parsed ||
1563
+ parsed.kind !== "delegation_call" ||
1564
+ !parsed.delegationItemId ||
1565
+ accepted.has(parsed.delegationItemId) ||
1566
+ pendingIds.has(parsed.delegationItemId)
1567
+ ) {
1568
+ return null;
1569
+ }
1570
+ pendingIds.add(parsed.delegationItemId);
1571
+ pendingDelegations.push(parsed);
1572
+ }
1573
+ const replay: OwnerDelegationReplay = {
1574
+ version: OWNER_DELEGATION_REPLAY_VERSION,
1575
+ acceptedDelegationItemIds,
1576
+ pendingDelegations,
1577
+ };
1578
+ if (
1579
+ new TextEncoder().encode(JSON.stringify(replay)).byteLength > OWNER_DELEGATION_REPLAY_MAX_BYTES
1580
+ ) {
1581
+ return null;
1582
+ }
1583
+ return replay;
1584
+ }
1585
+
1586
+ function readSessionRealtimeInboundEntry(value: unknown): SessionRealtimeInboundEntry | null {
1587
+ const record = recordValue(value);
1588
+ if (
1589
+ !record ||
1590
+ Object.keys(record).some((key) => !SESSION_REALTIME_INBOUND_ENTRY_KEYS.has(key)) ||
1591
+ typeof record.operationId !== "string" ||
1592
+ !UUID_PATTERN.test(record.operationId) ||
1593
+ typeof record.kind !== "string" ||
1594
+ !SESSION_REALTIME_INBOUND_KINDS.has(record.kind) ||
1595
+ !optionalNullableEnum(record.role, ["user", "assistant"]) ||
1596
+ !optionalNullableBoundedString(record.providerEventId, 1_024) ||
1597
+ !optionalNullableBoundedString(record.delegationItemId, 1_024) ||
1598
+ !optionalNullableBoundedString(record.text, 131_072) ||
1599
+ (record.payload !== undefined && recordValue(record.payload) === null) ||
1600
+ !optionalModelContext(record.modelContext)
1601
+ ) {
1602
+ return null;
1603
+ }
1604
+ return record as SessionRealtimeInboundEntry;
1605
+ }
1606
+
1607
+ function optionalNullableEnum(value: unknown, allowed: readonly string[]): boolean {
1608
+ return (
1609
+ value === undefined || value === null || (typeof value === "string" && allowed.includes(value))
1610
+ );
1611
+ }
1612
+
1613
+ function optionalNullableBoundedString(value: unknown, maxLength: number): boolean {
1614
+ return (
1615
+ value === undefined ||
1616
+ value === null ||
1617
+ (typeof value === "string" && value.length <= maxLength)
1618
+ );
1619
+ }
1620
+
1621
+ function optionalModelContext(value: unknown): boolean {
1622
+ return (
1623
+ value === undefined ||
1624
+ (typeof value === "string" &&
1625
+ value.length >= 1 &&
1626
+ value.length <= 32_768 &&
1627
+ value.trim() === value)
1628
+ );
1629
+ }
1630
+
1383
1631
  function defaultStorage(): CodexRealtimeOwnerStorage | undefined {
1384
1632
  return typeof sessionStorage === "undefined" ? undefined : sessionStorage;
1385
1633
  }
@@ -66,6 +66,7 @@ export function projectSessionRealtimeLifecycle(
66
66
 
67
67
  function realtimeModel(value: unknown): SessionRealtimeModel | null {
68
68
  return value === "gpt-live-1-boulder-alpha" ||
69
+ value === "supergrok/grok-voice-think-fast-2.0" ||
69
70
  value === "opengeni-gateway/openai/gpt-realtime-2.1" ||
70
71
  value === "opengeni-gateway/openai/gpt-realtime-mini" ||
71
72
  value === "opengeni-gateway/xai/grok-voice-think-fast-2.0" ||