@opengeni/db 0.36.1 → 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.
Files changed (47) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-HSOWSYOX.js → chunk-4SR4HKKA.js} +2 -2
  3. package/dist/{chunk-5KTIEDX7.js → chunk-6ZYKCRFO.js} +124 -16
  4. package/dist/chunk-6ZYKCRFO.js.map +1 -0
  5. package/dist/{chunk-VTPRIXKG.js → chunk-CUPM3FGB.js} +3 -3
  6. package/dist/{chunk-GYO7ED2Q.js → chunk-LTG3S6ZY.js} +2 -2
  7. package/dist/{chunk-VKLB2ZGI.js → chunk-NXAFLCQX.js} +81 -2
  8. package/dist/chunk-NXAFLCQX.js.map +1 -0
  9. package/dist/{chunk-FPVAD4CO.js → chunk-WD5ROSLL.js} +2 -2
  10. package/dist/{chunk-Z7VYMNPZ.js → chunk-ZWREYEWA.js} +2 -2
  11. package/dist/editable-artifact-durable-export.d.ts +10 -0
  12. package/dist/editable-artifact-durable-export.js +18 -3
  13. package/dist/editable-artifact-durable-export.js.map +1 -1
  14. package/dist/editable-artifacts.js +3 -3
  15. package/dist/index.d.ts +40 -8
  16. package/dist/index.js +509 -59
  17. package/dist/index.js.map +1 -1
  18. package/dist/knowledge-source-sync-schema.d.ts +730 -0
  19. package/dist/knowledge-source-sync.d.ts +60 -0
  20. package/dist/provision-roles.js +1 -1
  21. package/dist/runtime-posture.d.ts +3 -3
  22. package/dist/schema.d.ts +21 -4
  23. package/dist/schema.js +5 -1
  24. package/dist/session-queue-commands.d.ts +1 -1
  25. package/dist/session-realtime-ledger.d.ts +1 -0
  26. package/dist/session-tenancy.js +3 -3
  27. package/dist/video-generation.js +3 -3
  28. package/drizzle/0240_model_context_user_messages.sql +204 -0
  29. package/drizzle/0243_google_drive_object_acl_authority.sql +554 -0
  30. package/package.json +4 -4
  31. package/src/editable-artifact-durable-export.ts +27 -0
  32. package/src/index.ts +212 -13
  33. package/src/knowledge-source-sync-schema.ts +83 -0
  34. package/src/knowledge-source-sync.ts +530 -0
  35. package/src/provision-roles.ts +36 -0
  36. package/src/runtime-posture.ts +49 -1
  37. package/src/schema.ts +30 -7
  38. package/src/session-queue-commands.ts +13 -14
  39. package/src/session-realtime-context.ts +16 -0
  40. package/src/session-realtime-ledger.ts +53 -1
  41. package/dist/chunk-5KTIEDX7.js.map +0 -1
  42. package/dist/chunk-VKLB2ZGI.js.map +0 -1
  43. /package/dist/{chunk-HSOWSYOX.js.map → chunk-4SR4HKKA.js.map} +0 -0
  44. /package/dist/{chunk-VTPRIXKG.js.map → chunk-CUPM3FGB.js.map} +0 -0
  45. /package/dist/{chunk-GYO7ED2Q.js.map → chunk-LTG3S6ZY.js.map} +0 -0
  46. /package/dist/{chunk-FPVAD4CO.js.map → chunk-WD5ROSLL.js.map} +0 -0
  47. /package/dist/{chunk-Z7VYMNPZ.js.map → chunk-ZWREYEWA.js.map} +0 -0
package/src/schema.ts CHANGED
@@ -2464,10 +2464,10 @@ export const sessions = pgTable(
2464
2464
  status: text("status").notNull().default("queued"),
2465
2465
  initialMessage: losslessText("initial_message").notNull(),
2466
2466
  initialMessageCodecVersion: losslessCodecVersion("initial_message_codec_version"),
2467
- // Invisible host context frozen with the winning session create. The
2468
- // initial turn copies this value so an idempotent repair can never adopt a
2469
- // retrying caller's different instructions.
2470
- initialTurnInstructions: text("initial_turn_instructions"),
2467
+ // Model-visible application context frozen with the winning create. The
2468
+ // initial turn copies it into the canonical user message so an idempotent
2469
+ // repair cannot adopt a retrying caller's different message context.
2470
+ initialModelContext: text("initial_model_context"),
2471
2471
  title: text("title"),
2472
2472
  titleSource: text("title_source"),
2473
2473
  // Per-session agent persona/system instructions supplied at create (the
@@ -2762,6 +2762,11 @@ export const sessions = pgTable(
2762
2762
  table.rootSessionId,
2763
2763
  table.nestedAgentDepth,
2764
2764
  ),
2765
+ initialModelContextValid: check(
2766
+ "sessions_initial_model_context_check",
2767
+ sql`${table.initialModelContext} is null
2768
+ or opengeni_private.model_context_value_valid(${table.initialModelContext})`,
2769
+ ),
2765
2770
  }),
2766
2771
  );
2767
2772
 
@@ -3115,6 +3120,9 @@ export const sessionRealtimeEntries = pgTable(
3115
3120
  textCodecVersion: losslessCodecVersion("text_codec_version"),
3116
3121
  payload: losslessJsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
3117
3122
  payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
3123
+ // Application context for an exact provider-in delegation or finalized
3124
+ // transcript. It is materialized as ordinary user-role message content.
3125
+ modelContext: text("model_context"),
3118
3126
  clientAckedAt: timestamp("client_acked_at", { withTimezone: true }),
3119
3127
  providerAckedAt: timestamp("provider_acked_at", { withTimezone: true }),
3120
3128
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
@@ -3190,6 +3198,15 @@ export const sessionRealtimeEntries = pgTable(
3190
3198
  or (${table.kind} = 'assistant_transcript' and ${table.role} = 'assistant' and ${table.text} is not null)
3191
3199
  or (${table.kind} not in ('user_transcript', 'assistant_transcript') and ${table.role} is null)`,
3192
3200
  ),
3201
+ modelContextValid: check(
3202
+ "session_realtime_entries_model_context_check",
3203
+ sql`${table.modelContext} is null
3204
+ or (
3205
+ ${table.direction} = 'provider_in'
3206
+ and ${table.kind} in ('delegation_call', 'user_transcript', 'assistant_transcript')
3207
+ and opengeni_private.model_context_value_valid(${table.modelContext})
3208
+ )`,
3209
+ ),
3193
3210
  }),
3194
3211
  );
3195
3212
 
@@ -4497,9 +4514,10 @@ export const sessionTurns = pgTable(
4497
4514
  prompt: losslessText("prompt").notNull(),
4498
4515
  promptCodecVersion: losslessCodecVersion("prompt_codec_version"),
4499
4516
  annotations: jsonb("annotations").$type<TimelineAnnotation[]>().notNull().default([]),
4500
- // Host context for this exact turn. System-level at runtime and deliberately
4501
- // separate from the visible prompt/event payload.
4502
- turnInstructions: text("turn_instructions"),
4517
+ // Application context for this exact user message. It is copied into the
4518
+ // canonical user-role history item at claim and omitted from public queue
4519
+ // projections; full event/audit data retains it.
4520
+ modelContext: text("model_context"),
4503
4521
  resources: jsonb("resources").$type<unknown[]>().notNull().default([]),
4504
4522
  tools: jsonb("tools").$type<unknown[]>().notNull().default([]),
4505
4523
  // false = inherit the durable session policy; true = this turn explicitly
@@ -4579,6 +4597,11 @@ export const sessionTurns = pgTable(
4579
4597
  "session_turns_latency_mode_check",
4580
4598
  sql`${table.latencyMode} in ('standard', 'priority', 'fast')`,
4581
4599
  ),
4600
+ modelContextValid: check(
4601
+ "session_turns_model_context_check",
4602
+ sql`${table.modelContext} is null
4603
+ or opengeni_private.model_context_value_valid(${table.modelContext})`,
4604
+ ),
4582
4605
  }),
4583
4606
  );
4584
4607
 
@@ -1414,7 +1414,7 @@ export async function submitHumanPromptInTransaction(
1414
1414
  expectedDraftRevision?: number | null;
1415
1415
  text: string;
1416
1416
  annotations?: TimelineAnnotation[];
1417
- turnInstructions?: string | null;
1417
+ modelContext?: string | null;
1418
1418
  resources: ResourceRef[];
1419
1419
  model?: string | null;
1420
1420
  reasoningEffort?: ReasoningEffort | null;
@@ -1458,7 +1458,7 @@ export async function submitHumanPromptInTransaction(
1458
1458
  expectedDraftRevision: input.expectedDraftRevision ?? null,
1459
1459
  text: input.text,
1460
1460
  annotations,
1461
- turnInstructions: input.turnInstructions ?? null,
1461
+ modelContext: input.modelContext ?? null,
1462
1462
  resources: withCanonicalResourceMountPaths(input.resources),
1463
1463
  model: input.model ?? null,
1464
1464
  reasoningEffort: input.reasoningEffort ?? null,
@@ -1626,7 +1626,7 @@ export async function submitHumanPromptInTransaction(
1626
1626
  }
1627
1627
 
1628
1628
  let editedSourceTurn: QueuedTurnRow | undefined;
1629
- let editedSourceTurnInstructions: string | null | undefined;
1629
+ let editedSourceModelContext: string | null | undefined;
1630
1630
  if (draft?.sourceTurnId) {
1631
1631
  const sourceLocks = await lockSessionEventWriteRows(db, {
1632
1632
  workspaceId: input.workspaceId,
@@ -1661,14 +1661,11 @@ export async function submitHumanPromptInTransaction(
1661
1661
  },
1662
1662
  );
1663
1663
  }
1664
- // This is the sole private-instruction source for an edited replacement.
1665
- // It is deliberately held separately from the public draft and event
1666
- // projections below. The public draft is expected to differ after editing;
1667
- // source identity is fenced by its exact withdrawn row version, not by
1668
- // comparing the replacement content with the old prompt. A client-supplied
1669
- // instruction value must never override the private source value.
1664
+ // Preserve the non-rendered message segment when editing only the visible
1665
+ // draft. Source identity is fenced by the exact withdrawn row version; a
1666
+ // replacement request cannot accidentally detach or replace its context.
1670
1667
  editedSourceTurn = sourceTurn;
1671
- editedSourceTurnInstructions = sourceTurn.turnInstructions ?? null;
1668
+ editedSourceModelContext = sourceTurn.modelContext ?? null;
1672
1669
  }
1673
1670
 
1674
1671
  for (const update of input.mcpCredentialUpdates ?? []) {
@@ -1725,6 +1722,10 @@ export async function submitHumanPromptInTransaction(
1725
1722
  const acceptedEventId = crypto.randomUUID();
1726
1723
  const turnId = crypto.randomUUID();
1727
1724
  const workflowId = session.temporalWorkflowId ?? `session-${session.id}`;
1725
+ const effectiveModelContext =
1726
+ editedSourceModelContext !== undefined
1727
+ ? editedSourceModelContext
1728
+ : (input.modelContext ?? null);
1728
1729
  let sequence = session.lastSequence;
1729
1730
  const eventValues: SessionEventInsertWithPayload[] = [
1730
1731
  {
@@ -1747,6 +1748,7 @@ export async function submitHumanPromptInTransaction(
1747
1748
  }
1748
1749
  : {}),
1749
1750
  ...(input.resources.length ? { resources: input.resources } : {}),
1751
+ ...(effectiveModelContext ? { modelContext: effectiveModelContext } : {}),
1750
1752
  ...(input.model ? { model: input.model } : {}),
1751
1753
  ...(input.reasoningEffort ? { reasoningEffort: input.reasoningEffort } : {}),
1752
1754
  ...(input.latencyMode ? { latencyMode: input.latencyMode } : {}),
@@ -1773,10 +1775,7 @@ export async function submitHumanPromptInTransaction(
1773
1775
  position: input.delivery === "steer" ? 0 : existingQueued.length + 1,
1774
1776
  prompt: input.text,
1775
1777
  annotations,
1776
- turnInstructions:
1777
- editedSourceTurnInstructions !== undefined
1778
- ? editedSourceTurnInstructions
1779
- : (input.turnInstructions ?? null),
1778
+ modelContext: effectiveModelContext,
1780
1779
  resources: input.resources,
1781
1780
  tools: [],
1782
1781
  toolsProvided: false,
@@ -237,6 +237,7 @@ export async function flushSessionRealtimeTranscriptTailInTransaction(
237
237
  textCodecVersion: schema.sessionRealtimeEntries.textCodecVersion,
238
238
  payload: schema.sessionRealtimeEntries.payload,
239
239
  payloadCodecVersion: schema.sessionRealtimeEntries.payloadCodecVersion,
240
+ modelContext: schema.sessionRealtimeEntries.modelContext,
240
241
  })
241
242
  .from(schema.sessionRealtimeEntries)
242
243
  .where(
@@ -258,6 +259,20 @@ export async function flushSessionRealtimeTranscriptTailInTransaction(
258
259
  }));
259
260
  const rendered = renderSessionRealtimeTail(decodedRows);
260
261
  if (!rendered.context) return null;
262
+ const renderedRows = decodedRows.slice(decodedRows.length - rendered.includedEntryCount);
263
+ let foundUserTranscript = false;
264
+ let modelContext: string | null = null;
265
+ for (let index = renderedRows.length - 1; index >= 0; index -= 1) {
266
+ const entry = renderedRows[index]!;
267
+ if (entry.role === "user") {
268
+ foundUserTranscript = true;
269
+ modelContext = entry.modelContext;
270
+ break;
271
+ }
272
+ }
273
+ if (!foundUserTranscript) {
274
+ modelContext = renderedRows.at(-1)?.modelContext ?? null;
275
+ }
261
276
 
262
277
  const [session] = await db
263
278
  .select({ metadata: schema.sessions.metadata })
@@ -286,6 +301,7 @@ export async function flushSessionRealtimeTranscriptTailInTransaction(
286
301
  operationKey: deterministicUuid(`opengeni:session-realtime-tail-flush:${mode.id}`),
287
302
  delivery: "steer",
288
303
  text: rendered.context,
304
+ modelContext,
289
305
  messagePresentation: {
290
306
  kind: "realtime_voice_handoff",
291
307
  text: "Voice session ended. Remaining conversation context was sent to the agent.",
@@ -155,6 +155,7 @@ export type SessionRealtimeInboundEntryInput = {
155
155
  delegationItemId?: string | null | undefined;
156
156
  text?: string | null | undefined;
157
157
  payload?: Record<string, unknown> | undefined;
158
+ modelContext?: string | undefined;
158
159
  };
159
160
 
160
161
  export type SyncSessionRealtimeLedgerInput = AssertSessionRealtimeOwnerInput & {
@@ -1016,12 +1017,34 @@ function canonicalJsonValue(value: unknown): unknown {
1016
1017
  return value;
1017
1018
  }
1018
1019
 
1020
+ function normalizedModelContext(input: SessionRealtimeInboundEntryInput): string | null {
1021
+ const value = input.modelContext?.trim();
1022
+ if (!value) {
1023
+ if (input.modelContext !== undefined) {
1024
+ throw new Error("Realtime modelContext must not be empty");
1025
+ }
1026
+ return null;
1027
+ }
1028
+ if (value.length > 32_768) {
1029
+ throw new Error("Realtime modelContext exceeds the server limit");
1030
+ }
1031
+ if (
1032
+ input.kind !== "delegation_call" &&
1033
+ input.kind !== "user_transcript" &&
1034
+ input.kind !== "assistant_transcript"
1035
+ ) {
1036
+ throw new Error("Realtime modelContext requires a delegation or finalized transcript");
1037
+ }
1038
+ return value;
1039
+ }
1040
+
1019
1041
  function inboundReplayMatches(
1020
1042
  row: EntryRow,
1021
1043
  input: SessionRealtimeInboundEntryInput,
1022
1044
  role: "user" | "assistant" | null,
1023
1045
  text: string | null,
1024
1046
  payload: Record<string, unknown>,
1047
+ modelContext: string | null,
1025
1048
  ): boolean {
1026
1049
  const storedText =
1027
1050
  row.text === null ? null : fromPostgresLosslessText(row.text, row.textCodecVersion);
@@ -1034,11 +1057,32 @@ function inboundReplayMatches(
1034
1057
  row.delegationItemId === (input.delegationItemId ?? null) &&
1035
1058
  row.sourceUpdateId === null &&
1036
1059
  storedText === text &&
1060
+ row.modelContext === modelContext &&
1037
1061
  JSON.stringify(canonicalJsonValue(storedPayload)) ===
1038
1062
  JSON.stringify(canonicalJsonValue(payload))
1039
1063
  );
1040
1064
  }
1041
1065
 
1066
+ async function delegationModelContextMatches(
1067
+ db: Database,
1068
+ row: EntryRow,
1069
+ modelContext: string | null,
1070
+ ): Promise<boolean> {
1071
+ if (row.kind !== "delegation_call" || row.turnId === null) return true;
1072
+ const [turn] = await db
1073
+ .select({ modelContext: schema.sessionTurns.modelContext })
1074
+ .from(schema.sessionTurns)
1075
+ .where(
1076
+ and(
1077
+ eq(schema.sessionTurns.workspaceId, row.workspaceId),
1078
+ eq(schema.sessionTurns.sessionId, row.sessionId),
1079
+ eq(schema.sessionTurns.id, row.turnId),
1080
+ ),
1081
+ )
1082
+ .limit(1);
1083
+ return turn?.modelContext === modelContext;
1084
+ }
1085
+
1042
1086
  async function appendInvalidDelegationFailure(
1043
1087
  db: Database,
1044
1088
  input: Pick<
@@ -1083,6 +1127,7 @@ async function admitRealtimeDelegationInTransaction(
1083
1127
  accountId: string,
1084
1128
  incoming: SessionRealtimeInboundEntryInput,
1085
1129
  entryId: string,
1130
+ modelContext: string | null,
1086
1131
  ): Promise<{ turnId: string; eventIds: string[]; wakeRevision: number }> {
1087
1132
  const [session] = await db
1088
1133
  .select()
@@ -1142,6 +1187,7 @@ async function admitRealtimeDelegationInTransaction(
1142
1187
  operationKey: incoming.operationId,
1143
1188
  delivery: "steer",
1144
1189
  text: incoming.text!,
1190
+ modelContext,
1145
1191
  messagePresentation: {
1146
1192
  kind: "realtime_voice",
1147
1193
  text: inputTranscript,
@@ -1359,6 +1405,7 @@ export async function syncSessionRealtimeLedgerInTransaction(
1359
1405
  const payload = boundedPayload(incoming.payload);
1360
1406
  const role = expectedRole(incoming);
1361
1407
  const text = incoming.text ?? null;
1408
+ const modelContext = normalizedModelContext(incoming);
1362
1409
  assertBoundedString(text, SESSION_REALTIME_LEDGER_MAX_TEXT_BYTES, "Realtime text");
1363
1410
  if (
1364
1411
  (incoming.kind === "user_transcript" || incoming.kind === "assistant_transcript") &&
@@ -1392,7 +1439,10 @@ export async function syncSessionRealtimeLedgerInTransaction(
1392
1439
  )
1393
1440
  .limit(1);
1394
1441
  if (existing) {
1395
- if (!inboundReplayMatches(existing, incoming, role, text, payload)) {
1442
+ if (
1443
+ !inboundReplayMatches(existing, incoming, role, text, payload, modelContext) ||
1444
+ !(await delegationModelContextMatches(db, existing, modelContext))
1445
+ ) {
1396
1446
  throw new SessionRealtimeConflictError(
1397
1447
  incoming.kind === "delegation_call"
1398
1448
  ? "REALTIME_DELEGATION_CHANGED"
@@ -1423,6 +1473,7 @@ export async function syncSessionRealtimeLedgerInTransaction(
1423
1473
  textCodecVersion: LOSSLESS_CONTENT_CODEC_VERSION,
1424
1474
  payload,
1425
1475
  payloadCodecVersion: LOSSLESS_CONTENT_CODEC_VERSION,
1476
+ modelContext,
1426
1477
  createdAt: now,
1427
1478
  updatedAt: now,
1428
1479
  })
@@ -1447,6 +1498,7 @@ export async function syncSessionRealtimeLedgerInTransaction(
1447
1498
  modeRow.accountId,
1448
1499
  incoming,
1449
1500
  entry.id,
1501
+ modelContext,
1450
1502
  );
1451
1503
  const [linked] = await db
1452
1504
  .update(schema.sessionRealtimeEntries)