@opengeni/db 0.7.3 → 0.10.7
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-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/{chunk-B22X3IEZ.js → chunk-P6PKXY5W.js} +604 -32
- package/dist/chunk-P6PKXY5W.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +7408 -2276
- 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 +1122 -73
- package/dist/provision-roles.js +1 -1
- package/dist/{schema-BN5mB9xZ.d.ts → schema-CqkzrBRS.d.ts} +6967 -4457
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +19 -1
- package/drizzle/0053_codex_credential_leases.sql +2 -2
- package/drizzle/0057_durable_queue_control.sql +1 -1
- package/drizzle/0061_session_workflow_wake_outbox.sql +1 -1
- package/drizzle/0062_session_list_snapshot_reaper.sql +1 -1
- package/drizzle/0063_session_control_mega_foundation.sql +1 -1
- package/drizzle/0064_rotation_strategy_sharded_backfill.sql +15 -0
- package/drizzle/0065_codex_subscription_overview.sql +168 -0
- package/drizzle/0065_session_attempt_quiescence.sql +26 -0
- package/drizzle/0065_session_tool_policy.sql +38 -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/drizzle/0106_session_attempt_mcp_approval_policies.sql +29 -0
- package/drizzle/0107_host_export_lineage_contract.sql +381 -0
- package/drizzle/0108_fence_invalidated_warming_epochs.sql +76 -0
- package/package.json +5 -4
- package/src/codex-token-resolver.ts +175 -14
- package/src/connection-token-resolver.ts +428 -119
- package/src/event-payload-sanitizer.ts +88 -20
- package/src/index.ts +7208 -1273
- 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 +658 -34
- package/src/session-control.ts +520 -38
- package/src/session-queue-commands.ts +385 -61
- 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,7 @@
|
|
|
1
|
+
import type { McpServerConnectionRef, SessionMcpApprovalPolicy } from "@opengeni/contracts";
|
|
1
2
|
import { sql } from "drizzle-orm";
|
|
3
|
+
import type { SessionToolPolicy } from "@opengeni/contracts";
|
|
4
|
+
import type { HumanInputQuestion, HumanInputResponse } from "@opengeni/contracts";
|
|
2
5
|
import {
|
|
3
6
|
bigint,
|
|
4
7
|
boolean,
|
|
@@ -88,7 +91,9 @@ export const workspaceInferenceControls = pgTable(
|
|
|
88
91
|
accountId: uuid("account_id").notNull(),
|
|
89
92
|
revision: bigint("revision", { mode: "number" }).notNull().default(0),
|
|
90
93
|
workspaceState: text("workspace_state").notNull().default("active"),
|
|
91
|
-
workspacePauseRevision: bigint("workspace_pause_revision", {
|
|
94
|
+
workspacePauseRevision: bigint("workspace_pause_revision", {
|
|
95
|
+
mode: "number",
|
|
96
|
+
}),
|
|
92
97
|
reason: text("reason"),
|
|
93
98
|
changedBy: text("changed_by"),
|
|
94
99
|
changedAt: timestamp("changed_at", { withTimezone: true }),
|
|
@@ -121,6 +126,30 @@ export const workspaceInferenceControls = pgTable(
|
|
|
121
126
|
}),
|
|
122
127
|
);
|
|
123
128
|
|
|
129
|
+
// One transactionally allocated activity clock per workspace. An updated-order
|
|
130
|
+
// first page takes a SHARE lock on this row after the workspace-control lock;
|
|
131
|
+
// semantic writers allocate after UUID-ordered session locks. Plain MVCC page
|
|
132
|
+
// reads never lock session rows, so that ordering cannot form a cycle.
|
|
133
|
+
export const workspaceSessionActivityRevisions = pgTable(
|
|
134
|
+
"workspace_session_activity_revisions",
|
|
135
|
+
{
|
|
136
|
+
workspaceId: uuid("workspace_id").primaryKey(),
|
|
137
|
+
accountId: uuid("account_id").notNull(),
|
|
138
|
+
revision: bigint("revision", { mode: "number" }).notNull().default(0),
|
|
139
|
+
},
|
|
140
|
+
(table) => ({
|
|
141
|
+
workspaceAccount: foreignKey({
|
|
142
|
+
name: "workspace_session_activity_revisions_workspace_account_fk",
|
|
143
|
+
columns: [table.workspaceId, table.accountId],
|
|
144
|
+
foreignColumns: [workspaces.id, workspaces.accountId],
|
|
145
|
+
}).onDelete("cascade"),
|
|
146
|
+
revisionValid: check(
|
|
147
|
+
"workspace_session_activity_revisions_revision_check",
|
|
148
|
+
sql`${table.revision} >= 0`,
|
|
149
|
+
),
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
|
|
124
153
|
export const workspaceMemberships = pgTable(
|
|
125
154
|
"workspace_memberships",
|
|
126
155
|
{
|
|
@@ -155,7 +184,9 @@ export const apiKeys = pgTable(
|
|
|
155
184
|
accountId: uuid("account_id")
|
|
156
185
|
.notNull()
|
|
157
186
|
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
158
|
-
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
187
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
188
|
+
onDelete: "cascade",
|
|
189
|
+
}),
|
|
159
190
|
name: text("name").notNull(),
|
|
160
191
|
prefix: text("prefix").notNull(),
|
|
161
192
|
keyHash: text("key_hash").notNull(),
|
|
@@ -280,14 +311,29 @@ export const codexSubscriptionCredentials = pgTable(
|
|
|
280
311
|
// excluded). The writer only ever sets a NON-empty set, so a flaky empty turn
|
|
281
312
|
// can't false-drop coverage. connectorsCheckedAt is the freshness clock.
|
|
282
313
|
connectorNamespaces: text("connector_namespaces").array(),
|
|
283
|
-
connectorsCheckedAt: timestamp("connectors_checked_at", {
|
|
314
|
+
connectorsCheckedAt: timestamp("connectors_checked_at", {
|
|
315
|
+
withTimezone: true,
|
|
316
|
+
}),
|
|
284
317
|
// Workspace-local, server-held fairness cursor. Provider usage headers are
|
|
285
318
|
// capacity hints, never the sole allocator: live lease count is ranked first
|
|
286
319
|
// and this cursor deterministically breaks equal-load/equal-capacity ties.
|
|
287
320
|
// This flag controls NEW automatic allocations only. Credential health,
|
|
288
321
|
// refresh, encrypted material, and already-frozen/in-flight turns are
|
|
289
|
-
// intentionally independent.
|
|
322
|
+
// intentionally independent. account eligibility policy owns toggle OCC/audit and product UI.
|
|
290
323
|
allocatorEnabled: boolean("allocator_enabled").notNull().default(true),
|
|
324
|
+
// Independent OCC/audit sequence for the allocator toggle. Token refresh
|
|
325
|
+
// continues to own `version`; quota/cache writes own neither counter.
|
|
326
|
+
allocatorVersion: integer("allocator_version").notNull().default(1),
|
|
327
|
+
allocatorUpdatedBySubjectId: text("allocator_updated_by_subject_id"),
|
|
328
|
+
allocatorUpdatedAt: timestamp("allocator_updated_at", { withTimezone: true }),
|
|
329
|
+
// Authoritative count-only summary cached from /wham/usage. Detailed rows
|
|
330
|
+
// are never persisted as redemption authority; every first POST preflights
|
|
331
|
+
// the provider's fresh detail endpoint.
|
|
332
|
+
resetCreditAvailableCount: integer("reset_credit_available_count"),
|
|
333
|
+
resetCreditsCheckedAt: timestamp("reset_credits_checked_at", { withTimezone: true }),
|
|
334
|
+
// Set only by a direct Better Auth cookie connection/reconnection. Legacy,
|
|
335
|
+
// configured, delegated, API-key, and agent-created rows remain view-only.
|
|
336
|
+
connectedBySubjectId: text("connected_by_subject_id"),
|
|
291
337
|
selectionCount: integer("selection_count").notNull().default(0),
|
|
292
338
|
lastSelectedAt: timestamp("last_selected_at", { withTimezone: true }),
|
|
293
339
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -311,6 +357,82 @@ export const codexSubscriptionCredentials = pgTable(
|
|
|
311
357
|
}),
|
|
312
358
|
);
|
|
313
359
|
|
|
360
|
+
// One durable logical human redemption. `processing` means the fresh provider
|
|
361
|
+
// detail preflight is still owed; `provider_started` means the POST may have
|
|
362
|
+
// reached upstream and every retry must reuse upstreamIdempotencyKey without
|
|
363
|
+
// requiring the credit to remain visible as available.
|
|
364
|
+
export const codexResetRedemptionAttempts = pgTable(
|
|
365
|
+
"codex_reset_redemption_attempts",
|
|
366
|
+
{
|
|
367
|
+
id: uuid("id").primaryKey(),
|
|
368
|
+
accountId: uuid("account_id")
|
|
369
|
+
.notNull()
|
|
370
|
+
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
371
|
+
workspaceId: uuid("workspace_id")
|
|
372
|
+
.notNull()
|
|
373
|
+
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
374
|
+
credentialId: uuid("credential_id").notNull(),
|
|
375
|
+
subjectId: text("subject_id").notNull(),
|
|
376
|
+
browserSessionHash: text("browser_session_hash").notNull(),
|
|
377
|
+
creditId: text("credit_id").notNull(),
|
|
378
|
+
upstreamIdempotencyKey: uuid("upstream_idempotency_key").notNull().defaultRandom(),
|
|
379
|
+
status: text("status").notNull().default("processing"),
|
|
380
|
+
outcome: text("outcome"),
|
|
381
|
+
claimHolderId: uuid("claim_holder_id"),
|
|
382
|
+
claimExpiresAt: timestamp("claim_expires_at", { withTimezone: true }),
|
|
383
|
+
confirmationExpiresAt: timestamp("confirmation_expires_at", { withTimezone: true }).notNull(),
|
|
384
|
+
providerStartedAt: timestamp("provider_started_at", { withTimezone: true }),
|
|
385
|
+
completedAt: timestamp("completed_at", { withTimezone: true }),
|
|
386
|
+
lastFailureKind: text("last_failure_kind"),
|
|
387
|
+
retryCount: integer("retry_count").notNull().default(0),
|
|
388
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
389
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
390
|
+
},
|
|
391
|
+
(table) => ({
|
|
392
|
+
workspaceAccount: foreignKey({
|
|
393
|
+
name: "codex_reset_redemption_workspace_account_fk",
|
|
394
|
+
columns: [table.workspaceId, table.accountId],
|
|
395
|
+
foreignColumns: [workspaces.id, workspaces.accountId],
|
|
396
|
+
}).onDelete("cascade"),
|
|
397
|
+
upstreamKey: uniqueIndex("codex_reset_redemption_upstream_key_idx").on(
|
|
398
|
+
table.upstreamIdempotencyKey,
|
|
399
|
+
),
|
|
400
|
+
credentialCredit: uniqueIndex("codex_reset_redemption_credential_credit_idx")
|
|
401
|
+
.on(table.workspaceId, table.credentialId, table.creditId)
|
|
402
|
+
.where(
|
|
403
|
+
sql`${table.status} <> 'completed' or ${table.outcome} in ('reset', 'alreadyRedeemed')`,
|
|
404
|
+
),
|
|
405
|
+
workspaceCredential: index("codex_reset_redemption_workspace_credential_idx").on(
|
|
406
|
+
table.workspaceId,
|
|
407
|
+
table.credentialId,
|
|
408
|
+
table.createdAt,
|
|
409
|
+
),
|
|
410
|
+
claimExpiry: index("codex_reset_redemption_claim_expiry_idx")
|
|
411
|
+
.on(table.claimExpiresAt)
|
|
412
|
+
.where(sql`${table.status} <> 'completed'`),
|
|
413
|
+
statusValid: check(
|
|
414
|
+
"codex_reset_redemption_status_check",
|
|
415
|
+
sql`${table.status} in ('processing', 'provider_started', 'completed')`,
|
|
416
|
+
),
|
|
417
|
+
outcomeValid: check(
|
|
418
|
+
"codex_reset_redemption_outcome_check",
|
|
419
|
+
sql`${table.outcome} is null or ${table.outcome} in ('reset', 'nothingToReset', 'noCredit', 'alreadyRedeemed')`,
|
|
420
|
+
),
|
|
421
|
+
completionConsistent: check(
|
|
422
|
+
"codex_reset_redemption_completed_check",
|
|
423
|
+
sql`(${table.status} = 'completed') = (${table.outcome} is not null and ${table.completedAt} is not null)`,
|
|
424
|
+
),
|
|
425
|
+
retryCountValid: check(
|
|
426
|
+
"codex_reset_redemption_retry_count_check",
|
|
427
|
+
sql`${table.retryCount} >= 0`,
|
|
428
|
+
),
|
|
429
|
+
humanSubjectValid: check(
|
|
430
|
+
"codex_reset_redemption_human_subject_check",
|
|
431
|
+
sql`${table.subjectId} like 'user:_%'`,
|
|
432
|
+
),
|
|
433
|
+
}),
|
|
434
|
+
);
|
|
435
|
+
|
|
314
436
|
// Generic external-service credential spine. credential_encrypted is the ONLY
|
|
315
437
|
// secret-bearing column; normal API reads use metadata-only helpers below the DB
|
|
316
438
|
// layer. Runtime token material is decrypted only by the broker accessor.
|
|
@@ -432,7 +554,7 @@ export const codexRotationSettings = pgTable(
|
|
|
432
554
|
// code reads this bit; old binaries safely ignore it and keep the legacy
|
|
433
555
|
// pin/rotation policy.
|
|
434
556
|
leaseRotationEnabled: boolean("lease_rotation_enabled").notNull().default(false),
|
|
435
|
-
rotationStrategy: text("rotation_strategy").notNull().default("
|
|
557
|
+
rotationStrategy: text("rotation_strategy").notNull().default("sharded"), // sharded-rotation policy: legacy residue; behavior is always sharded
|
|
436
558
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
437
559
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
438
560
|
},
|
|
@@ -527,6 +649,10 @@ export const sessions = pgTable(
|
|
|
527
649
|
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
528
650
|
status: text("status").notNull().default("queued"),
|
|
529
651
|
initialMessage: text("initial_message").notNull(),
|
|
652
|
+
// Invisible host context frozen with the winning session create. The
|
|
653
|
+
// initial turn copies this value so an idempotent repair can never adopt a
|
|
654
|
+
// retrying caller's different instructions.
|
|
655
|
+
initialTurnInstructions: text("initial_turn_instructions"),
|
|
530
656
|
title: text("title"),
|
|
531
657
|
titleSource: text("title_source"),
|
|
532
658
|
// Per-session agent persona/system instructions supplied at create (the
|
|
@@ -539,6 +665,14 @@ export const sessions = pgTable(
|
|
|
539
665
|
resources: jsonb("resources").$type<unknown[]>().notNull().default([]),
|
|
540
666
|
tools: jsonb("tools").$type<unknown[]>().notNull().default([]),
|
|
541
667
|
metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default({}),
|
|
668
|
+
// Frozen creator fact. This is used for creation attribution and for the
|
|
669
|
+
// idempotent first-turn repair only; later turns capture their own actor.
|
|
670
|
+
createdByKind: text("created_by_kind").notNull().default("service"),
|
|
671
|
+
createdBySubjectId: text("created_by_subject_id").notNull().default("unattributed-legacy"),
|
|
672
|
+
createdByContext: jsonb("created_by_context")
|
|
673
|
+
.$type<Record<string, unknown>>()
|
|
674
|
+
.notNull()
|
|
675
|
+
.default({ backfill: true }),
|
|
542
676
|
model: text("model").notNull(),
|
|
543
677
|
sandboxBackend: text("sandbox_backend").notNull(),
|
|
544
678
|
// The OS this session's box runs. Defaults to 'linux' (today's only OS, so
|
|
@@ -554,8 +688,8 @@ export const sessions = pgTable(
|
|
|
554
688
|
// The app generates the uuid and uses it for both id and sandbox_group_id in
|
|
555
689
|
// one insert — it cannot SQL-default to id (id is defaultRandom()).
|
|
556
690
|
sandboxGroupId: uuid("sandbox_group_id").notNull(),
|
|
557
|
-
// The first-class swappable-sandbox POINTER (bring-your-own-compute M2
|
|
558
|
-
//
|
|
691
|
+
// The first-class swappable-sandbox POINTER (bring-your-own-compute M2).
|
|
692
|
+
// NULL == "use the session's own group sandbox" (the
|
|
559
693
|
// backward-compat default — every existing/new row is a behavior-preserving
|
|
560
694
|
// no-op). The routing proxy re-reads (active_sandbox_id, active_epoch) PER
|
|
561
695
|
// TOOL CALL to make a Modal<->selfhosted hot-swap seamless. The FK
|
|
@@ -590,6 +724,10 @@ export const sessions = pgTable(
|
|
|
590
724
|
// Non-default first-party MCP token permissions (manager-style sessions);
|
|
591
725
|
// null means the fixed worker default set in @opengeni/runtime.
|
|
592
726
|
firstPartyMcpPermissions: jsonb("first_party_mcp_permissions").$type<string[]>(),
|
|
727
|
+
// Durable tool-policy origin. NULL is retained for pre-migration rows;
|
|
728
|
+
// mapSession exposes those rows as `legacy` instead of guessing omitted vs
|
|
729
|
+
// explicit [].
|
|
730
|
+
toolPolicy: jsonb("tool_policy").$type<SessionToolPolicy>(),
|
|
593
731
|
// The manager session that spawned this one via session_create. Set only
|
|
594
732
|
// when the creating grant carried a worker-signed sessionId claim (a session
|
|
595
733
|
// spawning a worker); null for direct API creates and scheduled-task runs.
|
|
@@ -620,11 +758,15 @@ export const sessions = pgTable(
|
|
|
620
758
|
queueTailPosition: bigint("queue_tail_position", { mode: "number" }).notNull().default(0),
|
|
621
759
|
directControlState: text("direct_control_state").notNull().default("active"),
|
|
622
760
|
directPauseRevision: bigint("direct_pause_revision", { mode: "number" }),
|
|
623
|
-
subtreeRunOverrideRevision: bigint("subtree_run_override_revision", {
|
|
761
|
+
subtreeRunOverrideRevision: bigint("subtree_run_override_revision", {
|
|
762
|
+
mode: "number",
|
|
763
|
+
}),
|
|
624
764
|
controlVersion: bigint("control_version", { mode: "number" }).notNull().default(0),
|
|
625
765
|
directControlReason: text("direct_control_reason"),
|
|
626
766
|
directControlChangedBy: text("direct_control_changed_by"),
|
|
627
|
-
directControlChangedAt: timestamp("direct_control_changed_at", {
|
|
767
|
+
directControlChangedAt: timestamp("direct_control_changed_at", {
|
|
768
|
+
withTimezone: true,
|
|
769
|
+
}),
|
|
628
770
|
lastSequence: integer("last_sequence").notNull().default(0),
|
|
629
771
|
// The session's PINNED Codex account (manual override from the in-session
|
|
630
772
|
// switcher). NULL ⇒ follow the workspace active pointer. FK declared in the
|
|
@@ -645,6 +787,10 @@ export const sessions = pgTable(
|
|
|
645
787
|
codexPinSource: text("codex_pin_source"),
|
|
646
788
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
647
789
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
790
|
+
// Assigned by the database trigger whenever canonical updated_at activity
|
|
791
|
+
// advances. Legacy rows remain zero until touched; raw delta-only writers
|
|
792
|
+
// intentionally omit updated_at and therefore do not allocate revisions.
|
|
793
|
+
activityRevision: bigint("activity_revision", { mode: "number" }).notNull().default(0),
|
|
648
794
|
},
|
|
649
795
|
(table) => ({
|
|
650
796
|
workspaceIdentity: uniqueIndex("sessions_workspace_id_idx").on(table.workspaceId, table.id),
|
|
@@ -652,6 +798,25 @@ export const sessions = pgTable(
|
|
|
652
798
|
table.workspaceId,
|
|
653
799
|
table.createdAt,
|
|
654
800
|
),
|
|
801
|
+
// Model-facing monitoring pages use exact (timestamp,id) keysets. Keep the
|
|
802
|
+
// older prefix index during rolling deploys; these composites serve both
|
|
803
|
+
// deterministic traversal and updatedAfter change scans.
|
|
804
|
+
workspaceCreatedId: index("sessions_workspace_created_id_idx").on(
|
|
805
|
+
table.workspaceId,
|
|
806
|
+
table.createdAt.desc(),
|
|
807
|
+
table.id.desc(),
|
|
808
|
+
),
|
|
809
|
+
workspaceUpdatedId: index("sessions_workspace_updated_id_idx").on(
|
|
810
|
+
table.workspaceId,
|
|
811
|
+
table.updatedAt.desc(),
|
|
812
|
+
table.id.desc(),
|
|
813
|
+
),
|
|
814
|
+
workspaceActivityRevision: index("sessions_workspace_activity_revision_idx").on(
|
|
815
|
+
table.workspaceId,
|
|
816
|
+
table.activityRevision.desc(),
|
|
817
|
+
table.updatedAt.desc(),
|
|
818
|
+
table.id.desc(),
|
|
819
|
+
),
|
|
655
820
|
variableSet: index("sessions_variable_set_idx").on(table.workspaceId, table.variableSetId),
|
|
656
821
|
parent: index("sessions_parent_idx").on(table.workspaceId, table.parentSessionId),
|
|
657
822
|
// Routing index: resolve session_id -> sandbox_group_id at every lease entry
|
|
@@ -801,6 +966,9 @@ export const sessionMcpServers = pgTable(
|
|
|
801
966
|
// Human-approval policy: `true` = every tool requires approval, a string[] of
|
|
802
967
|
// UNPREFIXED tool names = only those require it, null/absent = auto-run.
|
|
803
968
|
requireApproval: jsonb("require_approval").$type<boolean | string[]>(),
|
|
969
|
+
// Non-secret pointer resolved at request time by the standalone broker or
|
|
970
|
+
// an embedding host. Unlike headersEncrypted, this is safe to project.
|
|
971
|
+
connectionRef: jsonb("connection_ref").$type<McpServerConnectionRef>(),
|
|
804
972
|
// Map of header name -> AES-GCM ciphertext. Values are decrypted only by the
|
|
805
973
|
// worker's run-preparation path and never returned by API helpers.
|
|
806
974
|
headersEncrypted: jsonb("headers_encrypted")
|
|
@@ -1077,8 +1245,14 @@ export const sessionTurns = pgTable(
|
|
|
1077
1245
|
source: text("source").notNull().default("user"),
|
|
1078
1246
|
position: bigint("position", { mode: "number" }).notNull(),
|
|
1079
1247
|
prompt: text("prompt").notNull(),
|
|
1248
|
+
// Host context for this exact turn. System-level at runtime and deliberately
|
|
1249
|
+
// separate from the visible prompt/event payload.
|
|
1250
|
+
turnInstructions: text("turn_instructions"),
|
|
1080
1251
|
resources: jsonb("resources").$type<unknown[]>().notNull().default([]),
|
|
1081
1252
|
tools: jsonb("tools").$type<unknown[]>().notNull().default([]),
|
|
1253
|
+
// false = inherit the durable session policy; true = this turn explicitly
|
|
1254
|
+
// replaces it with `tools` after the core subset fence.
|
|
1255
|
+
toolsProvided: boolean("tools_provided").notNull().default(false),
|
|
1082
1256
|
model: text("model").notNull(),
|
|
1083
1257
|
reasoningEffort: text("reasoning_effort").notNull(),
|
|
1084
1258
|
sandboxBackend: text("sandbox_backend").notNull(),
|
|
@@ -1094,6 +1268,14 @@ export const sessionTurns = pgTable(
|
|
|
1094
1268
|
// the SQL constraint is therefore DEFERRABLE INITIALLY DEFERRED.
|
|
1095
1269
|
activeAttemptId: uuid("active_attempt_id"),
|
|
1096
1270
|
lineage: jsonb("lineage").$type<Record<string, unknown>>().notNull().default({}),
|
|
1271
|
+
// Required immutable authority captured when the turn is accepted. These
|
|
1272
|
+
// columns are deliberately outside mutable metadata/lineage.
|
|
1273
|
+
initiatorKind: text("initiator_kind").notNull().default("service"),
|
|
1274
|
+
initiatorSubjectId: text("initiator_subject_id").notNull().default("unattributed-legacy"),
|
|
1275
|
+
initiatorContext: jsonb("initiator_context")
|
|
1276
|
+
.$type<Record<string, unknown>>()
|
|
1277
|
+
.notNull()
|
|
1278
|
+
.default({ backfill: true }),
|
|
1097
1279
|
cancelledBy: text("cancelled_by"),
|
|
1098
1280
|
cancelReason: text("cancel_reason"),
|
|
1099
1281
|
// Atomic per-turn toolspace call budget counter (migration 0043). Incremented
|
|
@@ -1142,10 +1324,22 @@ export const sessionTurnAttempts = pgTable(
|
|
|
1142
1324
|
workerId: text("worker_id"),
|
|
1143
1325
|
leaseId: text("lease_id"),
|
|
1144
1326
|
leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
|
|
1145
|
-
verifiedControlRevision: bigint("verified_control_revision", {
|
|
1327
|
+
verifiedControlRevision: bigint("verified_control_revision", {
|
|
1328
|
+
mode: "number",
|
|
1329
|
+
}).notNull(),
|
|
1330
|
+
// Immutable policy snapshot captured under the session lock at claim.
|
|
1331
|
+
mcpApprovalPolicies: jsonb("mcp_approval_policies")
|
|
1332
|
+
.$type<Record<string, SessionMcpApprovalPolicy>>()
|
|
1333
|
+
.notNull(),
|
|
1146
1334
|
startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1147
1335
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1148
1336
|
closedAt: timestamp("closed_at", { withTimezone: true }),
|
|
1337
|
+
// The cancelled activity writes this after losing inference, user-visible
|
|
1338
|
+
// output, and workspace-persistence authority. Fenced/idempotent cleanup
|
|
1339
|
+
// and telemetry may still finish. Temporal activity cancellation or
|
|
1340
|
+
// terminalization is transport state only; queue admission and clients use
|
|
1341
|
+
// this durable receipt as the physical-quiescence authority.
|
|
1342
|
+
quiescedAt: timestamp("quiesced_at", { withTimezone: true }),
|
|
1149
1343
|
},
|
|
1150
1344
|
(table) => ({
|
|
1151
1345
|
workspaceAccount: foreignKey({
|
|
@@ -1167,12 +1361,25 @@ export const sessionTurnAttempts = pgTable(
|
|
|
1167
1361
|
table.workspaceId,
|
|
1168
1362
|
table.id,
|
|
1169
1363
|
),
|
|
1364
|
+
ownershipIdentity: uniqueIndex("session_turn_attempts_human_input_owner_uq").on(
|
|
1365
|
+
table.accountId,
|
|
1366
|
+
table.workspaceId,
|
|
1367
|
+
table.sessionId,
|
|
1368
|
+
table.turnId,
|
|
1369
|
+
table.id,
|
|
1370
|
+
),
|
|
1170
1371
|
liveTurn: uniqueIndex("session_turn_attempts_live_turn_uq")
|
|
1171
1372
|
.on(table.workspaceId, table.turnId)
|
|
1172
1373
|
.where(sql`${table.state} in ('claimed', 'running')`),
|
|
1173
1374
|
liveSession: uniqueIndex("session_turn_attempts_live_session_uq")
|
|
1174
1375
|
.on(table.workspaceId, table.sessionId)
|
|
1175
1376
|
.where(sql`${table.state} in ('claimed', 'running')`),
|
|
1377
|
+
latestSessionAttempt: index("session_turn_attempts_latest_session_idx").on(
|
|
1378
|
+
table.workspaceId,
|
|
1379
|
+
table.sessionId,
|
|
1380
|
+
table.startedAt.desc(),
|
|
1381
|
+
table.id.desc(),
|
|
1382
|
+
),
|
|
1176
1383
|
dispatch: uniqueIndex("session_turn_attempts_dispatch_uq").on(
|
|
1177
1384
|
table.workspaceId,
|
|
1178
1385
|
table.temporalWorkflowRunId,
|
|
@@ -1217,7 +1424,9 @@ export const sessionCommandReceipts = pgTable(
|
|
|
1217
1424
|
targetTurnId: uuid("target_turn_id"),
|
|
1218
1425
|
operationKey: text("operation_key").notNull(),
|
|
1219
1426
|
canonicalRequestHash: text("canonical_request_hash").notNull(),
|
|
1220
|
-
appliedControlRevision: bigint("applied_control_revision", {
|
|
1427
|
+
appliedControlRevision: bigint("applied_control_revision", {
|
|
1428
|
+
mode: "number",
|
|
1429
|
+
}),
|
|
1221
1430
|
appliedQueueVersion: integer("applied_queue_version"),
|
|
1222
1431
|
appliedTurnVersion: integer("applied_turn_version"),
|
|
1223
1432
|
appliedDraftRevision: bigint("applied_draft_revision", { mode: "number" }),
|
|
@@ -1262,7 +1471,7 @@ export const sessionCommandReceipts = pgTable(
|
|
|
1262
1471
|
and ${table.actorAttemptId} is not null
|
|
1263
1472
|
and ${table.actorSubjectId} is null
|
|
1264
1473
|
) or (
|
|
1265
|
-
${table.actorType} in ('human', 'operator')
|
|
1474
|
+
${table.actorType} in ('human', 'operator', 'service')
|
|
1266
1475
|
and ${table.actorSubjectId} is not null
|
|
1267
1476
|
and ${table.actorAttemptId} is null
|
|
1268
1477
|
)`,
|
|
@@ -1286,7 +1495,11 @@ export const workspaceControlEvents = pgTable(
|
|
|
1286
1495
|
action: text("action").notNull(),
|
|
1287
1496
|
automatic: boolean("automatic").notNull().default(false),
|
|
1288
1497
|
reason: text("reason"),
|
|
1498
|
+
reasonOriginalBytes: integer("reason_original_bytes"),
|
|
1289
1499
|
actor: text("actor").notNull(),
|
|
1500
|
+
// Null is the rolling-upgrade shape for untouched, already-bounded legacy
|
|
1501
|
+
// rows. New writes and rewritten poison rows carry exact source byte facts.
|
|
1502
|
+
actorOriginalBytes: integer("actor_original_bytes"),
|
|
1290
1503
|
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1291
1504
|
},
|
|
1292
1505
|
(table) => ({
|
|
@@ -1390,6 +1603,7 @@ export const composerDrafts = pgTable(
|
|
|
1390
1603
|
text: text("text").notNull().default(""),
|
|
1391
1604
|
resources: jsonb("resources").$type<unknown[]>().notNull().default([]),
|
|
1392
1605
|
tools: jsonb("tools").$type<unknown[]>().notNull().default([]),
|
|
1606
|
+
toolsProvided: boolean("tools_provided").notNull().default(false),
|
|
1393
1607
|
model: text("model").notNull(),
|
|
1394
1608
|
reasoningEffort: text("reasoning_effort").notNull(),
|
|
1395
1609
|
sourceTurnId: uuid("source_turn_id"),
|
|
@@ -1628,7 +1842,7 @@ export const sessionGoals = pgTable(
|
|
|
1628
1842
|
}),
|
|
1629
1843
|
);
|
|
1630
1844
|
|
|
1631
|
-
//
|
|
1845
|
+
// credential allocator: one durable, coalescing capacity waiter per session. The row is both
|
|
1632
1846
|
// the wait state and the commit->signal outbox: capacity mutations increment
|
|
1633
1847
|
// wakeRevision in the SAME transaction as the mutation, while the session
|
|
1634
1848
|
// workflow advances observedWakeRevision only after it has re-evaluated the
|
|
@@ -1638,7 +1852,7 @@ export const sessionGoals = pgTable(
|
|
|
1638
1852
|
// The session/goal/turn foreign keys are declared in migration 0053 so the
|
|
1639
1853
|
// table keeps the same composite workspace-integrity posture as credential
|
|
1640
1854
|
// leases. Control is evaluated independently at admission and never changes a
|
|
1641
|
-
// capacity waiter's identity.
|
|
1855
|
+
// capacity waiter's identity. policy filter supplies policyHash when accepted-turn
|
|
1642
1856
|
// pool routing lands.
|
|
1643
1857
|
export const codexCapacityWaiters = pgTable(
|
|
1644
1858
|
"codex_capacity_waiters",
|
|
@@ -1744,6 +1958,41 @@ export const sessionEvents = pgTable(
|
|
|
1744
1958
|
table.sessionId,
|
|
1745
1959
|
table.createdAt,
|
|
1746
1960
|
),
|
|
1961
|
+
sessionTypeSequence: index("session_events_workspace_session_type_sequence_idx").on(
|
|
1962
|
+
table.workspaceId,
|
|
1963
|
+
table.sessionId,
|
|
1964
|
+
table.type,
|
|
1965
|
+
table.sequence,
|
|
1966
|
+
),
|
|
1967
|
+
monitoringTail: index("session_events_workspace_session_monitoring_tail_idx")
|
|
1968
|
+
.on(table.workspaceId, table.sessionId, table.sequence)
|
|
1969
|
+
.where(
|
|
1970
|
+
sql`${table.type} not in ('agent.message.delta', 'agent.reasoning.delta', 'sandbox.command.output.delta', 'terminal.pty.output.delta')`,
|
|
1971
|
+
),
|
|
1972
|
+
payloadBytes: check(
|
|
1973
|
+
"session_events_payload_bytes_check",
|
|
1974
|
+
sql`octet_length(${table.payload}::text) <= 65536`,
|
|
1975
|
+
),
|
|
1976
|
+
typeBytes: check(
|
|
1977
|
+
"session_events_type_bytes_check",
|
|
1978
|
+
sql`octet_length(${table.type}) <= 256 and position(E'\n' in ${table.type}) = 0 and position(E'\r' in ${table.type}) = 0`,
|
|
1979
|
+
),
|
|
1980
|
+
clientEventIdBytes: check(
|
|
1981
|
+
"session_events_client_event_id_bytes_check",
|
|
1982
|
+
sql`${table.clientEventId} is null or octet_length(${table.clientEventId}) <= 1024`,
|
|
1983
|
+
),
|
|
1984
|
+
producerIdBytes: check(
|
|
1985
|
+
"session_events_producer_id_bytes_check",
|
|
1986
|
+
sql`${table.producerId} is null or octet_length(${table.producerId}) <= 1024`,
|
|
1987
|
+
),
|
|
1988
|
+
turnAssociationBytes: check(
|
|
1989
|
+
"session_events_turn_association_bytes_check",
|
|
1990
|
+
sql`${table.turnAssociation} is null or octet_length(${table.turnAssociation}) <= 64`,
|
|
1991
|
+
),
|
|
1992
|
+
duplicateReasonBytes: check(
|
|
1993
|
+
"session_events_duplicate_reason_bytes_check",
|
|
1994
|
+
sql`${table.duplicateReason} is null or octet_length(${table.duplicateReason}) <= 4096`,
|
|
1995
|
+
),
|
|
1747
1996
|
}),
|
|
1748
1997
|
);
|
|
1749
1998
|
|
|
@@ -1758,7 +2007,9 @@ export const agentRunStates = pgTable("agent_run_states", {
|
|
|
1758
2007
|
sessionId: uuid("session_id")
|
|
1759
2008
|
.notNull()
|
|
1760
2009
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
1761
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2010
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2011
|
+
onDelete: "set null",
|
|
2012
|
+
}),
|
|
1762
2013
|
stateVersion: integer("state_version").notNull(),
|
|
1763
2014
|
serializedRunState: text("serialized_run_state").notNull(),
|
|
1764
2015
|
pendingApprovals: jsonb("pending_approvals").$type<unknown[]>().notNull().default([]),
|
|
@@ -1781,6 +2032,88 @@ export const agentRunStates = pgTable("agent_run_states", {
|
|
|
1781
2032
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1782
2033
|
});
|
|
1783
2034
|
|
|
2035
|
+
// Durable structured human-input requests. The creation attempt is immutable
|
|
2036
|
+
// provenance; legitimate resume attempts are newer owners of the SAME logical
|
|
2037
|
+
// turn. Settlement therefore fences on the active session/turn plus the
|
|
2038
|
+
// request's turn generation, never on creationAttemptId.
|
|
2039
|
+
export const sessionHumanInputRequests = pgTable(
|
|
2040
|
+
"session_human_input_requests",
|
|
2041
|
+
{
|
|
2042
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
2043
|
+
accountId: uuid("account_id").notNull(),
|
|
2044
|
+
workspaceId: uuid("workspace_id").notNull(),
|
|
2045
|
+
sessionId: uuid("session_id").notNull(),
|
|
2046
|
+
turnId: uuid("turn_id").notNull(),
|
|
2047
|
+
turnGeneration: integer("turn_generation").notNull(),
|
|
2048
|
+
creationAttemptId: uuid("creation_attempt_id").notNull(),
|
|
2049
|
+
toolCallId: text("tool_call_id").notNull(),
|
|
2050
|
+
status: text("status").notNull().default("pending"),
|
|
2051
|
+
questions: jsonb("questions").$type<HumanInputQuestion[]>().notNull(),
|
|
2052
|
+
allowSkip: boolean("allow_skip").notNull().default(false),
|
|
2053
|
+
response: jsonb("response").$type<HumanInputResponse>(),
|
|
2054
|
+
respondedBy: text("responded_by"),
|
|
2055
|
+
respondedAt: timestamp("responded_at", { withTimezone: true }),
|
|
2056
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }),
|
|
2057
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2058
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2059
|
+
},
|
|
2060
|
+
(table) => ({
|
|
2061
|
+
creationAttemptOwner: foreignKey({
|
|
2062
|
+
name: "session_human_input_requests_creation_attempt_fk",
|
|
2063
|
+
columns: [
|
|
2064
|
+
table.accountId,
|
|
2065
|
+
table.workspaceId,
|
|
2066
|
+
table.sessionId,
|
|
2067
|
+
table.turnId,
|
|
2068
|
+
table.creationAttemptId,
|
|
2069
|
+
],
|
|
2070
|
+
foreignColumns: [
|
|
2071
|
+
sessionTurnAttempts.accountId,
|
|
2072
|
+
sessionTurnAttempts.workspaceId,
|
|
2073
|
+
sessionTurnAttempts.sessionId,
|
|
2074
|
+
sessionTurnAttempts.turnId,
|
|
2075
|
+
sessionTurnAttempts.id,
|
|
2076
|
+
],
|
|
2077
|
+
}).onDelete("cascade"),
|
|
2078
|
+
toolCall: uniqueIndex("session_human_input_requests_tool_call_uq").on(
|
|
2079
|
+
table.workspaceId,
|
|
2080
|
+
table.sessionId,
|
|
2081
|
+
table.turnId,
|
|
2082
|
+
table.toolCallId,
|
|
2083
|
+
),
|
|
2084
|
+
pendingSession: index("session_human_input_requests_pending_session_idx")
|
|
2085
|
+
.on(table.workspaceId, table.sessionId, table.createdAt, table.id)
|
|
2086
|
+
.where(sql`${table.status} = 'pending'`),
|
|
2087
|
+
pendingExpiry: index("session_human_input_requests_pending_expiry_idx")
|
|
2088
|
+
.on(table.expiresAt, table.id)
|
|
2089
|
+
.where(sql`${table.status} = 'pending' and ${table.expiresAt} is not null`),
|
|
2090
|
+
status: check(
|
|
2091
|
+
"session_human_input_requests_status_check",
|
|
2092
|
+
sql`${table.status} in ('pending','answered','skipped','expired','cancelled')`,
|
|
2093
|
+
),
|
|
2094
|
+
generation: check(
|
|
2095
|
+
"session_human_input_requests_generation_check",
|
|
2096
|
+
sql`${table.turnGeneration} > 0`,
|
|
2097
|
+
),
|
|
2098
|
+
toolCallBytes: check(
|
|
2099
|
+
"session_human_input_requests_tool_call_bytes_check",
|
|
2100
|
+
sql`octet_length(${table.toolCallId}) between 1 and 1024`,
|
|
2101
|
+
),
|
|
2102
|
+
questionsBytes: check(
|
|
2103
|
+
"session_human_input_requests_questions_bytes_check",
|
|
2104
|
+
sql`octet_length(${table.questions}::text) <= 49152`,
|
|
2105
|
+
),
|
|
2106
|
+
responseBytes: check(
|
|
2107
|
+
"session_human_input_requests_response_bytes_check",
|
|
2108
|
+
sql`${table.response} is null or octet_length(${table.response}::text) <= 49152`,
|
|
2109
|
+
),
|
|
2110
|
+
actorBytes: check(
|
|
2111
|
+
"session_human_input_requests_actor_bytes_check",
|
|
2112
|
+
sql`${table.respondedBy} is null or octet_length(${table.respondedBy}) <= 1024`,
|
|
2113
|
+
),
|
|
2114
|
+
}),
|
|
2115
|
+
);
|
|
2116
|
+
|
|
1784
2117
|
// Conversation truth: ordered, verbatim SDK input items (issue #35). The
|
|
1785
2118
|
// model-facing memory store — unredacted and replay-ready. session_events
|
|
1786
2119
|
// remains the redacted human/audit timeline.
|
|
@@ -1797,7 +2130,9 @@ export const sessionHistoryItems = pgTable(
|
|
|
1797
2130
|
sessionId: uuid("session_id")
|
|
1798
2131
|
.notNull()
|
|
1799
2132
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
1800
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2133
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2134
|
+
onDelete: "set null",
|
|
2135
|
+
}),
|
|
1801
2136
|
// Numeric (not integer) so the synthetic compaction-summary row can be
|
|
1802
2137
|
// inserted at a FRACTIONAL position (boundaryPosition - 0.5) that sorts ahead
|
|
1803
2138
|
// of the kept tail without colliding with — and thus overwriting — the real
|
|
@@ -2061,7 +2396,9 @@ export const sessionRecordings = pgTable(
|
|
|
2061
2396
|
sessionId: uuid("session_id")
|
|
2062
2397
|
.notNull()
|
|
2063
2398
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
2064
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2399
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2400
|
+
onDelete: "set null",
|
|
2401
|
+
}),
|
|
2065
2402
|
|
|
2066
2403
|
state: text("state", { enum: sessionRecordingStateValues }).notNull(),
|
|
2067
2404
|
mode: text("mode", { enum: sessionRecordingModeValues }).notNull(),
|
|
@@ -2088,7 +2425,7 @@ export const sessionRecordings = pgTable(
|
|
|
2088
2425
|
}),
|
|
2089
2426
|
);
|
|
2090
2427
|
|
|
2091
|
-
// Workbench v2 turn-end workspace capture (
|
|
2428
|
+
// Workbench v2 turn-end workspace capture (model: sessionRecordings).
|
|
2092
2429
|
// One row per capture revision — a point-in-time snapshot of a session's changed
|
|
2093
2430
|
// files, probed off the live box at turn end. The manifest (tree index + per-repo
|
|
2094
2431
|
// status/diff + file index) and each after-image blob live in @opengeni/storage;
|
|
@@ -2111,7 +2448,9 @@ export const workspaceCaptures = pgTable(
|
|
|
2111
2448
|
sessionId: uuid("session_id")
|
|
2112
2449
|
.notNull()
|
|
2113
2450
|
.references(() => sessions.id, { onDelete: "cascade" }),
|
|
2114
|
-
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2451
|
+
turnId: uuid("turn_id").references(() => sessionTurns.id, {
|
|
2452
|
+
onDelete: "set null",
|
|
2453
|
+
}),
|
|
2115
2454
|
|
|
2116
2455
|
revision: bigint("revision", { mode: "number" }).notNull(),
|
|
2117
2456
|
leaseEpoch: integer("lease_epoch").notNull(),
|
|
@@ -2147,8 +2486,8 @@ export const workspaceCaptures = pgTable(
|
|
|
2147
2486
|
}),
|
|
2148
2487
|
);
|
|
2149
2488
|
|
|
2150
|
-
//
|
|
2151
|
-
//
|
|
2489
|
+
// Interactive PTY sessions. This is the persistent state needed for a live
|
|
2490
|
+
// terminal; file and Git reads remain stateless point
|
|
2152
2491
|
// queries; an interactive PTY is a live in-box process keyed by the SDK's numeric
|
|
2153
2492
|
// exec-session id (writeStdin({sessionId})). We map our UUID ptyId <-> that id,
|
|
2154
2493
|
// the owning workspace/session, the lease_epoch that fences it to the box it was
|
|
@@ -2193,7 +2532,7 @@ export const sandboxPtySessions = pgTable(
|
|
|
2193
2532
|
|
|
2194
2533
|
// ============================================================================
|
|
2195
2534
|
// Bring-your-own-compute (M2): first-class swappable sandboxes + enrollment +
|
|
2196
|
-
// metrics (migration 0024
|
|
2535
|
+
// metrics (migration 0024). The session→box
|
|
2197
2536
|
// binding becomes a per-session mutable, epoch-fenced active_sandbox_id pointer
|
|
2198
2537
|
// (declared on sessions above) that the routing proxy resolves PER TOOL CALL.
|
|
2199
2538
|
|
|
@@ -2267,7 +2606,7 @@ export const enrollments = pgTable(
|
|
|
2267
2606
|
);
|
|
2268
2607
|
|
|
2269
2608
|
// The OAuth 2.0 device-authorization (RFC 8628) PENDING request (M5, migration
|
|
2270
|
-
// 0025 /
|
|
2609
|
+
// 0025 / enrollment + §18 LOUD consent). An agent's `enroll` starts
|
|
2271
2610
|
// a flow (POST /enrollments/device/start) → one short-TTL, single-use row keyed by
|
|
2272
2611
|
// an opaque `device_code` (the agent polls with) + a short `user_code` (the user
|
|
2273
2612
|
// types at the approve page). The user (workspace-membership / workspace:admin
|
|
@@ -2308,7 +2647,9 @@ export const deviceEnrollmentRequests = pgTable(
|
|
|
2308
2647
|
machineName: text("machine_name"),
|
|
2309
2648
|
// The exposure the agent REQUESTED (whole-machine in v1; loudly consented at
|
|
2310
2649
|
// approve). Mirrors the enrollment column domain.
|
|
2311
|
-
requestedExposure: text("requested_exposure", {
|
|
2650
|
+
requestedExposure: text("requested_exposure", {
|
|
2651
|
+
enum: enrollmentExposureValues,
|
|
2652
|
+
})
|
|
2312
2653
|
.notNull()
|
|
2313
2654
|
.default("whole-machine"),
|
|
2314
2655
|
// The agent CAN offer a display (a real screen / Xvfb is available) — gates
|
|
@@ -2328,8 +2669,12 @@ export const deviceEnrollmentRequests = pgTable(
|
|
|
2328
2669
|
approvedAt: timestamp("approved_at", { withTimezone: true }),
|
|
2329
2670
|
// The enrollment + sandbox the approve produced (acceptance #2: an enrollment
|
|
2330
2671
|
// row AND a sandbox row appear). Null until approved.
|
|
2331
|
-
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2332
|
-
|
|
2672
|
+
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2673
|
+
onDelete: "set null",
|
|
2674
|
+
}),
|
|
2675
|
+
sandboxId: uuid("sandbox_id").references(() => sandboxes.id, {
|
|
2676
|
+
onDelete: "set null",
|
|
2677
|
+
}),
|
|
2333
2678
|
// The short-TTL expiry; a pending row past this is EXPIRED on poll.
|
|
2334
2679
|
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
2335
2680
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -2370,7 +2715,9 @@ export const sandboxes = pgTable(
|
|
|
2370
2715
|
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
2371
2716
|
kind: text("kind", { enum: sandboxKindValues }).notNull(),
|
|
2372
2717
|
name: text("name").notNull(),
|
|
2373
|
-
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2718
|
+
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, {
|
|
2719
|
+
onDelete: "set null",
|
|
2720
|
+
}),
|
|
2374
2721
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2375
2722
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2376
2723
|
},
|
|
@@ -2521,7 +2868,9 @@ export const scheduledTaskRuns = pgTable(
|
|
|
2521
2868
|
triggerType: text("trigger_type").notNull(),
|
|
2522
2869
|
scheduledAt: timestamp("scheduled_at", { withTimezone: true }),
|
|
2523
2870
|
firedAt: timestamp("fired_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2524
|
-
sessionId: uuid("session_id").references(() => sessions.id, {
|
|
2871
|
+
sessionId: uuid("session_id").references(() => sessions.id, {
|
|
2872
|
+
onDelete: "set null",
|
|
2873
|
+
}),
|
|
2525
2874
|
triggerEventId: uuid("trigger_event_id"),
|
|
2526
2875
|
// Stable Temporal producer identity. Activity replay/re-dispatch returns
|
|
2527
2876
|
// the exact run instead of allocating a second schedule source row.
|
|
@@ -2559,6 +2908,8 @@ export const githubInstallations = pgTable(
|
|
|
2559
2908
|
installationId: integer("installation_id").notNull(),
|
|
2560
2909
|
accountLogin: text("account_login"),
|
|
2561
2910
|
accountType: text("account_type"),
|
|
2911
|
+
repositoryScope: text("repository_scope").notNull().default("all"),
|
|
2912
|
+
linkedBySubjectId: text("linked_by_subject_id"),
|
|
2562
2913
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2563
2914
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2564
2915
|
},
|
|
@@ -2569,6 +2920,47 @@ export const githubInstallations = pgTable(
|
|
|
2569
2920
|
),
|
|
2570
2921
|
installation: index("github_installations_installation_idx").on(table.installationId),
|
|
2571
2922
|
workspace: index("github_installations_workspace_idx").on(table.workspaceId),
|
|
2923
|
+
repositoryScopeCheck: check(
|
|
2924
|
+
"github_installations_repository_scope_check",
|
|
2925
|
+
sql`${table.repositoryScope} in ('all', 'selected')`,
|
|
2926
|
+
),
|
|
2927
|
+
}),
|
|
2928
|
+
);
|
|
2929
|
+
|
|
2930
|
+
export const githubInstallationRepositories = pgTable(
|
|
2931
|
+
"github_installation_repositories",
|
|
2932
|
+
{
|
|
2933
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
2934
|
+
accountId: uuid("account_id")
|
|
2935
|
+
.notNull()
|
|
2936
|
+
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
2937
|
+
workspaceId: uuid("workspace_id")
|
|
2938
|
+
.notNull()
|
|
2939
|
+
.references(() => workspaces.id, { onDelete: "cascade" }),
|
|
2940
|
+
installationId: integer("installation_id").notNull(),
|
|
2941
|
+
repositoryId: bigint("repository_id", { mode: "number" }).notNull(),
|
|
2942
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2943
|
+
},
|
|
2944
|
+
(table) => ({
|
|
2945
|
+
installationRepository: uniqueIndex("github_install_repo_workspace_installation_repo_idx").on(
|
|
2946
|
+
table.workspaceId,
|
|
2947
|
+
table.installationId,
|
|
2948
|
+
table.repositoryId,
|
|
2949
|
+
),
|
|
2950
|
+
workspaceInstallation: index("github_install_repo_workspace_installation_idx").on(
|
|
2951
|
+
table.workspaceId,
|
|
2952
|
+
table.installationId,
|
|
2953
|
+
),
|
|
2954
|
+
workspaceAccount: foreignKey({
|
|
2955
|
+
columns: [table.workspaceId, table.accountId],
|
|
2956
|
+
foreignColumns: [workspaces.id, workspaces.accountId],
|
|
2957
|
+
name: "github_installation_repositories_workspace_account_fk",
|
|
2958
|
+
}).onDelete("cascade"),
|
|
2959
|
+
installationBinding: foreignKey({
|
|
2960
|
+
columns: [table.workspaceId, table.installationId],
|
|
2961
|
+
foreignColumns: [githubInstallations.workspaceId, githubInstallations.installationId],
|
|
2962
|
+
name: "github_installation_repositories_installation_fk",
|
|
2963
|
+
}).onDelete("cascade"),
|
|
2572
2964
|
}),
|
|
2573
2965
|
);
|
|
2574
2966
|
|
|
@@ -2588,10 +2980,26 @@ export const usageEvents = pgTable(
|
|
|
2588
2980
|
unit: text("unit").notNull(),
|
|
2589
2981
|
sourceResourceType: text("source_resource_type"),
|
|
2590
2982
|
sourceResourceId: text("source_resource_id"),
|
|
2983
|
+
// Exact execution source for host usage export. These are deliberately
|
|
2984
|
+
// validated soft references rather than cascading foreign keys: usage is
|
|
2985
|
+
// an immutable billing/audit fact and must retain its source identity after
|
|
2986
|
+
// a session or turn is deleted.
|
|
2987
|
+
sessionId: uuid("session_id"),
|
|
2988
|
+
turnId: uuid("turn_id"),
|
|
2989
|
+
turnAttemptId: uuid("turn_attempt_id"),
|
|
2990
|
+
initiatorKind: text("initiator_kind"),
|
|
2991
|
+
initiatorSubjectId: text("initiator_subject_id"),
|
|
2992
|
+
initiatorContext: jsonb("initiator_context")
|
|
2993
|
+
.$type<Record<string, unknown>>()
|
|
2994
|
+
.notNull()
|
|
2995
|
+
.default({}),
|
|
2996
|
+
origin: text("origin"),
|
|
2591
2997
|
idempotencyKey: text("idempotency_key").notNull(),
|
|
2592
2998
|
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
|
|
2593
2999
|
recordedAt: timestamp("recorded_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2594
|
-
exportedToBillingAt: timestamp("exported_to_billing_at", {
|
|
3000
|
+
exportedToBillingAt: timestamp("exported_to_billing_at", {
|
|
3001
|
+
withTimezone: true,
|
|
3002
|
+
}),
|
|
2595
3003
|
billingProviderEventId: text("billing_provider_event_id"),
|
|
2596
3004
|
},
|
|
2597
3005
|
(table) => ({
|
|
@@ -2606,6 +3014,212 @@ export const usageEvents = pgTable(
|
|
|
2606
3014
|
table.eventType,
|
|
2607
3015
|
table.occurredAt,
|
|
2608
3016
|
),
|
|
3017
|
+
workspaceSession: index("usage_events_workspace_session_idx").on(
|
|
3018
|
+
table.workspaceId,
|
|
3019
|
+
table.sessionId,
|
|
3020
|
+
table.occurredAt,
|
|
3021
|
+
),
|
|
3022
|
+
contextHierarchy: check(
|
|
3023
|
+
"usage_events_context_hierarchy_check",
|
|
3024
|
+
sql`(${table.turnId} is null or ${table.sessionId} is not null)
|
|
3025
|
+
and (${table.turnAttemptId} is null or ${table.turnId} is not null)`,
|
|
3026
|
+
),
|
|
3027
|
+
initiatorConsistent: check(
|
|
3028
|
+
"usage_events_initiator_check",
|
|
3029
|
+
sql`(${table.initiatorKind} is null and ${table.initiatorSubjectId} is null)
|
|
3030
|
+
or (${table.initiatorKind} in ('subject', 'service')
|
|
3031
|
+
and ${table.initiatorSubjectId} is not null
|
|
3032
|
+
and octet_length(${table.initiatorSubjectId}) between 1 and 1024)`,
|
|
3033
|
+
),
|
|
3034
|
+
attributionContextBytes: check(
|
|
3035
|
+
"usage_events_initiator_context_bytes_check",
|
|
3036
|
+
sql`octet_length(${table.initiatorContext}::text) <= 4096`,
|
|
3037
|
+
),
|
|
3038
|
+
originValid: check(
|
|
3039
|
+
"usage_events_origin_check",
|
|
3040
|
+
sql`${table.origin} is null or ${table.origin} in (
|
|
3041
|
+
'user', 'scheduled_task', 'api', 'goal', 'system', 'compaction'
|
|
3042
|
+
)`,
|
|
3043
|
+
),
|
|
3044
|
+
}),
|
|
3045
|
+
);
|
|
3046
|
+
|
|
3047
|
+
/** Singleton, migration-installed gate. Standalone defaults keep both off. */
|
|
3048
|
+
export const hostExportConfig = pgTable(
|
|
3049
|
+
"host_export_config",
|
|
3050
|
+
{
|
|
3051
|
+
id: integer("id").primaryKey().default(1),
|
|
3052
|
+
sessionEventsEnabled: boolean("session_events_enabled").notNull().default(false),
|
|
3053
|
+
usageEventsEnabled: boolean("usage_events_enabled").notNull().default(false),
|
|
3054
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3055
|
+
},
|
|
3056
|
+
(table) => ({
|
|
3057
|
+
singleton: check("host_export_config_singleton_check", sql`${table.id} = 1`),
|
|
3058
|
+
}),
|
|
3059
|
+
);
|
|
3060
|
+
|
|
3061
|
+
/**
|
|
3062
|
+
* Transactional delivery buffer. It intentionally has no tenant/source FKs:
|
|
3063
|
+
* a workspace deletion must not erase an already-committed, unacknowledged
|
|
3064
|
+
* host fact. Payloads are sanitized/bounded before they reach this table.
|
|
3065
|
+
*/
|
|
3066
|
+
export const hostExportOutbox = pgTable(
|
|
3067
|
+
"host_export_outbox",
|
|
3068
|
+
{
|
|
3069
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3070
|
+
exportKind: text("export_kind").notNull(),
|
|
3071
|
+
exportCursor: bigint("export_cursor", { mode: "bigint" }),
|
|
3072
|
+
sourceId: uuid("source_id").notNull(),
|
|
3073
|
+
accountId: uuid("account_id").notNull(),
|
|
3074
|
+
workspaceId: uuid("workspace_id").notNull(),
|
|
3075
|
+
sessionId: uuid("session_id"),
|
|
3076
|
+
rootSessionId: uuid("root_session_id"),
|
|
3077
|
+
turnId: uuid("turn_id"),
|
|
3078
|
+
turnGeneration: integer("turn_generation"),
|
|
3079
|
+
turnAttemptId: uuid("turn_attempt_id"),
|
|
3080
|
+
sessionSequence: integer("session_sequence"),
|
|
3081
|
+
clientEventId: text("client_event_id"),
|
|
3082
|
+
turnAssociation: text("turn_association"),
|
|
3083
|
+
duplicateOfEventId: uuid("duplicate_of_event_id"),
|
|
3084
|
+
duplicateReason: text("duplicate_reason"),
|
|
3085
|
+
eventType: text("event_type").notNull(),
|
|
3086
|
+
idempotencyKey: text("idempotency_key").notNull(),
|
|
3087
|
+
initiator: jsonb("initiator").$type<Record<string, unknown> | null>(),
|
|
3088
|
+
initiatorContext: jsonb("initiator_context")
|
|
3089
|
+
.$type<Record<string, unknown>>()
|
|
3090
|
+
.notNull()
|
|
3091
|
+
.default({}),
|
|
3092
|
+
origin: text("origin"),
|
|
3093
|
+
payload: jsonb("payload").$type<unknown>().notNull(),
|
|
3094
|
+
envelopeBytes: integer("envelope_bytes").notNull(),
|
|
3095
|
+
occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(),
|
|
3096
|
+
sourceRecordedAt: timestamp("source_recorded_at", { withTimezone: true }).notNull(),
|
|
3097
|
+
enqueuedAt: timestamp("enqueued_at", { withTimezone: true }).notNull(),
|
|
3098
|
+
},
|
|
3099
|
+
(table) => ({
|
|
3100
|
+
kindValid: check(
|
|
3101
|
+
"host_export_outbox_kind_check",
|
|
3102
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3103
|
+
),
|
|
3104
|
+
rootSessionCaptured: check(
|
|
3105
|
+
"host_export_outbox_root_session_check",
|
|
3106
|
+
sql`${table.sessionId} is null or ${table.rootSessionId} is not null`,
|
|
3107
|
+
),
|
|
3108
|
+
sourceUnique: uniqueIndex("host_export_outbox_source_uq").on(table.exportKind, table.sourceId),
|
|
3109
|
+
cursorUnique: uniqueIndex("host_export_outbox_cursor_uq")
|
|
3110
|
+
.on(table.exportKind, table.exportCursor)
|
|
3111
|
+
.where(sql`${table.exportCursor} is not null`),
|
|
3112
|
+
unassigned: index("host_export_outbox_unassigned_idx")
|
|
3113
|
+
.on(table.exportKind, table.enqueuedAt, table.id)
|
|
3114
|
+
.where(sql`${table.exportCursor} is null`),
|
|
3115
|
+
unassignedSession: index("host_export_outbox_unassigned_session_idx")
|
|
3116
|
+
.on(table.exportKind, table.sessionId, table.sessionSequence)
|
|
3117
|
+
.where(sql`${table.exportCursor} is null and ${table.sessionId} is not null`),
|
|
3118
|
+
contextBytes: check(
|
|
3119
|
+
"host_export_outbox_context_bytes_check",
|
|
3120
|
+
sql`octet_length(${table.initiatorContext}::text) <= 4096`,
|
|
3121
|
+
),
|
|
3122
|
+
originValid: check(
|
|
3123
|
+
"host_export_outbox_origin_check",
|
|
3124
|
+
sql`${table.origin} is null or ${table.origin} in (
|
|
3125
|
+
'user', 'scheduled_task', 'api', 'goal', 'system', 'compaction'
|
|
3126
|
+
)`,
|
|
3127
|
+
),
|
|
3128
|
+
}),
|
|
3129
|
+
);
|
|
3130
|
+
|
|
3131
|
+
export const hostExportCursorState = pgTable(
|
|
3132
|
+
"host_export_cursor_state",
|
|
3133
|
+
{
|
|
3134
|
+
exportKind: text("export_kind").primaryKey(),
|
|
3135
|
+
nextCursor: bigint("next_cursor", { mode: "bigint" }).notNull().default(1n),
|
|
3136
|
+
prunedThrough: bigint("pruned_through", { mode: "bigint" }).notNull().default(0n),
|
|
3137
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3138
|
+
},
|
|
3139
|
+
(table) => ({
|
|
3140
|
+
kindValid: check(
|
|
3141
|
+
"host_export_cursor_state_kind_check",
|
|
3142
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3143
|
+
),
|
|
3144
|
+
cursorValid: check(
|
|
3145
|
+
"host_export_cursor_state_next_check",
|
|
3146
|
+
sql`${table.nextCursor} > 0 and ${table.prunedThrough} >= 0
|
|
3147
|
+
and ${table.prunedThrough} < ${table.nextCursor}`,
|
|
3148
|
+
),
|
|
3149
|
+
}),
|
|
3150
|
+
);
|
|
3151
|
+
|
|
3152
|
+
/** Named at-least-once consumer checkpoint and one-batch lease. */
|
|
3153
|
+
export const hostExportConsumers = pgTable(
|
|
3154
|
+
"host_export_consumers",
|
|
3155
|
+
{
|
|
3156
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3157
|
+
consumerId: text("consumer_id").notNull(),
|
|
3158
|
+
exportKind: text("export_kind").notNull(),
|
|
3159
|
+
checkpoint: bigint("checkpoint", { mode: "bigint" }).notNull().default(0n),
|
|
3160
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
3161
|
+
leaseToken: uuid("lease_token"),
|
|
3162
|
+
leaseHolderId: text("lease_holder_id"),
|
|
3163
|
+
leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
|
|
3164
|
+
leaseFrom: bigint("lease_from", { mode: "bigint" }),
|
|
3165
|
+
leaseThrough: bigint("lease_through", { mode: "bigint" }),
|
|
3166
|
+
consecutiveFailures: integer("consecutive_failures").notNull().default(0),
|
|
3167
|
+
nextAttemptAt: timestamp("next_attempt_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3168
|
+
lastError: text("last_error"),
|
|
3169
|
+
lastErrorAt: timestamp("last_error_at", { withTimezone: true }),
|
|
3170
|
+
blockedAt: timestamp("blocked_at", { withTimezone: true }),
|
|
3171
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3172
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3173
|
+
},
|
|
3174
|
+
(table) => ({
|
|
3175
|
+
consumerKind: uniqueIndex("host_export_consumers_kind_id_uq").on(
|
|
3176
|
+
table.exportKind,
|
|
3177
|
+
table.consumerId,
|
|
3178
|
+
),
|
|
3179
|
+
kindValid: check(
|
|
3180
|
+
"host_export_consumers_kind_check",
|
|
3181
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3182
|
+
),
|
|
3183
|
+
checkpointValid: check("host_export_consumers_checkpoint_check", sql`${table.checkpoint} >= 0`),
|
|
3184
|
+
due: index("host_export_consumers_due_idx").on(
|
|
3185
|
+
table.enabled,
|
|
3186
|
+
table.blockedAt,
|
|
3187
|
+
table.nextAttemptAt,
|
|
3188
|
+
),
|
|
3189
|
+
}),
|
|
3190
|
+
);
|
|
3191
|
+
|
|
3192
|
+
/** Explicit poison-row disposition; transient failures never write here. */
|
|
3193
|
+
export const hostExportDeadLetters = pgTable(
|
|
3194
|
+
"host_export_dead_letters",
|
|
3195
|
+
{
|
|
3196
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
3197
|
+
consumerId: text("consumer_id").notNull(),
|
|
3198
|
+
exportKind: text("export_kind").notNull(),
|
|
3199
|
+
exportCursor: bigint("export_cursor", { mode: "bigint" }).notNull(),
|
|
3200
|
+
sourceId: uuid("source_id").notNull(),
|
|
3201
|
+
reason: text("reason").notNull(),
|
|
3202
|
+
envelope: jsonb("envelope").$type<Record<string, unknown>>().notNull(),
|
|
3203
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
3204
|
+
},
|
|
3205
|
+
(table) => ({
|
|
3206
|
+
consumerCursor: uniqueIndex("host_export_dead_letters_consumer_cursor_uq").on(
|
|
3207
|
+
table.exportKind,
|
|
3208
|
+
table.consumerId,
|
|
3209
|
+
table.exportCursor,
|
|
3210
|
+
),
|
|
3211
|
+
kindValid: check(
|
|
3212
|
+
"host_export_dead_letters_kind_check",
|
|
3213
|
+
sql`${table.exportKind} in ('session_event', 'usage_event')`,
|
|
3214
|
+
),
|
|
3215
|
+
reasonValid: check(
|
|
3216
|
+
"host_export_dead_letters_reason_check",
|
|
3217
|
+
sql`length(${table.reason}) between 1 and 500`,
|
|
3218
|
+
),
|
|
3219
|
+
envelopeBytes: check(
|
|
3220
|
+
"host_export_dead_letters_envelope_bytes_check",
|
|
3221
|
+
sql`pg_column_size(${table.envelope}) <= 114688`,
|
|
3222
|
+
),
|
|
2609
3223
|
}),
|
|
2610
3224
|
);
|
|
2611
3225
|
|
|
@@ -2616,7 +3230,9 @@ export const creditLedgerEntries = pgTable(
|
|
|
2616
3230
|
accountId: uuid("account_id")
|
|
2617
3231
|
.notNull()
|
|
2618
3232
|
.references(() => managedAccounts.id, { onDelete: "cascade" }),
|
|
2619
|
-
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3233
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3234
|
+
onDelete: "set null",
|
|
3235
|
+
}),
|
|
2620
3236
|
type: text("type").notNull(),
|
|
2621
3237
|
amountMicros: bigint("amount_micros", { mode: "number" }).notNull(),
|
|
2622
3238
|
currency: text("currency").notNull().default("usd"),
|
|
@@ -2674,8 +3290,12 @@ export const auditEvents = pgTable(
|
|
|
2674
3290
|
"audit_events",
|
|
2675
3291
|
{
|
|
2676
3292
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
2677
|
-
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
2678
|
-
|
|
3293
|
+
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
3294
|
+
onDelete: "set null",
|
|
3295
|
+
}),
|
|
3296
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3297
|
+
onDelete: "set null",
|
|
3298
|
+
}),
|
|
2679
3299
|
subjectId: text("subject_id"),
|
|
2680
3300
|
action: text("action").notNull(),
|
|
2681
3301
|
targetType: text("target_type"),
|
|
@@ -2770,8 +3390,12 @@ export const capabilityCatalogItems = pgTable(
|
|
|
2770
3390
|
"capability_catalog_items",
|
|
2771
3391
|
{
|
|
2772
3392
|
id: text("id").notNull(),
|
|
2773
|
-
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
2774
|
-
|
|
3393
|
+
accountId: uuid("account_id").references(() => managedAccounts.id, {
|
|
3394
|
+
onDelete: "cascade",
|
|
3395
|
+
}),
|
|
3396
|
+
workspaceId: uuid("workspace_id").references(() => workspaces.id, {
|
|
3397
|
+
onDelete: "cascade",
|
|
3398
|
+
}),
|
|
2775
3399
|
kind: text("kind").notNull(),
|
|
2776
3400
|
source: text("source").notNull().default("manual"),
|
|
2777
3401
|
name: text("name").notNull(),
|