@opengeni/db 0.7.3 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-B22X3IEZ.js → chunk-4LG5NBTC.js} +512 -32
- package/dist/chunk-4LG5NBTC.js.map +1 -0
- package/dist/{chunk-YFQ7SGE4.js → chunk-BMFDXFPA.js} +23 -1
- package/dist/chunk-BMFDXFPA.js.map +1 -0
- package/dist/chunk-KW526IJA.js +127 -0
- package/dist/chunk-KW526IJA.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6068 -2090
- package/dist/index.js.map +1 -1
- package/dist/migrate.d.ts +29 -4
- package/dist/migrate.js +3 -1
- package/dist/provision-roles.d.ts +721 -46
- package/dist/provision-roles.js +1 -1
- package/dist/{schema-BN5mB9xZ.d.ts → schema-CdPGTHlD.d.ts} +2118 -119
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +17 -1
- package/drizzle/0064_rotation_strategy_sharded_backfill.sql +15 -0
- package/drizzle/0065_session_attempt_quiescence.sql +26 -0
- package/drizzle/0066_session_interruption_attempt_lookup.sql +4 -0
- package/drizzle/0067_session_event_payload_bounds.sql +209 -0
- package/drizzle/0068_workspace_control_event_bounds.sql +134 -0
- package/drizzle/0069_session_event_history_backfill.sql +32 -0
- package/drizzle/0070_session_event_type_sequence_lookup.sql +4 -0
- package/drizzle/0071_session_event_monitoring_tail.sql +10 -0
- package/drizzle/0072_sessions_workspace_created_id_idx.sql +4 -0
- package/drizzle/0073_sessions_workspace_updated_id_idx.sql +4 -0
- package/drizzle/0074_session_activity_revisions.sql +72 -0
- package/drizzle/0075_sessions_workspace_activity_revision_idx.sql +4 -0
- package/drizzle/0076_session_workflow_wake_acl.sql +13 -0
- package/drizzle/0077_session_attempt_latest_lookup.sql +4 -0
- package/drizzle/0094_quarantine_credential_bearing_catalog_urls.sql +51 -0
- package/drizzle/0095_github_existing_installations.sql +84 -0
- package/drizzle/0096_session_turn_initiators.sql +97 -0
- package/drizzle/0097_host_export_outbox.sql +1220 -0
- package/drizzle/0098_usage_events_workspace_session_idx.sql +4 -0
- package/drizzle/0099_session_human_input_attempt_owner_index.sql +6 -0
- package/drizzle/0100_session_human_input_requests.sql +89 -0
- package/drizzle/0101_session_mcp_connection_refs.sql +30 -0
- package/drizzle/0102_session_command_receipt_service_actor.sql +16 -0
- package/drizzle/0103_host_export_root_session.sql +166 -0
- package/drizzle/0104_host_export_root_session_backfill.sql +27 -0
- package/drizzle/0105_session_turn_instructions.sql +9 -0
- package/package.json +4 -4
- package/src/connection-token-resolver.ts +287 -1
- package/src/event-payload-sanitizer.ts +57 -19
- package/src/index.ts +5377 -1125
- package/src/memory-domain.ts +1 -1
- package/src/migrate.ts +86 -23
- package/src/persistence-errors.ts +252 -0
- package/src/provision-roles.ts +42 -0
- package/src/schema.ts +552 -34
- package/src/session-control.ts +518 -38
- package/src/session-queue-commands.ts +308 -57
- package/src/session-tool-call-settlement.ts +58 -7
- package/src/turn-initiator.ts +155 -0
- package/dist/chunk-7LDU7F5P.js +0 -80
- package/dist/chunk-7LDU7F5P.js.map +0 -1
- package/dist/chunk-B22X3IEZ.js.map +0 -1
- package/dist/chunk-YFQ7SGE4.js.map +0 -1
package/src/schema.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { McpServerConnectionRef } from "@opengeni/contracts";
|
|
1
2
|
import { sql } from "drizzle-orm";
|
|
3
|
+
import type { HumanInputQuestion, HumanInputResponse } from "@opengeni/contracts";
|
|
2
4
|
import {
|
|
3
5
|
bigint,
|
|
4
6
|
boolean,
|
|
@@ -88,7 +90,9 @@ export const workspaceInferenceControls = pgTable(
|
|
|
88
90
|
accountId: uuid("account_id").notNull(),
|
|
89
91
|
revision: bigint("revision", { mode: "number" }).notNull().default(0),
|
|
90
92
|
workspaceState: text("workspace_state").notNull().default("active"),
|
|
91
|
-
workspacePauseRevision: bigint("workspace_pause_revision", {
|
|
93
|
+
workspacePauseRevision: bigint("workspace_pause_revision", {
|
|
94
|
+
mode: "number",
|
|
95
|
+
}),
|
|
92
96
|
reason: text("reason"),
|
|
93
97
|
changedBy: text("changed_by"),
|
|
94
98
|
changedAt: timestamp("changed_at", { withTimezone: true }),
|
|
@@ -121,6 +125,30 @@ export const workspaceInferenceControls = pgTable(
|
|
|
121
125
|
}),
|
|
122
126
|
);
|
|
123
127
|
|
|
128
|
+
// One transactionally allocated activity clock per workspace. An updated-order
|
|
129
|
+
// first page takes a SHARE lock on this row after the workspace-control lock;
|
|
130
|
+
// semantic writers allocate after UUID-ordered session locks. Plain MVCC page
|
|
131
|
+
// reads never lock session rows, so that ordering cannot form a cycle.
|
|
132
|
+
export const workspaceSessionActivityRevisions = pgTable(
|
|
133
|
+
"workspace_session_activity_revisions",
|
|
134
|
+
{
|
|
135
|
+
workspaceId: uuid("workspace_id").primaryKey(),
|
|
136
|
+
accountId: uuid("account_id").notNull(),
|
|
137
|
+
revision: bigint("revision", { mode: "number" }).notNull().default(0),
|
|
138
|
+
},
|
|
139
|
+
(table) => ({
|
|
140
|
+
workspaceAccount: foreignKey({
|
|
141
|
+
name: "workspace_session_activity_revisions_workspace_account_fk",
|
|
142
|
+
columns: [table.workspaceId, table.accountId],
|
|
143
|
+
foreignColumns: [workspaces.id, workspaces.accountId],
|
|
144
|
+
}).onDelete("cascade"),
|
|
145
|
+
revisionValid: check(
|
|
146
|
+
"workspace_session_activity_revisions_revision_check",
|
|
147
|
+
sql`${table.revision} >= 0`,
|
|
148
|
+
),
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
|
|
124
152
|
export const workspaceMemberships = pgTable(
|
|
125
153
|
"workspace_memberships",
|
|
126
154
|
{
|
|
@@ -155,7 +183,9 @@ export const apiKeys = pgTable(
|
|
|
155
183
|
accountId: uuid("account_id")
|
|
156
184
|
.notNull()
|
|
157
185
|
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
158
|
-
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
186
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
187
|
+
onDelete: "cascade",
|
|
188
|
+
}),
|
|
159
189
|
name: text("name").notNull(),
|
|
160
190
|
prefix: text("prefix").notNull(),
|
|
161
191
|
keyHash: text("key_hash").notNull(),
|
|
@@ -280,13 +310,15 @@ export const codexSubscriptionCredentials = pgTable(
|
|
|
280
310
|
// excluded). The writer only ever sets a NON-empty set, so a flaky empty turn
|
|
281
311
|
// can't false-drop coverage. connectorsCheckedAt is the freshness clock.
|
|
282
312
|
connectorNamespaces: text("connector_namespaces").array(),
|
|
283
|
-
connectorsCheckedAt: timestamp("connectors_checked_at", {
|
|
313
|
+
connectorsCheckedAt: timestamp("connectors_checked_at", {
|
|
314
|
+
withTimezone: true,
|
|
315
|
+
}),
|
|
284
316
|
// Workspace-local, server-held fairness cursor. Provider usage headers are
|
|
285
317
|
// capacity hints, never the sole allocator: live lease count is ranked first
|
|
286
318
|
// and this cursor deterministically breaks equal-load/equal-capacity ties.
|
|
287
319
|
// This flag controls NEW automatic allocations only. Credential health,
|
|
288
320
|
// refresh, encrypted material, and already-frozen/in-flight turns are
|
|
289
|
-
// intentionally independent.
|
|
321
|
+
// intentionally independent. account eligibility policy owns toggle OCC/audit and product UI.
|
|
290
322
|
allocatorEnabled: boolean("allocator_enabled").notNull().default(true),
|
|
291
323
|
selectionCount: integer("selection_count").notNull().default(0),
|
|
292
324
|
lastSelectedAt: timestamp("last_selected_at", { withTimezone: true }),
|
|
@@ -432,7 +464,7 @@ export const codexRotationSettings = pgTable(
|
|
|
432
464
|
// code reads this bit; old binaries safely ignore it and keep the legacy
|
|
433
465
|
// pin/rotation policy.
|
|
434
466
|
leaseRotationEnabled: boolean("lease_rotation_enabled").notNull().default(false),
|
|
435
|
-
rotationStrategy: text("rotation_strategy").notNull().default("
|
|
467
|
+
rotationStrategy: text("rotation_strategy").notNull().default("sharded"), // sharded-rotation policy: legacy residue; behavior is always sharded
|
|
436
468
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
437
469
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
438
470
|
},
|
|
@@ -527,6 +559,10 @@ export const sessions = pgTable(
|
|
|
527
559
|
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
528
560
|
status: text("status").notNull().default("queued"),
|
|
529
561
|
initialMessage: text("initial_message").notNull(),
|
|
562
|
+
// Invisible host context frozen with the winning session create. The
|
|
563
|
+
// initial turn copies this value so an idempotent repair can never adopt a
|
|
564
|
+
// retrying caller's different instructions.
|
|
565
|
+
initialTurnInstructions: text("initial_turn_instructions"),
|
|
530
566
|
title: text("title"),
|
|
531
567
|
titleSource: text("title_source"),
|
|
532
568
|
// Per-session agent persona/system instructions supplied at create (the
|
|
@@ -539,6 +575,14 @@ export const sessions = pgTable(
|
|
|
539
575
|
resources: jsonb("resources").$type<unknown[]>().notNull().default([]),
|
|
540
576
|
tools: jsonb("tools").$type<unknown[]>().notNull().default([]),
|
|
541
577
|
metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default({}),
|
|
578
|
+
// Frozen creator fact. This is used for creation attribution and for the
|
|
579
|
+
// idempotent first-turn repair only; later turns capture their own actor.
|
|
580
|
+
createdByKind: text("created_by_kind").notNull().default("service"),
|
|
581
|
+
createdBySubjectId: text("created_by_subject_id").notNull().default("unattributed-legacy"),
|
|
582
|
+
createdByContext: jsonb("created_by_context")
|
|
583
|
+
.$type<Record<string, unknown>>()
|
|
584
|
+
.notNull()
|
|
585
|
+
.default({ backfill: true }),
|
|
542
586
|
model: text("model").notNull(),
|
|
543
587
|
sandboxBackend: text("sandbox_backend").notNull(),
|
|
544
588
|
// The OS this session's box runs. Defaults to 'linux' (today's only OS, so
|
|
@@ -554,8 +598,8 @@ export const sessions = pgTable(
|
|
|
554
598
|
// The app generates the uuid and uses it for both id and sandbox_group_id in
|
|
555
599
|
// one insert — it cannot SQL-default to id (id is defaultRandom()).
|
|
556
600
|
sandboxGroupId: uuid("sandbox_group_id").notNull(),
|
|
557
|
-
// The first-class swappable-sandbox POINTER (bring-your-own-compute M2
|
|
558
|
-
//
|
|
601
|
+
// The first-class swappable-sandbox POINTER (bring-your-own-compute M2).
|
|
602
|
+
// NULL == "use the session's own group sandbox" (the
|
|
559
603
|
// backward-compat default — every existing/new row is a behavior-preserving
|
|
560
604
|
// no-op). The routing proxy re-reads (active_sandbox_id, active_epoch) PER
|
|
561
605
|
// TOOL CALL to make a Modal<->selfhosted hot-swap seamless. The FK
|
|
@@ -620,11 +664,15 @@ export const sessions = pgTable(
|
|
|
620
664
|
queueTailPosition: bigint("queue_tail_position", { mode: "number" }).notNull().default(0),
|
|
621
665
|
directControlState: text("direct_control_state").notNull().default("active"),
|
|
622
666
|
directPauseRevision: bigint("direct_pause_revision", { mode: "number" }),
|
|
623
|
-
subtreeRunOverrideRevision: bigint("subtree_run_override_revision", {
|
|
667
|
+
subtreeRunOverrideRevision: bigint("subtree_run_override_revision", {
|
|
668
|
+
mode: "number",
|
|
669
|
+
}),
|
|
624
670
|
controlVersion: bigint("control_version", { mode: "number" }).notNull().default(0),
|
|
625
671
|
directControlReason: text("direct_control_reason"),
|
|
626
672
|
directControlChangedBy: text("direct_control_changed_by"),
|
|
627
|
-
directControlChangedAt: timestamp("direct_control_changed_at", {
|
|
673
|
+
directControlChangedAt: timestamp("direct_control_changed_at", {
|
|
674
|
+
withTimezone: true,
|
|
675
|
+
}),
|
|
628
676
|
lastSequence: integer("last_sequence").notNull().default(0),
|
|
629
677
|
// The session's PINNED Codex account (manual override from the in-session
|
|
630
678
|
// switcher). NULL ⇒ follow the workspace active pointer. FK declared in the
|
|
@@ -645,6 +693,10 @@ export const sessions = pgTable(
|
|
|
645
693
|
codexPinSource: text("codex_pin_source"),
|
|
646
694
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
647
695
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
696
|
+
// Assigned by the database trigger whenever canonical updated_at activity
|
|
697
|
+
// advances. Legacy rows remain zero until touched; raw delta-only writers
|
|
698
|
+
// intentionally omit updated_at and therefore do not allocate revisions.
|
|
699
|
+
activityRevision: bigint("activity_revision", { mode: "number" }).notNull().default(0),
|
|
648
700
|
},
|
|
649
701
|
(table) => ({
|
|
650
702
|
workspaceIdentity: uniqueIndex("sessions_workspace_id_idx").on(table.workspaceId, table.id),
|
|
@@ -652,6 +704,25 @@ export const sessions = pgTable(
|
|
|
652
704
|
table.workspaceId,
|
|
653
705
|
table.createdAt,
|
|
654
706
|
),
|
|
707
|
+
// Model-facing monitoring pages use exact (timestamp,id) keysets. Keep the
|
|
708
|
+
// older prefix index during rolling deploys; these composites serve both
|
|
709
|
+
// deterministic traversal and updatedAfter change scans.
|
|
710
|
+
workspaceCreatedId: index("sessions_workspace_created_id_idx").on(
|
|
711
|
+
table.workspaceId,
|
|
712
|
+
table.createdAt.desc(),
|
|
713
|
+
table.id.desc(),
|
|
714
|
+
),
|
|
715
|
+
workspaceUpdatedId: index("sessions_workspace_updated_id_idx").on(
|
|
716
|
+
table.workspaceId,
|
|
717
|
+
table.updatedAt.desc(),
|
|
718
|
+
table.id.desc(),
|
|
719
|
+
),
|
|
720
|
+
workspaceActivityRevision: index("sessions_workspace_activity_revision_idx").on(
|
|
721
|
+
table.workspaceId,
|
|
722
|
+
table.activityRevision.desc(),
|
|
723
|
+
table.updatedAt.desc(),
|
|
724
|
+
table.id.desc(),
|
|
725
|
+
),
|
|
655
726
|
variableSet: index("sessions_variable_set_idx").on(table.workspaceId, table.variableSetId),
|
|
656
727
|
parent: index("sessions_parent_idx").on(table.workspaceId, table.parentSessionId),
|
|
657
728
|
// Routing index: resolve session_id -> sandbox_group_id at every lease entry
|
|
@@ -801,6 +872,9 @@ export const sessionMcpServers = pgTable(
|
|
|
801
872
|
// Human-approval policy: `true` = every tool requires approval, a string[] of
|
|
802
873
|
// UNPREFIXED tool names = only those require it, null/absent = auto-run.
|
|
803
874
|
requireApproval: jsonb("require_approval").$type<boolean | string[]>(),
|
|
875
|
+
// Non-secret pointer resolved at request time by the standalone broker or
|
|
876
|
+
// an embedding host. Unlike headersEncrypted, this is safe to project.
|
|
877
|
+
connectionRef: jsonb("connection_ref").$type<McpServerConnectionRef>(),
|
|
804
878
|
// Map of header name -> AES-GCM ciphertext. Values are decrypted only by the
|
|
805
879
|
// worker's run-preparation path and never returned by API helpers.
|
|
806
880
|
headersEncrypted: jsonb("headers_encrypted")
|
|
@@ -1077,6 +1151,9 @@ export const sessionTurns = pgTable(
|
|
|
1077
1151
|
source: text("source").notNull().default("user"),
|
|
1078
1152
|
position: bigint("position", { mode: "number" }).notNull(),
|
|
1079
1153
|
prompt: text("prompt").notNull(),
|
|
1154
|
+
// Host context for this exact turn. System-level at runtime and deliberately
|
|
1155
|
+
// separate from the visible prompt/event payload.
|
|
1156
|
+
turnInstructions: text("turn_instructions"),
|
|
1080
1157
|
resources: jsonb("resources").$type<unknown[]>().notNull().default([]),
|
|
1081
1158
|
tools: jsonb("tools").$type<unknown[]>().notNull().default([]),
|
|
1082
1159
|
model: text("model").notNull(),
|
|
@@ -1094,6 +1171,14 @@ export const sessionTurns = pgTable(
|
|
|
1094
1171
|
// the SQL constraint is therefore DEFERRABLE INITIALLY DEFERRED.
|
|
1095
1172
|
activeAttemptId: uuid("active_attempt_id"),
|
|
1096
1173
|
lineage: jsonb("lineage").$type<Record<string, unknown>>().notNull().default({}),
|
|
1174
|
+
// Required immutable authority captured when the turn is accepted. These
|
|
1175
|
+
// columns are deliberately outside mutable metadata/lineage.
|
|
1176
|
+
initiatorKind: text("initiator_kind").notNull().default("service"),
|
|
1177
|
+
initiatorSubjectId: text("initiator_subject_id").notNull().default("unattributed-legacy"),
|
|
1178
|
+
initiatorContext: jsonb("initiator_context")
|
|
1179
|
+
.$type<Record<string, unknown>>()
|
|
1180
|
+
.notNull()
|
|
1181
|
+
.default({ backfill: true }),
|
|
1097
1182
|
cancelledBy: text("cancelled_by"),
|
|
1098
1183
|
cancelReason: text("cancel_reason"),
|
|
1099
1184
|
// Atomic per-turn toolspace call budget counter (migration 0043). Incremented
|
|
@@ -1142,10 +1227,18 @@ export const sessionTurnAttempts = pgTable(
|
|
|
1142
1227
|
workerId: text("worker_id"),
|
|
1143
1228
|
leaseId: text("lease_id"),
|
|
1144
1229
|
leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
|
|
1145
|
-
verifiedControlRevision: bigint("verified_control_revision", {
|
|
1230
|
+
verifiedControlRevision: bigint("verified_control_revision", {
|
|
1231
|
+
mode: "number",
|
|
1232
|
+
}).notNull(),
|
|
1146
1233
|
startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1147
1234
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1148
1235
|
closedAt: timestamp("closed_at", { withTimezone: true }),
|
|
1236
|
+
// The cancelled activity writes this after losing inference, user-visible
|
|
1237
|
+
// output, and workspace-persistence authority. Fenced/idempotent cleanup
|
|
1238
|
+
// and telemetry may still finish. Temporal activity cancellation or
|
|
1239
|
+
// terminalization is transport state only; queue admission and clients use
|
|
1240
|
+
// this durable receipt as the physical-quiescence authority.
|
|
1241
|
+
quiescedAt: timestamp("quiesced_at", { withTimezone: true }),
|
|
1149
1242
|
},
|
|
1150
1243
|
(table) => ({
|
|
1151
1244
|
workspaceAccount: foreignKey({
|
|
@@ -1167,12 +1260,25 @@ export const sessionTurnAttempts = pgTable(
|
|
|
1167
1260
|
table.workspaceId,
|
|
1168
1261
|
table.id,
|
|
1169
1262
|
),
|
|
1263
|
+
ownershipIdentity: uniqueIndex("session_turn_attempts_human_input_owner_uq").on(
|
|
1264
|
+
table.accountId,
|
|
1265
|
+
table.workspaceId,
|
|
1266
|
+
table.sessionId,
|
|
1267
|
+
table.turnId,
|
|
1268
|
+
table.id,
|
|
1269
|
+
),
|
|
1170
1270
|
liveTurn: uniqueIndex("session_turn_attempts_live_turn_uq")
|
|
1171
1271
|
.on(table.workspaceId, table.turnId)
|
|
1172
1272
|
.where(sql`${table.state} in ('claimed', 'running')`),
|
|
1173
1273
|
liveSession: uniqueIndex("session_turn_attempts_live_session_uq")
|
|
1174
1274
|
.on(table.workspaceId, table.sessionId)
|
|
1175
1275
|
.where(sql`${table.state} in ('claimed', 'running')`),
|
|
1276
|
+
latestSessionAttempt: index("session_turn_attempts_latest_session_idx").on(
|
|
1277
|
+
table.workspaceId,
|
|
1278
|
+
table.sessionId,
|
|
1279
|
+
table.startedAt.desc(),
|
|
1280
|
+
table.id.desc(),
|
|
1281
|
+
),
|
|
1176
1282
|
dispatch: uniqueIndex("session_turn_attempts_dispatch_uq").on(
|
|
1177
1283
|
table.workspaceId,
|
|
1178
1284
|
table.temporalWorkflowRunId,
|
|
@@ -1217,7 +1323,9 @@ export const sessionCommandReceipts = pgTable(
|
|
|
1217
1323
|
targetTurnId: uuid("target_turn_id"),
|
|
1218
1324
|
operationKey: text("operation_key").notNull(),
|
|
1219
1325
|
canonicalRequestHash: text("canonical_request_hash").notNull(),
|
|
1220
|
-
appliedControlRevision: bigint("applied_control_revision", {
|
|
1326
|
+
appliedControlRevision: bigint("applied_control_revision", {
|
|
1327
|
+
mode: "number",
|
|
1328
|
+
}),
|
|
1221
1329
|
appliedQueueVersion: integer("applied_queue_version"),
|
|
1222
1330
|
appliedTurnVersion: integer("applied_turn_version"),
|
|
1223
1331
|
appliedDraftRevision: bigint("applied_draft_revision", { mode: "number" }),
|
|
@@ -1262,7 +1370,7 @@ export const sessionCommandReceipts = pgTable(
|
|
|
1262
1370
|
and ${table.actorAttemptId} is not null
|
|
1263
1371
|
and ${table.actorSubjectId} is null
|
|
1264
1372
|
) or (
|
|
1265
|
-
${table.actorType} in ('human', 'operator')
|
|
1373
|
+
${table.actorType} in ('human', 'operator', 'service')
|
|
1266
1374
|
and ${table.actorSubjectId} is not null
|
|
1267
1375
|
and ${table.actorAttemptId} is null
|
|
1268
1376
|
)`,
|
|
@@ -1286,7 +1394,11 @@ export const workspaceControlEvents = pgTable(
|
|
|
1286
1394
|
action: text("action").notNull(),
|
|
1287
1395
|
automatic: boolean("automatic").notNull().default(false),
|
|
1288
1396
|
reason: text("reason"),
|
|
1397
|
+
reasonOriginalBytes: integer("reason_original_bytes"),
|
|
1289
1398
|
actor: text("actor").notNull(),
|
|
1399
|
+
// Null is the rolling-upgrade shape for untouched, already-bounded legacy
|
|
1400
|
+
// rows. New writes and rewritten poison rows carry exact source byte facts.
|
|
1401
|
+
actorOriginalBytes: integer("actor_original_bytes"),
|
|
1290
1402
|
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1291
1403
|
},
|
|
1292
1404
|
(table) => ({
|
|
@@ -1628,7 +1740,7 @@ export const sessionGoals = pgTable(
|
|
|
1628
1740
|
}),
|
|
1629
1741
|
);
|
|
1630
1742
|
|
|
1631
|
-
//
|
|
1743
|
+
// credential allocator: one durable, coalescing capacity waiter per session. The row is both
|
|
1632
1744
|
// the wait state and the commit->signal outbox: capacity mutations increment
|
|
1633
1745
|
// wakeRevision in the SAME transaction as the mutation, while the session
|
|
1634
1746
|
// workflow advances observedWakeRevision only after it has re-evaluated the
|
|
@@ -1638,7 +1750,7 @@ export const sessionGoals = pgTable(
|
|
|
1638
1750
|
// The session/goal/turn foreign keys are declared in migration 0053 so the
|
|
1639
1751
|
// table keeps the same composite workspace-integrity posture as credential
|
|
1640
1752
|
// leases. Control is evaluated independently at admission and never changes a
|
|
1641
|
-
// capacity waiter's identity.
|
|
1753
|
+
// capacity waiter's identity. policy filter supplies policyHash when accepted-turn
|
|
1642
1754
|
// pool routing lands.
|
|
1643
1755
|
export const codexCapacityWaiters = pgTable(
|
|
1644
1756
|
"codex_capacity_waiters",
|
|
@@ -1744,6 +1856,41 @@ export const sessionEvents = pgTable(
|
|
|
1744
1856
|
table.sessionId,
|
|
1745
1857
|
table.createdAt,
|
|
1746
1858
|
),
|
|
1859
|
+
sessionTypeSequence: index("session_events_workspace_session_type_sequence_idx").on(
|
|
1860
|
+
table.workspaceId,
|
|
1861
|
+
table.sessionId,
|
|
1862
|
+
table.type,
|
|
1863
|
+
table.sequence,
|
|
1864
|
+
),
|
|
1865
|
+
monitoringTail: index("session_events_workspace_session_monitoring_tail_idx")
|
|
1866
|
+
.on(table.workspaceId, table.sessionId, table.sequence)
|
|
1867
|
+
.where(
|
|
1868
|
+
sql`${table.type} not in ('agent.message.delta', 'agent.reasoning.delta', 'sandbox.command.output.delta', 'terminal.pty.output.delta')`,
|
|
1869
|
+
),
|
|
1870
|
+
payloadBytes: check(
|
|
1871
|
+
"session_events_payload_bytes_check",
|
|
1872
|
+
sql`octet_length(${table.payload}::text) <= 65536`,
|
|
1873
|
+
),
|
|
1874
|
+
typeBytes: check(
|
|
1875
|
+
"session_events_type_bytes_check",
|
|
1876
|
+
sql`octet_length(${table.type}) <= 256 and position(E'\n' in ${table.type}) = 0 and position(E'\r' in ${table.type}) = 0`,
|
|
1877
|
+
),
|
|
1878
|
+
clientEventIdBytes: check(
|
|
1879
|
+
"session_events_client_event_id_bytes_check",
|
|
1880
|
+
sql`${table.clientEventId} is null or octet_length(${table.clientEventId}) <= 1024`,
|
|
1881
|
+
),
|
|
1882
|
+
producerIdBytes: check(
|
|
1883
|
+
"session_events_producer_id_bytes_check",
|
|
1884
|
+
sql`${table.producerId} is null or octet_length(${table.producerId}) <= 1024`,
|
|
1885
|
+
),
|
|
1886
|
+
turnAssociationBytes: check(
|
|
1887
|
+
"session_events_turn_association_bytes_check",
|
|
1888
|
+
sql`${table.turnAssociation} is null or octet_length(${table.turnAssociation}) <= 64`,
|
|
1889
|
+
),
|
|
1890
|
+
duplicateReasonBytes: check(
|
|
1891
|
+
"session_events_duplicate_reason_bytes_check",
|
|
1892
|
+
sql`${table.duplicateReason} is null or octet_length(${table.duplicateReason}) <= 4096`,
|
|
1893
|
+
),
|
|
1747
1894
|
}),
|
|
1748
1895
|
);
|
|
1749
1896
|
|
|
@@ -1758,7 +1905,9 @@ export const agentRunStates = pgTable("agent_run_states", {
|
|
|
1758
1905
|
sessionId: uuid("session_id")
|
|
1759
1906
|
.notNull()
|
|
1760
1907
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
1761
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
1908
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
1909
|
+
onDelete: "set null",
|
|
1910
|
+
}),
|
|
1762
1911
|
stateVersion: integer("state_version").notNull(),
|
|
1763
1912
|
serializedRunState: text("serialized_run_state").notNull(),
|
|
1764
1913
|
pendingApprovals: jsonb("pending_approvals").$type<unknown[]>().notNull().default([]),
|
|
@@ -1781,6 +1930,88 @@ export const agentRunStates = pgTable("agent_run_states", {
|
|
|
1781
1930
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1782
1931
|
});
|
|
1783
1932
|
|
|
1933
|
+
// Durable structured human-input requests. The creation attempt is immutable
|
|
1934
|
+
// provenance; legitimate resume attempts are newer owners of the SAME logical
|
|
1935
|
+
// turn. Settlement therefore fences on the active session/turn plus the
|
|
1936
|
+
// request's turn generation, never on creationAttemptId.
|
|
1937
|
+
export const sessionHumanInputRequests = pgTable(
|
|
1938
|
+
"session_human_input_requests",
|
|
1939
|
+
{
|
|
1940
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
1941
|
+
accountId: uuid("account_id").notNull(),
|
|
1942
|
+
workspaceId: uuid("workspace_id").notNull(),
|
|
1943
|
+
sessionId: uuid("session_id").notNull(),
|
|
1944
|
+
turnId: uuid("turn_id").notNull(),
|
|
1945
|
+
turnGeneration: integer("turn_generation").notNull(),
|
|
1946
|
+
creationAttemptId: uuid("creation_attempt_id").notNull(),
|
|
1947
|
+
toolCallId: text("tool_call_id").notNull(),
|
|
1948
|
+
status: text("status").notNull().default("pending"),
|
|
1949
|
+
questions: jsonb("questions").$type<HumanInputQuestion[]>().notNull(),
|
|
1950
|
+
allowSkip: boolean("allow_skip").notNull().default(false),
|
|
1951
|
+
response: jsonb("response").$type<HumanInputResponse>(),
|
|
1952
|
+
respondedBy: text("responded_by"),
|
|
1953
|
+
respondedAt: timestamp("responded_at", { withTimezone: true }),
|
|
1954
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }),
|
|
1955
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1956
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1957
|
+
},
|
|
1958
|
+
(table) => ({
|
|
1959
|
+
creationAttemptOwner: foreignKey({
|
|
1960
|
+
name: "session_human_input_requests_creation_attempt_fk",
|
|
1961
|
+
columns: [
|
|
1962
|
+
table.accountId,
|
|
1963
|
+
table.workspaceId,
|
|
1964
|
+
table.sessionId,
|
|
1965
|
+
table.turnId,
|
|
1966
|
+
table.creationAttemptId,
|
|
1967
|
+
],
|
|
1968
|
+
foreignColumns: [
|
|
1969
|
+
sessionTurnAttempts.accountId,
|
|
1970
|
+
sessionTurnAttempts.workspaceId,
|
|
1971
|
+
sessionTurnAttempts.sessionId,
|
|
1972
|
+
sessionTurnAttempts.turnId,
|
|
1973
|
+
sessionTurnAttempts.id,
|
|
1974
|
+
],
|
|
1975
|
+
}).onDelete("cascade"),
|
|
1976
|
+
toolCall: uniqueIndex("session_human_input_requests_tool_call_uq").on(
|
|
1977
|
+
table.workspaceId,
|
|
1978
|
+
table.sessionId,
|
|
1979
|
+
table.turnId,
|
|
1980
|
+
table.toolCallId,
|
|
1981
|
+
),
|
|
1982
|
+
pendingSession: index("session_human_input_requests_pending_session_idx")
|
|
1983
|
+
.on(table.workspaceId, table.sessionId, table.createdAt, table.id)
|
|
1984
|
+
.where(sql`${table.status} = 'pending'`),
|
|
1985
|
+
pendingExpiry: index("session_human_input_requests_pending_expiry_idx")
|
|
1986
|
+
.on(table.expiresAt, table.id)
|
|
1987
|
+
.where(sql`${table.status} = 'pending' and ${table.expiresAt} is not null`),
|
|
1988
|
+
status: check(
|
|
1989
|
+
"session_human_input_requests_status_check",
|
|
1990
|
+
sql`${table.status} in ('pending','answered','skipped','expired','cancelled')`,
|
|
1991
|
+
),
|
|
1992
|
+
generation: check(
|
|
1993
|
+
"session_human_input_requests_generation_check",
|
|
1994
|
+
sql`${table.turnGeneration} > 0`,
|
|
1995
|
+
),
|
|
1996
|
+
toolCallBytes: check(
|
|
1997
|
+
"session_human_input_requests_tool_call_bytes_check",
|
|
1998
|
+
sql`octet_length(${table.toolCallId}) between 1 and 1024`,
|
|
1999
|
+
),
|
|
2000
|
+
questionsBytes: check(
|
|
2001
|
+
"session_human_input_requests_questions_bytes_check",
|
|
2002
|
+
sql`octet_length(${table.questions}::text) <= 49152`,
|
|
2003
|
+
),
|
|
2004
|
+
responseBytes: check(
|
|
2005
|
+
"session_human_input_requests_response_bytes_check",
|
|
2006
|
+
sql`${table.response} is null or octet_length(${table.response}::text) <= 49152`,
|
|
2007
|
+
),
|
|
2008
|
+
actorBytes: check(
|
|
2009
|
+
"session_human_input_requests_actor_bytes_check",
|
|
2010
|
+
sql`${table.respondedBy} is null or octet_length(${table.respondedBy}) <= 1024`,
|
|
2011
|
+
),
|
|
2012
|
+
}),
|
|
2013
|
+
);
|
|
2014
|
+
|
|
1784
2015
|
// Conversation truth: ordered, verbatim SDK input items (issue #35). The
|
|
1785
2016
|
// model-facing memory store — unredacted and replay-ready. session_events
|
|
1786
2017
|
// remains the redacted human/audit timeline.
|
|
@@ -1797,7 +2028,9 @@ export const sessionHistoryItems = pgTable(
|
|
|
1797
2028
|
sessionId: uuid("session_id")
|
|
1798
2029
|
.notNull()
|
|
1799
2030
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
1800
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2031
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2032
|
+
onDelete: "set null",
|
|
2033
|
+
}),
|
|
1801
2034
|
// Numeric (not integer) so the synthetic compaction-summary row can be
|
|
1802
2035
|
// inserted at a FRACTIONAL position (boundaryPosition - 0.5) that sorts ahead
|
|
1803
2036
|
// of the kept tail without colliding with — and thus overwriting — the real
|
|
@@ -2061,7 +2294,9 @@ export const sessionRecordings = pgTable(
|
|
|
2061
2294
|
sessionId: uuid("session_id")
|
|
2062
2295
|
.notNull()
|
|
2063
2296
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
2064
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2297
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2298
|
+
onDelete: "set null",
|
|
2299
|
+
}),
|
|
2065
2300
|
|
|
2066
2301
|
state: text("state", { enum: sessionRecordingStateValues }).notNull(),
|
|
2067
2302
|
mode: text("mode", { enum: sessionRecordingModeValues }).notNull(),
|
|
@@ -2088,7 +2323,7 @@ export const sessionRecordings = pgTable(
|
|
|
2088
2323
|
}),
|
|
2089
2324
|
);
|
|
2090
2325
|
|
|
2091
|
-
// Workbench v2 turn-end workspace capture (
|
|
2326
|
+
// Workbench v2 turn-end workspace capture (model: sessionRecordings).
|
|
2092
2327
|
// One row per capture revision — a point-in-time snapshot of a session's changed
|
|
2093
2328
|
// files, probed off the live box at turn end. The manifest (tree index + per-repo
|
|
2094
2329
|
// status/diff + file index) and each after-image blob live in @opengeni/storage;
|
|
@@ -2111,7 +2346,9 @@ export const workspaceCaptures = pgTable(
|
|
|
2111
2346
|
sessionId: uuid("session_id")
|
|
2112
2347
|
.notNull()
|
|
2113
2348
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
2114
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2349
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2350
|
+
onDelete: "set null",
|
|
2351
|
+
}),
|
|
2115
2352
|
|
|
2116
2353
|
revision: bigint("revision", { mode: "number" }).notNull(),
|
|
2117
2354
|
leaseEpoch: integer("lease_epoch").notNull(),
|
|
@@ -2147,8 +2384,8 @@ export const workspaceCaptures = pgTable(
|
|
|
2147
2384
|
}),
|
|
2148
2385
|
);
|
|
2149
2386
|
|
|
2150
|
-
//
|
|
2151
|
-
//
|
|
2387
|
+
// Interactive PTY sessions. This is the persistent state needed for a live
|
|
2388
|
+
// terminal; file and Git reads remain stateless point
|
|
2152
2389
|
// queries; an interactive PTY is a live in-box process keyed by the SDK's numeric
|
|
2153
2390
|
// exec-session id (writeStdin({sessionId})). We map our UUID ptyId <-> that id,
|
|
2154
2391
|
// the owning workspace/session, the lease_epoch that fences it to the box it was
|
|
@@ -2193,7 +2430,7 @@ export const sandboxPtySessions = pgTable(
|
|
|
2193
2430
|
|
|
2194
2431
|
// ============================================================================
|
|
2195
2432
|
// Bring-your-own-compute (M2): first-class swappable sandboxes + enrollment +
|
|
2196
|
-
// metrics (migration 0024
|
|
2433
|
+
// metrics (migration 0024). The session→box
|
|
2197
2434
|
// binding becomes a per-session mutable, epoch-fenced active_sandbox_id pointer
|
|
2198
2435
|
// (declared on sessions above) that the routing proxy resolves PER TOOL CALL.
|
|
2199
2436
|
|
|
@@ -2267,7 +2504,7 @@ export const enrollments = pgTable(
|
|
|
2267
2504
|
);
|
|
2268
2505
|
|
|
2269
2506
|
// The OAuth 2.0 device-authorization (RFC 8628) PENDING request (M5, migration
|
|
2270
|
-
// 0025 /
|
|
2507
|
+
// 0025 / enrollment + §18 LOUD consent). An agent's `enroll` starts
|
|
2271
2508
|
// a flow (POST /enrollments/device/start) → one short-TTL, single-use row keyed by
|
|
2272
2509
|
// an opaque `device_code` (the agent polls with) + a short `user_code` (the user
|
|
2273
2510
|
// types at the approve page). The user (workspace-membership / workspace:admin
|
|
@@ -2308,7 +2545,9 @@ export const deviceEnrollmentRequests = pgTable(
|
|
|
2308
2545
|
machineName: text("machine_name"),
|
|
2309
2546
|
// The exposure the agent REQUESTED (whole-machine in v1; loudly consented at
|
|
2310
2547
|
// approve). Mirrors the enrollment column domain.
|
|
2311
|
-
requestedExposure: text("requested_exposure", {
|
|
2548
|
+
requestedExposure: text("requested_exposure", {
|
|
2549
|
+
enum: enrollmentExposureValues,
|
|
2550
|
+
})
|
|
2312
2551
|
.notNull()
|
|
2313
2552
|
.default("whole-machine"),
|
|
2314
2553
|
// The agent CAN offer a display (a real screen / Xvfb is available) — gates
|
|
@@ -2328,8 +2567,12 @@ export const deviceEnrollmentRequests = pgTable(
|
|
|
2328
2567
|
approvedAt: timestamp("approved_at", { withTimezone: true }),
|
|
2329
2568
|
// The enrollment + sandbox the approve produced (acceptance #2: an enrollment
|
|
2330
2569
|
// row AND a sandbox row appear). Null until approved.
|
|
2331
|
-
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2332
|
-
|
|
2570
|
+
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2571
|
+
onDelete: "set null",
|
|
2572
|
+
}),
|
|
2573
|
+
sandboxId: uuid("sandbox_id").references(() => sandboxes.id, {
|
|
2574
|
+
onDelete: "set null",
|
|
2575
|
+
}),
|
|
2333
2576
|
// The short-TTL expiry; a pending row past this is EXPIRED on poll.
|
|
2334
2577
|
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
2335
2578
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -2370,7 +2613,9 @@ export const sandboxes = pgTable(
|
|
|
2370
2613
|
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
2371
2614
|
kind: text("kind", { enum: sandboxKindValues }).notNull(),
|
|
2372
2615
|
name: text("name").notNull(),
|
|
2373
|
-
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2616
|
+
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2617
|
+
onDelete: "set null",
|
|
2618
|
+
}),
|
|
2374
2619
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2375
2620
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2376
2621
|
},
|
|
@@ -2521,7 +2766,9 @@ export const scheduledTaskRuns = pgTable(
|
|
|
2521
2766
|
triggerType: text("trigger_type").notNull(),
|
|
2522
2767
|
scheduledAt: timestamp("scheduled_at", { withTimezone: true }),
|
|
2523
2768
|
firedAt: timestamp("fired_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2524
|
-
sessionId: uuid("session_id").references(() => sessions.id, {
|
|
2769
|
+
sessionId: uuid("session_id").references(() => sessions.id, {
|
|
2770
|
+
onDelete: "set null",
|
|
2771
|
+
}),
|
|
2525
2772
|
triggerEventId: uuid("trigger_event_id"),
|
|
2526
2773
|
// Stable Temporal producer identity. Activity replay/re-dispatch returns
|
|
2527
2774
|
// the exact run instead of allocating a second schedule source row.
|
|
@@ -2559,6 +2806,8 @@ export const githubInstallations = pgTable(
|
|
|
2559
2806
|
installationId: integer("installation_id").notNull(),
|
|
2560
2807
|
accountLogin: text("account_login"),
|
|
2561
2808
|
accountType: text("account_type"),
|
|
2809
|
+
repositoryScope: text("repository_scope").notNull().default("all"),
|
|
2810
|
+
linkedBySubjectId: text("linked_by_subject_id"),
|
|
2562
2811
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2563
2812
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2564
2813
|
},
|
|
@@ -2569,6 +2818,47 @@ export const githubInstallations = pgTable(
|
|
|
2569
2818
|
),
|
|
2570
2819
|
installation: index("github_installations_installation_idx").on(table.installationId),
|
|
2571
2820
|
workspace: index("github_installations_workspace_idx").on(table.workspaceId),
|
|
2821
|
+
repositoryScopeCheck: check(
|
|
2822
|
+
"github_installations_repository_scope_check",
|
|
2823
|
+
sql`${table.repositoryScope} in ('all', 'selected')`,
|
|
2824
|
+
),
|
|
2825
|
+
}),
|
|
2826
|
+
);
|
|
2827
|
+
|
|
2828
|
+
export const githubInstallationRepositories = pgTable(
|
|
2829
|
+
"github_installation_repositories",
|
|
2830
|
+
{
|
|
2831
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
2832
|
+
accountId: uuid("account_id")
|
|
2833
|
+
.notNull()
|
|
2834
|
+
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
2835
|
+
workspaceId: uuid("workspace_id")
|
|
2836
|
+
.notNull()
|
|
2837
|
+
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
2838
|
+
installationId: integer("installation_id").notNull(),
|
|
2839
|
+
repositoryId: bigint("repository_id", { mode: "number" }).notNull(),
|
|
2840
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2841
|
+
},
|
|
2842
|
+
(table) => ({
|
|
2843
|
+
installationRepository: uniqueIndex("github_install_repo_workspace_installation_repo_idx").on(
|
|
2844
|
+
table.workspaceId,
|
|
2845
|
+
table.installationId,
|
|
2846
|
+
table.repositoryId,
|
|
2847
|
+
),
|
|
2848
|
+
workspaceInstallation: index("github_install_repo_workspace_installation_idx").on(
|
|
2849
|
+
table.workspaceId,
|
|
2850
|
+
table.installationId,
|
|
2851
|
+
),
|
|
2852
|
+
workspaceAccount: foreignKey({
|
|
2853
|
+
columns: [table.workspaceId, table.accountId],
|
|
2854
|
+
foreignColumns: [workspaces.id, workspaces.accountId],
|
|
2855
|
+
name: "github_installation_repositories_workspace_account_fk",
|
|
2856
|
+
}).onDelete("cascade"),
|
|
2857
|
+
installationBinding: foreignKey({
|
|
2858
|
+
columns: [table.workspaceId, table.installationId],
|
|
2859
|
+
foreignColumns: [githubInstallations.workspaceId, githubInstallations.installationId],
|
|
2860
|
+
name: "github_installation_repositories_installation_fk",
|
|
2861
|
+
}).onDelete("cascade"),
|
|
2572
2862
|
}),
|
|
2573
2863
|
);
|
|
2574
2864
|
|
|
@@ -2588,10 +2878,26 @@ export const usageEvents = pgTable(
|
|
|
2588
2878
|
unit: text("unit").notNull(),
|
|
2589
2879
|
sourceResourceType: text("source_resource_type"),
|
|
2590
2880
|
sourceResourceId: text("source_resource_id"),
|
|
2881
|
+
// Exact execution source for host usage export. These are deliberately
|
|
2882
|
+
// validated soft references rather than cascading foreign keys: usage is
|
|
2883
|
+
// an immutable billing/audit fact and must retain its source identity after
|
|
2884
|
+
// a session or turn is deleted.
|
|
2885
|
+
sessionId: uuid("session_id"),
|
|
2886
|
+
turnId: uuid("turn_id"),
|
|
2887
|
+
turnAttemptId: uuid("turn_attempt_id"),
|
|
2888
|
+
initiatorKind: text("initiator_kind"),
|
|
2889
|
+
initiatorSubjectId: text("initiator_subject_id"),
|
|
2890
|
+
initiatorContext: jsonb("initiator_context")
|
|
2891
|
+
.$type<Record<string, unknown>>()
|
|
2892
|
+
.notNull()
|
|
2893
|
+
.default({}),
|
|
2894
|
+
origin: text("origin"),
|
|
2591
2895
|
idempotencyKey: text("idempotency_key").notNull(),
|
|
2592
2896
|
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
|
|
2593
2897
|
recordedAt: timestamp("recorded_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2594
|
-
exportedToBillingAt: timestamp("exported_to_billing_at", {
|
|
2898
|
+
exportedToBillingAt: timestamp("exported_to_billing_at", {
|
|
2899
|
+
withTimezone: true,
|
|
2900
|
+
}),
|
|
2595
2901
|
billingProviderEventId: text("billing_provider_event_id"),
|
|
2596
2902
|
},
|
|
2597
2903
|
(table) => ({
|
|
@@ -2606,6 +2912,208 @@ export const usageEvents = pgTable(
|
|
|
2606
2912
|
table.eventType,
|
|
2607
2913
|
table.occurredAt,
|
|
2608
2914
|
),
|
|
2915
|
+
workspaceSession: index("usage_events_workspace_session_idx").on(
|
|
2916
|
+
table.workspaceId,
|
|
2917
|
+
table.sessionId,
|
|
2918
|
+
table.occurredAt,
|
|
2919
|
+
),
|
|
2920
|
+
contextHierarchy: check(
|
|
2921
|
+
"usage_events_context_hierarchy_check",
|
|
2922
|
+
sql`(${table.turnId} is null or ${table.sessionId} is not null)
|
|
2923
|
+
and (${table.turnAttemptId} is null or ${table.turnId} is not null)`,
|
|
2924
|
+
),
|
|
2925
|
+
initiatorConsistent: check(
|
|
2926
|
+
"usage_events_initiator_check",
|
|
2927
|
+
sql`(${table.initiatorKind} is null and ${table.initiatorSubjectId} is null)
|
|
2928
|
+
or (${table.initiatorKind} in ('subject', 'service')
|
|
2929
|
+
and ${table.initiatorSubjectId} is not null
|
|
2930
|
+
and octet_length(${table.initiatorSubjectId}) between 1 and 1024)`,
|
|
2931
|
+
),
|
|
2932
|
+
attributionContextBytes: check(
|
|
2933
|
+
"usage_events_initiator_context_bytes_check",
|
|
2934
|
+
sql`octet_length(${table.initiatorContext}::text) <= 4096`,
|
|
2935
|
+
),
|
|
2936
|
+
originValid: check(
|
|
2937
|
+
"usage_events_origin_check",
|
|
2938
|
+
sql`${table.origin} is null or ${table.origin} in (
|
|
2939
|
+
'user', 'scheduled_task', 'api', 'goal', 'system', 'compaction'
|
|
2940
|
+
)`,
|
|
2941
|
+
),
|
|
2942
|
+
}),
|
|
2943
|
+
);
|
|
2944
|
+
|
|
2945
|
+
/** Singleton, migration-installed gate. Standalone defaults keep both off. */
|
|
2946
|
+
export const hostExportConfig = pgTable(
|
|
2947
|
+
"host_export_config",
|
|
2948
|
+
{
|
|
2949
|
+
id: integer("id").primaryKey().default(1),
|
|
2950
|
+
sessionEventsEnabled: boolean("session_events_enabled").notNull().default(false),
|
|
2951
|
+
usageEventsEnabled: boolean("usage_events_enabled").notNull().default(false),
|
|
2952
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2953
|
+
},
|
|
2954
|
+
(table) => ({
|
|
2955
|
+
singleton: check("host_export_config_singleton_check", sql`${table.id} = 1`),
|
|
2956
|
+
}),
|
|
2957
|
+
);
|
|
2958
|
+
|
|
2959
|
+
/**
|
|
2960
|
+
* Transactional delivery buffer. It intentionally has no tenant/source FKs:
|
|
2961
|
+
* a workspace deletion must not erase an already-committed, unacknowledged
|
|
2962
|
+
* host fact. Payloads are sanitized/bounded before they reach this table.
|
|
2963
|
+
*/
|
|
2964
|
+
export const hostExportOutbox = pgTable(
|
|
2965
|
+
"host_export_outbox",
|
|
2966
|
+
{
|
|
2967
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
2968
|
+
exportKind: text("export_kind").notNull(),
|
|
2969
|
+
exportCursor: bigint("export_cursor", { mode: "bigint" }),
|
|
2970
|
+
sourceId: uuid("source_id").notNull(),
|
|
2971
|
+
accountId: uuid("account_id").notNull(),
|
|
2972
|
+
workspaceId: uuid("workspace_id").notNull(),
|
|
2973
|
+
sessionId: uuid("session_id"),
|
|
2974
|
+
rootSessionId: uuid("root_session_id"),
|
|
2975
|
+
turnId: uuid("turn_id"),
|
|
2976
|
+
turnGeneration: integer("turn_generation"),
|
|
2977
|
+
turnAttemptId: uuid("turn_attempt_id"),
|
|
2978
|
+
sessionSequence: integer("session_sequence"),
|
|
2979
|
+
clientEventId: text("client_event_id"),
|
|
2980
|
+
turnAssociation: text("turn_association"),
|
|
2981
|
+
duplicateOfEventId: uuid("duplicate_of_event_id"),
|
|
2982
|
+
duplicateReason: text("duplicate_reason"),
|
|
2983
|
+
eventType: text("event_type").notNull(),
|
|
2984
|
+
idempotencyKey: text("idempotency_key").notNull(),
|
|
2985
|
+
initiator: jsonb("initiator").$type<Record<string, unknown> | null>(),
|
|
2986
|
+
initiatorContext: jsonb("initiator_context")
|
|
2987
|
+
.$type<Record<string, unknown>>()
|
|
2988
|
+
.notNull()
|
|
2989
|
+
.default({}),
|
|
2990
|
+
origin: text("origin"),
|
|
2991
|
+
payload: jsonb("payload").$type<unknown>().notNull(),
|
|
2992
|
+
envelopeBytes: integer("envelope_bytes").notNull(),
|
|
2993
|
+
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
|
|
2994
|
+
sourceRecordedAt: timestamp("source_recorded_at", { withTimezone: true }).notNull(),
|
|
2995
|
+
enqueuedAt: timestamp("enqueued_at", { withTimezone: true }).notNull(),
|
|
2996
|
+
},
|
|
2997
|
+
(table) => ({
|
|
2998
|
+
kindValid: check(
|
|
2999
|
+
"host_export_outbox_kind_check",
|
|
3000
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3001
|
+
),
|
|
3002
|
+
sourceUnique: uniqueIndex("host_export_outbox_source_uq").on(table.exportKind, table.sourceId),
|
|
3003
|
+
cursorUnique: uniqueIndex("host_export_outbox_cursor_uq")
|
|
3004
|
+
.on(table.exportKind, table.exportCursor)
|
|
3005
|
+
.where(sql`${table.exportCursor} is not null`),
|
|
3006
|
+
unassigned: index("host_export_outbox_unassigned_idx")
|
|
3007
|
+
.on(table.exportKind, table.enqueuedAt, table.id)
|
|
3008
|
+
.where(sql`${table.exportCursor} is null`),
|
|
3009
|
+
unassignedSession: index("host_export_outbox_unassigned_session_idx")
|
|
3010
|
+
.on(table.exportKind, table.sessionId, table.sessionSequence)
|
|
3011
|
+
.where(sql`${table.exportCursor} is null and ${table.sessionId} is not null`),
|
|
3012
|
+
contextBytes: check(
|
|
3013
|
+
"host_export_outbox_context_bytes_check",
|
|
3014
|
+
sql`octet_length(${table.initiatorContext}::text) <= 4096`,
|
|
3015
|
+
),
|
|
3016
|
+
originValid: check(
|
|
3017
|
+
"host_export_outbox_origin_check",
|
|
3018
|
+
sql`${table.origin} is null or ${table.origin} in (
|
|
3019
|
+
'user', 'scheduled_task', 'api', 'goal', 'system', 'compaction'
|
|
3020
|
+
)`,
|
|
3021
|
+
),
|
|
3022
|
+
}),
|
|
3023
|
+
);
|
|
3024
|
+
|
|
3025
|
+
export const hostExportCursorState = pgTable(
|
|
3026
|
+
"host_export_cursor_state",
|
|
3027
|
+
{
|
|
3028
|
+
exportKind: text("export_kind").primaryKey(),
|
|
3029
|
+
nextCursor: bigint("next_cursor", { mode: "bigint" }).notNull().default(1n),
|
|
3030
|
+
prunedThrough: bigint("pruned_through", { mode: "bigint" }).notNull().default(0n),
|
|
3031
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3032
|
+
},
|
|
3033
|
+
(table) => ({
|
|
3034
|
+
kindValid: check(
|
|
3035
|
+
"host_export_cursor_state_kind_check",
|
|
3036
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3037
|
+
),
|
|
3038
|
+
cursorValid: check(
|
|
3039
|
+
"host_export_cursor_state_next_check",
|
|
3040
|
+
sql`${table.nextCursor} > 0 and ${table.prunedThrough} >= 0
|
|
3041
|
+
and ${table.prunedThrough} < ${table.nextCursor}`,
|
|
3042
|
+
),
|
|
3043
|
+
}),
|
|
3044
|
+
);
|
|
3045
|
+
|
|
3046
|
+
/** Named at-least-once consumer checkpoint and one-batch lease. */
|
|
3047
|
+
export const hostExportConsumers = pgTable(
|
|
3048
|
+
"host_export_consumers",
|
|
3049
|
+
{
|
|
3050
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3051
|
+
consumerId: text("consumer_id").notNull(),
|
|
3052
|
+
exportKind: text("export_kind").notNull(),
|
|
3053
|
+
checkpoint: bigint("checkpoint", { mode: "bigint" }).notNull().default(0n),
|
|
3054
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
3055
|
+
leaseToken: uuid("lease_token"),
|
|
3056
|
+
leaseHolderId: text("lease_holder_id"),
|
|
3057
|
+
leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
|
|
3058
|
+
leaseFrom: bigint("lease_from", { mode: "bigint" }),
|
|
3059
|
+
leaseThrough: bigint("lease_through", { mode: "bigint" }),
|
|
3060
|
+
consecutiveFailures: integer("consecutive_failures").notNull().default(0),
|
|
3061
|
+
nextAttemptAt: timestamp("next_attempt_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3062
|
+
lastError: text("last_error"),
|
|
3063
|
+
lastErrorAt: timestamp("last_error_at", { withTimezone: true }),
|
|
3064
|
+
blockedAt: timestamp("blocked_at", { withTimezone: true }),
|
|
3065
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3066
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3067
|
+
},
|
|
3068
|
+
(table) => ({
|
|
3069
|
+
consumerKind: uniqueIndex("host_export_consumers_kind_id_uq").on(
|
|
3070
|
+
table.exportKind,
|
|
3071
|
+
table.consumerId,
|
|
3072
|
+
),
|
|
3073
|
+
kindValid: check(
|
|
3074
|
+
"host_export_consumers_kind_check",
|
|
3075
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3076
|
+
),
|
|
3077
|
+
checkpointValid: check("host_export_consumers_checkpoint_check", sql`${table.checkpoint} >= 0`),
|
|
3078
|
+
due: index("host_export_consumers_due_idx").on(
|
|
3079
|
+
table.enabled,
|
|
3080
|
+
table.blockedAt,
|
|
3081
|
+
table.nextAttemptAt,
|
|
3082
|
+
),
|
|
3083
|
+
}),
|
|
3084
|
+
);
|
|
3085
|
+
|
|
3086
|
+
/** Explicit poison-row disposition; transient failures never write here. */
|
|
3087
|
+
export const hostExportDeadLetters = pgTable(
|
|
3088
|
+
"host_export_dead_letters",
|
|
3089
|
+
{
|
|
3090
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3091
|
+
consumerId: text("consumer_id").notNull(),
|
|
3092
|
+
exportKind: text("export_kind").notNull(),
|
|
3093
|
+
exportCursor: bigint("export_cursor", { mode: "bigint" }).notNull(),
|
|
3094
|
+
sourceId: uuid("source_id").notNull(),
|
|
3095
|
+
reason: text("reason").notNull(),
|
|
3096
|
+
envelope: jsonb("envelope").$type<Record<string, unknown>>().notNull(),
|
|
3097
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3098
|
+
},
|
|
3099
|
+
(table) => ({
|
|
3100
|
+
consumerCursor: uniqueIndex("host_export_dead_letters_consumer_cursor_uq").on(
|
|
3101
|
+
table.exportKind,
|
|
3102
|
+
table.consumerId,
|
|
3103
|
+
table.exportCursor,
|
|
3104
|
+
),
|
|
3105
|
+
kindValid: check(
|
|
3106
|
+
"host_export_dead_letters_kind_check",
|
|
3107
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3108
|
+
),
|
|
3109
|
+
reasonValid: check(
|
|
3110
|
+
"host_export_dead_letters_reason_check",
|
|
3111
|
+
sql`length(${table.reason}) between 1 and 500`,
|
|
3112
|
+
),
|
|
3113
|
+
envelopeBytes: check(
|
|
3114
|
+
"host_export_dead_letters_envelope_bytes_check",
|
|
3115
|
+
sql`pg_column_size(${table.envelope}) <= 114688`,
|
|
3116
|
+
),
|
|
2609
3117
|
}),
|
|
2610
3118
|
);
|
|
2611
3119
|
|
|
@@ -2616,7 +3124,9 @@ export const creditLedgerEntries = pgTable(
|
|
|
2616
3124
|
accountId: uuid("account_id")
|
|
2617
3125
|
.notNull()
|
|
2618
3126
|
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
2619
|
-
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3127
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3128
|
+
onDelete: "set null",
|
|
3129
|
+
}),
|
|
2620
3130
|
type: text("type").notNull(),
|
|
2621
3131
|
amountMicros: bigint("amount_micros", { mode: "number" }).notNull(),
|
|
2622
3132
|
currency: text("currency").notNull().default("usd"),
|
|
@@ -2674,8 +3184,12 @@ export const auditEvents = pgTable(
|
|
|
2674
3184
|
"audit_events",
|
|
2675
3185
|
{
|
|
2676
3186
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
2677
|
-
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
2678
|
-
|
|
3187
|
+
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
3188
|
+
onDelete: "set null",
|
|
3189
|
+
}),
|
|
3190
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3191
|
+
onDelete: "set null",
|
|
3192
|
+
}),
|
|
2679
3193
|
subjectId: text("subject_id"),
|
|
2680
3194
|
action: text("action").notNull(),
|
|
2681
3195
|
targetType: text("target_type"),
|
|
@@ -2770,8 +3284,12 @@ export const capabilityCatalogItems = pgTable(
|
|
|
2770
3284
|
"capability_catalog_items",
|
|
2771
3285
|
{
|
|
2772
3286
|
id: text("id").notNull(),
|
|
2773
|
-
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
2774
|
-
|
|
3287
|
+
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
3288
|
+
onDelete: "cascade",
|
|
3289
|
+
}),
|
|
3290
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3291
|
+
onDelete: "cascade",
|
|
3292
|
+
}),
|
|
2775
3293
|
kind: text("kind").notNull(),
|
|
2776
3294
|
source: text("source").notNull().default("manual"),
|
|
2777
3295
|
name: text("name").notNull(),
|