@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/dist/schema.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'drizzle-orm';
|
|
2
2
|
import 'drizzle-orm/pg-core';
|
|
3
|
-
export {
|
|
3
|
+
export { n as agentRunStates, o as apiKeys, p as auditEvents, q as billingCustomers, r as capabilityCatalogItems, t as capabilityInstallations, u as codexCapacityWaiters, v as codexCredentialLeases, x as codexResetRedemptionAttempts, y as codexRotationSettings, z as codexSubscriptionCredentials, d as composerDrafts, A as connections, B as creditLedgerEntries, C as deviceEnrollmentRequests, h as deviceEnrollmentStatusValues, D as documentBases, E as documentChunks, F as documents, g as enrollmentExposureValues, f as enrollmentOsValues, i as enrollmentStatusValues, G as enrollments, H as fileUploads, I as files, J as githubInstallationRepositories, K as githubInstallations, L as hostExportConfig, M as hostExportConsumers, N as hostExportCursorState, O as hostExportDeadLetters, P as hostExportOutbox, Q as importBatches, R as integrationOauthClients, S as integrationOauthStateNonces, T as knowledgeMemories, U as machineMetricsLatest, V as machineMetricsSeries, W as managedAccounts, X as packInstallations, Y as rigChanges, Z as rigVersions, _ as rigs, j as sandboxKindValues, $ as sandboxLeaseHolders, a0 as sandboxLeaseLivenessValues, a1 as sandboxLeases, a2 as sandboxPtySessions, a3 as sandboxSessionEnvelopes, a4 as sandboxes, a5 as scheduledTaskRuns, a6 as scheduledTasks, a7 as sessionAttemptInterruptions, s as sessionCommandReceipts, a8 as sessionEvents, a9 as sessionGoals, aa as sessionHistoryItems, ab as sessionHumanInputRequests, ac as sessionListSnapshots, ad as sessionMcpServers, ae as sessionPendingToolCalls, af as sessionPins, k as sessionRecordingCodecValues, l as sessionRecordingModeValues, m as sessionRecordingStateValues, ag as sessionRecordings, ah as sessionSystemUpdateOutbox, ai as sessionSystemUpdates, c as sessionTurnAttempts, b as sessionTurns, aj as sessionWorkflowWakeOutbox, a as sessions, ak as socialConnections, al as socialPosts, am as stripeWebhookEvents, an as usageEvents, ao as workspaceCaptures, ap as workspaceControlEvents, aq as workspaceInferenceControls, ar as workspaceMemberships, as as workspaceModelPolicies, at as workspacePacks, au as workspaceSessionActivityRevisions, av as workspaceVariableSetVariables, aw as workspaceVariableSets, w as workspaces } from './schema-CqkzrBRS.js';
|
package/dist/schema.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
capabilityInstallations,
|
|
8
8
|
codexCapacityWaiters,
|
|
9
9
|
codexCredentialLeases,
|
|
10
|
+
codexResetRedemptionAttempts,
|
|
10
11
|
codexRotationSettings,
|
|
11
12
|
codexSubscriptionCredentials,
|
|
12
13
|
composerDrafts,
|
|
@@ -23,7 +24,13 @@ import {
|
|
|
23
24
|
enrollments,
|
|
24
25
|
fileUploads,
|
|
25
26
|
files,
|
|
27
|
+
githubInstallationRepositories,
|
|
26
28
|
githubInstallations,
|
|
29
|
+
hostExportConfig,
|
|
30
|
+
hostExportConsumers,
|
|
31
|
+
hostExportCursorState,
|
|
32
|
+
hostExportDeadLetters,
|
|
33
|
+
hostExportOutbox,
|
|
27
34
|
importBatches,
|
|
28
35
|
integrationOauthClients,
|
|
29
36
|
integrationOauthStateNonces,
|
|
@@ -49,6 +56,7 @@ import {
|
|
|
49
56
|
sessionEvents,
|
|
50
57
|
sessionGoals,
|
|
51
58
|
sessionHistoryItems,
|
|
59
|
+
sessionHumanInputRequests,
|
|
52
60
|
sessionListSnapshots,
|
|
53
61
|
sessionMcpServers,
|
|
54
62
|
sessionPendingToolCalls,
|
|
@@ -73,10 +81,11 @@ import {
|
|
|
73
81
|
workspaceMemberships,
|
|
74
82
|
workspaceModelPolicies,
|
|
75
83
|
workspacePacks,
|
|
84
|
+
workspaceSessionActivityRevisions,
|
|
76
85
|
workspaceVariableSetVariables,
|
|
77
86
|
workspaceVariableSets,
|
|
78
87
|
workspaces
|
|
79
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-P6PKXY5W.js";
|
|
80
89
|
import "./chunk-PZ5AY32C.js";
|
|
81
90
|
export {
|
|
82
91
|
agentRunStates,
|
|
@@ -87,6 +96,7 @@ export {
|
|
|
87
96
|
capabilityInstallations,
|
|
88
97
|
codexCapacityWaiters,
|
|
89
98
|
codexCredentialLeases,
|
|
99
|
+
codexResetRedemptionAttempts,
|
|
90
100
|
codexRotationSettings,
|
|
91
101
|
codexSubscriptionCredentials,
|
|
92
102
|
composerDrafts,
|
|
@@ -103,7 +113,13 @@ export {
|
|
|
103
113
|
enrollments,
|
|
104
114
|
fileUploads,
|
|
105
115
|
files,
|
|
116
|
+
githubInstallationRepositories,
|
|
106
117
|
githubInstallations,
|
|
118
|
+
hostExportConfig,
|
|
119
|
+
hostExportConsumers,
|
|
120
|
+
hostExportCursorState,
|
|
121
|
+
hostExportDeadLetters,
|
|
122
|
+
hostExportOutbox,
|
|
107
123
|
importBatches,
|
|
108
124
|
integrationOauthClients,
|
|
109
125
|
integrationOauthStateNonces,
|
|
@@ -129,6 +145,7 @@ export {
|
|
|
129
145
|
sessionEvents,
|
|
130
146
|
sessionGoals,
|
|
131
147
|
sessionHistoryItems,
|
|
148
|
+
sessionHumanInputRequests,
|
|
132
149
|
sessionListSnapshots,
|
|
133
150
|
sessionMcpServers,
|
|
134
151
|
sessionPendingToolCalls,
|
|
@@ -153,6 +170,7 @@ export {
|
|
|
153
170
|
workspaceMemberships,
|
|
154
171
|
workspaceModelPolicies,
|
|
155
172
|
workspacePacks,
|
|
173
|
+
workspaceSessionActivityRevisions,
|
|
156
174
|
workspaceVariableSetVariables,
|
|
157
175
|
workspaceVariableSets,
|
|
158
176
|
workspaces
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
-- 0053_codex_credential_leases.sql
|
|
2
|
-
--
|
|
2
|
+
-- tracking-21: fair, atomic Codex subscription selection across worker replicas.
|
|
3
3
|
--
|
|
4
4
|
-- Every field in this migration is workspace-local. In particular, the same
|
|
5
5
|
-- external ChatGPT subscription connected in two workspaces is intentionally
|
|
@@ -227,4 +227,4 @@ BEGIN
|
|
|
227
227
|
app_role
|
|
228
228
|
);
|
|
229
229
|
END IF;
|
|
230
|
-
END $$;
|
|
230
|
+
END $$;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
--
|
|
1
|
+
-- tracking-18/tracking-21/tracking-22: one prompt queue, one current inference, portable
|
|
2
2
|
-- compaction, attempt-fenced publication, coalesced internal updates, and
|
|
3
3
|
-- session/workspace Pause.
|
|
4
4
|
--
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
--
|
|
1
|
+
-- tracking-50: replace the blind periodic scan of "enrollable" sessions with a
|
|
2
2
|
-- transactional, revisioned, coalescing delivery ledger. Postgres is the
|
|
3
3
|
-- durable work source; a Temporal signal is an idempotent nudge.
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
--
|
|
1
|
+
-- tracking-44: session list reads are serializable and subject-scoped. Performing a
|
|
2
2
|
-- global expired-row delete inside every read makes concurrent API replicas
|
|
3
3
|
-- conflict on the same rows. Move TTL cleanup to one bounded SKIP LOCKED
|
|
4
4
|
-- reaper operation and keep the request transaction strictly request-local.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
-- deployment-mode: maintenance
|
|
2
|
-
--
|
|
2
|
+
-- tracking-18/tracking-54: one-way queue/control maintenance cutover. Old workers are
|
|
3
3
|
-- drained before this migration; obsolete columns are removed in this same
|
|
4
4
|
-- transaction so no mixed runtime architecture can survive deployment.
|
|
5
5
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- tracking-36: the strategy picker is gone — rotation-enabled always behaves as
|
|
3
|
+
-- sticky-sharded (worker-side effectiveRotationStrategy normalizes every read).
|
|
4
|
+
-- Backfill stored legacy values so the column tells the truth going forward.
|
|
5
|
+
-- The column itself is KEPT (old-binary rollback safety): an old worker reading
|
|
6
|
+
-- 'sharded' runs the strategy that was already live on every real workspace.
|
|
7
|
+
|
|
8
|
+
SET lock_timeout = '5s';
|
|
9
|
+
SET statement_timeout = '10min';
|
|
10
|
+
|
|
11
|
+
UPDATE "codex_rotation_settings"
|
|
12
|
+
SET "rotation_strategy" = 'sharded'
|
|
13
|
+
WHERE "rotation_strategy" IS DISTINCT FROM 'sharded';
|
|
14
|
+
|
|
15
|
+
ALTER TABLE "codex_rotation_settings" ALTER COLUMN "rotation_strategy" SET DEFAULT 'sharded';
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Codex quota: trustworthy Codex subscription overview, allocator OCC/audit, and
|
|
3
|
+
-- owning-human reset-credit redemption.
|
|
4
|
+
--
|
|
5
|
+
-- Old API binaries do not write connected_by_subject_id and do not fence
|
|
6
|
+
-- disconnect/reconnect against unresolved provider attempts. Drain every old
|
|
7
|
+
-- API replica before applying this migration, then start only this revision;
|
|
8
|
+
-- mixed-version writers would invalidate the owning-human redemption boundary.
|
|
9
|
+
--
|
|
10
|
+
-- tracking-21 exclusively owns allocator_enabled in migration 0053. This migration
|
|
11
|
+
-- only adds the independent OCC/audit fields, provider summary cache, connecting
|
|
12
|
+
-- human attribution, and the durable ambiguity-safe redemption state machine.
|
|
13
|
+
|
|
14
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
15
|
+
ADD COLUMN IF NOT EXISTS "allocator_version" integer NOT NULL DEFAULT 1,
|
|
16
|
+
ADD COLUMN IF NOT EXISTS "allocator_updated_by_subject_id" text,
|
|
17
|
+
ADD COLUMN IF NOT EXISTS "allocator_updated_at" timestamptz,
|
|
18
|
+
ADD COLUMN IF NOT EXISTS "reset_credit_available_count" integer,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS "reset_credits_checked_at" timestamptz,
|
|
20
|
+
ADD COLUMN IF NOT EXISTS "connected_by_subject_id" text;
|
|
21
|
+
|
|
22
|
+
DO $$
|
|
23
|
+
BEGIN
|
|
24
|
+
IF NOT EXISTS (
|
|
25
|
+
SELECT 1 FROM pg_constraint
|
|
26
|
+
WHERE conname = 'codex_subscription_credentials_allocator_version_check'
|
|
27
|
+
AND conrelid = 'codex_subscription_credentials'::regclass
|
|
28
|
+
) THEN
|
|
29
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
30
|
+
ADD CONSTRAINT "codex_subscription_credentials_allocator_version_check"
|
|
31
|
+
CHECK (allocator_version > 0) NOT VALID;
|
|
32
|
+
END IF;
|
|
33
|
+
IF NOT EXISTS (
|
|
34
|
+
SELECT 1 FROM pg_constraint
|
|
35
|
+
WHERE conname = 'codex_subscription_credentials_reset_count_check'
|
|
36
|
+
AND conrelid = 'codex_subscription_credentials'::regclass
|
|
37
|
+
) THEN
|
|
38
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
39
|
+
ADD CONSTRAINT "codex_subscription_credentials_reset_count_check"
|
|
40
|
+
CHECK (reset_credit_available_count IS NULL OR reset_credit_available_count >= 0) NOT VALID;
|
|
41
|
+
END IF;
|
|
42
|
+
IF NOT EXISTS (
|
|
43
|
+
SELECT 1 FROM pg_constraint
|
|
44
|
+
WHERE conname = 'codex_subscription_credentials_human_owner_check'
|
|
45
|
+
AND conrelid = 'codex_subscription_credentials'::regclass
|
|
46
|
+
) THEN
|
|
47
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
48
|
+
ADD CONSTRAINT "codex_subscription_credentials_human_owner_check"
|
|
49
|
+
CHECK (connected_by_subject_id IS NULL OR connected_by_subject_id LIKE 'user:_%') NOT VALID;
|
|
50
|
+
END IF;
|
|
51
|
+
END $$;
|
|
52
|
+
|
|
53
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
54
|
+
VALIDATE CONSTRAINT "codex_subscription_credentials_allocator_version_check";
|
|
55
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
56
|
+
VALIDATE CONSTRAINT "codex_subscription_credentials_reset_count_check";
|
|
57
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
58
|
+
VALIDATE CONSTRAINT "codex_subscription_credentials_human_owner_check";
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS "codex_reset_redemption_attempts" (
|
|
61
|
+
-- Browser-generated logical id. Reusing it is what prevents one ambiguous
|
|
62
|
+
-- click/reload/retry from becoming a second logical redemption.
|
|
63
|
+
"id" uuid PRIMARY KEY NOT NULL,
|
|
64
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
65
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
66
|
+
"credential_id" uuid NOT NULL,
|
|
67
|
+
"subject_id" text NOT NULL,
|
|
68
|
+
-- SHA-256 of the Better Auth session id. The raw session id and cookies never
|
|
69
|
+
-- enter product tables, logs, audit metadata, or browser responses.
|
|
70
|
+
"browser_session_hash" text NOT NULL,
|
|
71
|
+
-- Required for an ambiguity retry. It is intentionally absent from audit and
|
|
72
|
+
-- event metadata; provider opaque ids are not observability dimensions.
|
|
73
|
+
"credit_id" text NOT NULL,
|
|
74
|
+
-- Generated once by the server and reused for every upstream retry.
|
|
75
|
+
"upstream_idempotency_key" uuid NOT NULL DEFAULT gen_random_uuid(),
|
|
76
|
+
"status" text NOT NULL DEFAULT 'processing',
|
|
77
|
+
"outcome" text,
|
|
78
|
+
"claim_holder_id" uuid,
|
|
79
|
+
"claim_expires_at" timestamptz,
|
|
80
|
+
"confirmation_expires_at" timestamptz NOT NULL,
|
|
81
|
+
"provider_started_at" timestamptz,
|
|
82
|
+
"completed_at" timestamptz,
|
|
83
|
+
"last_failure_kind" text,
|
|
84
|
+
"retry_count" integer NOT NULL DEFAULT 0,
|
|
85
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
86
|
+
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
87
|
+
CONSTRAINT "codex_reset_redemption_workspace_account_fk"
|
|
88
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
89
|
+
REFERENCES "workspaces"("id", "account_id")
|
|
90
|
+
ON DELETE CASCADE,
|
|
91
|
+
-- Deliberately no credential FK: a disconnect must never cascade-delete the
|
|
92
|
+
-- only durable upstream redeem_request_id after provider work may have begun.
|
|
93
|
+
-- Accessors validate and lock the workspace credential before every send;
|
|
94
|
+
-- completed history may outlive a later safe credential disconnect.
|
|
95
|
+
CONSTRAINT "codex_reset_redemption_status_check"
|
|
96
|
+
CHECK (status IN ('processing', 'provider_started', 'completed')),
|
|
97
|
+
CONSTRAINT "codex_reset_redemption_outcome_check"
|
|
98
|
+
CHECK (outcome IS NULL OR outcome IN ('reset', 'nothingToReset', 'noCredit', 'alreadyRedeemed')),
|
|
99
|
+
CONSTRAINT "codex_reset_redemption_completed_check"
|
|
100
|
+
CHECK ((status = 'completed') = (outcome IS NOT NULL AND completed_at IS NOT NULL)),
|
|
101
|
+
CONSTRAINT "codex_reset_redemption_retry_count_check"
|
|
102
|
+
CHECK (retry_count >= 0),
|
|
103
|
+
CONSTRAINT "codex_reset_redemption_human_subject_check"
|
|
104
|
+
CHECK (subject_id LIKE 'user:_%')
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
-- Keep this additive/idempotent for forward recovery from a previously completed
|
|
108
|
+
-- equivalent schema state.
|
|
109
|
+
DO $$
|
|
110
|
+
BEGIN
|
|
111
|
+
IF NOT EXISTS (
|
|
112
|
+
SELECT 1 FROM pg_constraint
|
|
113
|
+
WHERE conname = 'codex_reset_redemption_human_subject_check'
|
|
114
|
+
AND conrelid = 'codex_reset_redemption_attempts'::regclass
|
|
115
|
+
) THEN
|
|
116
|
+
ALTER TABLE "codex_reset_redemption_attempts"
|
|
117
|
+
ADD CONSTRAINT "codex_reset_redemption_human_subject_check"
|
|
118
|
+
CHECK (subject_id LIKE 'user:_%') NOT VALID;
|
|
119
|
+
END IF;
|
|
120
|
+
END $$;
|
|
121
|
+
ALTER TABLE "codex_reset_redemption_attempts"
|
|
122
|
+
VALIDATE CONSTRAINT "codex_reset_redemption_human_subject_check";
|
|
123
|
+
|
|
124
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "codex_reset_redemption_upstream_key_idx"
|
|
125
|
+
ON "codex_reset_redemption_attempts" ("upstream_idempotency_key");
|
|
126
|
+
-- A provider credit can have only one active or successfully consumed logical
|
|
127
|
+
-- attempt. Provider outcomes nothingToReset/noCredit are explicitly non-success
|
|
128
|
+
-- outcomes and must not permanently prevent a later, newly confirmed attempt.
|
|
129
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "codex_reset_redemption_credential_credit_idx"
|
|
130
|
+
ON "codex_reset_redemption_attempts" ("workspace_id", "credential_id", "credit_id")
|
|
131
|
+
WHERE status <> 'completed' OR outcome IN ('reset', 'alreadyRedeemed');
|
|
132
|
+
CREATE INDEX IF NOT EXISTS "codex_reset_redemption_workspace_credential_idx"
|
|
133
|
+
ON "codex_reset_redemption_attempts" ("workspace_id", "credential_id", "created_at" DESC);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS "codex_reset_redemption_claim_expiry_idx"
|
|
135
|
+
ON "codex_reset_redemption_attempts" ("claim_expires_at")
|
|
136
|
+
WHERE status <> 'completed';
|
|
137
|
+
|
|
138
|
+
ALTER TABLE "codex_reset_redemption_attempts" ENABLE ROW LEVEL SECURITY;
|
|
139
|
+
ALTER TABLE "codex_reset_redemption_attempts" FORCE ROW LEVEL SECURITY;
|
|
140
|
+
DO $$
|
|
141
|
+
BEGIN
|
|
142
|
+
IF EXISTS (
|
|
143
|
+
SELECT 1 FROM pg_policies
|
|
144
|
+
WHERE schemaname = current_schema()
|
|
145
|
+
AND tablename = 'codex_reset_redemption_attempts'
|
|
146
|
+
AND policyname = 'workspace_isolation'
|
|
147
|
+
) THEN
|
|
148
|
+
DROP POLICY workspace_isolation ON "codex_reset_redemption_attempts";
|
|
149
|
+
END IF;
|
|
150
|
+
END $$;
|
|
151
|
+
|
|
152
|
+
CREATE POLICY workspace_isolation ON "codex_reset_redemption_attempts"
|
|
153
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
154
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
155
|
+
|
|
156
|
+
DO $$
|
|
157
|
+
DECLARE
|
|
158
|
+
target_schema text := current_schema();
|
|
159
|
+
app_role text := 'opengeni_app';
|
|
160
|
+
BEGIN
|
|
161
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = app_role) THEN
|
|
162
|
+
EXECUTE format(
|
|
163
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO %I',
|
|
164
|
+
target_schema,
|
|
165
|
+
app_role
|
|
166
|
+
);
|
|
167
|
+
END IF;
|
|
168
|
+
END $$;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- A cancelled attempt records the exact boundary after which it has no
|
|
3
|
+
-- inference, user-visible output, or workspace-persistence authority.
|
|
4
|
+
-- Fenced/idempotent cleanup and telemetry may still finish. Temporal
|
|
5
|
+
-- independently waits for the activity to terminate; this receipt lets queue
|
|
6
|
+
-- admission and UI state use durable truth.
|
|
7
|
+
|
|
8
|
+
SET lock_timeout = '5s';
|
|
9
|
+
SET statement_timeout = '5min';
|
|
10
|
+
|
|
11
|
+
ALTER TABLE "session_turn_attempts"
|
|
12
|
+
ADD COLUMN "quiesced_at" timestamptz;
|
|
13
|
+
|
|
14
|
+
-- Maintenance mode drains the pre-cutover workers before this backfill, so an
|
|
15
|
+
-- interrupted attempt that is already closed has also left its physical
|
|
16
|
+
-- activity. Seed only that historical population; new activity/workflow code
|
|
17
|
+
-- owns all later receipts.
|
|
18
|
+
UPDATE "session_turn_attempts" AS attempt
|
|
19
|
+
SET "quiesced_at" = COALESCE(attempt."closed_at", attempt."updated_at", now())
|
|
20
|
+
WHERE attempt."state" = 'closed'
|
|
21
|
+
AND EXISTS (
|
|
22
|
+
SELECT 1
|
|
23
|
+
FROM "session_attempt_interruptions" AS interruption
|
|
24
|
+
WHERE interruption."workspace_id" = attempt."workspace_id"
|
|
25
|
+
AND interruption."attempt_id" = attempt."id"
|
|
26
|
+
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Persist the origin of a session's tool allow-list. NULL is a
|
|
3
|
+
-- deliberate legacy marker for rows created before policy provenance existed.
|
|
4
|
+
ALTER TABLE "sessions"
|
|
5
|
+
ADD COLUMN IF NOT EXISTS "tool_policy" jsonb;
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "sessions"
|
|
8
|
+
DROP CONSTRAINT IF EXISTS "sessions_tool_policy_shape_check";
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "sessions"
|
|
11
|
+
ADD CONSTRAINT "sessions_tool_policy_shape_check"
|
|
12
|
+
CHECK (
|
|
13
|
+
"tool_policy" IS NULL
|
|
14
|
+
OR (
|
|
15
|
+
jsonb_typeof("tool_policy") = 'object'
|
|
16
|
+
AND "tool_policy" ? 'mode'
|
|
17
|
+
AND ("tool_policy" ->> 'mode') IN ('workspace_default', 'explicit', 'inherited', 'legacy')
|
|
18
|
+
AND "tool_policy" ? 'inheritedFromSessionId'
|
|
19
|
+
AND (
|
|
20
|
+
("tool_policy" ->> 'inheritedFromSessionId') IS NULL
|
|
21
|
+
OR ("tool_policy" ->> 'inheritedFromSessionId') ~ '^[0-9a-fA-F-]{36}$'
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
) NOT VALID;
|
|
25
|
+
|
|
26
|
+
-- Follow-up turns also need provenance: an omitted tools key inherits the
|
|
27
|
+
-- session policy, while an explicit array replaces it for that turn (subject
|
|
28
|
+
-- to the session subset fence). Existing turns used merge semantics and their
|
|
29
|
+
-- selected refs are already materialized on sessions.tools, so false preserves
|
|
30
|
+
-- their effective allow-list during the rolling upgrade.
|
|
31
|
+
ALTER TABLE "session_turns"
|
|
32
|
+
ADD COLUMN IF NOT EXISTS "tools_provided" boolean NOT NULL DEFAULT false;
|
|
33
|
+
|
|
34
|
+
-- Human composer drafts need the same provenance across reloads. Without this,
|
|
35
|
+
-- [] cannot distinguish "inherit the session policy" from an intentional
|
|
36
|
+
-- per-turn empty selection.
|
|
37
|
+
ALTER TABLE "composer_drafts"
|
|
38
|
+
ADD COLUMN IF NOT EXISTS "tools_provided" boolean NOT NULL DEFAULT false;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- tracking-64: session_events is the lossy human/audit projection, not a blob store.
|
|
3
|
+
-- Install the BEFORE trigger before the NOT VALID check so an older pod in the
|
|
4
|
+
-- same rolling deployment is normalized instead of having an oversized write
|
|
5
|
+
-- rejected. Historical oversized rows stay readable and can be defensively
|
|
6
|
+
-- bounded by NATS/SSE; NOT VALID enforces the invariant for every new row.
|
|
7
|
+
|
|
8
|
+
SET lock_timeout = '5s';
|
|
9
|
+
SET statement_timeout = '10min';
|
|
10
|
+
|
|
11
|
+
CREATE OR REPLACE FUNCTION opengeni_private.project_session_event_payload(source_payload jsonb)
|
|
12
|
+
RETURNS jsonb
|
|
13
|
+
LANGUAGE plpgsql
|
|
14
|
+
IMMUTABLE
|
|
15
|
+
SET search_path = pg_catalog
|
|
16
|
+
AS $function$
|
|
17
|
+
DECLARE
|
|
18
|
+
original_bytes integer;
|
|
19
|
+
delivered_bytes integer;
|
|
20
|
+
bounded jsonb;
|
|
21
|
+
BEGIN
|
|
22
|
+
original_bytes := octet_length(source_payload::text);
|
|
23
|
+
IF original_bytes <= 65536 THEN
|
|
24
|
+
RETURN source_payload;
|
|
25
|
+
END IF;
|
|
26
|
+
|
|
27
|
+
-- This is intentionally a small content-free fallback. Normal application
|
|
28
|
+
-- writers produce richer deterministic head/tail previews before INSERT; the
|
|
29
|
+
-- trigger exists for rolling old binaries and unforeseen direct writers.
|
|
30
|
+
bounded := jsonb_strip_nulls(jsonb_build_object(
|
|
31
|
+
'id', left(source_payload ->> 'id', 256),
|
|
32
|
+
'name', left(coalesce(source_payload ->> 'name', source_payload ->> 'toolName'), 256),
|
|
33
|
+
'type', left(source_payload ->> 'type', 128),
|
|
34
|
+
'status', left(source_payload ->> 'status', 128),
|
|
35
|
+
'code', left(source_payload ->> 'code', 128),
|
|
36
|
+
'isError', source_payload -> 'isError',
|
|
37
|
+
'preview', '[event payload omitted by durable audit storage guard]',
|
|
38
|
+
'truncation', jsonb_build_object(
|
|
39
|
+
'truncated', true,
|
|
40
|
+
'surface', 'database_guard',
|
|
41
|
+
'reason', 'database_guard',
|
|
42
|
+
'originalBytes', original_bytes,
|
|
43
|
+
'deliveredBytes', 0,
|
|
44
|
+
'omittedBytes', original_bytes,
|
|
45
|
+
'estimatedOriginalTokens', ceil(original_bytes / 4.0)::integer,
|
|
46
|
+
'estimatedDeliveredTokens', 0,
|
|
47
|
+
'fullEvidence', jsonb_build_object('available', false, 'reason', 'not_retained'),
|
|
48
|
+
'details', jsonb_build_array(jsonb_build_object(
|
|
49
|
+
'path', '$',
|
|
50
|
+
'kind', 'object',
|
|
51
|
+
'originalBytes', original_bytes
|
|
52
|
+
))
|
|
53
|
+
)
|
|
54
|
+
));
|
|
55
|
+
|
|
56
|
+
-- Size fields affect their own JSON width. Converge to an exact fixed point
|
|
57
|
+
-- without retaining any omitted content; fail the write rather than publish
|
|
58
|
+
-- false accounting if PostgreSQL's representation ever stops converging.
|
|
59
|
+
FOR pass IN 1..8 LOOP
|
|
60
|
+
delivered_bytes := octet_length(bounded::text);
|
|
61
|
+
EXIT WHEN (bounded #>> '{truncation,deliveredBytes}')::integer = delivered_bytes
|
|
62
|
+
AND (bounded #>> '{truncation,omittedBytes}')::integer
|
|
63
|
+
= greatest(0, original_bytes - delivered_bytes)
|
|
64
|
+
AND (bounded #>> '{truncation,estimatedDeliveredTokens}')::integer
|
|
65
|
+
= ceil(delivered_bytes / 4.0)::integer;
|
|
66
|
+
bounded := jsonb_set(bounded, '{truncation,deliveredBytes}', to_jsonb(delivered_bytes), false);
|
|
67
|
+
bounded := jsonb_set(
|
|
68
|
+
bounded,
|
|
69
|
+
'{truncation,omittedBytes}',
|
|
70
|
+
to_jsonb(greatest(0, original_bytes - delivered_bytes)),
|
|
71
|
+
false
|
|
72
|
+
);
|
|
73
|
+
bounded := jsonb_set(
|
|
74
|
+
bounded,
|
|
75
|
+
'{truncation,estimatedDeliveredTokens}',
|
|
76
|
+
to_jsonb(ceil(delivered_bytes / 4.0)::integer),
|
|
77
|
+
false
|
|
78
|
+
);
|
|
79
|
+
END LOOP;
|
|
80
|
+
|
|
81
|
+
delivered_bytes := octet_length(bounded::text);
|
|
82
|
+
IF (bounded #>> '{truncation,deliveredBytes}')::integer <> delivered_bytes
|
|
83
|
+
OR (bounded #>> '{truncation,omittedBytes}')::integer
|
|
84
|
+
<> greatest(0, original_bytes - delivered_bytes)
|
|
85
|
+
OR (bounded #>> '{truncation,estimatedDeliveredTokens}')::integer
|
|
86
|
+
<> ceil(delivered_bytes / 4.0)::integer THEN
|
|
87
|
+
RAISE EXCEPTION 'session event payload accounting failed to converge';
|
|
88
|
+
END IF;
|
|
89
|
+
|
|
90
|
+
RETURN bounded;
|
|
91
|
+
END;
|
|
92
|
+
$function$;
|
|
93
|
+
|
|
94
|
+
CREATE OR REPLACE FUNCTION opengeni_private.bound_session_event_payload()
|
|
95
|
+
RETURNS trigger
|
|
96
|
+
LANGUAGE plpgsql
|
|
97
|
+
SET search_path = pg_catalog
|
|
98
|
+
AS $function$
|
|
99
|
+
DECLARE
|
|
100
|
+
original_type text := NEW.type;
|
|
101
|
+
original_event_bytes integer;
|
|
102
|
+
fields jsonb := '[]'::jsonb;
|
|
103
|
+
delivered text;
|
|
104
|
+
BEGIN
|
|
105
|
+
original_event_bytes := octet_length(row_to_json(NEW)::text);
|
|
106
|
+
-- Bound every free-form envelope string by UTF-8 bytes. left() uses
|
|
107
|
+
-- characters, so one quarter of each byte limit is a conservative bound for
|
|
108
|
+
-- both ASCII and four-byte Unicode without ever splitting a code point.
|
|
109
|
+
IF octet_length(NEW.type) > 256 OR position(E'\n' in NEW.type) > 0
|
|
110
|
+
OR position(E'\r' in NEW.type) > 0 THEN
|
|
111
|
+
delivered := 'session.event.envelope_omitted';
|
|
112
|
+
fields := fields || jsonb_build_array(jsonb_build_object(
|
|
113
|
+
'field', 'type',
|
|
114
|
+
'originalBytes', octet_length(NEW.type),
|
|
115
|
+
'deliveredBytes', octet_length(delivered)
|
|
116
|
+
));
|
|
117
|
+
NEW.type := delivered;
|
|
118
|
+
END IF;
|
|
119
|
+
|
|
120
|
+
IF NEW.client_event_id IS NOT NULL AND octet_length(NEW.client_event_id) > 1024 THEN
|
|
121
|
+
delivered := left(NEW.client_event_id, 256);
|
|
122
|
+
fields := fields || jsonb_build_array(jsonb_build_object(
|
|
123
|
+
'field', 'clientEventId',
|
|
124
|
+
'originalBytes', octet_length(NEW.client_event_id),
|
|
125
|
+
'deliveredBytes', octet_length(delivered)
|
|
126
|
+
));
|
|
127
|
+
NEW.client_event_id := delivered;
|
|
128
|
+
END IF;
|
|
129
|
+
|
|
130
|
+
IF NEW.producer_id IS NOT NULL AND octet_length(NEW.producer_id) > 1024 THEN
|
|
131
|
+
delivered := left(NEW.producer_id, 256);
|
|
132
|
+
fields := fields || jsonb_build_array(jsonb_build_object(
|
|
133
|
+
'field', 'producerId',
|
|
134
|
+
'originalBytes', octet_length(NEW.producer_id),
|
|
135
|
+
'deliveredBytes', octet_length(delivered)
|
|
136
|
+
));
|
|
137
|
+
NEW.producer_id := delivered;
|
|
138
|
+
END IF;
|
|
139
|
+
|
|
140
|
+
IF NEW.turn_association IS NOT NULL AND NEW.turn_association NOT IN (
|
|
141
|
+
'current', 'late_rejected', 'duplicate'
|
|
142
|
+
) THEN
|
|
143
|
+
fields := fields || jsonb_build_array(jsonb_build_object(
|
|
144
|
+
'field', 'turnAssociation',
|
|
145
|
+
'originalBytes', octet_length(NEW.turn_association),
|
|
146
|
+
'deliveredBytes', 0
|
|
147
|
+
));
|
|
148
|
+
NEW.turn_association := NULL;
|
|
149
|
+
END IF;
|
|
150
|
+
|
|
151
|
+
IF NEW.duplicate_reason IS NOT NULL AND octet_length(NEW.duplicate_reason) > 4096 THEN
|
|
152
|
+
delivered := left(NEW.duplicate_reason, 1024);
|
|
153
|
+
fields := fields || jsonb_build_array(jsonb_build_object(
|
|
154
|
+
'field', 'duplicateReason',
|
|
155
|
+
'originalBytes', octet_length(NEW.duplicate_reason),
|
|
156
|
+
'deliveredBytes', octet_length(delivered)
|
|
157
|
+
));
|
|
158
|
+
NEW.duplicate_reason := delivered;
|
|
159
|
+
END IF;
|
|
160
|
+
|
|
161
|
+
IF jsonb_array_length(fields) > 0 THEN
|
|
162
|
+
NEW.payload := jsonb_build_object(
|
|
163
|
+
'preview', '[legacy event envelope normalized by durable audit storage guard]',
|
|
164
|
+
'originalEventBytes', original_event_bytes,
|
|
165
|
+
'originalType', left(original_type, 64),
|
|
166
|
+
'envelopeProjection', jsonb_build_object(
|
|
167
|
+
'truncated', true,
|
|
168
|
+
'surface', 'database_guard',
|
|
169
|
+
'fields', fields
|
|
170
|
+
),
|
|
171
|
+
'fullEvidence', jsonb_build_object('available', false, 'reason', 'not_retained')
|
|
172
|
+
);
|
|
173
|
+
END IF;
|
|
174
|
+
|
|
175
|
+
NEW.payload := opengeni_private.project_session_event_payload(NEW.payload);
|
|
176
|
+
RETURN NEW;
|
|
177
|
+
END;
|
|
178
|
+
$function$;
|
|
179
|
+
|
|
180
|
+
DROP TRIGGER IF EXISTS session_events_bound_payload_before_insert ON session_events;
|
|
181
|
+
CREATE TRIGGER session_events_bound_payload_before_insert
|
|
182
|
+
BEFORE INSERT OR UPDATE OF payload, type, client_event_id, producer_id, turn_association, duplicate_reason
|
|
183
|
+
ON session_events
|
|
184
|
+
FOR EACH ROW
|
|
185
|
+
EXECUTE FUNCTION opengeni_private.bound_session_event_payload();
|
|
186
|
+
|
|
187
|
+
ALTER TABLE session_events
|
|
188
|
+
ADD CONSTRAINT session_events_payload_bytes_check
|
|
189
|
+
CHECK (octet_length(payload::text) <= 65536)
|
|
190
|
+
NOT VALID,
|
|
191
|
+
ADD CONSTRAINT session_events_type_bytes_check
|
|
192
|
+
CHECK (
|
|
193
|
+
octet_length(type) <= 256
|
|
194
|
+
AND position(E'\n' in type) = 0
|
|
195
|
+
AND position(E'\r' in type) = 0
|
|
196
|
+
)
|
|
197
|
+
NOT VALID,
|
|
198
|
+
ADD CONSTRAINT session_events_client_event_id_bytes_check
|
|
199
|
+
CHECK (client_event_id IS NULL OR octet_length(client_event_id) <= 1024)
|
|
200
|
+
NOT VALID,
|
|
201
|
+
ADD CONSTRAINT session_events_producer_id_bytes_check
|
|
202
|
+
CHECK (producer_id IS NULL OR octet_length(producer_id) <= 1024)
|
|
203
|
+
NOT VALID,
|
|
204
|
+
ADD CONSTRAINT session_events_turn_association_bytes_check
|
|
205
|
+
CHECK (turn_association IS NULL OR octet_length(turn_association) <= 64)
|
|
206
|
+
NOT VALID,
|
|
207
|
+
ADD CONSTRAINT session_events_duplicate_reason_bytes_check
|
|
208
|
+
CHECK (duplicate_reason IS NULL OR octet_length(duplicate_reason) <= 4096)
|
|
209
|
+
NOT VALID;
|