@opengeni/sdk 0.57.0 → 1.0.1
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.
- package/README.md +6 -0
- package/dist/artifacts.js +3 -3
- package/dist/{chunk-HJ3HHUT5.js → chunk-4DV37UUA.js} +2 -2
- package/dist/chunk-4DV37UUA.js.map +1 -0
- package/dist/{chunk-JDLDDRNE.js → chunk-A5DC5WEM.js} +269 -32
- package/dist/chunk-A5DC5WEM.js.map +1 -0
- package/dist/{chunk-JRPFWI6T.js → chunk-TYZ4JL4J.js} +2 -2
- package/dist/{chunk-PKQ377ED.js → chunk-YGH5P47G.js} +2 -2
- package/dist/chunk-YGH5P47G.js.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/codex-realtime-controller.d.ts +2 -0
- package/dist/codex-realtime-controller.js +1 -1
- package/dist/codex-realtime-v3.d.ts +13 -2
- package/dist/core.js +2 -2
- package/dist/editable-artifacts.js +1 -1
- package/dist/index.js +4 -4
- package/dist/realtime.js +1 -1
- package/dist/types.d.ts +16 -5
- package/package.json +2 -2
- package/src/client.ts +2 -2
- package/src/codex-realtime-controller.ts +214 -5
- package/src/codex-realtime-v3.ts +162 -31
- package/src/types.ts +17 -5
- package/dist/chunk-HJ3HHUT5.js.map +0 -1
- package/dist/chunk-JDLDDRNE.js.map +0 -1
- package/dist/chunk-PKQ377ED.js.map +0 -1
- /package/dist/{chunk-JRPFWI6T.js.map → chunk-TYZ4JL4J.js.map} +0 -0
package/src/codex-realtime-v3.ts
CHANGED
|
@@ -35,9 +35,10 @@ export const CODEX_REALTIME_V3_PENDING_MAX_ENTRIES = 256;
|
|
|
35
35
|
export const CODEX_REALTIME_V3_PENDING_MAX_BYTES = 16 * 1024 * 1024;
|
|
36
36
|
const REALTIME_DELEGATION_TRANSCRIPT_MAX_BYTES = 65_536;
|
|
37
37
|
const REALTIME_DELEGATION_INPUT_MAX_BYTES = 65_536;
|
|
38
|
+
const REALTIME_MODEL_CONTEXT_MAX_CHARACTERS = 32_768;
|
|
38
39
|
|
|
39
40
|
export type CodexRealtimeV3BridgeFatal = {
|
|
40
|
-
code: "pending_overflow";
|
|
41
|
+
code: "pending_overflow" | "replay_journal_failed";
|
|
41
42
|
message: string;
|
|
42
43
|
};
|
|
43
44
|
|
|
@@ -72,6 +73,19 @@ export type CodexRealtimeV3BridgeOptions = {
|
|
|
72
73
|
>;
|
|
73
74
|
sync(request: SyncSessionRealtimeLedgerRequest): Promise<SyncSessionRealtimeLedgerResponse>;
|
|
74
75
|
randomUUID?: (() => string) | undefined;
|
|
76
|
+
/** Model-visible application context captured once for each durable message-bearing entry. */
|
|
77
|
+
getModelContext?: (() => string | undefined) | undefined;
|
|
78
|
+
/** Controller-lifetime delegation identities shared across provider connection rotations. */
|
|
79
|
+
acceptedDelegationItemIds?: Set<string> | undefined;
|
|
80
|
+
/** Unsynced delegation snapshots retained exactly across provider connection rotations. */
|
|
81
|
+
pendingDelegations?: Map<string, SessionRealtimeInboundEntry> | undefined;
|
|
82
|
+
/** Persist exact delegation replay state before it becomes browser-reload-sensitive. */
|
|
83
|
+
onDelegationReplayStateChange?:
|
|
84
|
+
| ((state: {
|
|
85
|
+
acceptedDelegationItemIds: ReadonlySet<string>;
|
|
86
|
+
pendingDelegations: ReadonlyMap<string, SessionRealtimeInboundEntry>;
|
|
87
|
+
}) => void)
|
|
88
|
+
| undefined;
|
|
75
89
|
/** The controller installs its activation FIFO first, then enables this listener synchronously. */
|
|
76
90
|
listen?: boolean | undefined;
|
|
77
91
|
onSnapshot?: ((snapshot: CodexRealtimeV3BridgeSnapshot) => void) | undefined;
|
|
@@ -125,9 +139,32 @@ export function createCodexRealtimeV3Bridge(
|
|
|
125
139
|
const clientReceivedSequences = new Set<number>();
|
|
126
140
|
const sentSequences = new Set<number>();
|
|
127
141
|
const finalizedTurnIds = new Set<string>();
|
|
142
|
+
const acceptedDelegationItemIds = options.acceptedDelegationItemIds ?? new Set<string>();
|
|
143
|
+
const pendingDelegations = options.pendingDelegations ?? new Map();
|
|
144
|
+
const locallyQueuedDelegationItemIds = new Set<string>();
|
|
128
145
|
let transcriptSinceDelegation: FinalizedTranscript[] = [];
|
|
129
|
-
let pendingDelegationUserTranscript: {
|
|
146
|
+
let pendingDelegationUserTranscript: {
|
|
147
|
+
delegationItemId: string;
|
|
148
|
+
text: string;
|
|
149
|
+
} | null = null;
|
|
130
150
|
const randomUUID = options.randomUUID ?? defaultRandomUUID;
|
|
151
|
+
const currentModelContext = (): string | undefined => {
|
|
152
|
+
let context: string | undefined;
|
|
153
|
+
try {
|
|
154
|
+
context = options.getModelContext?.()?.trim();
|
|
155
|
+
} catch {
|
|
156
|
+
lastError =
|
|
157
|
+
"Realtime model context callback failed; the provider message continued without application context";
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
if (!context) return undefined;
|
|
161
|
+
if (context.length > REALTIME_MODEL_CONTEXT_MAX_CHARACTERS) {
|
|
162
|
+
lastError =
|
|
163
|
+
"Realtime model context exceeded the 32768-character limit; the provider message continued without application context";
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
return context;
|
|
167
|
+
};
|
|
131
168
|
|
|
132
169
|
const snapshot = (): CodexRealtimeV3BridgeSnapshot => ({
|
|
133
170
|
connectionId: options.connectionId,
|
|
@@ -148,9 +185,9 @@ export function createCodexRealtimeV3Bridge(
|
|
|
148
185
|
});
|
|
149
186
|
const publish = (): void => options.onSnapshot?.(snapshot());
|
|
150
187
|
|
|
151
|
-
const
|
|
188
|
+
const triggerFatalCode = (code: CodexRealtimeV3BridgeFatal["code"], message: string): void => {
|
|
152
189
|
if (closed || fatal) return;
|
|
153
|
-
fatal = { code
|
|
190
|
+
fatal = { code, message };
|
|
154
191
|
lastError = message;
|
|
155
192
|
publish();
|
|
156
193
|
try {
|
|
@@ -161,6 +198,10 @@ export function createCodexRealtimeV3Bridge(
|
|
|
161
198
|
}
|
|
162
199
|
};
|
|
163
200
|
|
|
201
|
+
const triggerFatal = (message: string): void => {
|
|
202
|
+
triggerFatalCode("pending_overflow", message);
|
|
203
|
+
};
|
|
204
|
+
|
|
164
205
|
const enqueue = (entry: SessionRealtimeInboundEntry): boolean => {
|
|
165
206
|
if (closed || sealed || fatal) return false;
|
|
166
207
|
const bytes = utf8ByteLength(JSON.stringify(entry));
|
|
@@ -177,6 +218,14 @@ export function createCodexRealtimeV3Bridge(
|
|
|
177
218
|
return true;
|
|
178
219
|
};
|
|
179
220
|
|
|
221
|
+
// Same-browser reload reconstructs this exact map from the persisted owner
|
|
222
|
+
// journal. Queue those first-frozen calls before listening to the replacement
|
|
223
|
+
// provider connection; startup proof or a duplicate call drives the normal
|
|
224
|
+
// flush path without resampling application context.
|
|
225
|
+
for (const [delegationItemId, entry] of pendingDelegations) {
|
|
226
|
+
if (enqueue(entry)) locallyQueuedDelegationItemIds.add(delegationItemId);
|
|
227
|
+
}
|
|
228
|
+
|
|
180
229
|
const hasWork = (): boolean =>
|
|
181
230
|
pendingInbound.length > 0 ||
|
|
182
231
|
(!providerStartedAccepted && providerStarted !== undefined) ||
|
|
@@ -213,7 +262,46 @@ export function createCodexRealtimeV3Bridge(
|
|
|
213
262
|
throw error;
|
|
214
263
|
}
|
|
215
264
|
|
|
265
|
+
const acceptedAfterSync = new Set(acceptedDelegationItemIds);
|
|
266
|
+
const pendingAfterSync = new Map(pendingDelegations);
|
|
267
|
+
let delegationReplayChanged = false;
|
|
268
|
+
for (const item of batch) {
|
|
269
|
+
if (item.entry.kind === "delegation_call" && item.entry.delegationItemId) {
|
|
270
|
+
delegationReplayChanged = true;
|
|
271
|
+
acceptedAfterSync.add(item.entry.delegationItemId);
|
|
272
|
+
if (pendingAfterSync.get(item.entry.delegationItemId) === item.entry) {
|
|
273
|
+
pendingAfterSync.delete(item.entry.delegationItemId);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (delegationReplayChanged) {
|
|
278
|
+
try {
|
|
279
|
+
options.onDelegationReplayStateChange?.({
|
|
280
|
+
acceptedDelegationItemIds: acceptedAfterSync,
|
|
281
|
+
pendingDelegations: pendingAfterSync,
|
|
282
|
+
});
|
|
283
|
+
} catch (error) {
|
|
284
|
+
// The server may already have admitted this exact batch. Keep it
|
|
285
|
+
// queued with the original operation identity and stop this bridge.
|
|
286
|
+
// Recovery can safely replay it because the prior pending journal
|
|
287
|
+
// state remains authoritative until the accepted transition writes.
|
|
288
|
+
pendingInbound = [...batch, ...pendingInbound];
|
|
289
|
+
triggerFatalCode(
|
|
290
|
+
"replay_journal_failed",
|
|
291
|
+
`Codex realtime delegation replay journal failed: ${safeError(error)}`,
|
|
292
|
+
);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
216
297
|
for (const item of batch) {
|
|
298
|
+
if (item.entry.kind === "delegation_call" && item.entry.delegationItemId) {
|
|
299
|
+
acceptedDelegationItemIds.add(item.entry.delegationItemId);
|
|
300
|
+
if (pendingDelegations.get(item.entry.delegationItemId) === item.entry) {
|
|
301
|
+
pendingDelegations.delete(item.entry.delegationItemId);
|
|
302
|
+
}
|
|
303
|
+
locallyQueuedDelegationItemIds.delete(item.entry.delegationItemId);
|
|
304
|
+
}
|
|
217
305
|
pendingInboundCount -= 1;
|
|
218
306
|
pendingInboundBytes -= item.bytes;
|
|
219
307
|
}
|
|
@@ -345,31 +433,67 @@ export function createCodexRealtimeV3Bridge(
|
|
|
345
433
|
// These events are provider UI deltas. `turn.done` is the single
|
|
346
434
|
// authoritative finalized transcript persisted below.
|
|
347
435
|
} else if (event.type === "delegation.created") {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
436
|
+
if (acceptedDelegationItemIds.has(event.delegationItemId)) {
|
|
437
|
+
ignoredEventCount += 1;
|
|
438
|
+
lastIgnoredEventType = event.type;
|
|
439
|
+
} else if (locallyQueuedDelegationItemIds.has(event.delegationItemId)) {
|
|
440
|
+
ignoredEventCount += 1;
|
|
441
|
+
lastIgnoredEventType = event.type;
|
|
442
|
+
durable = true;
|
|
443
|
+
} else {
|
|
444
|
+
let entry = pendingDelegations.get(event.delegationItemId);
|
|
445
|
+
if (!entry) {
|
|
446
|
+
const transcript = delegationTranscript(transcriptSinceDelegation, event.inputTranscript);
|
|
447
|
+
const coveredTurnIds = transcriptSinceDelegation.map((item) => item.turnId);
|
|
448
|
+
const modelContext = currentModelContext();
|
|
449
|
+
entry = {
|
|
450
|
+
operationId: randomUUID(),
|
|
451
|
+
kind: "delegation_call",
|
|
452
|
+
providerEventId: event.providerEventId,
|
|
453
|
+
delegationItemId: event.delegationItemId,
|
|
454
|
+
text: renderRealtimeDelegationInput(event.inputTranscript, transcript),
|
|
455
|
+
payload: {
|
|
456
|
+
offsetMs: event.offsetMs,
|
|
457
|
+
inputTranscript: event.inputTranscript,
|
|
458
|
+
transcriptFenceTurnIds: coveredTurnIds,
|
|
459
|
+
},
|
|
460
|
+
...(modelContext ? { modelContext } : {}),
|
|
461
|
+
};
|
|
462
|
+
pendingDelegations.set(event.delegationItemId, entry);
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
options.onDelegationReplayStateChange?.({
|
|
466
|
+
acceptedDelegationItemIds,
|
|
467
|
+
pendingDelegations,
|
|
468
|
+
});
|
|
469
|
+
} catch (error) {
|
|
470
|
+
triggerFatalCode(
|
|
471
|
+
"replay_journal_failed",
|
|
472
|
+
`Codex realtime delegation replay journal failed: ${safeError(error)}`,
|
|
473
|
+
);
|
|
474
|
+
return Promise.resolve();
|
|
475
|
+
}
|
|
476
|
+
durable = enqueue(entry);
|
|
477
|
+
if (durable) {
|
|
478
|
+
locallyQueuedDelegationItemIds.add(event.delegationItemId);
|
|
479
|
+
activeDelegationId = event.delegationItemId;
|
|
480
|
+
const frozenInputTranscript =
|
|
481
|
+
typeof entry.payload.inputTranscript === "string"
|
|
482
|
+
? entry.payload.inputTranscript
|
|
483
|
+
: event.inputTranscript;
|
|
484
|
+
const alreadyFinalized = transcriptSinceDelegation.some(
|
|
485
|
+
(item) =>
|
|
486
|
+
item.role === "user" &&
|
|
487
|
+
normalizedTranscript(item.text) === normalizedTranscript(frozenInputTranscript),
|
|
488
|
+
);
|
|
489
|
+
pendingDelegationUserTranscript = alreadyFinalized
|
|
490
|
+
? null
|
|
491
|
+
: {
|
|
492
|
+
delegationItemId: event.delegationItemId,
|
|
493
|
+
text: frozenInputTranscript,
|
|
494
|
+
};
|
|
495
|
+
transcriptSinceDelegation = [];
|
|
496
|
+
}
|
|
373
497
|
}
|
|
374
498
|
} else if (event.type === "output_audio.delta") {
|
|
375
499
|
speaking = true;
|
|
@@ -383,7 +507,9 @@ export function createCodexRealtimeV3Bridge(
|
|
|
383
507
|
normalizedTranscript(pendingDelegationUserTranscript.text)
|
|
384
508
|
? pendingDelegationUserTranscript.delegationItemId
|
|
385
509
|
: null;
|
|
386
|
-
durable = enqueue(
|
|
510
|
+
durable = enqueue(
|
|
511
|
+
finalTranscript(randomUUID, event, coveredByDelegationItemId, currentModelContext()),
|
|
512
|
+
);
|
|
387
513
|
if (durable) {
|
|
388
514
|
finalizedTurnIds.add(event.turnId);
|
|
389
515
|
if (coveredByDelegationItemId) {
|
|
@@ -446,6 +572,7 @@ function finalTranscript(
|
|
|
446
572
|
randomUUID: () => string,
|
|
447
573
|
event: Extract<CodexRealtimeV3Event, { type: "turn.done" }>,
|
|
448
574
|
coveredByDelegationItemId: string | null,
|
|
575
|
+
modelContext: string | undefined,
|
|
449
576
|
): SessionRealtimeInboundEntry {
|
|
450
577
|
return {
|
|
451
578
|
operationId: randomUUID(),
|
|
@@ -456,6 +583,7 @@ function finalTranscript(
|
|
|
456
583
|
turnId: event.turnId,
|
|
457
584
|
...(coveredByDelegationItemId ? { coveredByDelegationItemId } : {}),
|
|
458
585
|
},
|
|
586
|
+
...(modelContext ? { modelContext } : {}),
|
|
459
587
|
};
|
|
460
588
|
}
|
|
461
589
|
|
|
@@ -539,7 +667,10 @@ function sendOutbound(events: RTCDataChannel, entry: SessionRealtimeLedgerEntry)
|
|
|
539
667
|
text,
|
|
540
668
|
channel: payloadChannel ?? "speakable",
|
|
541
669
|
})
|
|
542
|
-
: encodeCodexRealtimeV3SessionContextAppend({
|
|
670
|
+
: encodeCodexRealtimeV3SessionContextAppend({
|
|
671
|
+
text,
|
|
672
|
+
channel: payloadChannel,
|
|
673
|
+
});
|
|
543
674
|
for (const message of messages) events.send(JSON.stringify(message));
|
|
544
675
|
}
|
|
545
676
|
|
package/src/types.ts
CHANGED
|
@@ -125,6 +125,8 @@ export type SessionRealtimeInboundEntry = {
|
|
|
125
125
|
delegationItemId?: string | null | undefined;
|
|
126
126
|
text?: string | null | undefined;
|
|
127
127
|
payload?: Record<string, unknown> | undefined;
|
|
128
|
+
/** Model-visible application context attached to this exact realtime message. */
|
|
129
|
+
modelContext?: string | undefined;
|
|
128
130
|
};
|
|
129
131
|
|
|
130
132
|
export type SyncSessionRealtimeLedgerRequest = {
|
|
@@ -743,8 +745,9 @@ export type GoogleDriveConnectionMetadata = {
|
|
|
743
745
|
googleEmail: string;
|
|
744
746
|
googleDisplayName: string | null;
|
|
745
747
|
verifiedAt: string;
|
|
746
|
-
accessMode: "metadata_readonly" | "readonly";
|
|
748
|
+
accessMode: "file_only" | "metadata_readonly" | "readonly";
|
|
747
749
|
lifecycle?: GoogleDriveConnectionLifecycle | undefined;
|
|
750
|
+
outputDestination?: GoogleDriveOutputDestination | undefined;
|
|
748
751
|
documentDestination?: ConnectorDocumentDestination | undefined;
|
|
749
752
|
selectedSources?: GoogleDriveSelectedSource[] | undefined;
|
|
750
753
|
/** @deprecated Read selectedSources; retained while existing connections migrate. */
|
|
@@ -752,8 +755,17 @@ export type GoogleDriveConnectionMetadata = {
|
|
|
752
755
|
[key: string]: unknown;
|
|
753
756
|
};
|
|
754
757
|
|
|
758
|
+
export type GoogleDriveOutputDestination = {
|
|
759
|
+
folderId: string;
|
|
760
|
+
folderName: string;
|
|
761
|
+
driveId: string | null;
|
|
762
|
+
location: "my_drive" | "shared_drive";
|
|
763
|
+
selectedAt: string;
|
|
764
|
+
};
|
|
765
|
+
|
|
755
766
|
export type GoogleDriveOAuthStartRequest = {
|
|
756
767
|
connectionId?: string | undefined;
|
|
768
|
+
capability?: "source_read" | "publish" | undefined;
|
|
757
769
|
};
|
|
758
770
|
|
|
759
771
|
export type GoogleDriveOAuthStartResponse = {
|
|
@@ -2270,8 +2282,8 @@ export type CreateSessionRequest = {
|
|
|
2270
2282
|
initialMessage?: string | undefined;
|
|
2271
2283
|
/** Create an idle session shell so realtime voice can be the first interaction. */
|
|
2272
2284
|
startMode?: "realtime" | undefined;
|
|
2273
|
-
/**
|
|
2274
|
-
|
|
2285
|
+
/** Model-visible application context attached to the initial user message; omitted by standard timeline rendering. */
|
|
2286
|
+
modelContext?: string | undefined;
|
|
2275
2287
|
// Per-session agent persona/system instructions (org-visible metadata, not a
|
|
2276
2288
|
// secret). Delivered system-level, composed AFTER the per-workspace persona —
|
|
2277
2289
|
// how a host supplies per-agent-type prompts without leaking them into the
|
|
@@ -2984,7 +2996,7 @@ export type ClientAuthConfig =
|
|
|
2984
2996
|
|
|
2985
2997
|
// Kept value-identical to @opengeni/contracts and pinned by the SDK contract
|
|
2986
2998
|
// parity suite. The SDK has no runtime dependency on the Zod contracts package.
|
|
2987
|
-
export const OPENGENI_API_CONTRACT_REVISION = "2026-08-
|
|
2999
|
+
export const OPENGENI_API_CONTRACT_REVISION = "2026-08-model-context-v1" as const;
|
|
2988
3000
|
export const OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract" as const;
|
|
2989
3001
|
/** Bounded request/response identifier shared by browser, ingress, and API diagnostics. */
|
|
2990
3002
|
export const OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id" as const;
|
|
@@ -6006,7 +6018,7 @@ export type UserMessageEventInput = {
|
|
|
6006
6018
|
payload: {
|
|
6007
6019
|
text: string;
|
|
6008
6020
|
annotations?: SubmittedTimelineAnnotation[] | undefined;
|
|
6009
|
-
|
|
6021
|
+
modelContext?: string | undefined;
|
|
6010
6022
|
resources?: ResourceRef[] | undefined;
|
|
6011
6023
|
model?: string | undefined;
|
|
6012
6024
|
reasoningEffort?: ReasoningEffort | undefined;
|