@opengeni/db 0.22.1 → 0.23.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 (48) hide show
  1. package/dist/{chunk-CYGFLLMN.js → chunk-3UCHDMKG.js} +656 -282
  2. package/dist/chunk-3UCHDMKG.js.map +1 -0
  3. package/dist/{chunk-BNGEN5QZ.js → chunk-L6ADMZHE.js} +24 -2
  4. package/dist/chunk-L6ADMZHE.js.map +1 -0
  5. package/dist/index.d.ts +42 -3
  6. package/dist/index.js +6919 -3760
  7. package/dist/index.js.map +1 -1
  8. package/dist/provision-roles.js +1 -1
  9. package/dist/runtime-posture.d.ts +3 -3
  10. package/dist/schema.d.ts +1416 -92
  11. package/dist/schema.js +11 -1
  12. package/dist/session-control.d.ts +7 -1
  13. package/dist/session-queue-commands.d.ts +8 -0
  14. package/dist/session-realtime-context.d.ts +56 -0
  15. package/dist/session-realtime-ledger.d.ts +188 -0
  16. package/dist/session-realtime-mirror.d.ts +30 -0
  17. package/dist/session-realtime-state.d.ts +2 -0
  18. package/dist/session-realtime-terminal.d.ts +40 -0
  19. package/dist/session-realtime.d.ts +59 -0
  20. package/dist/workspace-instruction-policies-schema.d.ts +239 -0
  21. package/dist/workspace-instruction-policies.d.ts +44 -0
  22. package/drizzle/0156_slack_reaction_trigger.sql +49 -0
  23. package/drizzle/0157_session_policy_role_snapshots.sql +1146 -0
  24. package/drizzle/0158_session_realtime_mode.sql +88 -0
  25. package/drizzle/0159_session_realtime_ledger.sql +198 -0
  26. package/drizzle/0160_session_realtime_delegation_terminal.sql +38 -0
  27. package/drizzle/0161_session_realtime_context_projection.sql +82 -0
  28. package/drizzle/0162_session_realtime_connection_promotion.sql +53 -0
  29. package/drizzle/0163_session_realtime_delegation_progress.sql +35 -0
  30. package/drizzle/0164_session_realtime_models.sql +28 -0
  31. package/package.json +4 -4
  32. package/src/index.ts +670 -79
  33. package/src/preference-registry.ts +11 -6
  34. package/src/provision-roles.ts +12 -0
  35. package/src/runtime-posture.ts +10 -0
  36. package/src/schema.ts +400 -43
  37. package/src/session-control.ts +596 -21
  38. package/src/session-queue-commands.ts +76 -7
  39. package/src/session-realtime-context.ts +393 -0
  40. package/src/session-realtime-ledger.ts +1790 -0
  41. package/src/session-realtime-mirror.ts +160 -0
  42. package/src/session-realtime-state.ts +25 -0
  43. package/src/session-realtime-terminal.ts +306 -0
  44. package/src/session-realtime.ts +611 -0
  45. package/src/workspace-instruction-policies-schema.ts +41 -0
  46. package/src/workspace-instruction-policies.ts +131 -2
  47. package/dist/chunk-BNGEN5QZ.js.map +0 -1
  48. package/dist/chunk-CYGFLLMN.js.map +0 -1
@@ -0,0 +1,1790 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { LatencyMode, ReasoningEffort, type SessionRealtimeMode } from "@opengeni/contracts";
4
+ import { and, asc, desc, eq, inArray, isNull, or, sql } from "drizzle-orm";
5
+
6
+ import { sanitizeEventPayload } from "./event-payload-sanitizer";
7
+ import type { Database } from "./index";
8
+ import * as schema from "./schema";
9
+ import {
10
+ assertSessionRealtimeOwnerInTransaction,
11
+ SessionRealtimeConflictError,
12
+ type AssertSessionRealtimeOwnerInput,
13
+ } from "./session-realtime";
14
+ import { lockSessionEventWriteRows, lockWorkspaceInferenceControl } from "./session-control";
15
+ import { submitHumanPromptInTransaction } from "./session-queue-commands";
16
+ import { mirrorSessionRealtimeContextInTransaction } from "./session-realtime-mirror";
17
+
18
+ export const SESSION_REALTIME_LEDGER_MAX_BATCH = 64;
19
+ export const SESSION_REALTIME_LEDGER_MAX_TEXT_BYTES = 131_072;
20
+ export const SESSION_REALTIME_LEDGER_MAX_PAYLOAD_BYTES = 131_072;
21
+ export const SESSION_REALTIME_LEDGER_MAX_OUTBOUND = 100;
22
+ export const SESSION_REALTIME_STARTUP_MAX_ENTRIES = 100;
23
+ const SESSION_REALTIME_SOURCE_EVENT_IDS_MAX = 64;
24
+
25
+ export type SessionRealtimeConnectionState =
26
+ | "negotiating"
27
+ | "ready"
28
+ | "active"
29
+ | "failed"
30
+ | "closed";
31
+
32
+ export type SessionRealtimeConnectionPromotionMode = "legacy" | "staged";
33
+
34
+ export type SessionRealtimeConnection = {
35
+ id: string;
36
+ realtimeId: string;
37
+ operationId: string;
38
+ connectionEpoch: number;
39
+ startupFenceSequence: number;
40
+ promotionMode: SessionRealtimeConnectionPromotionMode;
41
+ state: SessionRealtimeConnectionState;
42
+ sdpAnswer: string | null;
43
+ failureCode: string | null;
44
+ providerSessionId: string | null;
45
+ startupEventId: string | null;
46
+ startupAcknowledgedAt: string | null;
47
+ negotiatedAt: string | null;
48
+ closedAt: string | null;
49
+ createdAt: string;
50
+ updatedAt: string;
51
+ };
52
+
53
+ export type ClaimSessionRealtimeConnectionInput = AssertSessionRealtimeOwnerInput & {
54
+ operationId: string;
55
+ expectedConnectionEpoch: number;
56
+ rotate: boolean;
57
+ promotionMode?: SessionRealtimeConnectionPromotionMode | undefined;
58
+ };
59
+
60
+ export type ClaimSessionRealtimeConnectionResult = {
61
+ connection: SessionRealtimeConnection;
62
+ startupEntries: SessionRealtimeLedgerEntry[];
63
+ mode: SessionRealtimeMode;
64
+ modeVersion: number;
65
+ replay: boolean;
66
+ };
67
+
68
+ export type CompleteSessionRealtimeConnectionInput = {
69
+ workspaceId: string;
70
+ sessionId: string;
71
+ realtimeId: string;
72
+ connectionId: string;
73
+ operationId: string;
74
+ connectionEpoch: number;
75
+ sdpAnswer: string;
76
+ now?: Date;
77
+ };
78
+
79
+ export type CompleteSessionRealtimeConnectionResult = {
80
+ connection: SessionRealtimeConnection;
81
+ replay: boolean;
82
+ };
83
+
84
+ export type ActivateSessionRealtimeConnectionInput = AssertSessionRealtimeOwnerInput & {
85
+ operationId: string;
86
+ connectionId: string;
87
+ connectionEpoch: number;
88
+ expectedConnectionEpoch: number;
89
+ };
90
+
91
+ export type ActivateSessionRealtimeConnectionResult = {
92
+ connection: SessionRealtimeConnection;
93
+ mode: SessionRealtimeMode;
94
+ replay: boolean;
95
+ };
96
+
97
+ export type FailSessionRealtimeConnectionInput = Omit<
98
+ CompleteSessionRealtimeConnectionInput,
99
+ "sdpAnswer"
100
+ > & {
101
+ failureCode: string;
102
+ };
103
+
104
+ export type FailSessionRealtimeConnectionResult = {
105
+ connection: SessionRealtimeConnection;
106
+ replay: boolean;
107
+ };
108
+
109
+ export type SessionRealtimeLedgerDirection = "provider_in" | "provider_out";
110
+
111
+ export type SessionRealtimeLedgerKind =
112
+ | "user_transcript"
113
+ | "assistant_transcript"
114
+ | "delegation_call"
115
+ | "delegation_progress"
116
+ | "delegation_result"
117
+ | "interruption"
118
+ | "session_update"
119
+ | "error";
120
+
121
+ export type SessionRealtimeLedgerEntry = {
122
+ id: string;
123
+ realtimeId: string;
124
+ operationId: string;
125
+ connectionEpoch: number;
126
+ sequence: number;
127
+ direction: SessionRealtimeLedgerDirection;
128
+ kind: SessionRealtimeLedgerKind;
129
+ role: "user" | "assistant" | null;
130
+ providerEventId: string | null;
131
+ delegationItemId: string | null;
132
+ sourceUpdateId: string | null;
133
+ historyItemId: string | null;
134
+ turnId: string | null;
135
+ text: string | null;
136
+ payload: Record<string, unknown>;
137
+ clientAckedAt: string | null;
138
+ providerAckedAt: string | null;
139
+ createdAt: string;
140
+ updatedAt: string;
141
+ };
142
+
143
+ export type SessionRealtimeInboundEntryInput = {
144
+ operationId: string;
145
+ kind: Exclude<
146
+ SessionRealtimeLedgerKind,
147
+ "delegation_progress" | "delegation_result" | "session_update"
148
+ >;
149
+ role?: "user" | "assistant" | null | undefined;
150
+ providerEventId?: string | null | undefined;
151
+ delegationItemId?: string | null | undefined;
152
+ text?: string | null | undefined;
153
+ payload?: Record<string, unknown> | undefined;
154
+ };
155
+
156
+ export type SyncSessionRealtimeLedgerInput = AssertSessionRealtimeOwnerInput & {
157
+ connectionId: string;
158
+ connectionEpoch: number;
159
+ entries?: SessionRealtimeInboundEntryInput[] | undefined;
160
+ clientAckThroughSequence?: number | null | undefined;
161
+ providerAckSequences?: number[] | undefined;
162
+ providerStarted?:
163
+ | { providerSessionId: string; providerEventId?: string | null | undefined }
164
+ | undefined;
165
+ };
166
+
167
+ export type SyncSessionRealtimeLedgerResult = {
168
+ accepted: Array<{ entry: SessionRealtimeLedgerEntry; replay: boolean }>;
169
+ outbound: SessionRealtimeLedgerEntry[];
170
+ eventIds: string[];
171
+ workflowWakeRevision: number | null;
172
+ };
173
+
174
+ export type SyncSessionRealtimeLedgerHooks = {
175
+ /** Test-only failure injection after both durable admission rows exist. */
176
+ afterDelegationAdmission?:
177
+ | ((admission: { entryId: string; turnId: string }) => void | Promise<void>)
178
+ | undefined;
179
+ };
180
+
181
+ export type AppendSessionRealtimeOutboundInput = {
182
+ workspaceId: string;
183
+ sessionId: string;
184
+ realtimeId: string;
185
+ operationId: string;
186
+ connectionEpoch: number;
187
+ kind: Extract<SessionRealtimeLedgerKind, "delegation_result" | "error">;
188
+ delegationItemId?: string | null;
189
+ text?: string | null;
190
+ payload?: Record<string, unknown>;
191
+ now?: Date;
192
+ };
193
+
194
+ export type AppendSessionRealtimeOutboundResult = {
195
+ entry: SessionRealtimeLedgerEntry;
196
+ replay: boolean;
197
+ };
198
+
199
+ export {
200
+ projectSessionRealtimeDelegationTerminalInTransaction,
201
+ type ProjectSessionRealtimeDelegationTerminalInput,
202
+ type ProjectSessionRealtimeDelegationTerminalResult,
203
+ } from "./session-realtime-terminal";
204
+
205
+ export type ProjectSessionRealtimeDelegationProgressInput = {
206
+ accountId: string;
207
+ workspaceId: string;
208
+ sessionId: string;
209
+ turnId: string;
210
+ events: ReadonlyArray<{
211
+ id: string;
212
+ sequence: number;
213
+ type: string;
214
+ payload: unknown;
215
+ }>;
216
+ now?: Date;
217
+ };
218
+
219
+ export type ProjectSessionRealtimeDelegationProgressResult = {
220
+ entries: SessionRealtimeLedgerEntry[];
221
+ };
222
+
223
+ type ConnectionRow = typeof schema.sessionRealtimeConnections.$inferSelect;
224
+ type EntryRow = typeof schema.sessionRealtimeEntries.$inferSelect;
225
+
226
+ function mapConnection(row: ConnectionRow): SessionRealtimeConnection {
227
+ return {
228
+ id: row.id,
229
+ realtimeId: row.realtimeId,
230
+ operationId: row.operationId,
231
+ connectionEpoch: row.connectionEpoch,
232
+ startupFenceSequence: row.startupFenceSequence,
233
+ promotionMode: row.promotionMode as SessionRealtimeConnectionPromotionMode,
234
+ state: row.state as SessionRealtimeConnectionState,
235
+ sdpAnswer: row.sdpAnswer,
236
+ failureCode: row.failureCode,
237
+ providerSessionId: row.providerSessionId,
238
+ startupEventId: row.startupEventId,
239
+ startupAcknowledgedAt: row.startupAcknowledgedAt?.toISOString() ?? null,
240
+ negotiatedAt: row.negotiatedAt?.toISOString() ?? null,
241
+ closedAt: row.closedAt?.toISOString() ?? null,
242
+ createdAt: row.createdAt.toISOString(),
243
+ updatedAt: row.updatedAt.toISOString(),
244
+ };
245
+ }
246
+
247
+ function mapEntry(row: EntryRow): SessionRealtimeLedgerEntry {
248
+ return {
249
+ id: row.id,
250
+ realtimeId: row.realtimeId,
251
+ operationId: row.operationId,
252
+ connectionEpoch: row.connectionEpoch,
253
+ sequence: row.sequence,
254
+ direction: row.direction as SessionRealtimeLedgerDirection,
255
+ kind: row.kind as SessionRealtimeLedgerKind,
256
+ role: row.role as "user" | "assistant" | null,
257
+ providerEventId: row.providerEventId,
258
+ delegationItemId: row.delegationItemId,
259
+ sourceUpdateId: row.sourceUpdateId,
260
+ historyItemId: row.historyItemId,
261
+ turnId: row.turnId,
262
+ text: row.text,
263
+ payload: row.payload,
264
+ clientAckedAt: row.clientAckedAt?.toISOString() ?? null,
265
+ providerAckedAt: row.providerAckedAt?.toISOString() ?? null,
266
+ createdAt: row.createdAt.toISOString(),
267
+ updatedAt: row.updatedAt.toISOString(),
268
+ };
269
+ }
270
+
271
+ function assertConnectionEpoch(value: number): void {
272
+ if (!Number.isSafeInteger(value) || value < 1) {
273
+ throw new SessionRealtimeConflictError(
274
+ "REALTIME_CONNECTION_CHANGED",
275
+ "Realtime connection epoch is invalid",
276
+ );
277
+ }
278
+ }
279
+
280
+ function assertBoundedString(
281
+ value: string | null | undefined,
282
+ maximum: number,
283
+ label: string,
284
+ ): void {
285
+ if (value !== null && value !== undefined && Buffer.byteLength(value, "utf8") > maximum) {
286
+ throw new Error(`${label} exceeds the durable realtime ledger limit`);
287
+ }
288
+ }
289
+
290
+ function boundedPayload(input: Record<string, unknown> | undefined): Record<string, unknown> {
291
+ const payload = sanitizeEventPayload(input ?? {});
292
+ if (
293
+ Buffer.byteLength(JSON.stringify(payload), "utf8") > SESSION_REALTIME_LEDGER_MAX_PAYLOAD_BYTES
294
+ ) {
295
+ throw new Error("Realtime payload exceeds the durable ledger limit");
296
+ }
297
+ return payload;
298
+ }
299
+
300
+ function realtimeDelegationMetadata(metadata: Record<string, unknown>): {
301
+ realtimeId: string;
302
+ connectionEpoch: number;
303
+ delegationItemId: string;
304
+ ledgerEntryId: string;
305
+ } | null {
306
+ const value = metadata.realtimeDelegation;
307
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
308
+ const delegation = value as Record<string, unknown>;
309
+ if (
310
+ typeof delegation.realtimeId !== "string" ||
311
+ typeof delegation.connectionEpoch !== "number" ||
312
+ !Number.isSafeInteger(delegation.connectionEpoch) ||
313
+ delegation.connectionEpoch < 1 ||
314
+ typeof delegation.delegationItemId !== "string" ||
315
+ delegation.delegationItemId.length === 0 ||
316
+ typeof delegation.ledgerEntryId !== "string"
317
+ ) {
318
+ return null;
319
+ }
320
+ return {
321
+ realtimeId: delegation.realtimeId,
322
+ connectionEpoch: delegation.connectionEpoch,
323
+ delegationItemId: delegation.delegationItemId,
324
+ ledgerEntryId: delegation.ledgerEntryId,
325
+ };
326
+ }
327
+
328
+ /** True only for an exact provider-call-linked ordinary turn shape. */
329
+ export function isSessionRealtimeDelegationTurnMetadata(
330
+ metadata: Record<string, unknown>,
331
+ ): boolean {
332
+ return realtimeDelegationMetadata(metadata) !== null;
333
+ }
334
+
335
+ function deterministicUuid(seed: string): string {
336
+ const bytes = createHash("sha256").update(seed, "utf8").digest().subarray(0, 16);
337
+ bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x50;
338
+ bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
339
+ const hex = bytes.toString("hex");
340
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
341
+ }
342
+
343
+ function sourceEventProvenance(ids: readonly string[]): {
344
+ identity: string;
345
+ ids: string[];
346
+ count: number;
347
+ truncated: boolean;
348
+ } {
349
+ const identity = createHash("sha256").update(ids.join(":"), "utf8").digest("hex");
350
+ if (ids.length <= SESSION_REALTIME_SOURCE_EVENT_IDS_MAX) {
351
+ return { identity, ids: [...ids], count: ids.length, truncated: false };
352
+ }
353
+ const half = SESSION_REALTIME_SOURCE_EVENT_IDS_MAX / 2;
354
+ return {
355
+ identity,
356
+ ids: [...ids.slice(0, half), ...ids.slice(-half)],
357
+ count: ids.length,
358
+ truncated: true,
359
+ };
360
+ }
361
+
362
+ function utf8Chunks(value: string, maximumBytes: number): string[] {
363
+ const bytes = Buffer.from(value, "utf8");
364
+ if (bytes.length <= maximumBytes) return [value];
365
+ const chunks: string[] = [];
366
+ let start = 0;
367
+ while (start < bytes.length) {
368
+ let end = Math.min(start + maximumBytes, bytes.length);
369
+ if (end < bytes.length) {
370
+ while (end > start && (bytes[end]! & 0xc0) === 0x80) end -= 1;
371
+ }
372
+ if (end === start) throw new Error("Realtime progress chunk boundary is invalid");
373
+ chunks.push(bytes.subarray(start, end).toString("utf8"));
374
+ start = end;
375
+ }
376
+ return chunks;
377
+ }
378
+
379
+ function expectedRole(input: SessionRealtimeInboundEntryInput): "user" | "assistant" | null {
380
+ if (input.kind === "user_transcript") {
381
+ if (input.role !== undefined && input.role !== "user") {
382
+ throw new Error("User transcript must use the user role");
383
+ }
384
+ return "user";
385
+ }
386
+ if (input.kind === "assistant_transcript") {
387
+ if (input.role !== undefined && input.role !== "assistant") {
388
+ throw new Error("Assistant transcript must use the assistant role");
389
+ }
390
+ return "assistant";
391
+ }
392
+ const role = input.role ?? null;
393
+ if (!["user_transcript", "assistant_transcript"].includes(input.kind) && role !== null) {
394
+ throw new Error("Only realtime transcripts may carry a role");
395
+ }
396
+ return null;
397
+ }
398
+
399
+ export async function claimSessionRealtimeConnectionInTransaction(
400
+ db: Database,
401
+ input: ClaimSessionRealtimeConnectionInput,
402
+ ): Promise<ClaimSessionRealtimeConnectionResult> {
403
+ assertConnectionEpoch(input.expectedConnectionEpoch);
404
+ const promotionMode = input.promotionMode ?? "staged";
405
+ let staleRotationError: SessionRealtimeConflictError | null = null;
406
+ let mode;
407
+ try {
408
+ mode = await assertSessionRealtimeOwnerInTransaction(db, input);
409
+ } catch (error) {
410
+ if (
411
+ !input.rotate ||
412
+ !(error instanceof SessionRealtimeConflictError) ||
413
+ error.code !== "REALTIME_VERSION_CHANGED"
414
+ ) {
415
+ throw error;
416
+ }
417
+ staleRotationError = error;
418
+ mode = await assertSessionRealtimeOwnerInTransaction(db, {
419
+ ...input,
420
+ expectedVersion: input.expectedVersion + 1,
421
+ });
422
+ }
423
+ const [existing] = await db
424
+ .select()
425
+ .from(schema.sessionRealtimeConnections)
426
+ .where(
427
+ and(
428
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
429
+ eq(schema.sessionRealtimeConnections.operationId, input.operationId),
430
+ ),
431
+ )
432
+ .limit(1);
433
+ if (existing) {
434
+ if (
435
+ existing.promotionMode !== promotionMode ||
436
+ (input.rotate && existing.connectionEpoch <= input.expectedConnectionEpoch) ||
437
+ (!input.rotate && existing.connectionEpoch !== input.expectedConnectionEpoch)
438
+ ) {
439
+ throw new SessionRealtimeConflictError(
440
+ "REALTIME_CONNECTION_CHANGED",
441
+ "Realtime connection operation was already used with different input",
442
+ );
443
+ }
444
+ return {
445
+ connection: mapConnection(existing),
446
+ startupEntries: await startupEntriesForConnection(db, existing),
447
+ mode,
448
+ modeVersion: mode.version,
449
+ replay: true,
450
+ };
451
+ }
452
+ if (staleRotationError) throw staleRotationError;
453
+ if (mode.connectionEpoch !== input.expectedConnectionEpoch) {
454
+ throw new SessionRealtimeConflictError(
455
+ "REALTIME_CONNECTION_CHANGED",
456
+ "Realtime connection epoch changed",
457
+ );
458
+ }
459
+
460
+ const connections = await db
461
+ .select()
462
+ .from(schema.sessionRealtimeConnections)
463
+ .where(eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId))
464
+ .for("update");
465
+ const now = input.now ?? new Date();
466
+ const active = connections.find((connection) => connection.state === "active");
467
+ const preparing = connections.find(
468
+ (connection) => connection.state === "negotiating" || connection.state === "ready",
469
+ );
470
+ if (!input.rotate && (active || preparing)) {
471
+ throw new SessionRealtimeConflictError(
472
+ "REALTIME_CONNECTION_ACTIVE",
473
+ "Realtime mode already has an open provider connection",
474
+ );
475
+ }
476
+ if (input.rotate) {
477
+ if (promotionMode === "staged") {
478
+ if (active?.promotionMode === "legacy" || preparing?.promotionMode === "legacy") {
479
+ throw new SessionRealtimeConflictError(
480
+ "REALTIME_CONNECTION_ACTIVE",
481
+ "Legacy realtime connection must finish rotating before staged promotion",
482
+ );
483
+ }
484
+ if (preparing) {
485
+ await db
486
+ .update(schema.sessionRealtimeConnections)
487
+ .set({ state: "closed", closedAt: now, updatedAt: now })
488
+ .where(eq(schema.sessionRealtimeConnections.id, preparing.id));
489
+ }
490
+ } else {
491
+ await db
492
+ .update(schema.sessionRealtimeConnections)
493
+ .set({ state: "closed", closedAt: now, updatedAt: now })
494
+ .where(
495
+ and(
496
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
497
+ inArray(schema.sessionRealtimeConnections.state, ["negotiating", "ready", "active"]),
498
+ ),
499
+ );
500
+ }
501
+ }
502
+
503
+ const highestEpoch = connections.reduce(
504
+ (maximum, connection) => Math.max(maximum, connection.connectionEpoch),
505
+ input.expectedConnectionEpoch,
506
+ );
507
+ const targetEpoch = input.rotate ? highestEpoch + 1 : input.expectedConnectionEpoch;
508
+
509
+ let modeVersion = mode.version;
510
+ if (input.rotate && promotionMode === "legacy") {
511
+ const [rotated] = await db
512
+ .update(schema.sessionRealtimeModes)
513
+ .set({
514
+ connectionEpoch: targetEpoch,
515
+ version: mode.version + 1,
516
+ updatedAt: now,
517
+ })
518
+ .where(
519
+ and(
520
+ eq(schema.sessionRealtimeModes.id, input.realtimeId),
521
+ eq(schema.sessionRealtimeModes.state, "active"),
522
+ eq(schema.sessionRealtimeModes.version, mode.version),
523
+ eq(schema.sessionRealtimeModes.connectionEpoch, input.expectedConnectionEpoch),
524
+ ),
525
+ )
526
+ .returning({ version: schema.sessionRealtimeModes.version });
527
+ if (!rotated) {
528
+ throw new SessionRealtimeConflictError(
529
+ "REALTIME_CONNECTION_CHANGED",
530
+ "Realtime connection changed while rotating",
531
+ );
532
+ }
533
+ modeVersion = rotated.version;
534
+ }
535
+
536
+ const [modeRow] = await db
537
+ .select({ accountId: schema.sessionRealtimeModes.accountId })
538
+ .from(schema.sessionRealtimeModes)
539
+ .where(eq(schema.sessionRealtimeModes.id, input.realtimeId))
540
+ .limit(1);
541
+ if (!modeRow) throw new Error("Realtime mode disappeared while claiming connection");
542
+
543
+ const startupEntries = await db
544
+ .select()
545
+ .from(schema.sessionRealtimeEntries)
546
+ .where(
547
+ and(
548
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
549
+ eq(schema.sessionRealtimeEntries.direction, "provider_out"),
550
+ isNull(schema.sessionRealtimeEntries.providerAckedAt),
551
+ ),
552
+ )
553
+ .orderBy(asc(schema.sessionRealtimeEntries.sequence))
554
+ .limit(SESSION_REALTIME_STARTUP_MAX_ENTRIES);
555
+ const startupFenceSequence = startupEntries.at(-1)?.sequence ?? 0;
556
+
557
+ const [connection] = await db
558
+ .insert(schema.sessionRealtimeConnections)
559
+ .values({
560
+ accountId: modeRow.accountId,
561
+ workspaceId: input.workspaceId,
562
+ sessionId: input.sessionId,
563
+ realtimeId: input.realtimeId,
564
+ operationId: input.operationId,
565
+ connectionEpoch: targetEpoch,
566
+ startupFenceSequence,
567
+ promotionMode,
568
+ state: "negotiating",
569
+ createdAt: now,
570
+ updatedAt: now,
571
+ })
572
+ .returning();
573
+ if (!connection) throw new Error("Failed to claim realtime connection");
574
+ return {
575
+ connection: mapConnection(connection),
576
+ startupEntries: startupEntries.map(mapEntry),
577
+ mode,
578
+ modeVersion,
579
+ replay: false,
580
+ };
581
+ }
582
+
583
+ async function startupEntriesForConnection(
584
+ db: Database,
585
+ connection: ConnectionRow,
586
+ ): Promise<SessionRealtimeLedgerEntry[]> {
587
+ if (connection.startupFenceSequence === 0) return [];
588
+ const rows = await db
589
+ .select()
590
+ .from(schema.sessionRealtimeEntries)
591
+ .where(
592
+ and(
593
+ eq(schema.sessionRealtimeEntries.realtimeId, connection.realtimeId),
594
+ eq(schema.sessionRealtimeEntries.direction, "provider_out"),
595
+ isNull(schema.sessionRealtimeEntries.providerAckedAt),
596
+ sql`${schema.sessionRealtimeEntries.sequence} <= ${connection.startupFenceSequence}`,
597
+ ),
598
+ )
599
+ .orderBy(asc(schema.sessionRealtimeEntries.sequence))
600
+ .limit(SESSION_REALTIME_STARTUP_MAX_ENTRIES);
601
+ return rows.map(mapEntry);
602
+ }
603
+
604
+ async function lockConnectionFinalizationMode(
605
+ db: Database,
606
+ input: Pick<
607
+ CompleteSessionRealtimeConnectionInput,
608
+ "workspaceId" | "sessionId" | "realtimeId" | "connectionEpoch" | "now"
609
+ >,
610
+ ) {
611
+ await lockSessionEventWriteRows(db, {
612
+ workspaceId: input.workspaceId,
613
+ controlLock: "share",
614
+ sessionIds: [input.sessionId],
615
+ });
616
+ const [mode] = await db
617
+ .select()
618
+ .from(schema.sessionRealtimeModes)
619
+ .where(
620
+ and(
621
+ eq(schema.sessionRealtimeModes.workspaceId, input.workspaceId),
622
+ eq(schema.sessionRealtimeModes.sessionId, input.sessionId),
623
+ eq(schema.sessionRealtimeModes.id, input.realtimeId),
624
+ ),
625
+ )
626
+ .for("update")
627
+ .limit(1);
628
+ if (!mode || mode.state !== "active" || mode.leaseExpiresAt <= (input.now ?? new Date())) {
629
+ throw new SessionRealtimeConflictError(
630
+ "REALTIME_NOT_ACTIVE",
631
+ "Realtime mode is no longer active",
632
+ );
633
+ }
634
+ if (mode.connectionEpoch > input.connectionEpoch) {
635
+ throw new SessionRealtimeConflictError(
636
+ "REALTIME_CONNECTION_CHANGED",
637
+ "Realtime connection epoch changed before negotiation completed",
638
+ );
639
+ }
640
+ return mode;
641
+ }
642
+
643
+ export async function completeSessionRealtimeConnectionInTransaction(
644
+ db: Database,
645
+ input: CompleteSessionRealtimeConnectionInput,
646
+ ): Promise<CompleteSessionRealtimeConnectionResult> {
647
+ assertConnectionEpoch(input.connectionEpoch);
648
+ assertBoundedString(input.sdpAnswer, 1_048_576, "Realtime SDP answer");
649
+ if (input.sdpAnswer.length === 0) throw new Error("Realtime SDP answer is empty");
650
+ await lockConnectionFinalizationMode(db, input);
651
+ const [connection] = await db
652
+ .select()
653
+ .from(schema.sessionRealtimeConnections)
654
+ .where(
655
+ and(
656
+ eq(schema.sessionRealtimeConnections.workspaceId, input.workspaceId),
657
+ eq(schema.sessionRealtimeConnections.sessionId, input.sessionId),
658
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
659
+ eq(schema.sessionRealtimeConnections.id, input.connectionId),
660
+ ),
661
+ )
662
+ .for("update")
663
+ .limit(1);
664
+ if (!connection) {
665
+ throw new SessionRealtimeConflictError(
666
+ "REALTIME_CONNECTION_NOT_FOUND",
667
+ "Realtime connection claim not found",
668
+ );
669
+ }
670
+ if (
671
+ connection.operationId !== input.operationId ||
672
+ connection.connectionEpoch !== input.connectionEpoch
673
+ ) {
674
+ throw new SessionRealtimeConflictError(
675
+ "REALTIME_CONNECTION_CHANGED",
676
+ "Realtime connection claim changed",
677
+ );
678
+ }
679
+ if (connection.state === "ready" || connection.state === "active") {
680
+ if (connection.sdpAnswer !== input.sdpAnswer) {
681
+ throw new SessionRealtimeConflictError(
682
+ "REALTIME_CONNECTION_STATE_CHANGED",
683
+ "Realtime connection was already completed with another answer",
684
+ );
685
+ }
686
+ return { connection: mapConnection(connection), replay: true };
687
+ }
688
+ if (connection.state !== "negotiating") {
689
+ throw new SessionRealtimeConflictError(
690
+ "REALTIME_CONNECTION_CHANGED",
691
+ "Realtime connection is no longer current",
692
+ );
693
+ }
694
+ const now = input.now ?? new Date();
695
+ const completedState = connection.promotionMode === "legacy" ? "active" : "ready";
696
+ const [completed] = await db
697
+ .update(schema.sessionRealtimeConnections)
698
+ .set({
699
+ state: completedState,
700
+ sdpAnswer: input.sdpAnswer,
701
+ negotiatedAt: now,
702
+ updatedAt: now,
703
+ })
704
+ .where(
705
+ and(
706
+ eq(schema.sessionRealtimeConnections.id, input.connectionId),
707
+ eq(schema.sessionRealtimeConnections.state, "negotiating"),
708
+ ),
709
+ )
710
+ .returning();
711
+ if (!completed) {
712
+ throw new SessionRealtimeConflictError(
713
+ "REALTIME_CONNECTION_STATE_CHANGED",
714
+ "Realtime connection changed while negotiation completed",
715
+ );
716
+ }
717
+ return { connection: mapConnection(completed), replay: false };
718
+ }
719
+
720
+ export async function activateSessionRealtimeConnectionInTransaction(
721
+ db: Database,
722
+ input: ActivateSessionRealtimeConnectionInput,
723
+ ): Promise<ActivateSessionRealtimeConnectionResult> {
724
+ assertConnectionEpoch(input.connectionEpoch);
725
+ assertConnectionEpoch(input.expectedConnectionEpoch);
726
+ let mode;
727
+ try {
728
+ mode = await assertSessionRealtimeOwnerInTransaction(db, input);
729
+ } catch (error) {
730
+ if (
731
+ !(error instanceof SessionRealtimeConflictError) ||
732
+ error.code !== "REALTIME_VERSION_CHANGED"
733
+ ) {
734
+ throw error;
735
+ }
736
+ mode = await assertSessionRealtimeOwnerInTransaction(db, {
737
+ ...input,
738
+ expectedVersion: input.expectedVersion + 1,
739
+ });
740
+ }
741
+
742
+ const [connection] = await db
743
+ .select()
744
+ .from(schema.sessionRealtimeConnections)
745
+ .where(
746
+ and(
747
+ eq(schema.sessionRealtimeConnections.workspaceId, input.workspaceId),
748
+ eq(schema.sessionRealtimeConnections.sessionId, input.sessionId),
749
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
750
+ eq(schema.sessionRealtimeConnections.id, input.connectionId),
751
+ ),
752
+ )
753
+ .for("update")
754
+ .limit(1);
755
+ if (!connection) {
756
+ throw new SessionRealtimeConflictError(
757
+ "REALTIME_CONNECTION_NOT_FOUND",
758
+ "Realtime connection claim not found",
759
+ );
760
+ }
761
+ if (
762
+ connection.operationId !== input.operationId ||
763
+ connection.connectionEpoch !== input.connectionEpoch
764
+ ) {
765
+ throw new SessionRealtimeConflictError(
766
+ "REALTIME_CONNECTION_CHANGED",
767
+ "Realtime connection claim changed",
768
+ );
769
+ }
770
+ if (connection.state === "active") {
771
+ if (mode.connectionEpoch !== connection.connectionEpoch) {
772
+ throw new SessionRealtimeConflictError(
773
+ "REALTIME_CONNECTION_CHANGED",
774
+ "Realtime connection is no longer current",
775
+ );
776
+ }
777
+ return {
778
+ connection: mapConnection(connection),
779
+ mode,
780
+ replay: true,
781
+ };
782
+ }
783
+ if (
784
+ connection.promotionMode !== "staged" ||
785
+ connection.state !== "ready" ||
786
+ mode.connectionEpoch !== input.expectedConnectionEpoch ||
787
+ connection.connectionEpoch < mode.connectionEpoch
788
+ ) {
789
+ throw new SessionRealtimeConflictError(
790
+ "REALTIME_CONNECTION_CHANGED",
791
+ "Realtime replacement is no longer ready for activation",
792
+ );
793
+ }
794
+
795
+ const now = input.now ?? new Date();
796
+ await db
797
+ .update(schema.sessionRealtimeConnections)
798
+ .set({ state: "closed", closedAt: now, updatedAt: now })
799
+ .where(
800
+ and(
801
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
802
+ eq(schema.sessionRealtimeConnections.state, "active"),
803
+ ),
804
+ );
805
+ const [activated] = await db
806
+ .update(schema.sessionRealtimeConnections)
807
+ .set({ state: "active", updatedAt: now })
808
+ .where(
809
+ and(
810
+ eq(schema.sessionRealtimeConnections.id, connection.id),
811
+ eq(schema.sessionRealtimeConnections.state, "ready"),
812
+ ),
813
+ )
814
+ .returning();
815
+ if (!activated) {
816
+ throw new SessionRealtimeConflictError(
817
+ "REALTIME_CONNECTION_STATE_CHANGED",
818
+ "Realtime replacement changed while activating",
819
+ );
820
+ }
821
+
822
+ let modeVersion = mode.version;
823
+ if (mode.connectionEpoch !== connection.connectionEpoch) {
824
+ const [promoted] = await db
825
+ .update(schema.sessionRealtimeModes)
826
+ .set({
827
+ connectionEpoch: connection.connectionEpoch,
828
+ version: mode.version + 1,
829
+ updatedAt: now,
830
+ })
831
+ .where(
832
+ and(
833
+ eq(schema.sessionRealtimeModes.id, input.realtimeId),
834
+ eq(schema.sessionRealtimeModes.state, "active"),
835
+ eq(schema.sessionRealtimeModes.version, mode.version),
836
+ eq(schema.sessionRealtimeModes.connectionEpoch, input.expectedConnectionEpoch),
837
+ ),
838
+ )
839
+ .returning({ version: schema.sessionRealtimeModes.version });
840
+ if (!promoted) {
841
+ throw new SessionRealtimeConflictError(
842
+ "REALTIME_CONNECTION_CHANGED",
843
+ "Realtime connection changed while activating replacement",
844
+ );
845
+ }
846
+ modeVersion = promoted.version;
847
+ }
848
+ return {
849
+ connection: mapConnection(activated),
850
+ mode: {
851
+ ...mode,
852
+ version: modeVersion,
853
+ connectionEpoch: connection.connectionEpoch,
854
+ },
855
+ replay: false,
856
+ };
857
+ }
858
+
859
+ export async function failSessionRealtimeConnectionInTransaction(
860
+ db: Database,
861
+ input: FailSessionRealtimeConnectionInput,
862
+ ): Promise<FailSessionRealtimeConnectionResult> {
863
+ assertConnectionEpoch(input.connectionEpoch);
864
+ assertBoundedString(input.failureCode, 128, "Realtime connection failure code");
865
+ if (!/^[a-z0-9_.:-]+$/i.test(input.failureCode)) {
866
+ throw new Error("Realtime connection failure code is invalid");
867
+ }
868
+ await lockConnectionFinalizationMode(db, input);
869
+ const [connection] = await db
870
+ .select()
871
+ .from(schema.sessionRealtimeConnections)
872
+ .where(
873
+ and(
874
+ eq(schema.sessionRealtimeConnections.workspaceId, input.workspaceId),
875
+ eq(schema.sessionRealtimeConnections.sessionId, input.sessionId),
876
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
877
+ eq(schema.sessionRealtimeConnections.id, input.connectionId),
878
+ ),
879
+ )
880
+ .for("update")
881
+ .limit(1);
882
+ if (!connection) {
883
+ throw new SessionRealtimeConflictError(
884
+ "REALTIME_CONNECTION_NOT_FOUND",
885
+ "Realtime connection claim not found",
886
+ );
887
+ }
888
+ if (
889
+ connection.operationId !== input.operationId ||
890
+ connection.connectionEpoch !== input.connectionEpoch
891
+ ) {
892
+ throw new SessionRealtimeConflictError(
893
+ "REALTIME_CONNECTION_CHANGED",
894
+ "Realtime connection claim changed",
895
+ );
896
+ }
897
+ if (connection.state === "failed") {
898
+ if (connection.failureCode !== input.failureCode) {
899
+ throw new SessionRealtimeConflictError(
900
+ "REALTIME_CONNECTION_STATE_CHANGED",
901
+ "Realtime connection was already failed with another code",
902
+ );
903
+ }
904
+ return { connection: mapConnection(connection), replay: true };
905
+ }
906
+ if (connection.state !== "negotiating") {
907
+ throw new SessionRealtimeConflictError(
908
+ "REALTIME_CONNECTION_CHANGED",
909
+ "Realtime connection is no longer negotiating",
910
+ );
911
+ }
912
+ const now = input.now ?? new Date();
913
+ const [failed] = await db
914
+ .update(schema.sessionRealtimeConnections)
915
+ .set({
916
+ state: "failed",
917
+ failureCode: input.failureCode,
918
+ closedAt: now,
919
+ updatedAt: now,
920
+ })
921
+ .where(
922
+ and(
923
+ eq(schema.sessionRealtimeConnections.id, input.connectionId),
924
+ eq(schema.sessionRealtimeConnections.state, "negotiating"),
925
+ ),
926
+ )
927
+ .returning();
928
+ if (!failed) {
929
+ throw new SessionRealtimeConflictError(
930
+ "REALTIME_CONNECTION_STATE_CHANGED",
931
+ "Realtime connection changed while negotiation failed",
932
+ );
933
+ }
934
+ return { connection: mapConnection(failed), replay: false };
935
+ }
936
+
937
+ async function nextLedgerSequence(db: Database, realtimeId: string): Promise<number> {
938
+ const [row] = await db
939
+ .select({
940
+ next: sql<number>`coalesce(max(${schema.sessionRealtimeEntries.sequence}), 0) + 1`,
941
+ })
942
+ .from(schema.sessionRealtimeEntries)
943
+ .where(eq(schema.sessionRealtimeEntries.realtimeId, realtimeId));
944
+ return Number(row?.next ?? 1);
945
+ }
946
+
947
+ async function validateActiveConnection(
948
+ db: Database,
949
+ input: {
950
+ workspaceId: string;
951
+ sessionId: string;
952
+ realtimeId: string;
953
+ connectionId: string;
954
+ connectionEpoch: number;
955
+ },
956
+ ) {
957
+ const [connection] = await db
958
+ .select()
959
+ .from(schema.sessionRealtimeConnections)
960
+ .where(
961
+ and(
962
+ eq(schema.sessionRealtimeConnections.workspaceId, input.workspaceId),
963
+ eq(schema.sessionRealtimeConnections.sessionId, input.sessionId),
964
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
965
+ eq(schema.sessionRealtimeConnections.id, input.connectionId),
966
+ ),
967
+ )
968
+ .for("update")
969
+ .limit(1);
970
+ if (!connection) {
971
+ throw new SessionRealtimeConflictError(
972
+ "REALTIME_CONNECTION_NOT_FOUND",
973
+ "Realtime connection not found",
974
+ );
975
+ }
976
+ if (connection.connectionEpoch !== input.connectionEpoch || connection.state !== "active") {
977
+ throw new SessionRealtimeConflictError(
978
+ "REALTIME_CONNECTION_CHANGED",
979
+ "Realtime connection is no longer active",
980
+ );
981
+ }
982
+ return connection;
983
+ }
984
+
985
+ function delegationCallFailure(
986
+ input: SessionRealtimeInboundEntryInput,
987
+ ): { code: "invalid_delegation_call"; message: string } | null {
988
+ if (!input.delegationItemId?.trim()) {
989
+ return {
990
+ code: "invalid_delegation_call",
991
+ message: "Realtime delegation call is missing its provider item identity",
992
+ };
993
+ }
994
+ if (!input.text?.trim()) {
995
+ return {
996
+ code: "invalid_delegation_call",
997
+ message: "Realtime delegation call is missing ordinary work instructions",
998
+ };
999
+ }
1000
+ return null;
1001
+ }
1002
+
1003
+ function canonicalJsonValue(value: unknown): unknown {
1004
+ if (Array.isArray(value)) return value.map(canonicalJsonValue);
1005
+ if (value && typeof value === "object") {
1006
+ return Object.fromEntries(
1007
+ Object.entries(value as Record<string, unknown>)
1008
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
1009
+ .map(([key, entry]) => [key, canonicalJsonValue(entry)]),
1010
+ );
1011
+ }
1012
+ return value;
1013
+ }
1014
+
1015
+ function inboundReplayMatches(
1016
+ row: EntryRow,
1017
+ input: SessionRealtimeInboundEntryInput,
1018
+ role: "user" | "assistant" | null,
1019
+ text: string | null,
1020
+ payload: Record<string, unknown>,
1021
+ ): boolean {
1022
+ return (
1023
+ row.direction === "provider_in" &&
1024
+ row.kind === input.kind &&
1025
+ row.role === role &&
1026
+ row.providerEventId === (input.providerEventId ?? null) &&
1027
+ row.delegationItemId === (input.delegationItemId ?? null) &&
1028
+ row.sourceUpdateId === null &&
1029
+ row.text === text &&
1030
+ JSON.stringify(canonicalJsonValue(row.payload)) === JSON.stringify(canonicalJsonValue(payload))
1031
+ );
1032
+ }
1033
+
1034
+ async function appendInvalidDelegationFailure(
1035
+ db: Database,
1036
+ input: Pick<
1037
+ SyncSessionRealtimeLedgerInput,
1038
+ "workspaceId" | "sessionId" | "realtimeId" | "connectionEpoch"
1039
+ >,
1040
+ accountId: string,
1041
+ incoming: SessionRealtimeInboundEntryInput,
1042
+ failure: NonNullable<ReturnType<typeof delegationCallFailure>>,
1043
+ sequence: number,
1044
+ now: Date,
1045
+ ): Promise<void> {
1046
+ await db.insert(schema.sessionRealtimeEntries).values({
1047
+ accountId,
1048
+ workspaceId: input.workspaceId,
1049
+ sessionId: input.sessionId,
1050
+ realtimeId: input.realtimeId,
1051
+ operationId: crypto.randomUUID(),
1052
+ connectionEpoch: input.connectionEpoch,
1053
+ sequence,
1054
+ direction: "provider_out",
1055
+ kind: "error",
1056
+ delegationItemId: incoming.delegationItemId ?? null,
1057
+ text: failure.message,
1058
+ payload: {
1059
+ code: failure.code,
1060
+ callOperationId: incoming.operationId,
1061
+ },
1062
+ createdAt: now,
1063
+ updatedAt: now,
1064
+ });
1065
+ }
1066
+
1067
+ async function admitRealtimeDelegationInTransaction(
1068
+ db: Database,
1069
+ input: Pick<
1070
+ SyncSessionRealtimeLedgerInput,
1071
+ "workspaceId" | "sessionId" | "realtimeId" | "connectionEpoch" | "ownerSubjectId"
1072
+ >,
1073
+ accountId: string,
1074
+ incoming: SessionRealtimeInboundEntryInput,
1075
+ entryId: string,
1076
+ ): Promise<{ turnId: string; eventIds: string[]; wakeRevision: number }> {
1077
+ const [session] = await db
1078
+ .select()
1079
+ .from(schema.sessions)
1080
+ .where(
1081
+ and(
1082
+ eq(schema.sessions.workspaceId, input.workspaceId),
1083
+ eq(schema.sessions.id, input.sessionId),
1084
+ ),
1085
+ )
1086
+ .limit(1);
1087
+ if (!session || session.accountId !== accountId || session.status === "cancelled") {
1088
+ throw new SessionRealtimeConflictError("REALTIME_NOT_FOUND", "Session not found");
1089
+ }
1090
+ const reasoning = ReasoningEffort.safeParse(session.metadata.reasoningEffort);
1091
+ const [latestStarted] = await db
1092
+ .select({
1093
+ model: schema.sessionTurns.model,
1094
+ reasoningEffort: schema.sessionTurns.reasoningEffort,
1095
+ latencyMode: schema.sessionTurns.latencyMode,
1096
+ })
1097
+ .from(schema.sessionTurns)
1098
+ .where(
1099
+ and(
1100
+ eq(schema.sessionTurns.workspaceId, input.workspaceId),
1101
+ eq(schema.sessionTurns.sessionId, input.sessionId),
1102
+ sql`${schema.sessionTurns.startedAt} is not null`,
1103
+ ),
1104
+ )
1105
+ .orderBy(desc(schema.sessionTurns.startedAt), desc(schema.sessionTurns.createdAt))
1106
+ .limit(1);
1107
+ const latestReasoning = ReasoningEffort.safeParse(latestStarted?.reasoningEffort);
1108
+ const latestLatency = LatencyMode.safeParse(latestStarted?.latencyMode);
1109
+ const provenance = {
1110
+ source: "realtime_provider_delegation",
1111
+ realtimeId: input.realtimeId,
1112
+ connectionEpoch: input.connectionEpoch,
1113
+ delegationItemId: incoming.delegationItemId!,
1114
+ ledgerEntryId: entryId,
1115
+ };
1116
+ const inputTranscript = incoming.payload?.inputTranscript;
1117
+ if (typeof inputTranscript !== "string" || inputTranscript.trim().length === 0) {
1118
+ throw new Error("Realtime delegation input transcript is required");
1119
+ }
1120
+ const admitted = await submitHumanPromptInTransaction(db, {
1121
+ accountId,
1122
+ workspaceId: input.workspaceId,
1123
+ sessionId: input.sessionId,
1124
+ subjectId: input.ownerSubjectId,
1125
+ subjectLabel: "Realtime",
1126
+ actor: {
1127
+ type: "service",
1128
+ subjectId: input.ownerSubjectId,
1129
+ subjectLabel: "Realtime",
1130
+ context: provenance,
1131
+ },
1132
+ operationKey: incoming.operationId,
1133
+ delivery: "steer",
1134
+ text: incoming.text!,
1135
+ messagePresentation: {
1136
+ kind: "realtime_voice",
1137
+ text: inputTranscript,
1138
+ context: incoming.text!,
1139
+ },
1140
+ resources: [],
1141
+ model: latestStarted?.model ?? session.model,
1142
+ reasoningEffort: latestReasoning.success
1143
+ ? latestReasoning.data
1144
+ : reasoning.success
1145
+ ? reasoning.data
1146
+ : "medium",
1147
+ latencyMode: latestLatency.success ? latestLatency.data : "standard",
1148
+ reasoningEffortFallback: reasoning.success ? reasoning.data : "medium",
1149
+ turnMetadata: {
1150
+ realtimeDelegation: { ...provenance, inputTranscript },
1151
+ },
1152
+ source: "api",
1153
+ });
1154
+ return {
1155
+ turnId: admitted.turnId,
1156
+ eventIds: admitted.eventIds,
1157
+ wakeRevision: admitted.wakeRevision,
1158
+ };
1159
+ }
1160
+
1161
+ async function materializeRealtimeUpdates(
1162
+ db: Database,
1163
+ input: Pick<
1164
+ SyncSessionRealtimeLedgerInput,
1165
+ "workspaceId" | "sessionId" | "realtimeId" | "connectionEpoch"
1166
+ >,
1167
+ accountId: string,
1168
+ allocateSequence: () => number,
1169
+ now: Date,
1170
+ ): Promise<void> {
1171
+ const rows = await db
1172
+ .select({ update: schema.sessionSystemUpdates })
1173
+ .from(schema.sessionSystemUpdates)
1174
+ .leftJoin(
1175
+ schema.sessionRealtimeEntries,
1176
+ and(
1177
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1178
+ eq(schema.sessionRealtimeEntries.sourceUpdateId, schema.sessionSystemUpdates.id),
1179
+ ),
1180
+ )
1181
+ .where(
1182
+ and(
1183
+ eq(schema.sessionSystemUpdates.workspaceId, input.workspaceId),
1184
+ eq(schema.sessionSystemUpdates.sessionId, input.sessionId),
1185
+ inArray(schema.sessionSystemUpdates.state, ["pending", "deferred"]),
1186
+ inArray(schema.sessionSystemUpdates.kind, ["agent_message", "child_terminal_result"]),
1187
+ isNull(schema.sessionRealtimeEntries.id),
1188
+ ),
1189
+ )
1190
+ .orderBy(asc(schema.sessionSystemUpdates.createdAt), asc(schema.sessionSystemUpdates.id))
1191
+ .limit(SESSION_REALTIME_LEDGER_MAX_OUTBOUND);
1192
+ const updates = rows.map(({ update }) => update);
1193
+ if (updates.length === 0) return;
1194
+ await db.insert(schema.sessionRealtimeEntries).values(
1195
+ updates.map((update) => ({
1196
+ accountId,
1197
+ workspaceId: input.workspaceId,
1198
+ sessionId: input.sessionId,
1199
+ realtimeId: input.realtimeId,
1200
+ operationId: update.id,
1201
+ connectionEpoch: input.connectionEpoch,
1202
+ sequence: allocateSequence(),
1203
+ direction: "provider_out",
1204
+ kind: "session_update",
1205
+ sourceUpdateId: update.id,
1206
+ text: update.summary,
1207
+ payload: boundedPayload({
1208
+ updateId: update.id,
1209
+ kind: update.kind,
1210
+ classification: update.classification,
1211
+ sourceId: update.sourceId,
1212
+ summary: update.summary,
1213
+ payload: update.payload,
1214
+ lineage: update.lineage,
1215
+ }),
1216
+ createdAt: now,
1217
+ updatedAt: now,
1218
+ })),
1219
+ );
1220
+ }
1221
+
1222
+ function assertAck(value: number | null | undefined, maximum: number): number | null {
1223
+ if (value === null || value === undefined) return null;
1224
+ if (!Number.isSafeInteger(value) || value < 0 || value > maximum) {
1225
+ throw new SessionRealtimeConflictError(
1226
+ "REALTIME_ACK_INVALID",
1227
+ "Realtime acknowledgment is outside the durable ledger",
1228
+ );
1229
+ }
1230
+ return value;
1231
+ }
1232
+
1233
+ function assertProviderAckSequences(values: number[] | undefined, maximum: number): number[] {
1234
+ if (!values || values.length === 0) return [];
1235
+ if (values.length > SESSION_REALTIME_LEDGER_MAX_OUTBOUND) {
1236
+ throw new SessionRealtimeConflictError(
1237
+ "REALTIME_ACK_INVALID",
1238
+ "Realtime provider acknowledgment batch exceeds the server limit",
1239
+ );
1240
+ }
1241
+ const unique = [...new Set(values)].sort((left, right) => left - right);
1242
+ if (
1243
+ unique.length !== values.length ||
1244
+ unique.some((value) => !Number.isSafeInteger(value) || value < 1 || value > maximum)
1245
+ ) {
1246
+ throw new SessionRealtimeConflictError(
1247
+ "REALTIME_ACK_INVALID",
1248
+ "Realtime provider acknowledgment is outside the durable ledger",
1249
+ );
1250
+ }
1251
+ return unique;
1252
+ }
1253
+
1254
+ export async function syncSessionRealtimeLedgerInTransaction(
1255
+ db: Database,
1256
+ input: SyncSessionRealtimeLedgerInput,
1257
+ hooks: SyncSessionRealtimeLedgerHooks = {},
1258
+ ): Promise<SyncSessionRealtimeLedgerResult> {
1259
+ assertConnectionEpoch(input.connectionEpoch);
1260
+ if ((input.entries?.length ?? 0) > SESSION_REALTIME_LEDGER_MAX_BATCH) {
1261
+ throw new Error("Realtime ledger batch exceeds the server limit");
1262
+ }
1263
+ // Realtime sync may admit canonical Steer work. Preserve the global lock
1264
+ // order before owner proof locks the session row.
1265
+ await lockWorkspaceInferenceControl(db, input.workspaceId, "update");
1266
+ const mode = await assertSessionRealtimeOwnerInTransaction(db, input);
1267
+ if (mode.connectionEpoch !== input.connectionEpoch) {
1268
+ throw new SessionRealtimeConflictError(
1269
+ "REALTIME_CONNECTION_CHANGED",
1270
+ "Realtime connection epoch changed",
1271
+ );
1272
+ }
1273
+ const connection = await validateActiveConnection(db, input);
1274
+ if (connection.id !== input.connectionId) {
1275
+ throw new SessionRealtimeConflictError(
1276
+ "REALTIME_CONNECTION_CHANGED",
1277
+ "Realtime connection changed",
1278
+ );
1279
+ }
1280
+
1281
+ const [modeRow] = await db
1282
+ .select({ accountId: schema.sessionRealtimeModes.accountId })
1283
+ .from(schema.sessionRealtimeModes)
1284
+ .where(eq(schema.sessionRealtimeModes.id, input.realtimeId))
1285
+ .limit(1);
1286
+ if (!modeRow) throw new Error("Realtime mode disappeared while syncing ledger");
1287
+
1288
+ let nextSequence = await nextLedgerSequence(db, input.realtimeId);
1289
+ let providerStartupAcknowledged = connection.startupAcknowledgedAt !== null;
1290
+ const eventIds: string[] = [];
1291
+ let workflowWakeRevision: number | null = null;
1292
+ const now = input.now ?? new Date();
1293
+ if (input.providerStarted) {
1294
+ assertBoundedString(
1295
+ input.providerStarted.providerSessionId,
1296
+ 1024,
1297
+ "Realtime provider session id",
1298
+ );
1299
+ assertBoundedString(input.providerStarted.providerEventId, 1024, "Realtime startup event id");
1300
+ if (connection.startupAcknowledgedAt) {
1301
+ if (
1302
+ connection.providerSessionId !== input.providerStarted.providerSessionId ||
1303
+ connection.startupEventId !== (input.providerStarted.providerEventId ?? null)
1304
+ ) {
1305
+ throw new SessionRealtimeConflictError(
1306
+ "REALTIME_CONNECTION_STATE_CHANGED",
1307
+ "Realtime provider startup was already acknowledged with different proof",
1308
+ );
1309
+ }
1310
+ } else {
1311
+ const [acknowledged] = await db
1312
+ .update(schema.sessionRealtimeConnections)
1313
+ .set({
1314
+ providerSessionId: input.providerStarted.providerSessionId,
1315
+ startupEventId: input.providerStarted.providerEventId ?? null,
1316
+ startupAcknowledgedAt: now,
1317
+ updatedAt: now,
1318
+ })
1319
+ .where(
1320
+ and(
1321
+ eq(schema.sessionRealtimeConnections.id, connection.id),
1322
+ isNull(schema.sessionRealtimeConnections.startupAcknowledgedAt),
1323
+ ),
1324
+ )
1325
+ .returning({ id: schema.sessionRealtimeConnections.id });
1326
+ if (!acknowledged) {
1327
+ throw new SessionRealtimeConflictError(
1328
+ "REALTIME_CONNECTION_STATE_CHANGED",
1329
+ "Realtime provider startup changed while acknowledging it",
1330
+ );
1331
+ }
1332
+ }
1333
+ providerStartupAcknowledged = true;
1334
+ }
1335
+ const accepted: SyncSessionRealtimeLedgerResult["accepted"] = [];
1336
+ for (const incoming of input.entries ?? []) {
1337
+ if (incoming.kind === "delegation_call" && !providerStartupAcknowledged) {
1338
+ throw new SessionRealtimeConflictError(
1339
+ "REALTIME_PROVIDER_NOT_STARTED",
1340
+ "Realtime provider startup proof is required before delegation",
1341
+ );
1342
+ }
1343
+ const payload = boundedPayload(incoming.payload);
1344
+ const role = expectedRole(incoming);
1345
+ const text = incoming.text ?? null;
1346
+ assertBoundedString(text, SESSION_REALTIME_LEDGER_MAX_TEXT_BYTES, "Realtime text");
1347
+ if (
1348
+ (incoming.kind === "user_transcript" || incoming.kind === "assistant_transcript") &&
1349
+ !text
1350
+ ) {
1351
+ throw new Error("Finalized realtime transcript text is required");
1352
+ }
1353
+ if (incoming.kind === "user_transcript" || incoming.kind === "assistant_transcript") {
1354
+ const turnId = payload.turnId;
1355
+ if (typeof turnId !== "string" || turnId.length === 0) {
1356
+ throw new Error("Finalized realtime transcript turn id is required");
1357
+ }
1358
+ assertBoundedString(turnId, 1_024, "Realtime transcript turn id");
1359
+ }
1360
+ const [existing] = await db
1361
+ .select()
1362
+ .from(schema.sessionRealtimeEntries)
1363
+ .where(
1364
+ and(
1365
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1366
+ incoming.kind === "delegation_call" && incoming.delegationItemId
1367
+ ? or(
1368
+ eq(schema.sessionRealtimeEntries.operationId, incoming.operationId),
1369
+ and(
1370
+ eq(schema.sessionRealtimeEntries.kind, "delegation_call"),
1371
+ eq(schema.sessionRealtimeEntries.delegationItemId, incoming.delegationItemId),
1372
+ ),
1373
+ )
1374
+ : eq(schema.sessionRealtimeEntries.operationId, incoming.operationId),
1375
+ ),
1376
+ )
1377
+ .limit(1);
1378
+ if (existing) {
1379
+ if (!inboundReplayMatches(existing, incoming, role, text, payload)) {
1380
+ throw new SessionRealtimeConflictError(
1381
+ incoming.kind === "delegation_call"
1382
+ ? "REALTIME_DELEGATION_CHANGED"
1383
+ : "REALTIME_ENTRY_CHANGED",
1384
+ "Realtime ledger operation was already used with different input",
1385
+ );
1386
+ }
1387
+ accepted.push({ entry: mapEntry(existing), replay: true });
1388
+ continue;
1389
+ }
1390
+ let [entry] = await db
1391
+ .insert(schema.sessionRealtimeEntries)
1392
+ .values({
1393
+ accountId: modeRow.accountId,
1394
+ workspaceId: input.workspaceId,
1395
+ sessionId: input.sessionId,
1396
+ realtimeId: input.realtimeId,
1397
+ operationId: incoming.operationId,
1398
+ connectionEpoch: input.connectionEpoch,
1399
+ sequence: nextSequence++,
1400
+ direction: "provider_in",
1401
+ kind: incoming.kind,
1402
+ role,
1403
+ providerEventId: incoming.providerEventId ?? null,
1404
+ delegationItemId: incoming.delegationItemId ?? null,
1405
+ historyItemId: null,
1406
+ text,
1407
+ payload,
1408
+ createdAt: now,
1409
+ updatedAt: now,
1410
+ })
1411
+ .returning();
1412
+ if (!entry) throw new Error("Failed to append realtime ledger entry");
1413
+ if (incoming.kind === "delegation_call") {
1414
+ const failure = delegationCallFailure(incoming);
1415
+ if (failure) {
1416
+ await appendInvalidDelegationFailure(
1417
+ db,
1418
+ input,
1419
+ modeRow.accountId,
1420
+ incoming,
1421
+ failure,
1422
+ nextSequence++,
1423
+ now,
1424
+ );
1425
+ } else {
1426
+ const admission = await admitRealtimeDelegationInTransaction(
1427
+ db,
1428
+ input,
1429
+ modeRow.accountId,
1430
+ incoming,
1431
+ entry.id,
1432
+ );
1433
+ const [linked] = await db
1434
+ .update(schema.sessionRealtimeEntries)
1435
+ .set({ turnId: admission.turnId, updatedAt: now })
1436
+ .where(eq(schema.sessionRealtimeEntries.id, entry.id))
1437
+ .returning();
1438
+ if (!linked) throw new Error("Failed to link realtime delegation turn");
1439
+ entry = linked;
1440
+ eventIds.push(...admission.eventIds);
1441
+ workflowWakeRevision = Math.max(workflowWakeRevision ?? 0, admission.wakeRevision);
1442
+ await hooks.afterDelegationAdmission?.({ entryId: entry.id, turnId: admission.turnId });
1443
+ }
1444
+ }
1445
+ accepted.push({ entry: mapEntry(entry), replay: false });
1446
+ }
1447
+
1448
+ await materializeRealtimeUpdates(db, input, modeRow.accountId, () => nextSequence++, now);
1449
+ const maximum = nextSequence - 1;
1450
+ const clientAck = assertAck(input.clientAckThroughSequence, maximum);
1451
+ if (clientAck !== null && clientAck > 0) {
1452
+ await db
1453
+ .update(schema.sessionRealtimeEntries)
1454
+ .set({ clientAckedAt: now, updatedAt: now })
1455
+ .where(
1456
+ and(
1457
+ eq(schema.sessionRealtimeEntries.workspaceId, input.workspaceId),
1458
+ eq(schema.sessionRealtimeEntries.sessionId, input.sessionId),
1459
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1460
+ eq(schema.sessionRealtimeEntries.direction, "provider_out"),
1461
+ isNull(schema.sessionRealtimeEntries.clientAckedAt),
1462
+ sql`${schema.sessionRealtimeEntries.sequence} <= ${clientAck}`,
1463
+ ),
1464
+ );
1465
+ }
1466
+ const providerAckSequences = assertProviderAckSequences(input.providerAckSequences, maximum);
1467
+ if (providerAckSequences.length > 0) {
1468
+ const ackable = await db
1469
+ .select({
1470
+ id: schema.sessionRealtimeEntries.id,
1471
+ sequence: schema.sessionRealtimeEntries.sequence,
1472
+ })
1473
+ .from(schema.sessionRealtimeEntries)
1474
+ .where(
1475
+ and(
1476
+ eq(schema.sessionRealtimeEntries.workspaceId, input.workspaceId),
1477
+ eq(schema.sessionRealtimeEntries.sessionId, input.sessionId),
1478
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1479
+ eq(schema.sessionRealtimeEntries.direction, "provider_out"),
1480
+ inArray(schema.sessionRealtimeEntries.sequence, providerAckSequences),
1481
+ sql`${schema.sessionRealtimeEntries.clientAckedAt} is not null`,
1482
+ ),
1483
+ )
1484
+ .for("update");
1485
+ if (
1486
+ ackable.length !== providerAckSequences.length ||
1487
+ ackable.some((entry) => !providerAckSequences.includes(entry.sequence))
1488
+ ) {
1489
+ throw new SessionRealtimeConflictError(
1490
+ "REALTIME_ACK_INVALID",
1491
+ "Realtime provider acknowledgment does not match client-received outbound entries",
1492
+ );
1493
+ }
1494
+ await db
1495
+ .update(schema.sessionRealtimeEntries)
1496
+ .set({ providerAckedAt: now, updatedAt: now })
1497
+ .where(
1498
+ and(
1499
+ eq(schema.sessionRealtimeEntries.workspaceId, input.workspaceId),
1500
+ eq(schema.sessionRealtimeEntries.sessionId, input.sessionId),
1501
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1502
+ inArray(
1503
+ schema.sessionRealtimeEntries.id,
1504
+ ackable.map((entry) => entry.id),
1505
+ ),
1506
+ isNull(schema.sessionRealtimeEntries.providerAckedAt),
1507
+ ),
1508
+ );
1509
+ }
1510
+ const outbound = await db
1511
+ .select()
1512
+ .from(schema.sessionRealtimeEntries)
1513
+ .where(
1514
+ and(
1515
+ eq(schema.sessionRealtimeEntries.workspaceId, input.workspaceId),
1516
+ eq(schema.sessionRealtimeEntries.sessionId, input.sessionId),
1517
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1518
+ eq(schema.sessionRealtimeEntries.direction, "provider_out"),
1519
+ isNull(schema.sessionRealtimeEntries.providerAckedAt),
1520
+ ),
1521
+ )
1522
+ .orderBy(
1523
+ sql`case when ${schema.sessionRealtimeEntries.clientAckedAt} is null then 0 else 1 end`,
1524
+ asc(schema.sessionRealtimeEntries.sequence),
1525
+ )
1526
+ .limit(SESSION_REALTIME_LEDGER_MAX_OUTBOUND);
1527
+ return {
1528
+ accepted,
1529
+ outbound: outbound.map(mapEntry),
1530
+ eventIds,
1531
+ workflowWakeRevision,
1532
+ };
1533
+ }
1534
+
1535
+ export async function appendSessionRealtimeOutboundInTransaction(
1536
+ db: Database,
1537
+ input: AppendSessionRealtimeOutboundInput,
1538
+ ): Promise<AppendSessionRealtimeOutboundResult> {
1539
+ assertConnectionEpoch(input.connectionEpoch);
1540
+ await lockSessionEventWriteRows(db, {
1541
+ workspaceId: input.workspaceId,
1542
+ controlLock: "share",
1543
+ sessionIds: [input.sessionId],
1544
+ });
1545
+ const [mode] = await db
1546
+ .select()
1547
+ .from(schema.sessionRealtimeModes)
1548
+ .where(
1549
+ and(
1550
+ eq(schema.sessionRealtimeModes.workspaceId, input.workspaceId),
1551
+ eq(schema.sessionRealtimeModes.sessionId, input.sessionId),
1552
+ eq(schema.sessionRealtimeModes.id, input.realtimeId),
1553
+ ),
1554
+ )
1555
+ .for("update")
1556
+ .limit(1);
1557
+ if (
1558
+ !mode ||
1559
+ mode.state !== "active" ||
1560
+ mode.connectionEpoch !== input.connectionEpoch ||
1561
+ mode.leaseExpiresAt <= (input.now ?? new Date())
1562
+ ) {
1563
+ throw new SessionRealtimeConflictError(
1564
+ "REALTIME_CONNECTION_CHANGED",
1565
+ "Realtime connection changed before outbound delivery",
1566
+ );
1567
+ }
1568
+ const [activeConnection] = await db
1569
+ .select({ id: schema.sessionRealtimeConnections.id })
1570
+ .from(schema.sessionRealtimeConnections)
1571
+ .where(
1572
+ and(
1573
+ eq(schema.sessionRealtimeConnections.realtimeId, input.realtimeId),
1574
+ eq(schema.sessionRealtimeConnections.connectionEpoch, input.connectionEpoch),
1575
+ eq(schema.sessionRealtimeConnections.state, "active"),
1576
+ ),
1577
+ )
1578
+ .limit(1);
1579
+ if (!activeConnection) {
1580
+ throw new SessionRealtimeConflictError(
1581
+ "REALTIME_CONNECTION_NOT_FOUND",
1582
+ "Active realtime connection not found",
1583
+ );
1584
+ }
1585
+ const [existing] = await db
1586
+ .select()
1587
+ .from(schema.sessionRealtimeEntries)
1588
+ .where(
1589
+ and(
1590
+ eq(schema.sessionRealtimeEntries.realtimeId, input.realtimeId),
1591
+ eq(schema.sessionRealtimeEntries.operationId, input.operationId),
1592
+ ),
1593
+ )
1594
+ .limit(1);
1595
+ if (existing) return { entry: mapEntry(existing), replay: true };
1596
+ const text = input.text ?? null;
1597
+ assertBoundedString(text, SESSION_REALTIME_LEDGER_MAX_TEXT_BYTES, "Realtime text");
1598
+ const payload = boundedPayload(input.payload);
1599
+ const nextSequence = await nextLedgerSequence(db, input.realtimeId);
1600
+ const now = input.now ?? new Date();
1601
+ const [entry] = await db
1602
+ .insert(schema.sessionRealtimeEntries)
1603
+ .values({
1604
+ accountId: mode.accountId,
1605
+ workspaceId: input.workspaceId,
1606
+ sessionId: input.sessionId,
1607
+ realtimeId: input.realtimeId,
1608
+ operationId: input.operationId,
1609
+ connectionEpoch: input.connectionEpoch,
1610
+ sequence: Number(nextSequence),
1611
+ direction: "provider_out",
1612
+ kind: input.kind,
1613
+ delegationItemId: input.delegationItemId,
1614
+ text,
1615
+ payload,
1616
+ createdAt: now,
1617
+ updatedAt: now,
1618
+ })
1619
+ .returning();
1620
+ if (!entry) throw new Error("Failed to append realtime outbound entry");
1621
+ return { entry: mapEntry(entry), replay: false };
1622
+ }
1623
+
1624
+ /**
1625
+ * Under the caller's canonical session/turn fence, mirror accepted assistant
1626
+ * text deltas into the active delegation's durable provider-out stream. One
1627
+ * append batch becomes one or more bounded progress rows, preserving the
1628
+ * ordinary event order without making the browser's live SSE authoritative.
1629
+ */
1630
+ export async function projectSessionRealtimeDelegationProgressInTransaction(
1631
+ db: Database,
1632
+ input: ProjectSessionRealtimeDelegationProgressInput,
1633
+ ): Promise<ProjectSessionRealtimeDelegationProgressResult> {
1634
+ const progressEvents = input.events.flatMap((event) => {
1635
+ if (
1636
+ event.type !== "agent.message.delta" ||
1637
+ !event.payload ||
1638
+ typeof event.payload !== "object" ||
1639
+ Array.isArray(event.payload)
1640
+ ) {
1641
+ return [];
1642
+ }
1643
+ const text = (event.payload as Record<string, unknown>).text;
1644
+ return typeof text === "string" && text.length > 0 ? [{ ...event, text }] : [];
1645
+ });
1646
+ if (progressEvents.length === 0) return { entries: [] };
1647
+
1648
+ const [call] = await db
1649
+ .select()
1650
+ .from(schema.sessionRealtimeEntries)
1651
+ .where(
1652
+ and(
1653
+ eq(schema.sessionRealtimeEntries.accountId, input.accountId),
1654
+ eq(schema.sessionRealtimeEntries.workspaceId, input.workspaceId),
1655
+ eq(schema.sessionRealtimeEntries.sessionId, input.sessionId),
1656
+ eq(schema.sessionRealtimeEntries.turnId, input.turnId),
1657
+ eq(schema.sessionRealtimeEntries.direction, "provider_in"),
1658
+ eq(schema.sessionRealtimeEntries.kind, "delegation_call"),
1659
+ ),
1660
+ )
1661
+ .for("update")
1662
+ .limit(1);
1663
+ const text = progressEvents.map((event) => event.text).join("");
1664
+ const source = sourceEventProvenance(progressEvents.map((event) => event.id));
1665
+ const sourceEventIds = source.ids;
1666
+ const sourceEventSequences = progressEvents.map((event) => event.sequence);
1667
+ const now = input.now ?? new Date();
1668
+ if (!call) {
1669
+ const mirrored = await mirrorSessionRealtimeContextInTransaction(db, {
1670
+ accountId: input.accountId,
1671
+ workspaceId: input.workspaceId,
1672
+ sessionId: input.sessionId,
1673
+ sourceKind: "assistant_progress",
1674
+ sourceId: source.identity,
1675
+ turnId: input.turnId,
1676
+ channel: "commentary",
1677
+ text,
1678
+ payload: {
1679
+ route: "session_context",
1680
+ status: "running",
1681
+ sourceEventIds,
1682
+ sourceEventCount: source.count,
1683
+ sourceEventIdsTruncated: source.truncated,
1684
+ firstSourceEventSequence: sourceEventSequences[0],
1685
+ lastSourceEventSequence: sourceEventSequences.at(-1),
1686
+ },
1687
+ now,
1688
+ });
1689
+ return { entries: mirrored ? [mapEntry(mirrored.entry)] : [] };
1690
+ }
1691
+ if (!call.delegationItemId) {
1692
+ throw new Error(`Delegation turn ${input.turnId} has no provider item identity`);
1693
+ }
1694
+
1695
+ const [mode] = await db
1696
+ .select({
1697
+ connectionEpoch: schema.sessionRealtimeModes.connectionEpoch,
1698
+ state: schema.sessionRealtimeModes.state,
1699
+ leaseExpiresAt: schema.sessionRealtimeModes.leaseExpiresAt,
1700
+ })
1701
+ .from(schema.sessionRealtimeModes)
1702
+ .where(
1703
+ and(
1704
+ eq(schema.sessionRealtimeModes.id, call.realtimeId),
1705
+ eq(schema.sessionRealtimeModes.accountId, input.accountId),
1706
+ eq(schema.sessionRealtimeModes.workspaceId, input.workspaceId),
1707
+ eq(schema.sessionRealtimeModes.sessionId, input.sessionId),
1708
+ ),
1709
+ )
1710
+ .limit(1);
1711
+ if (!mode) {
1712
+ throw new Error(`Delegation turn ${input.turnId} lost its realtime mode ownership`);
1713
+ }
1714
+ if (mode.state !== "active" || mode.leaseExpiresAt <= now) {
1715
+ const mirrored = await mirrorSessionRealtimeContextInTransaction(db, {
1716
+ accountId: input.accountId,
1717
+ workspaceId: input.workspaceId,
1718
+ sessionId: input.sessionId,
1719
+ sourceKind: "assistant_progress",
1720
+ sourceId: source.identity,
1721
+ turnId: input.turnId,
1722
+ channel: "commentary",
1723
+ text,
1724
+ payload: {
1725
+ route: "session_context",
1726
+ status: "running",
1727
+ priorRealtimeId: call.realtimeId,
1728
+ sourceEventIds,
1729
+ sourceEventCount: source.count,
1730
+ sourceEventIdsTruncated: source.truncated,
1731
+ firstSourceEventSequence: sourceEventSequences[0],
1732
+ lastSourceEventSequence: sourceEventSequences.at(-1),
1733
+ },
1734
+ now,
1735
+ });
1736
+ return { entries: mirrored ? [mapEntry(mirrored.entry)] : [] };
1737
+ }
1738
+
1739
+ const chunks = utf8Chunks(text, SESSION_REALTIME_LEDGER_MAX_TEXT_BYTES);
1740
+ const operationSeed = source.identity;
1741
+ let nextSequence = await nextLedgerSequence(db, call.realtimeId);
1742
+ const inserted = await db
1743
+ .insert(schema.sessionRealtimeEntries)
1744
+ .values(
1745
+ chunks.map((chunk, chunkIndex) => ({
1746
+ accountId: input.accountId,
1747
+ workspaceId: input.workspaceId,
1748
+ sessionId: input.sessionId,
1749
+ realtimeId: call.realtimeId,
1750
+ operationId: deterministicUuid(
1751
+ `opengeni:session-realtime-delegation-progress:${input.turnId}:${operationSeed}:${chunkIndex}`,
1752
+ ),
1753
+ connectionEpoch: mode.connectionEpoch,
1754
+ sequence: nextSequence++,
1755
+ direction: "provider_out" as const,
1756
+ kind: "delegation_progress" as const,
1757
+ delegationItemId: call.delegationItemId,
1758
+ turnId: input.turnId,
1759
+ text: chunk,
1760
+ payload: boundedPayload({
1761
+ route: "delegation_context",
1762
+ channel: "commentary",
1763
+ status: "running",
1764
+ turnId: input.turnId,
1765
+ callOperationId: call.operationId,
1766
+ callLedgerEntryId: call.id,
1767
+ sourceEventIds,
1768
+ sourceEventCount: source.count,
1769
+ sourceEventIdsTruncated: source.truncated,
1770
+ firstSourceEventSequence: sourceEventSequences[0],
1771
+ lastSourceEventSequence: sourceEventSequences.at(-1),
1772
+ chunkIndex,
1773
+ chunkCount: chunks.length,
1774
+ }),
1775
+ createdAt: now,
1776
+ updatedAt: now,
1777
+ })),
1778
+ )
1779
+ .returning();
1780
+ return { entries: inserted.map(mapEntry) };
1781
+ }
1782
+
1783
+ /**
1784
+ * Project the authoritative terminal event of an accepted same-session
1785
+ * delegation into exactly one outbound ledger row. The caller already owns the
1786
+ * canonical session/turn settlement locks; this helper deliberately does not
1787
+ * require the original provider connection (or even the mode) to remain
1788
+ * active. A later valid rotation may therefore replay the same durable row,
1789
+ * while the immutable realtime id prevents projection into another mode.
1790
+ */