@opengeni/db 0.27.9 → 0.28.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.
@@ -18,6 +18,7 @@ export const FORCE_RLS_TABLES = [
18
18
  "billing_customers",
19
19
  "capability_catalog_items",
20
20
  "capability_installations",
21
+ "codex_apps_settings",
21
22
  "codex_capacity_waiters",
22
23
  "codex_credential_leases",
23
24
  "codex_reset_redemption_attempts",
@@ -117,6 +118,10 @@ export const FORCE_RLS_TABLES = [
117
118
  "slack_interactions",
118
119
  "social_connections",
119
120
  "social_posts",
121
+ "transcription_recording_chunks",
122
+ "transcription_recording_objects",
123
+ "transcription_recording_segments",
124
+ "transcription_recordings",
120
125
  "usage_events",
121
126
  "workspace_artifact_events",
122
127
  "workspace_artifact_versions",
@@ -172,6 +177,7 @@ export const RUNTIME_FULL_DML_TABLES = [
172
177
  "billing_customers",
173
178
  "capability_catalog_items",
174
179
  "capability_installations",
180
+ "codex_apps_settings",
175
181
  "codex_capacity_waiters",
176
182
  "codex_credential_leases",
177
183
  "codex_reset_redemption_attempts",
@@ -246,6 +252,10 @@ export const RUNTIME_FULL_DML_TABLES = [
246
252
  "social_connections",
247
253
  "social_posts",
248
254
  "stripe_webhook_events",
255
+ "transcription_recording_chunks",
256
+ "transcription_recording_objects",
257
+ "transcription_recording_segments",
258
+ "transcription_recordings",
249
259
  "usage_events",
250
260
  "workspace_artifacts",
251
261
  "workspace_captures",
package/src/schema.ts CHANGED
@@ -497,7 +497,8 @@ export const workspaceVariableSetVariables = pgTable(
497
497
  }),
498
498
  );
499
499
 
500
- // Per-workspace ChatGPT/Codex subscription credential. One row per workspace.
500
+ // Per-workspace ChatGPT/Codex subscription credential. One row per connected
501
+ // ChatGPT account within a workspace.
501
502
  // access/refresh/id tokens live INSIDE credential_encrypted (v1 AES-256-GCM,
502
503
  // same envelope as workspace_variable_set_variables); the other columns are
503
504
  // plaintext metadata (header value + UI). RLS-isolated per workspace.
@@ -536,21 +537,11 @@ export const codexSubscriptionCredentials = pgTable(
536
537
  // usage cap on a rotation turn; the rotation engine treats `exhausted_until > now()` as
537
538
  // capped/skip so it isn't immediately re-picked. Self-clears via the now() comparison.
538
539
  exhaustedUntil: timestamp("exhausted_until", { withTimezone: true }),
539
- // P4 connector-aware rotation cache (plaintext metadata; NEVER a token). The set
540
- // of ORIGINAL-dotted connector namespaces (github/gmail/linear/…) this account
541
- // exposes via codex_apps, captured from the per-turn tools/list. null ⇒ never
542
- // probed (the ranker treats it as unknown: never credited as covering, never
543
- // excluded). The writer only ever sets a NON-empty set, so a flaky empty turn
544
- // can't false-drop coverage. connectorsCheckedAt is the freshness clock.
545
- connectorNamespaces: text("connector_namespaces").array(),
546
- connectorsCheckedAt: timestamp("connectors_checked_at", {
547
- withTimezone: true,
548
- }),
549
540
  // Workspace-local, server-held fairness cursor. Provider usage headers are
550
541
  // capacity hints, never the sole allocator: live lease count is ranked first
551
542
  // and this cursor deterministically breaks equal-load/equal-capacity ties.
552
543
  // This flag controls NEW automatic allocations only. Credential health,
553
- // refresh, encrypted material, and already-frozen/in-flight turns are
544
+ // refresh, encrypted material, and an already-leased in-flight turn are
554
545
  // intentionally independent. account eligibility policy owns toggle OCC/audit and product UI.
555
546
  allocatorEnabled: boolean("allocator_enabled").notNull().default(true),
556
547
  // Independent OCC/audit sequence for the allocator toggle. Token refresh
@@ -586,6 +577,54 @@ export const codexSubscriptionCredentials = pgTable(
586
577
  table.workspaceId,
587
578
  table.id,
588
579
  ),
580
+ workspaceAccountIdentity: uniqueIndex(
581
+ "codex_subscription_credentials_workspace_account_id_idx",
582
+ ).on(table.workspaceId, table.accountId, table.id),
583
+ }),
584
+ );
585
+
586
+ // Optional workspace-level credential used only for ChatGPT connected Apps.
587
+ // Inference selection, usage, cooldown, allocator, pins, and leases never read
588
+ // this row. A durable null row retains the OCC sequence after designation clear.
589
+ export const codexAppsSettings = pgTable(
590
+ "codex_apps_settings",
591
+ {
592
+ id: uuid("id").primaryKey().defaultRandom(),
593
+ accountId: uuid("account_id")
594
+ .notNull()
595
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
596
+ workspaceId: uuid("workspace_id")
597
+ .notNull()
598
+ .references(() => workspaces.id, { onDelete: "cascade" }),
599
+ credentialId: uuid("credential_id"),
600
+ version: integer("version").notNull().default(1),
601
+ designatedAt: timestamp("designated_at", { withTimezone: true }),
602
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
603
+ },
604
+ (table) => ({
605
+ workspaceAccount: foreignKey({
606
+ name: "codex_apps_settings_workspace_account_fk",
607
+ columns: [table.workspaceId, table.accountId],
608
+ foreignColumns: [workspaces.id, workspaces.accountId],
609
+ }).onDelete("cascade"),
610
+ credentialScope: foreignKey({
611
+ name: "codex_apps_settings_credential_scope_fk",
612
+ columns: [table.workspaceId, table.accountId, table.credentialId],
613
+ foreignColumns: [
614
+ codexSubscriptionCredentials.workspaceId,
615
+ codexSubscriptionCredentials.accountId,
616
+ codexSubscriptionCredentials.id,
617
+ ],
618
+ }).onDelete("cascade"),
619
+ workspace: uniqueIndex("codex_apps_settings_workspace_idx").on(table.workspaceId),
620
+ designationShape: check(
621
+ "codex_apps_settings_designation_shape_chk",
622
+ sql`${table.version} > 0 and (
623
+ (${table.credentialId} is null and ${table.designatedAt} is null)
624
+ or
625
+ (${table.credentialId} is not null and ${table.designatedAt} is not null)
626
+ )`,
627
+ ),
589
628
  }),
590
629
  );
591
630
 
@@ -1460,10 +1499,9 @@ export const sessions = pgTable(
1460
1499
  nestedAgentDepthPolicySessionId: uuid("nested_agent_depth_policy_session_id"),
1461
1500
  temporalWorkflowId: text("temporal_workflow_id"),
1462
1501
  activeTurnId: uuid("active_turn_id"),
1463
- // Actual input tokens reported for the last model call of the most recent
1464
- // turn. The pre-turn portable compaction trigger reads this as its budget
1465
- // signal (char/4 estimate is the same-turn fallback). Null until a turn with
1466
- // usage has completed.
1502
+ // Actual input tokens reported for the latest authoritative ordinary model
1503
+ // call. Compaction/context clearing invalidates it to null; local history
1504
+ // estimates must never be stored here.
1467
1505
  lastInputTokens: integer("last_input_tokens"),
1468
1506
  // Operator /compact request flag. The API sets
1469
1507
  // it true; the worker honors it BEFORE the next turn's model call by forcing
@@ -3519,25 +3557,9 @@ export const agentRunStates = pgTable("agent_run_states", {
3519
3557
  stateVersion: integer("state_version").notNull(),
3520
3558
  serializedRunState: text("serialized_run_state").notNull(),
3521
3559
  pendingApprovals: jsonb("pending_approvals").$type<unknown[]>().notNull().default([]),
3522
- // The Codex account that FROZE this run state: the turn's resolved codex
3523
- // credential id (pin > workspace-active), or NULL when frozen on the
3524
- // non-codex / Azure path (or before this column existed). The serialized
3525
- // RunState blob round-trips `reasoning.encrypted_content` minted by the
3526
- // ChatGPT/Codex backend — account/org-bound, so a foreign blob 400s — and the
3527
- // foreign reasoning ids the Responses backend validates; but the blob carries
3528
- // NO per-item producer tag (those live only on session_history_items). So we
3529
- // stamp the freezing account here: on an approval resume whose codex account
3530
- // DIFFERS from this value,
3531
- // the replay path neutralizes every reasoning item's account-bound identity
3532
- // (encrypted_content + provider id) in the blob before it reaches the model.
3533
- // Deliberately NO FK: provenance must OUTLIVE the account's hard-disconnect (a
3534
- // stale-but-null tag still mismatches a live codex id, so the strip stays
3535
- // correct either way). NULL on both sides (non-codex freeze + non-codex
3536
- // resume) is a no-op, so single-account and non-codex sessions are unchanged.
3537
- frozenCodexCredentialId: uuid("frozen_codex_credential_id"),
3538
- // Exact provider rejection invalidates only the opaque reasoning identity
3539
- // inside this frozen state. The serialized receipt remains durable and
3540
- // auditable; the resume path neutralizes its provider-bound identity.
3560
+ // Exact provider rejection marks the latest current-turn receipt only when it
3561
+ // was part of the rejected request. The serialized receipt remains durable;
3562
+ // recovery builds a temporary view without unusable opaque artifacts.
3541
3563
  providerArtifactInvalidatedAt: timestamp("provider_artifact_invalidated_at", {
3542
3564
  withTimezone: true,
3543
3565
  }),
@@ -3661,22 +3683,11 @@ export const sessionHistoryItems = pgTable(
3661
3683
  // inserts ONE synthetic active summary row at the boundary. Defaults true so
3662
3684
  // every existing and normally-appended row is live.
3663
3685
  active: boolean("active").notNull().default(true),
3664
- // The Codex account that PRODUCED these items: the per-turn resolved codex
3665
- // credential id (pin > workspace-active), or NULL when produced on the
3666
- // non-codex / Azure path (or before this column existed). Used to strip
3667
- // cross-account `reasoning.encrypted_content` blobs those are account/org-
3668
- // bound, minted by the ChatGPT/Codex backend, so replaying account A's blob
3669
- // into a turn running on account B 400s. The read path drops the encrypted
3670
- // reasoning of any item whose producer != the turn's current codex account.
3671
- // Deliberately NO FK: provenance must OUTLIVE the account's hard-disconnect
3672
- // (an ON DELETE SET NULL would erase the tag, and a stale-but-null tag still
3673
- // mismatches a live codex id so the strip stays correct either way).
3674
- producerCodexCredentialId: uuid("producer_codex_credential_id"),
3675
- // An exact provider 400 can prove that an otherwise same-credential opaque
3676
- // reasoning artifact is no longer decryptable. Keep the original item and
3677
- // producer provenance immutable, but record the attempt-fenced rejection so
3678
- // later model reads omit only the provider-bound identity. No FK: the receipt
3679
- // must outlive operational attempt retention just like producer provenance.
3686
+ // An exact provider 400 can prove that the request's active opaque artifact
3687
+ // set is no longer usable. Keep each canonical item immutable, but record the
3688
+ // attempt-fenced rejection on the exact candidate row IDs so later model
3689
+ // reads build a temporary projection. No FK: the receipt must outlive
3690
+ // operational attempt retention.
3680
3691
  providerArtifactInvalidatedAt: timestamp("provider_artifact_invalidated_at", {
3681
3692
  withTimezone: true,
3682
3693
  }),
@@ -6025,3 +6036,4 @@ export * from "./workspace-instruction-policies-schema";
6025
6036
  export * from "./preference-registry-schema";
6026
6037
  export * from "./memory-governance-schema";
6027
6038
  export * from "./scoped-knowledge-schema";
6039
+ export * from "./transcription-recordings-schema";
@@ -0,0 +1,250 @@
1
+ import { sql } from "drizzle-orm";
2
+ import {
3
+ boolean,
4
+ check,
5
+ index,
6
+ integer,
7
+ jsonb,
8
+ pgTable,
9
+ primaryKey,
10
+ text,
11
+ timestamp,
12
+ uniqueIndex,
13
+ uuid,
14
+ } from "drizzle-orm/pg-core";
15
+
16
+ export const transcriptionRecordingStateValues = [
17
+ "uploading",
18
+ "segmenting",
19
+ "ready",
20
+ "transcribing",
21
+ "complete",
22
+ "failed",
23
+ "discarded",
24
+ ] as const;
25
+
26
+ export const transcriptionRecordingChunkStateValues = ["uploading", "complete"] as const;
27
+ export const transcriptionRecordingObjectKindValues = ["chunk", "segment"] as const;
28
+
29
+ export const transcriptionRecordingSegmentStateValues = [
30
+ "preparing",
31
+ "pending",
32
+ "transcribing",
33
+ "complete",
34
+ "failed",
35
+ ] as const;
36
+
37
+ export const transcriptionRecordings = pgTable(
38
+ "transcription_recordings",
39
+ {
40
+ id: uuid("id").primaryKey(),
41
+ accountId: uuid("account_id").notNull(),
42
+ workspaceId: uuid("workspace_id").notNull(),
43
+ subjectId: text("subject_id").notNull(),
44
+ mimeType: text("mime_type").notNull(),
45
+ state: text("state", { enum: transcriptionRecordingStateValues })
46
+ .notNull()
47
+ .default("uploading"),
48
+ nextChunkNumber: integer("next_chunk_number").notNull().default(0),
49
+ chunkCount: integer("chunk_count").notNull().default(0),
50
+ totalBytes: integer("total_bytes").notNull().default(0),
51
+ totalDurationMilliseconds: integer("total_duration_milliseconds").notNull().default(0),
52
+ segmentCount: integer("segment_count").notNull().default(0),
53
+ completedSegmentCount: integer("completed_segment_count").notNull().default(0),
54
+ transcriptText: text("transcript_text"),
55
+ languages: jsonb("languages").$type<string[]>().notNull().default([]),
56
+ errorCode: text("error_code"),
57
+ retryable: boolean("retryable").notNull().default(false),
58
+ providerId: text("provider_id"),
59
+ processingGeneration: integer("processing_generation").notNull().default(0),
60
+ processingOwner: uuid("processing_owner"),
61
+ processingStartedAt: timestamp("processing_started_at", { withTimezone: true }),
62
+ objectsCleanedAt: timestamp("objects_cleaned_at", { withTimezone: true }),
63
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
64
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
65
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
66
+ },
67
+ (table) => ({
68
+ exactAuthority: uniqueIndex("transcription_recordings_exact_authority_uq").on(
69
+ table.accountId,
70
+ table.workspaceId,
71
+ table.subjectId,
72
+ table.id,
73
+ ),
74
+ subjectCreated: index("transcription_recordings_subject_created_idx").on(
75
+ table.workspaceId,
76
+ table.subjectId,
77
+ table.createdAt,
78
+ ),
79
+ expiry: index("transcription_recordings_expiry_idx").on(table.expiresAt, table.id),
80
+ valuesValid: check(
81
+ "transcription_recordings_values_check",
82
+ sql`${table.nextChunkNumber} >= 0
83
+ and ${table.chunkCount} >= 0
84
+ and ${table.totalBytes} >= 0
85
+ and ${table.totalDurationMilliseconds} >= 0
86
+ and ${table.segmentCount} >= 0
87
+ and ${table.completedSegmentCount} >= 0
88
+ and ${table.completedSegmentCount} <= ${table.segmentCount}
89
+ and octet_length(${table.subjectId}) between 1 and 1024
90
+ and octet_length(${table.mimeType}) between 1 and 128
91
+ and (
92
+ ${table.providerId} is null
93
+ or octet_length(${table.providerId}) between 1 and 128
94
+ )`,
95
+ ),
96
+ processingValid: check(
97
+ "transcription_recordings_processing_check",
98
+ sql`(
99
+ ${table.processingOwner} is null
100
+ and ${table.processingStartedAt} is null
101
+ ) or (
102
+ ${table.processingOwner} is not null
103
+ and ${table.processingStartedAt} is not null
104
+ )`,
105
+ ),
106
+ }),
107
+ );
108
+
109
+ export const transcriptionRecordingObjects = pgTable(
110
+ "transcription_recording_objects",
111
+ {
112
+ accountId: uuid("account_id").notNull(),
113
+ workspaceId: uuid("workspace_id").notNull(),
114
+ subjectId: text("subject_id").notNull(),
115
+ recordingId: uuid("recording_id").notNull(),
116
+ objectKey: text("object_key").primaryKey(),
117
+ kind: text("kind", { enum: transcriptionRecordingObjectKindValues }).notNull(),
118
+ cleanupAfter: timestamp("cleanup_after", { withTimezone: true }).notNull(),
119
+ cleanupClaimId: uuid("cleanup_claim_id"),
120
+ cleanupClaimedAt: timestamp("cleanup_claimed_at", { withTimezone: true }),
121
+ cleanedAt: timestamp("cleaned_at", { withTimezone: true }),
122
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
123
+ },
124
+ (table) => ({
125
+ dueCleanup: index("transcription_recording_objects_due_cleanup_idx")
126
+ .on(table.cleanupAfter, table.objectKey)
127
+ .where(sql`${table.cleanedAt} is null`),
128
+ claimRecovery: index("transcription_recording_objects_claim_recovery_idx")
129
+ .on(table.cleanupClaimedAt, table.objectKey)
130
+ .where(sql`${table.cleanedAt} is null and ${table.cleanupClaimId} is not null`),
131
+ valuesValid: check(
132
+ "transcription_recording_objects_values_check",
133
+ sql`octet_length(${table.objectKey}) between 1 and 1024
134
+ and (
135
+ (${table.cleanupClaimId} is null and ${table.cleanupClaimedAt} is null)
136
+ or (${table.cleanupClaimId} is not null and ${table.cleanupClaimedAt} is not null)
137
+ )
138
+ and (
139
+ ${table.cleanedAt} is null
140
+ or (${table.cleanupClaimId} is null and ${table.cleanupClaimedAt} is null)
141
+ )`,
142
+ ),
143
+ }),
144
+ );
145
+
146
+ export const transcriptionRecordingChunks = pgTable(
147
+ "transcription_recording_chunks",
148
+ {
149
+ accountId: uuid("account_id").notNull(),
150
+ workspaceId: uuid("workspace_id").notNull(),
151
+ subjectId: text("subject_id").notNull(),
152
+ recordingId: uuid("recording_id").notNull(),
153
+ chunkNumber: integer("chunk_number").notNull(),
154
+ state: text("state", { enum: transcriptionRecordingChunkStateValues })
155
+ .notNull()
156
+ .default("uploading"),
157
+ byteLength: integer("byte_length").notNull(),
158
+ sha256: text("sha256").notNull(),
159
+ startMilliseconds: integer("start_milliseconds").notNull(),
160
+ durationMilliseconds: integer("duration_milliseconds").notNull(),
161
+ objectKey: text("object_key").notNull(),
162
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
163
+ completedAt: timestamp("completed_at", { withTimezone: true }),
164
+ },
165
+ (table) => ({
166
+ pk: primaryKey({
167
+ name: "transcription_recording_chunks_pk",
168
+ columns: [table.recordingId, table.chunkNumber],
169
+ }),
170
+ recordingOrder: index("transcription_recording_chunks_order_idx").on(
171
+ table.workspaceId,
172
+ table.recordingId,
173
+ table.chunkNumber,
174
+ ),
175
+ objectKey: uniqueIndex("transcription_recording_chunks_object_key_uq").on(table.objectKey),
176
+ valuesValid: check(
177
+ "transcription_recording_chunks_values_check",
178
+ sql`${table.chunkNumber} >= 0
179
+ and ${table.byteLength} > 0
180
+ and ${table.startMilliseconds} >= 0
181
+ and ${table.durationMilliseconds} >= 0
182
+ and ${table.sha256} ~ '^[0-9a-f]{64}$'
183
+ and octet_length(${table.objectKey}) between 1 and 1024
184
+ and (
185
+ (${table.state} = 'uploading' and ${table.completedAt} is null)
186
+ or (${table.state} = 'complete' and ${table.completedAt} is not null)
187
+ )`,
188
+ ),
189
+ }),
190
+ );
191
+
192
+ export const transcriptionRecordingSegments = pgTable(
193
+ "transcription_recording_segments",
194
+ {
195
+ accountId: uuid("account_id").notNull(),
196
+ workspaceId: uuid("workspace_id").notNull(),
197
+ subjectId: text("subject_id").notNull(),
198
+ recordingId: uuid("recording_id").notNull(),
199
+ segmentNumber: integer("segment_number").notNull(),
200
+ generation: integer("generation").notNull(),
201
+ state: text("state", { enum: transcriptionRecordingSegmentStateValues })
202
+ .notNull()
203
+ .default("preparing"),
204
+ byteLength: integer("byte_length").notNull(),
205
+ sha256: text("sha256").notNull(),
206
+ startMilliseconds: integer("start_milliseconds").notNull(),
207
+ durationMilliseconds: integer("duration_milliseconds").notNull(),
208
+ objectKey: text("object_key").notNull(),
209
+ attemptId: uuid("attempt_id"),
210
+ attemptStartedAt: timestamp("attempt_started_at", { withTimezone: true }),
211
+ attemptDeadlineAt: timestamp("attempt_deadline_at", { withTimezone: true }),
212
+ transcriptText: text("transcript_text"),
213
+ languages: jsonb("languages").$type<string[]>().notNull().default([]),
214
+ providerId: text("provider_id"),
215
+ errorCode: text("error_code"),
216
+ retryable: boolean("retryable").notNull().default(false),
217
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
218
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
219
+ },
220
+ (table) => ({
221
+ pk: primaryKey({
222
+ name: "transcription_recording_segments_pk",
223
+ columns: [table.recordingId, table.segmentNumber],
224
+ }),
225
+ recordingOrder: index("transcription_recording_segments_order_idx").on(
226
+ table.workspaceId,
227
+ table.recordingId,
228
+ table.segmentNumber,
229
+ ),
230
+ objectKey: uniqueIndex("transcription_recording_segments_object_key_uq").on(table.objectKey),
231
+ valuesValid: check(
232
+ "transcription_recording_segments_values_check",
233
+ sql`${table.segmentNumber} >= 0
234
+ and ${table.generation} > 0
235
+ and ${table.byteLength} > 0
236
+ and ${table.startMilliseconds} >= 0
237
+ and ${table.durationMilliseconds} > 0
238
+ and ${table.sha256} ~ '^[0-9a-f]{64}$'
239
+ and octet_length(${table.objectKey}) between 1 and 1024
240
+ and (
241
+ (${table.attemptId} is null
242
+ and ${table.attemptStartedAt} is null
243
+ and ${table.attemptDeadlineAt} is null)
244
+ or (${table.attemptId} is not null
245
+ and ${table.attemptStartedAt} is not null
246
+ and ${table.attemptDeadlineAt} is not null)
247
+ )`,
248
+ ),
249
+ }),
250
+ );