@opengeni/db 0.28.1 → 0.28.9

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 (52) hide show
  1. package/dist/{chunk-HT5T62CC.js → chunk-P6CUHXQZ.js} +651 -206
  2. package/dist/chunk-P6CUHXQZ.js.map +1 -0
  3. package/dist/{chunk-DBS5HPEW.js → chunk-VHBFHMPC.js} +7 -1
  4. package/dist/chunk-VHBFHMPC.js.map +1 -0
  5. package/dist/environment-crypto.d.ts +8 -4
  6. package/dist/index.d.ts +265 -20
  7. package/dist/index.js +3966 -2185
  8. package/dist/index.js.map +1 -1
  9. package/dist/lossless-columns.d.ts +58 -0
  10. package/dist/lossless-json.d.ts +39 -0
  11. package/dist/memory-domain.d.ts +3 -6
  12. package/dist/persistence-errors.d.ts +7 -14
  13. package/dist/provision-roles.js +1 -1
  14. package/dist/runtime-posture.d.ts +2 -2
  15. package/dist/schema.d.ts +1419 -271
  16. package/dist/schema.js +9 -1
  17. package/dist/session-realtime-terminal.d.ts +7 -0
  18. package/dist/transcription-recordings-schema.d.ts +44 -6
  19. package/dist/transcription-recordings.d.ts +1 -1
  20. package/drizzle/0065_enrollment_credential_generation.sql +10 -0
  21. package/drizzle/0140_retained_screenshot_artifacts.sql +192 -0
  22. package/drizzle/0176_lossless_canonical_json.sql +975 -0
  23. package/drizzle/0177_session_events_workspace_turn_type_index.sql +5 -0
  24. package/drizzle/0178_permissioned_secret_reads.sql +14 -0
  25. package/drizzle/0179_slack_private_shortcut_delivery_gate.sql +85 -0
  26. package/drizzle/0180_retained_screenshot_lifecycle_fences.sql +273 -0
  27. package/drizzle/0181_connected_machine_removal.sql +52 -0
  28. package/drizzle/0182_connected_machine_remove_session_default.sql +77 -0
  29. package/package.json +4 -4
  30. package/src/database.ts +7 -1
  31. package/src/environment-crypto.ts +22 -9
  32. package/src/index.ts +4253 -1601
  33. package/src/lossless-columns.ts +35 -0
  34. package/src/lossless-json.ts +322 -0
  35. package/src/memory-domain.ts +5 -44
  36. package/src/persistence-errors.ts +29 -34
  37. package/src/runtime-posture.ts +6 -0
  38. package/src/schema.ts +260 -38
  39. package/src/session-control.ts +125 -76
  40. package/src/session-queue-commands.ts +325 -234
  41. package/src/session-realtime-context.ts +18 -5
  42. package/src/session-realtime-ledger.ts +29 -7
  43. package/src/session-realtime-mirror.ts +4 -2
  44. package/src/session-realtime-terminal.ts +89 -21
  45. package/src/session-realtime.ts +3 -2
  46. package/src/session-tool-call-settlement.ts +29 -15
  47. package/src/transcription-recordings-schema.ts +5 -2
  48. package/src/transcription-recordings.ts +70 -40
  49. package/dist/chunk-DBS5HPEW.js.map +0 -1
  50. package/dist/chunk-HT5T62CC.js.map +0 -1
  51. package/dist/event-payload-sanitizer.d.ts +0 -32
  52. package/src/event-payload-sanitizer.ts +0 -377
package/src/schema.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  bigint,
12
12
  boolean,
13
13
  check,
14
+ customType,
14
15
  foreignKey,
15
16
  index,
16
17
  integer,
@@ -21,8 +22,8 @@ import {
21
22
  timestamp,
22
23
  uniqueIndex,
23
24
  uuid,
24
- customType,
25
25
  } from "drizzle-orm/pg-core";
26
+ import { losslessCodecVersion, losslessJsonb, losslessText } from "./lossless-columns";
26
27
 
27
28
  const vector = customType<{ data: number[]; driverData: string }>({
28
29
  dataType() {
@@ -1371,7 +1372,8 @@ export const sessions = pgTable(
1371
1372
  .notNull()
1372
1373
  .references(() => workspaces.id, { onDelete: "cascade" }),
1373
1374
  status: text("status").notNull().default("queued"),
1374
- initialMessage: text("initial_message").notNull(),
1375
+ initialMessage: losslessText("initial_message").notNull(),
1376
+ initialMessageCodecVersion: losslessCodecVersion("initial_message_codec_version"),
1375
1377
  // Invisible host context frozen with the winning session create. The
1376
1378
  // initial turn copies this value so an idempotent repair can never adopt a
1377
1379
  // retrying caller's different instructions.
@@ -1825,8 +1827,10 @@ export const sessionRealtimeEntries = pgTable(
1825
1827
  // result/error to the same ordinary turn. It never denotes a child/fork
1826
1828
  // session.
1827
1829
  turnId: uuid("turn_id"),
1828
- text: text("text"),
1829
- payload: jsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
1830
+ text: losslessText("text"),
1831
+ textCodecVersion: losslessCodecVersion("text_codec_version"),
1832
+ payload: losslessJsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
1833
+ payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
1830
1834
  clientAckedAt: timestamp("client_acked_at", { withTimezone: true }),
1831
1835
  providerAckedAt: timestamp("provider_acked_at", { withTimezone: true }),
1832
1836
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
@@ -1890,14 +1894,6 @@ export const sessionRealtimeEntries = pgTable(
1890
1894
  "session_realtime_entries_delegation_item_check",
1891
1895
  sql`${table.delegationItemId} is null or octet_length(${table.delegationItemId}) between 1 and 1024`,
1892
1896
  ),
1893
- textValid: check(
1894
- "session_realtime_entries_text_check",
1895
- sql`${table.text} is null or octet_length(${table.text}) <= 131072`,
1896
- ),
1897
- payloadValid: check(
1898
- "session_realtime_entries_payload_check",
1899
- sql`octet_length(${table.payload}::text) <= 131072`,
1900
- ),
1901
1897
  turnValid: check(
1902
1898
  "session_realtime_entries_turn_check",
1903
1899
  sql`${table.turnId} is null
@@ -2171,6 +2167,145 @@ export const fileUploads = pgTable(
2171
2167
  }),
2172
2168
  );
2173
2169
 
2170
+ /** Exact pending/ready byte accounting for retained computer screenshots. */
2171
+ export const workspaceScreenshotQuotas = pgTable(
2172
+ "workspace_screenshot_quotas",
2173
+ {
2174
+ workspaceId: uuid("workspace_id")
2175
+ .primaryKey()
2176
+ .references(() => workspaces.id, { onDelete: "cascade" }),
2177
+ accountId: uuid("account_id")
2178
+ .notNull()
2179
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
2180
+ reservedBytes: bigint("reserved_bytes", { mode: "number" }).notNull().default(0),
2181
+ readyBytes: bigint("ready_bytes", { mode: "number" }).notNull().default(0),
2182
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
2183
+ },
2184
+ (table) => ({
2185
+ workspaceAccount: foreignKey({
2186
+ columns: [table.workspaceId, table.accountId],
2187
+ foreignColumns: [workspaces.id, workspaces.accountId],
2188
+ name: "workspace_screenshot_quotas_workspace_account_fk",
2189
+ }).onDelete("cascade"),
2190
+ nonnegative: check(
2191
+ "workspace_screenshot_quotas_nonnegative_chk",
2192
+ sql`${table.reservedBytes} >= 0 and ${table.readyBytes} >= 0`,
2193
+ ),
2194
+ }),
2195
+ );
2196
+
2197
+ /** Provider-neutral screenshot lifecycle; canonical bytes stay in `files` storage. */
2198
+ export const retainedScreenshotArtifacts = pgTable(
2199
+ "retained_screenshot_artifacts",
2200
+ {
2201
+ // The composite file FK is RESTRICT in migration 0176: only the retained
2202
+ // screenshot lifecycle may remove its backing file after provider cleanup.
2203
+ artifactId: uuid("artifact_id").primaryKey(),
2204
+ accountId: uuid("account_id")
2205
+ .notNull()
2206
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
2207
+ workspaceId: uuid("workspace_id")
2208
+ .notNull()
2209
+ .references(() => workspaces.id, { onDelete: "cascade" }),
2210
+ sessionId: uuid("session_id").references(() => sessions.id, { onDelete: "set null" }),
2211
+ // Turn/attempt foreign keys are installed by migrations 0140/0176.
2212
+ // Those tables are declared later in this monolithic schema module, so
2213
+ // referencing them here would create an eager initialization cycle.
2214
+ turnId: uuid("turn_id"),
2215
+ attemptId: uuid("attempt_id"),
2216
+ settlementKey: text("settlement_key").notNull(),
2217
+ toolCallId: text("tool_call_id").notNull(),
2218
+ toolOutputId: text("tool_output_id").notNull(),
2219
+ status: text("status")
2220
+ .$type<
2221
+ | "pending"
2222
+ | "reconciling"
2223
+ | "ready"
2224
+ | "cleanup_queued"
2225
+ | "cleanup_pending"
2226
+ | "failed"
2227
+ | "expired"
2228
+ | "deleted"
2229
+ >()
2230
+ .notNull()
2231
+ .default("pending"),
2232
+ quotaState: text("quota_state")
2233
+ .$type<"reserved" | "ready" | "released">()
2234
+ .notNull()
2235
+ .default("reserved"),
2236
+ mediaType: text("media_type").notNull(),
2237
+ sizeBytes: bigint("size_bytes", { mode: "number" }).notNull(),
2238
+ sha256: text("sha256").notNull(),
2239
+ width: integer("width").notNull(),
2240
+ height: integer("height").notNull(),
2241
+ retentionExpiresAt: timestamp("retention_expires_at", { withTimezone: true }).notNull(),
2242
+ readyAt: timestamp("ready_at", { withTimezone: true }),
2243
+ cleanupReason: text("cleanup_reason"),
2244
+ lastError: text("last_error"),
2245
+ maintenanceClaimId: uuid("maintenance_claim_id"),
2246
+ maintenanceClaimedAt: timestamp("maintenance_claimed_at", { withTimezone: true }),
2247
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
2248
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
2249
+ },
2250
+ (table) => ({
2251
+ settlementKey: uniqueIndex("retained_screenshot_artifacts_settlement_key_uq").on(
2252
+ table.settlementKey,
2253
+ ),
2254
+ sessionCreated: index("retained_screenshot_artifacts_session_created_idx").on(
2255
+ table.workspaceId,
2256
+ table.sessionId,
2257
+ table.createdAt,
2258
+ table.artifactId,
2259
+ ),
2260
+ readyExpiry: index("retained_screenshot_artifacts_ready_expiry_idx")
2261
+ .on(table.retentionExpiresAt, table.artifactId)
2262
+ .where(sql`${table.status} = 'ready'`),
2263
+ pendingReconcile: index("retained_screenshot_artifacts_pending_reconcile_idx")
2264
+ .on(table.updatedAt, table.artifactId)
2265
+ .where(
2266
+ sql`${table.status} in ('pending', 'reconciling', 'cleanup_queued', 'cleanup_pending')`,
2267
+ ),
2268
+ workspaceAccount: foreignKey({
2269
+ columns: [table.workspaceId, table.accountId],
2270
+ foreignColumns: [workspaces.id, workspaces.accountId],
2271
+ name: "retained_screenshot_artifacts_workspace_account_fk",
2272
+ }).onDelete("cascade"),
2273
+ workspaceFile: foreignKey({
2274
+ columns: [table.workspaceId, table.artifactId],
2275
+ foreignColumns: [files.workspaceId, files.id],
2276
+ name: "retained_screenshot_artifacts_workspace_file_fk",
2277
+ }).onDelete("restrict"),
2278
+ statusValid: check(
2279
+ "retained_screenshot_artifacts_status_chk",
2280
+ sql`${table.status} in ('pending', 'reconciling', 'ready', 'cleanup_queued', 'cleanup_pending', 'failed', 'expired', 'deleted')`,
2281
+ ),
2282
+ quotaStateValid: check(
2283
+ "retained_screenshot_artifacts_quota_state_chk",
2284
+ sql`${table.quotaState} in ('reserved', 'ready', 'released')`,
2285
+ ),
2286
+ statusQuotaValid: check(
2287
+ "retained_screenshot_artifacts_status_quota_chk",
2288
+ sql`(${table.status} in ('pending', 'reconciling') and ${table.quotaState} = 'reserved')
2289
+ or (${table.status} = 'ready' and ${table.quotaState} = 'ready')
2290
+ or (${table.status} in ('cleanup_queued', 'cleanup_pending') and ${table.quotaState} in ('reserved', 'ready'))
2291
+ or (${table.status} in ('failed', 'expired', 'deleted') and ${table.quotaState} = 'released')`,
2292
+ ),
2293
+ claimShapeValid: check(
2294
+ "retained_screenshot_artifacts_claim_shape_chk",
2295
+ sql`(${table.status} in ('reconciling', 'cleanup_pending')
2296
+ and ${table.maintenanceClaimId} is not null
2297
+ and ${table.maintenanceClaimedAt} is not null)
2298
+ or (${table.status} not in ('reconciling', 'cleanup_pending')
2299
+ and ${table.maintenanceClaimId} is null
2300
+ and ${table.maintenanceClaimedAt} is null)`,
2301
+ ),
2302
+ dimensionsValid: check(
2303
+ "retained_screenshot_artifacts_dimensions_chk",
2304
+ sql`${table.width} between 1 and 16384 and ${table.height} between 1 and 16384 and (${table.width}::bigint * ${table.height}::bigint) <= 67108864`,
2305
+ ),
2306
+ }),
2307
+ );
2308
+
2174
2309
  export const documentBases = pgTable(
2175
2310
  "document_bases",
2176
2311
  {
@@ -2380,7 +2515,8 @@ export const knowledgeMemories = pgTable(
2380
2515
  status: text("status").notNull().default("proposed"),
2381
2516
  kind: text("kind").notNull().default("semantic"),
2382
2517
  scope: text("scope").notNull().default("workspace"),
2383
- text: text("text").notNull(),
2518
+ text: losslessText("text").notNull(),
2519
+ textCodecVersion: losslessCodecVersion("text_codec_version"),
2384
2520
  sourceRefs: jsonb("source_refs").$type<unknown[]>().notNull().default([]),
2385
2521
  confidence: integer("confidence").notNull().default(50),
2386
2522
  metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default({}),
@@ -2491,7 +2627,8 @@ export const sessionTurns = pgTable(
2491
2627
  status: text("status").notNull(),
2492
2628
  source: text("source").notNull().default("user"),
2493
2629
  position: bigint("position", { mode: "number" }).notNull(),
2494
- prompt: text("prompt").notNull(),
2630
+ prompt: losslessText("prompt").notNull(),
2631
+ promptCodecVersion: losslessCodecVersion("prompt_codec_version"),
2495
2632
  // Host context for this exact turn. System-level at runtime and deliberately
2496
2633
  // separate from the visible prompt/event payload.
2497
2634
  turnInstructions: text("turn_instructions"),
@@ -3150,8 +3287,10 @@ export const sessionSystemUpdates = pgTable(
3150
3287
  classification: text("classification").notNull().default("info"),
3151
3288
  sourceId: text("source_id").notNull(),
3152
3289
  dedupeKey: text("dedupe_key").notNull(),
3153
- summary: text("summary").notNull(),
3154
- payload: jsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
3290
+ summary: losslessText("summary").notNull(),
3291
+ summaryCodecVersion: losslessCodecVersion("summary_codec_version"),
3292
+ payload: losslessJsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
3293
+ payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
3155
3294
  lineage: jsonb("lineage").$type<Record<string, unknown>>().notNull().default({}),
3156
3295
  // Private immutable authority frozen when this machine input is accepted.
3157
3296
  // Public projections intentionally omit connection ids and owner subjects.
@@ -3235,8 +3374,10 @@ export const sessionSystemUpdateOutbox = pgTable(
3235
3374
  kind: text("kind").notNull(),
3236
3375
  classification: text("classification").notNull(),
3237
3376
  sourceId: text("source_id").notNull(),
3238
- summary: text("summary").notNull(),
3239
- payload: jsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
3377
+ summary: losslessText("summary").notNull(),
3378
+ summaryCodecVersion: losslessCodecVersion("summary_codec_version"),
3379
+ payload: losslessJsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
3380
+ payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
3240
3381
  lineage: jsonb("lineage").$type<Record<string, unknown>>().notNull().default({}),
3241
3382
  // Exact private authority copied from the causal parent turn in the source
3242
3383
  // terminal transaction. Delivery retries cannot replace this snapshot.
@@ -3472,7 +3613,8 @@ export const sessionEvents = pgTable(
3472
3613
  duplicateReason: text("duplicate_reason"),
3473
3614
  sequence: integer("sequence").notNull(),
3474
3615
  type: text("type").notNull(),
3475
- payload: jsonb("payload").$type<unknown>().notNull().default({}),
3616
+ payload: losslessJsonb("payload").$type<unknown>().notNull().default({}),
3617
+ payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
3476
3618
  clientEventId: text("client_event_id"),
3477
3619
  producerId: text("producer_id"),
3478
3620
  producerSeq: integer("producer_seq"),
@@ -3507,16 +3649,15 @@ export const sessionEvents = pgTable(
3507
3649
  table.type,
3508
3650
  table.sequence,
3509
3651
  ),
3652
+ workspaceTurnType: index("session_events_workspace_turn_type_idx")
3653
+ .on(table.workspaceId, table.turnId, table.type)
3654
+ .where(sql`${table.turnId} is not null`),
3510
3655
  monitoringTail: index("session_events_workspace_session_monitoring_tail_idx")
3511
3656
  .on(table.workspaceId, table.sessionId, table.sequence)
3512
3657
  .where(
3513
3658
  sql`${table.type} not in ('agent.message.delta', 'agent.reasoning.delta', 'sandbox.command.output.delta', 'terminal.pty.output.delta')`,
3514
3659
  ),
3515
3660
  duplicateOfEvent: index("session_events_duplicate_of_event_idx").on(table.duplicateOfEventId),
3516
- payloadBytes: check(
3517
- "session_events_payload_bytes_check",
3518
- sql`octet_length(${table.payload}::text) <= 65536`,
3519
- ),
3520
3661
  typeBytes: check(
3521
3662
  "session_events_type_bytes_check",
3522
3663
  sql`octet_length(${table.type}) <= 256 and position(E'\n' in ${table.type}) = 0 and position(E'\r' in ${table.type}) = 0`,
@@ -3555,8 +3696,10 @@ export const agentRunStates = pgTable("agent_run_states", {
3555
3696
  onDelete: "set null",
3556
3697
  }),
3557
3698
  stateVersion: integer("state_version").notNull(),
3558
- serializedRunState: text("serialized_run_state").notNull(),
3559
- pendingApprovals: jsonb("pending_approvals").$type<unknown[]>().notNull().default([]),
3699
+ serializedRunState: losslessText("serialized_run_state").notNull(),
3700
+ serializedRunStateCodecVersion: losslessCodecVersion("serialized_run_state_codec_version"),
3701
+ pendingApprovals: losslessJsonb("pending_approvals").$type<unknown[]>().notNull().default([]),
3702
+ pendingApprovalsCodecVersion: losslessCodecVersion("pending_approvals_codec_version"),
3560
3703
  // Exact provider rejection marks the latest current-turn receipt only when it
3561
3704
  // was part of the rejected request. The serialized receipt remains durable;
3562
3705
  // recovery builds a temporary view without unusable opaque artifacts.
@@ -3651,8 +3794,8 @@ export const sessionHumanInputRequests = pgTable(
3651
3794
  );
3652
3795
 
3653
3796
  // Conversation truth: ordered, verbatim SDK input items (issue #35). The
3654
- // model-facing memory store — unredacted and replay-ready. session_events
3655
- // remains the redacted human/audit timeline.
3797
+ // model-facing memory store — exact and replay-ready. session_events is also
3798
+ // exact canonical OpenGeni data; transport projections must not rewrite it.
3656
3799
  export const sessionHistoryItems = pgTable(
3657
3800
  "session_history_items",
3658
3801
  {
@@ -3676,7 +3819,8 @@ export const sessionHistoryItems = pgTable(
3676
3819
  // positions; only the summary uses the half-step. `mode: "number"` maps the
3677
3820
  // postgres.js string back to a JS number so every reader stays numeric.
3678
3821
  position: numeric("position", { mode: "number" }).notNull(),
3679
- item: jsonb("item").$type<Record<string, unknown>>().notNull(),
3822
+ item: losslessJsonb("item").$type<Record<string, unknown>>().notNull(),
3823
+ itemCodecVersion: losslessCodecVersion("item_codec_version"),
3680
3824
  // Live-row flag for client-side context compaction. The read path selects
3681
3825
  // only active rows; a compaction supersedes the summarized prefix (sets this
3682
3826
  // false — never deletes, so the full transcript stays as an audit trail) and
@@ -3706,7 +3850,7 @@ export const sessionHistoryItems = pgTable(
3706
3850
 
3707
3851
  // Turn-lineage ledger for a tool call that the SDK emitted but has not yet
3708
3852
  // produced a durably reconciled result. The raw call item is model-facing truth
3709
- // (not the redacted session-event projection). The attempt/generation identify
3853
+ // (not a transport projection). The attempt/generation identify
3710
3854
  // where the call originated, but the receipt survives an approval resume into a
3711
3855
  // newer attempt of the same logical turn. Turn-ending transactions
3712
3856
  // consume these rows atomically and append a valid interrupted result so a
@@ -3732,9 +3876,11 @@ export const sessionPendingToolCalls = pgTable(
3732
3876
  attemptId: uuid("attempt_id").notNull(),
3733
3877
  callId: text("call_id").notNull(),
3734
3878
  callType: text("call_type").notNull(),
3735
- callItem: jsonb("call_item").$type<Record<string, unknown>>().notNull(),
3879
+ callItem: losslessJsonb("call_item").$type<Record<string, unknown>>().notNull(),
3880
+ callItemCodecVersion: losslessCodecVersion("call_item_codec_version"),
3736
3881
  modelToolOutputTruncationTokens: integer("model_tool_output_truncation_tokens"),
3737
- resultItem: jsonb("result_item").$type<Record<string, unknown>>(),
3882
+ resultItem: losslessJsonb("result_item").$type<Record<string, unknown>>(),
3883
+ resultItemCodecVersion: losslessCodecVersion("result_item_codec_version"),
3738
3884
  resultRecordedAt: timestamp("result_recorded_at", { withTimezone: true }),
3739
3885
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
3740
3886
  },
@@ -3773,7 +3919,8 @@ export const sandboxSessionEnvelopes = pgTable(
3773
3919
  sessionId: uuid("session_id")
3774
3920
  .notNull()
3775
3921
  .references(() => sessions.id, { onDelete: "cascade" }),
3776
- envelope: jsonb("envelope").$type<Record<string, unknown>>().notNull(),
3922
+ envelope: losslessJsonb("envelope").$type<Record<string, unknown>>().notNull(),
3923
+ envelopeCodecVersion: losslessCodecVersion("envelope_codec_version"),
3777
3924
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
3778
3925
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
3779
3926
  },
@@ -4549,7 +4696,8 @@ export const sessionRecordings = pgTable(
4549
4696
  width: integer("width").notNull(),
4550
4697
  height: integer("height").notNull(),
4551
4698
 
4552
- reason: text("reason"),
4699
+ reason: losslessText("reason"),
4700
+ reasonCodecVersion: losslessCodecVersion("reason_codec_version"),
4553
4701
 
4554
4702
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
4555
4703
  finalizedAt: timestamp("finalized_at", { withTimezone: true }),
@@ -4759,6 +4907,10 @@ export const enrollments = pgTable(
4759
4907
  desktopUnavailableReason: text("desktop_unavailable_reason"),
4760
4908
  allowScreenControl: boolean("allow_screen_control").notNull().default(false),
4761
4909
  status: text("status", { enum: enrollmentStatusValues }).notNull().default("active"),
4910
+ // Credential-family fence. Existing rows/migration-era bearers are generation
4911
+ // 1; every successful re-enrollment increments this atomically before a new
4912
+ // bearer is signed. Auth and self-revoke require an exact row/claim match.
4913
+ credentialGeneration: integer("credential_generation").notNull().default(1),
4762
4914
  os: text("os", { enum: enrollmentOsValues }).notNull().default("linux"),
4763
4915
  arch: text("arch").notNull().default("x86_64"),
4764
4916
  // Heartbeat liveness cursor. Null until the first connect.
@@ -4786,6 +4938,68 @@ export const enrollments = pgTable(
4786
4938
  }),
4787
4939
  );
4788
4940
 
4941
+ // One durable receipt per workspace-authorized connected-machine removal
4942
+ // request. The enrollment row remains the lifecycle truth; this table preserves
4943
+ // the exact idempotency key, request fingerprint, and terminal result so a lost
4944
+ // response can be replayed without re-running dependency checks or mutating a
4945
+ // different enrollment that happens to share the same display name.
4946
+ export const machineRemovalOperationOutcomeValues = [
4947
+ "removed",
4948
+ "already_removed",
4949
+ "blocked",
4950
+ ] as const;
4951
+
4952
+ export const machineRemovalOperations = pgTable(
4953
+ "machine_removal_operations",
4954
+ {
4955
+ id: uuid("id").primaryKey().defaultRandom(),
4956
+ accountId: uuid("account_id")
4957
+ .notNull()
4958
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
4959
+ workspaceId: uuid("workspace_id")
4960
+ .notNull()
4961
+ .references(() => workspaces.id, { onDelete: "cascade" }),
4962
+ enrollmentId: uuid("enrollment_id")
4963
+ .notNull()
4964
+ .references(() => enrollments.id, { onDelete: "restrict" }),
4965
+ operationKey: text("operation_key").notNull(),
4966
+ requestFingerprint: text("request_fingerprint").notNull(),
4967
+ outcome: text("outcome", { enum: machineRemovalOperationOutcomeValues }).notNull(),
4968
+ result: jsonb("result").$type<Record<string, unknown>>().notNull().default({}),
4969
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
4970
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
4971
+ },
4972
+ (table) => ({
4973
+ workspaceAccount: foreignKey({
4974
+ name: "machine_removal_operations_workspace_account_fk",
4975
+ columns: [table.workspaceId, table.accountId],
4976
+ foreignColumns: [workspaces.id, workspaces.accountId],
4977
+ }).onDelete("cascade"),
4978
+ workspaceOperation: uniqueIndex("machine_removal_operations_workspace_operation_uq").on(
4979
+ table.workspaceId,
4980
+ table.operationKey,
4981
+ ),
4982
+ workspaceIdentity: uniqueIndex("machine_removal_operations_workspace_id_uq").on(
4983
+ table.workspaceId,
4984
+ table.id,
4985
+ ),
4986
+ enrollmentTimeline: index("machine_removal_operations_enrollment_created_idx").on(
4987
+ table.workspaceId,
4988
+ table.enrollmentId,
4989
+ table.createdAt,
4990
+ ),
4991
+ requestFingerprintValid: check(
4992
+ "machine_removal_operations_request_fingerprint_chk",
4993
+ sql`${table.requestFingerprint} ~ '^[0-9a-f]{64}$'`,
4994
+ ),
4995
+ operationKeyValid: check(
4996
+ "machine_removal_operations_operation_key_chk",
4997
+ sql`length(btrim(${table.operationKey})) between 1 and 200
4998
+ and ${table.operationKey} = btrim(${table.operationKey})`,
4999
+ ),
5000
+ }),
5001
+ );
5002
+
4789
5003
  // The OAuth 2.0 device-authorization (RFC 8628) PENDING request (M5, migration
4790
5004
  // 0025 / enrollment + §18 LOUD consent). An agent's `enroll` starts
4791
5005
  // a flow (POST /enrollments/device/start) → one short-TTL, single-use row keyed by
@@ -5404,7 +5618,8 @@ export const hostExportConfig = pgTable(
5404
5618
  /**
5405
5619
  * Transactional delivery buffer. It intentionally has no tenant/source FKs:
5406
5620
  * a workspace deletion must not erase an already-committed, unacknowledged
5407
- * host fact. Payloads are sanitized/bounded before they reach this table.
5621
+ * host fact. Session-event payloads retain their exact storage bytes plus
5622
+ * explicit codec truth; usage payloads remain ordinary JSON.
5408
5623
  */
5409
5624
  export const hostExportOutbox = pgTable(
5410
5625
  "host_export_outbox",
@@ -5434,6 +5649,7 @@ export const hostExportOutbox = pgTable(
5434
5649
  .default({}),
5435
5650
  origin: text("origin"),
5436
5651
  payload: jsonb("payload").$type<unknown>().notNull(),
5652
+ payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
5437
5653
  envelopeBytes: integer("envelope_bytes").notNull(),
5438
5654
  occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
5439
5655
  sourceRecordedAt: timestamp("source_recorded_at", { withTimezone: true }).notNull(),
@@ -5509,6 +5725,7 @@ export const hostExportConsumers = pgTable(
5509
5725
  consecutiveFailures: integer("consecutive_failures").notNull().default(0),
5510
5726
  nextAttemptAt: timestamp("next_attempt_at", { withTimezone: true }).notNull().defaultNow(),
5511
5727
  lastError: text("last_error"),
5728
+ lastErrorCodecVersion: losslessCodecVersion("last_error_codec_version"),
5512
5729
  lastErrorAt: timestamp("last_error_at", { withTimezone: true }),
5513
5730
  blockedAt: timestamp("blocked_at", { withTimezone: true }),
5514
5731
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
@@ -5542,7 +5759,9 @@ export const hostExportDeadLetters = pgTable(
5542
5759
  exportCursor: bigint("export_cursor", { mode: "bigint" }).notNull(),
5543
5760
  sourceId: uuid("source_id").notNull(),
5544
5761
  reason: text("reason").notNull(),
5545
- envelope: jsonb("envelope").$type<Record<string, unknown>>().notNull(),
5762
+ envelope: losslessJsonb("envelope").$type<Record<string, unknown>>().notNull(),
5763
+ envelopeCodecVersion: losslessCodecVersion("envelope_codec_version"),
5764
+ eventPayloadCodecVersion: losslessCodecVersion("event_payload_codec_version"),
5546
5765
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5547
5766
  },
5548
5767
  (table) => ({
@@ -5643,7 +5862,8 @@ export const auditEvents = pgTable(
5643
5862
  action: text("action").notNull(),
5644
5863
  targetType: text("target_type"),
5645
5864
  targetId: text("target_id"),
5646
- metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default({}),
5865
+ metadata: losslessJsonb("metadata").$type<Record<string, unknown>>().notNull().default({}),
5866
+ metadataCodecVersion: losslessCodecVersion("metadata_codec_version"),
5647
5867
  occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull().defaultNow(),
5648
5868
  },
5649
5869
  (table) => ({
@@ -6011,11 +6231,13 @@ export const rigChanges = pgTable(
6011
6231
  }),
6012
6232
  // 'setup_append' | 'definition_edit' (CHECK in migration 0047).
6013
6233
  kind: text("kind").notNull(),
6014
- payload: jsonb("payload").$type<Record<string, unknown>>().notNull(),
6234
+ payload: losslessJsonb("payload").$type<Record<string, unknown>>().notNull(),
6235
+ payloadCodecVersion: losslessCodecVersion("payload_codec_version"),
6015
6236
  // 'proposed' | 'verifying' | 'merged' | 'rejected' | 'failed' (CHECK in 0047).
6016
6237
  status: text("status").notNull().default("proposed"),
6017
6238
  proposedBy: text("proposed_by"),
6018
- verification: jsonb("verification").$type<Record<string, unknown>>(),
6239
+ verification: losslessJsonb("verification").$type<Record<string, unknown>>(),
6240
+ verificationCodecVersion: losslessCodecVersion("verification_codec_version"),
6019
6241
  resultVersionId: uuid("result_version_id").references(() => rigVersions.id, {
6020
6242
  onDelete: "set null",
6021
6243
  }),