@opengeni/db 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OGCE6O2X.js → chunk-7LDU7F5P.js} +31 -3
- package/dist/chunk-7LDU7F5P.js.map +1 -0
- package/dist/chunk-B22X3IEZ.js +2634 -0
- package/dist/chunk-B22X3IEZ.js.map +1 -0
- package/dist/{chunk-57MLICFR.js → chunk-YFQ7SGE4.js} +18 -6
- package/dist/chunk-YFQ7SGE4.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16571 -5018
- package/dist/index.js.map +1 -1
- package/dist/migrate.js +1 -1
- package/dist/provision-roles.d.ts +2121 -248
- package/dist/provision-roles.js +1 -1
- package/dist/{schema-BUbuMteO.d.ts → schema-BN5mB9xZ.d.ts} +8716 -3432
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +43 -5
- package/drizzle/0044_reap_dead_turn_holders.sql +104 -0
- package/drizzle/0045_workspace_captures.sql +83 -0
- package/drizzle/0045_workspace_memory_v1.sql +71 -0
- package/drizzle/0046_variable_sets_rename.sql +56 -0
- package/drizzle/0047_rigs.sql +151 -0
- package/drizzle/0048_rig_runtime.sql +9 -0
- package/drizzle/0049_enrollment_went_offline.sql +28 -0
- package/drizzle/0050_enrollment_op_stream.sql +1 -0
- package/drizzle/0051_codex_pin_source.sql +48 -0
- package/drizzle/0052_file_upload_cleanup.sql +91 -0
- package/drizzle/0053_codex_credential_leases.sql +230 -0
- package/drizzle/0054_session_pins.sql +85 -0
- package/drizzle/0055_session_list_snapshots.sql +73 -0
- package/drizzle/0056_workspace_model_policies.sql +48 -0
- package/drizzle/0057_durable_queue_control.sql +536 -0
- package/drizzle/0058_turn_admission_usage_enrollment.sql +158 -0
- package/drizzle/0059_workspace_pause_control_kind.sql +12 -0
- package/drizzle/0060_session_system_update_deferral.sql +10 -0
- package/drizzle/0061_session_workflow_wake_outbox.sql +157 -0
- package/drizzle/0062_session_list_snapshot_reaper.sql +48 -0
- package/drizzle/0063_session_control_mega_foundation.sql +1324 -0
- package/package.json +13 -13
- package/src/codex-token-resolver.ts +58 -23
- package/src/connection-token-resolver.ts +146 -57
- package/src/environment-crypto.ts +5 -1
- package/src/event-payload-sanitizer.ts +29 -1
- package/src/index.ts +20990 -6465
- package/src/memory-domain.ts +218 -0
- package/src/migrate.ts +58 -3
- package/src/provision-roles.ts +46 -17
- package/src/schema.ts +2888 -1121
- package/src/session-control.ts +1759 -0
- package/src/session-queue-commands.ts +1753 -0
- package/src/session-tool-call-settlement.ts +269 -0
- package/dist/chunk-57MLICFR.js.map +0 -1
- package/dist/chunk-OGCE6O2X.js.map +0 -1
- package/dist/chunk-ZIUCA2IO.js +0 -1268
- package/dist/chunk-ZIUCA2IO.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 { m as agentRunStates, n as apiKeys, o as auditEvents, p as billingCustomers, q as capabilityCatalogItems, r as capabilityInstallations, t as codexCapacityWaiters, u as codexCredentialLeases, v as codexRotationSettings, w as codexSubscriptionCredentials, c as composerDrafts, x as connections, y as creditLedgerEntries, z as deviceEnrollmentRequests, g as deviceEnrollmentStatusValues, A as documentBases, B as documentChunks, C as documents, f as enrollmentExposureValues, e as enrollmentOsValues, h as enrollmentStatusValues, D as enrollments, E as fileUploads, F as files, G as githubInstallations, H as importBatches, I as integrationOauthClients, J as integrationOauthStateNonces, K as knowledgeMemories, L as machineMetricsLatest, M as machineMetricsSeries, N as managedAccounts, O as packInstallations, P as rigChanges, Q as rigVersions, R as rigs, i as sandboxKindValues, S as sandboxLeaseHolders, T as sandboxLeaseLivenessValues, U as sandboxLeases, V as sandboxPtySessions, W as sandboxSessionEnvelopes, X as sandboxes, Y as scheduledTaskRuns, Z as scheduledTasks, _ as sessionAttemptInterruptions, s as sessionCommandReceipts, $ as sessionEvents, a0 as sessionGoals, a1 as sessionHistoryItems, a2 as sessionListSnapshots, a3 as sessionMcpServers, a4 as sessionPendingToolCalls, a5 as sessionPins, j as sessionRecordingCodecValues, k as sessionRecordingModeValues, l as sessionRecordingStateValues, a6 as sessionRecordings, a7 as sessionSystemUpdateOutbox, a8 as sessionSystemUpdates, a as sessionTurnAttempts, b as sessionTurns, a9 as sessionWorkflowWakeOutbox, aa as sessions, ab as socialConnections, ac as socialPosts, ad as stripeWebhookEvents, ae as usageEvents, af as workspaceCaptures, ag as workspaceControlEvents, ah as workspaceInferenceControls, ai as workspaceMemberships, aj as workspaceModelPolicies, ak as workspacePacks, al as workspaceVariableSetVariables, am as workspaceVariableSets, an as workspaces } from './schema-BN5mB9xZ.js';
|
package/dist/schema.js
CHANGED
|
@@ -5,8 +5,11 @@ import {
|
|
|
5
5
|
billingCustomers,
|
|
6
6
|
capabilityCatalogItems,
|
|
7
7
|
capabilityInstallations,
|
|
8
|
+
codexCapacityWaiters,
|
|
9
|
+
codexCredentialLeases,
|
|
8
10
|
codexRotationSettings,
|
|
9
11
|
codexSubscriptionCredentials,
|
|
12
|
+
composerDrafts,
|
|
10
13
|
connections,
|
|
11
14
|
creditLedgerEntries,
|
|
12
15
|
deviceEnrollmentRequests,
|
|
@@ -29,6 +32,9 @@ import {
|
|
|
29
32
|
machineMetricsSeries,
|
|
30
33
|
managedAccounts,
|
|
31
34
|
packInstallations,
|
|
35
|
+
rigChanges,
|
|
36
|
+
rigVersions,
|
|
37
|
+
rigs,
|
|
32
38
|
sandboxKindValues,
|
|
33
39
|
sandboxLeaseHolders,
|
|
34
40
|
sandboxLeaseLivenessValues,
|
|
@@ -38,26 +44,39 @@ import {
|
|
|
38
44
|
sandboxes,
|
|
39
45
|
scheduledTaskRuns,
|
|
40
46
|
scheduledTasks,
|
|
47
|
+
sessionAttemptInterruptions,
|
|
48
|
+
sessionCommandReceipts,
|
|
41
49
|
sessionEvents,
|
|
42
50
|
sessionGoals,
|
|
43
51
|
sessionHistoryItems,
|
|
52
|
+
sessionListSnapshots,
|
|
44
53
|
sessionMcpServers,
|
|
54
|
+
sessionPendingToolCalls,
|
|
55
|
+
sessionPins,
|
|
45
56
|
sessionRecordingCodecValues,
|
|
46
57
|
sessionRecordingModeValues,
|
|
47
58
|
sessionRecordingStateValues,
|
|
48
59
|
sessionRecordings,
|
|
60
|
+
sessionSystemUpdateOutbox,
|
|
61
|
+
sessionSystemUpdates,
|
|
62
|
+
sessionTurnAttempts,
|
|
49
63
|
sessionTurns,
|
|
64
|
+
sessionWorkflowWakeOutbox,
|
|
50
65
|
sessions,
|
|
51
66
|
socialConnections,
|
|
52
67
|
socialPosts,
|
|
53
68
|
stripeWebhookEvents,
|
|
54
69
|
usageEvents,
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
workspaceCaptures,
|
|
71
|
+
workspaceControlEvents,
|
|
72
|
+
workspaceInferenceControls,
|
|
57
73
|
workspaceMemberships,
|
|
74
|
+
workspaceModelPolicies,
|
|
58
75
|
workspacePacks,
|
|
76
|
+
workspaceVariableSetVariables,
|
|
77
|
+
workspaceVariableSets,
|
|
59
78
|
workspaces
|
|
60
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-B22X3IEZ.js";
|
|
61
80
|
import "./chunk-PZ5AY32C.js";
|
|
62
81
|
export {
|
|
63
82
|
agentRunStates,
|
|
@@ -66,8 +85,11 @@ export {
|
|
|
66
85
|
billingCustomers,
|
|
67
86
|
capabilityCatalogItems,
|
|
68
87
|
capabilityInstallations,
|
|
88
|
+
codexCapacityWaiters,
|
|
89
|
+
codexCredentialLeases,
|
|
69
90
|
codexRotationSettings,
|
|
70
91
|
codexSubscriptionCredentials,
|
|
92
|
+
composerDrafts,
|
|
71
93
|
connections,
|
|
72
94
|
creditLedgerEntries,
|
|
73
95
|
deviceEnrollmentRequests,
|
|
@@ -90,6 +112,9 @@ export {
|
|
|
90
112
|
machineMetricsSeries,
|
|
91
113
|
managedAccounts,
|
|
92
114
|
packInstallations,
|
|
115
|
+
rigChanges,
|
|
116
|
+
rigVersions,
|
|
117
|
+
rigs,
|
|
93
118
|
sandboxKindValues,
|
|
94
119
|
sandboxLeaseHolders,
|
|
95
120
|
sandboxLeaseLivenessValues,
|
|
@@ -99,24 +124,37 @@ export {
|
|
|
99
124
|
sandboxes,
|
|
100
125
|
scheduledTaskRuns,
|
|
101
126
|
scheduledTasks,
|
|
127
|
+
sessionAttemptInterruptions,
|
|
128
|
+
sessionCommandReceipts,
|
|
102
129
|
sessionEvents,
|
|
103
130
|
sessionGoals,
|
|
104
131
|
sessionHistoryItems,
|
|
132
|
+
sessionListSnapshots,
|
|
105
133
|
sessionMcpServers,
|
|
134
|
+
sessionPendingToolCalls,
|
|
135
|
+
sessionPins,
|
|
106
136
|
sessionRecordingCodecValues,
|
|
107
137
|
sessionRecordingModeValues,
|
|
108
138
|
sessionRecordingStateValues,
|
|
109
139
|
sessionRecordings,
|
|
140
|
+
sessionSystemUpdateOutbox,
|
|
141
|
+
sessionSystemUpdates,
|
|
142
|
+
sessionTurnAttempts,
|
|
110
143
|
sessionTurns,
|
|
144
|
+
sessionWorkflowWakeOutbox,
|
|
111
145
|
sessions,
|
|
112
146
|
socialConnections,
|
|
113
147
|
socialPosts,
|
|
114
148
|
stripeWebhookEvents,
|
|
115
149
|
usageEvents,
|
|
116
|
-
|
|
117
|
-
|
|
150
|
+
workspaceCaptures,
|
|
151
|
+
workspaceControlEvents,
|
|
152
|
+
workspaceInferenceControls,
|
|
118
153
|
workspaceMemberships,
|
|
154
|
+
workspaceModelPolicies,
|
|
119
155
|
workspacePacks,
|
|
156
|
+
workspaceVariableSetVariables,
|
|
157
|
+
workspaceVariableSets,
|
|
120
158
|
workspaces
|
|
121
159
|
};
|
|
122
160
|
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
-- 0044_reap_dead_turn_holders.sql
|
|
2
|
+
-- Dead-worker turn holders were TTL-exempt forever: a live turn refreshes its
|
|
3
|
+
-- holder every 10s (even a legit multi-day turn), but a worker killed without
|
|
4
|
+
-- cleanup (SIGKILL/OOM/deploy churn) leaves a holder whose heartbeat is frozen.
|
|
5
|
+
-- Left in place it pins refcount >= 1 FOREVER, so the lease never drains, the
|
|
6
|
+
-- reaper never persists /workspace, and the box rides the provider hard-timeout
|
|
7
|
+
-- to an UNPERSISTED death (2026-07-06 staging deploy churn left turn holders
|
|
8
|
+
-- frozen for hours). Reap turn holders whose heartbeat is older than the
|
|
9
|
+
-- caller-supplied horizon. A live holder is touched every 10s from the moment
|
|
10
|
+
-- it is registered (the worker's holder-liveness loop covers the warmup;
|
|
11
|
+
-- the turn heartbeat covers the run), so the horizon the worker passes
|
|
12
|
+
-- (warming-budget + lease-TTL) is generous defense-in-depth, never a bound a
|
|
13
|
+
-- live path can reach; a redispatched turn re-acquires under a NEW holder id,
|
|
14
|
+
-- so a live execution is never touched.
|
|
15
|
+
|
|
16
|
+
CREATE OR REPLACE FUNCTION opengeni_private.reap_sandbox_leases(
|
|
17
|
+
p_viewer_holder_ttl_ms bigint,
|
|
18
|
+
p_turn_holder_ttl_ms bigint,
|
|
19
|
+
p_idle_grace_ms bigint
|
|
20
|
+
)
|
|
21
|
+
RETURNS TABLE (workspace_id uuid, sandbox_group_id uuid, instance_id text, lease_epoch integer)
|
|
22
|
+
LANGUAGE plpgsql
|
|
23
|
+
SECURITY DEFINER
|
|
24
|
+
AS $$
|
|
25
|
+
BEGIN
|
|
26
|
+
DELETE FROM sandbox_lease_holders h
|
|
27
|
+
WHERE h.kind = 'viewer'
|
|
28
|
+
AND h.last_heartbeat_at < now() - make_interval(secs => p_viewer_holder_ttl_ms / 1000.0);
|
|
29
|
+
|
|
30
|
+
-- Dead-worker turn holders (see header). p_turn_holder_ttl_ms <= 0 preserves
|
|
31
|
+
-- the legacy never-reap behaviour.
|
|
32
|
+
IF p_turn_holder_ttl_ms > 0 THEN
|
|
33
|
+
DELETE FROM sandbox_lease_holders h
|
|
34
|
+
WHERE h.kind = 'turn'
|
|
35
|
+
AND h.last_heartbeat_at < now() - make_interval(secs => p_turn_holder_ttl_ms / 1000.0);
|
|
36
|
+
END IF;
|
|
37
|
+
|
|
38
|
+
UPDATE sandbox_leases L SET
|
|
39
|
+
refcount = c.total,
|
|
40
|
+
turn_holders = c.turns,
|
|
41
|
+
viewer_holders = c.viewers,
|
|
42
|
+
liveness = CASE WHEN L.liveness = 'warm' AND c.total = 0 AND c.turns = 0
|
|
43
|
+
THEN 'draining' ELSE L.liveness END,
|
|
44
|
+
expires_at = CASE WHEN L.liveness = 'warm' AND c.total = 0 AND c.turns = 0
|
|
45
|
+
THEN now() + make_interval(secs => p_idle_grace_ms / 1000.0)
|
|
46
|
+
ELSE L.expires_at END,
|
|
47
|
+
updated_at = now()
|
|
48
|
+
FROM (
|
|
49
|
+
SELECT L2.id,
|
|
50
|
+
(SELECT count(*) FROM sandbox_lease_holders h WHERE h.lease_id = L2.id)::int AS total,
|
|
51
|
+
(SELECT count(*) FROM sandbox_lease_holders h WHERE h.lease_id = L2.id AND h.kind = 'turn')::int AS turns,
|
|
52
|
+
(SELECT count(*) FROM sandbox_lease_holders h WHERE h.lease_id = L2.id AND h.kind = 'viewer')::int AS viewers
|
|
53
|
+
FROM sandbox_leases L2
|
|
54
|
+
) c
|
|
55
|
+
WHERE L.id = c.id;
|
|
56
|
+
|
|
57
|
+
-- Warming died before provider create returned: no instance was ever tracked.
|
|
58
|
+
UPDATE sandbox_leases AS L SET
|
|
59
|
+
liveness = 'cold', instance_id = NULL,
|
|
60
|
+
resume_backend_id = NULL, resume_state = NULL,
|
|
61
|
+
data_plane_url = NULL, terminal_data_plane_url = NULL, updated_at = now()
|
|
62
|
+
WHERE L.liveness = 'warming' AND L.expires_at < now() AND L.instance_id IS NULL;
|
|
63
|
+
|
|
64
|
+
-- Warming died after provider create returned: keep the instance_id and hand it
|
|
65
|
+
-- to the normal provider termination path in this same sweep.
|
|
66
|
+
UPDATE sandbox_leases AS L SET
|
|
67
|
+
liveness = 'draining',
|
|
68
|
+
refcount = 0,
|
|
69
|
+
turn_holders = 0,
|
|
70
|
+
viewer_holders = 0,
|
|
71
|
+
data_plane_url = NULL,
|
|
72
|
+
terminal_data_plane_url = NULL,
|
|
73
|
+
expires_at = now() - interval '1 millisecond',
|
|
74
|
+
updated_at = now()
|
|
75
|
+
WHERE L.liveness = 'warming' AND L.expires_at < now() AND L.instance_id IS NOT NULL;
|
|
76
|
+
|
|
77
|
+
RETURN QUERY
|
|
78
|
+
SELECT L.workspace_id, L.sandbox_group_id, L.instance_id, L.lease_epoch
|
|
79
|
+
FROM sandbox_leases L
|
|
80
|
+
WHERE L.liveness = 'draining' AND L.expires_at < now() AND L.refcount = 0;
|
|
81
|
+
END;
|
|
82
|
+
$$;
|
|
83
|
+
|
|
84
|
+
-- Rolling-deploy compat: workers built before this migration call the 2-arg
|
|
85
|
+
-- form. Delegate with turn-holder reaping OFF (legacy behaviour) until every
|
|
86
|
+
-- worker passes the explicit TTL.
|
|
87
|
+
CREATE OR REPLACE FUNCTION opengeni_private.reap_sandbox_leases(
|
|
88
|
+
p_viewer_holder_ttl_ms bigint,
|
|
89
|
+
p_idle_grace_ms bigint
|
|
90
|
+
)
|
|
91
|
+
RETURNS TABLE (workspace_id uuid, sandbox_group_id uuid, instance_id text, lease_epoch integer)
|
|
92
|
+
LANGUAGE sql
|
|
93
|
+
SECURITY DEFINER
|
|
94
|
+
AS $$
|
|
95
|
+
SELECT * FROM opengeni_private.reap_sandbox_leases(p_viewer_holder_ttl_ms, 0::bigint, p_idle_grace_ms);
|
|
96
|
+
$$;
|
|
97
|
+
|
|
98
|
+
DO $$
|
|
99
|
+
BEGIN
|
|
100
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
101
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.reap_sandbox_leases(bigint, bigint, bigint) TO opengeni_app;
|
|
102
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.reap_sandbox_leases(bigint, bigint) TO opengeni_app;
|
|
103
|
+
END IF;
|
|
104
|
+
END $$;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
-- Workbench v2 turn-end workspace capture (dossier §10.2; model: 0020 session_recordings).
|
|
2
|
+
--
|
|
3
|
+
-- One row per capture revision — a point-in-time snapshot of a session's changed
|
|
4
|
+
-- files, probed off the live box at TURN END (agent-turn.ts finally, between the
|
|
5
|
+
-- warm snapshot and release). The manifest (tree index + per-repo status/diff +
|
|
6
|
+
-- file index) and each after-image blob live in @opengeni/storage; this row is
|
|
7
|
+
-- the durable index the read routes serve from. `revision` is monotonic per
|
|
8
|
+
-- session (unique (session_id, revision)); `blob_keys` records the content-
|
|
9
|
+
-- addressed after-image keys this revision references so the keep-latest-10 GC
|
|
10
|
+
-- deletes only blobs no surviving revision shares. `lease_epoch` fences a write:
|
|
11
|
+
-- insertWorkspaceCapture only writes when a warm lease at that epoch still exists.
|
|
12
|
+
--
|
|
13
|
+
-- Behind OPENGENI_WORKSPACE_CAPTURE (default on). DDL is inert until the worker
|
|
14
|
+
-- writes rows; forward-only, no existing row references it. RLS + grants mirror
|
|
15
|
+
-- the current schema-aware boilerplate (0043).
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS "workspace_captures" (
|
|
18
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
19
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
20
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
21
|
+
"session_id" uuid NOT NULL REFERENCES "sessions"("id") ON DELETE CASCADE,
|
|
22
|
+
-- The turn that produced this capture. ON DELETE SET NULL: a deleted turn must
|
|
23
|
+
-- not cascade-kill the capture index row.
|
|
24
|
+
"turn_id" uuid REFERENCES "session_turns"("id") ON DELETE SET NULL,
|
|
25
|
+
|
|
26
|
+
-- Monotonic per session (unique on (session_id, revision)).
|
|
27
|
+
"revision" bigint NOT NULL,
|
|
28
|
+
-- The lease epoch the capture was fenced under (paired with revision for cache
|
|
29
|
+
-- invalidation client-side).
|
|
30
|
+
"lease_epoch" integer NOT NULL,
|
|
31
|
+
-- 'available' on a committed capture. 'failed' reserved for a future two-phase
|
|
32
|
+
-- write; the current synchronous capture only ever inserts 'available'.
|
|
33
|
+
"state" text NOT NULL DEFAULT 'available',
|
|
34
|
+
|
|
35
|
+
-- @opengeni/storage keys. manifest = the single JSON blob (tree + repos + file
|
|
36
|
+
-- refs); tree_index = the fs tree blob (kept separate so the API can inline or
|
|
37
|
+
-- sign it independently). blob_keys = the content-addressed after-image keys
|
|
38
|
+
-- this revision references (GC set-difference input).
|
|
39
|
+
"manifest_key" text,
|
|
40
|
+
"tree_index_key" text,
|
|
41
|
+
"blob_keys" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
42
|
+
|
|
43
|
+
"size_bytes" bigint,
|
|
44
|
+
"stats" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
45
|
+
|
|
46
|
+
"captured_at" timestamptz NOT NULL DEFAULT now(),
|
|
47
|
+
|
|
48
|
+
CONSTRAINT "workspace_captures_state_chk" CHECK ("state" IN ('available','failed'))
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
-- Monotonic revision per session (the assign-revision + read-latest key).
|
|
52
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "workspace_captures_session_revision_idx"
|
|
53
|
+
ON "workspace_captures" ("session_id", "revision");
|
|
54
|
+
-- Latest-capture lookup and GC scan without touching the event spine.
|
|
55
|
+
CREATE INDEX IF NOT EXISTS "workspace_captures_latest_idx"
|
|
56
|
+
ON "workspace_captures" ("workspace_id", "session_id", "revision" DESC);
|
|
57
|
+
|
|
58
|
+
-- ============== RLS + grants (schema-aware boilerplate, per 0043) =============
|
|
59
|
+
ALTER TABLE "workspace_captures" ENABLE ROW LEVEL SECURITY;
|
|
60
|
+
ALTER TABLE "workspace_captures" FORCE ROW LEVEL SECURITY;
|
|
61
|
+
|
|
62
|
+
DO $$
|
|
63
|
+
BEGIN
|
|
64
|
+
IF EXISTS (
|
|
65
|
+
SELECT 1 FROM pg_policies
|
|
66
|
+
WHERE schemaname = current_schema() AND tablename = 'workspace_captures' AND policyname = 'workspace_isolation'
|
|
67
|
+
) THEN
|
|
68
|
+
DROP POLICY workspace_isolation ON "workspace_captures";
|
|
69
|
+
END IF;
|
|
70
|
+
END $$;
|
|
71
|
+
CREATE POLICY workspace_isolation ON "workspace_captures"
|
|
72
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
73
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
74
|
+
|
|
75
|
+
DO $$
|
|
76
|
+
BEGIN
|
|
77
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
78
|
+
EXECUTE format(
|
|
79
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
|
|
80
|
+
current_schema()
|
|
81
|
+
);
|
|
82
|
+
END IF;
|
|
83
|
+
END $$;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
-- Workspace Memory V1.
|
|
2
|
+
-- Evolves the knowledge_memories table into an agent-writable, hybrid-searchable
|
|
3
|
+
-- memory store, and gives workspaces a growth-ready settings jsonb column.
|
|
4
|
+
-- Follows 0041_knowledge_layer.sql: IF NOT EXISTS column adds, current_schema()
|
|
5
|
+
-- guards, DO blocks for constraints. NO ANN index (3072 dims exceed pgvector's
|
|
6
|
+
-- HNSW cap; reads do a sequential cosine scan like document search). NO policy
|
|
7
|
+
-- changes (the table already has FORCE RLS + workspace_isolation from 0041).
|
|
8
|
+
|
|
9
|
+
-- Growth-ready per-workspace settings bag (holds memoryEnabled and future keys).
|
|
10
|
+
ALTER TABLE "workspaces" ADD COLUMN IF NOT EXISTS "settings" jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
11
|
+
|
|
12
|
+
-- Memory columns on the existing row (single short texts; no chunk table).
|
|
13
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "embedding" vector(3072);
|
|
14
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "embedding_model" text;
|
|
15
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "pinned" boolean NOT NULL DEFAULT false;
|
|
16
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "usage_count" integer NOT NULL DEFAULT 0;
|
|
17
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "last_used_at" timestamptz;
|
|
18
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "supersedes_id" uuid;
|
|
19
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "superseded_by_id" uuid;
|
|
20
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "valid_from" timestamptz NOT NULL DEFAULT now();
|
|
21
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "valid_until" timestamptz;
|
|
22
|
+
-- Exact-dedup key. Hash of the NORMALIZED text, where normalization is
|
|
23
|
+
-- lower(collapse-internal-whitespace(trim(text))). The application computes the
|
|
24
|
+
-- IDENTICAL value in packages/core/src/domain/memory.ts (normalizeMemoryText +
|
|
25
|
+
-- hashMemoryText) so exact-dup detection matches across the migration backfill
|
|
26
|
+
-- and every runtime write. Keep the two in lockstep.
|
|
27
|
+
ALTER TABLE "knowledge_memories" ADD COLUMN IF NOT EXISTS "text_hash" text;
|
|
28
|
+
|
|
29
|
+
-- Supersession links (same table). ON DELETE SET NULL: dropping a record must not
|
|
30
|
+
-- cascade-delete its chain neighbours.
|
|
31
|
+
DO $$
|
|
32
|
+
BEGIN
|
|
33
|
+
IF NOT EXISTS (
|
|
34
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'knowledge_memories_supersedes_fk'
|
|
35
|
+
) THEN
|
|
36
|
+
ALTER TABLE "knowledge_memories" ADD CONSTRAINT "knowledge_memories_supersedes_fk"
|
|
37
|
+
FOREIGN KEY ("supersedes_id") REFERENCES "knowledge_memories"("id") ON DELETE SET NULL;
|
|
38
|
+
END IF;
|
|
39
|
+
END $$;
|
|
40
|
+
|
|
41
|
+
DO $$
|
|
42
|
+
BEGIN
|
|
43
|
+
IF NOT EXISTS (
|
|
44
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'knowledge_memories_superseded_by_fk'
|
|
45
|
+
) THEN
|
|
46
|
+
ALTER TABLE "knowledge_memories" ADD CONSTRAINT "knowledge_memories_superseded_by_fk"
|
|
47
|
+
FOREIGN KEY ("superseded_by_id") REFERENCES "knowledge_memories"("id") ON DELETE SET NULL;
|
|
48
|
+
END IF;
|
|
49
|
+
END $$;
|
|
50
|
+
|
|
51
|
+
-- Working-set + dedup access paths.
|
|
52
|
+
CREATE INDEX IF NOT EXISTS "knowledge_memories_workspace_visible_idx"
|
|
53
|
+
ON "knowledge_memories" ("workspace_id", "pinned" DESC, "updated_at" DESC)
|
|
54
|
+
WHERE "status" IN ('active', 'approved');
|
|
55
|
+
CREATE INDEX IF NOT EXISTS "knowledge_memories_workspace_text_hash_idx"
|
|
56
|
+
ON "knowledge_memories" ("workspace_id", "text_hash");
|
|
57
|
+
-- Enforce the exact-dedup invariant for the agent-visible set. Proposed and
|
|
58
|
+
-- terminal rows remain outside the constraint because agents cannot act on them.
|
|
59
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "knowledge_memories_workspace_visible_text_hash_uq"
|
|
60
|
+
ON "knowledge_memories" ("workspace_id", "text_hash")
|
|
61
|
+
WHERE "status" IN ('active', 'approved') AND "text_hash" IS NOT NULL;
|
|
62
|
+
|
|
63
|
+
-- Backfill text_hash for pre-existing rows using the same normalization the app
|
|
64
|
+
-- applies at write time: lower(collapse-ws(trim(text))), sha256, hex-encoded.
|
|
65
|
+
-- Normalization MUST equal normalizeMemoryText in memory-domain.ts:
|
|
66
|
+
-- collapse every whitespace run to a single space, then trim, then lowercase.
|
|
67
|
+
-- JS: text.replace(/\s+/g, " ").trim().toLowerCase()
|
|
68
|
+
-- SQL: lower(btrim(regexp_replace(text, '\s+', ' ', 'g')))
|
|
69
|
+
UPDATE "knowledge_memories"
|
|
70
|
+
SET "text_hash" = encode(sha256(convert_to(lower(btrim(regexp_replace("text", '\s+', ' ', 'g'))), 'UTF8')), 'hex')
|
|
71
|
+
WHERE "text_hash" IS NULL;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
-- Rename workspace secret Environments to Variable Sets.
|
|
2
|
+
-- Keeps data in place; public compatibility aliases live in API/contracts/SDK.
|
|
3
|
+
|
|
4
|
+
ALTER TABLE IF EXISTS "workspace_environments" RENAME TO "workspace_variable_sets";
|
|
5
|
+
ALTER TABLE IF EXISTS "workspace_environment_variables" RENAME TO "workspace_variable_set_variables";
|
|
6
|
+
|
|
7
|
+
ALTER TABLE IF EXISTS "workspace_variable_set_variables" RENAME COLUMN "environment_id" TO "variable_set_id";
|
|
8
|
+
ALTER TABLE IF EXISTS "sessions" RENAME COLUMN "environment_id" TO "variable_set_id";
|
|
9
|
+
ALTER TABLE IF EXISTS "scheduled_tasks" RENAME COLUMN "environment_id" TO "variable_set_id";
|
|
10
|
+
|
|
11
|
+
ALTER INDEX IF EXISTS "workspace_environments_workspace_name_idx" RENAME TO "workspace_variable_sets_workspace_name_idx";
|
|
12
|
+
ALTER INDEX IF EXISTS "workspace_environments_workspace_created_idx" RENAME TO "workspace_variable_sets_workspace_created_idx";
|
|
13
|
+
ALTER INDEX IF EXISTS "workspace_environment_variables_env_name_idx" RENAME TO "workspace_variable_set_variables_env_name_idx";
|
|
14
|
+
ALTER INDEX IF EXISTS "workspace_environment_variables_workspace_env_idx" RENAME TO "workspace_variable_set_variables_workspace_env_idx";
|
|
15
|
+
ALTER INDEX IF EXISTS "sessions_environment_idx" RENAME TO "sessions_variable_set_idx";
|
|
16
|
+
ALTER INDEX IF EXISTS "scheduled_tasks_environment_idx" RENAME TO "scheduled_tasks_variable_set_idx";
|
|
17
|
+
|
|
18
|
+
ALTER TABLE "workspace_variable_sets" ENABLE ROW LEVEL SECURITY;
|
|
19
|
+
ALTER TABLE "workspace_variable_sets" FORCE ROW LEVEL SECURITY;
|
|
20
|
+
DO $$
|
|
21
|
+
BEGIN
|
|
22
|
+
IF EXISTS (
|
|
23
|
+
SELECT 1 FROM pg_policies
|
|
24
|
+
WHERE schemaname = current_schema() AND tablename = 'workspace_variable_sets' AND policyname = 'workspace_isolation'
|
|
25
|
+
) THEN
|
|
26
|
+
DROP POLICY workspace_isolation ON "workspace_variable_sets";
|
|
27
|
+
END IF;
|
|
28
|
+
END $$;
|
|
29
|
+
CREATE POLICY workspace_isolation ON "workspace_variable_sets"
|
|
30
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
31
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
32
|
+
|
|
33
|
+
ALTER TABLE "workspace_variable_set_variables" ENABLE ROW LEVEL SECURITY;
|
|
34
|
+
ALTER TABLE "workspace_variable_set_variables" FORCE ROW LEVEL SECURITY;
|
|
35
|
+
DO $$
|
|
36
|
+
BEGIN
|
|
37
|
+
IF EXISTS (
|
|
38
|
+
SELECT 1 FROM pg_policies
|
|
39
|
+
WHERE schemaname = current_schema() AND tablename = 'workspace_variable_set_variables' AND policyname = 'workspace_isolation'
|
|
40
|
+
) THEN
|
|
41
|
+
DROP POLICY workspace_isolation ON "workspace_variable_set_variables";
|
|
42
|
+
END IF;
|
|
43
|
+
END $$;
|
|
44
|
+
CREATE POLICY workspace_isolation ON "workspace_variable_set_variables"
|
|
45
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
46
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
47
|
+
|
|
48
|
+
DO $$
|
|
49
|
+
BEGIN
|
|
50
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
51
|
+
EXECUTE format(
|
|
52
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
|
|
53
|
+
current_schema()
|
|
54
|
+
);
|
|
55
|
+
END IF;
|
|
56
|
+
END $$;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
-- Rigs: workspace-scoped, versioned sandbox machine definitions.
|
|
2
|
+
-- rigs — the named definition (one row per rig).
|
|
3
|
+
-- rig_versions — append-only, content-immutable versions; exactly one active
|
|
4
|
+
-- per rig (partial unique index). Only the `active` flag flips.
|
|
5
|
+
-- rig_changes — proposed/verified change records (M4 substrate only here).
|
|
6
|
+
-- Plus M3 wiring columns (sessions/scheduled_tasks/workspaces) added now so the
|
|
7
|
+
-- runtime-binding milestone needs no further migration. RLS/GRANT idiom mirrors
|
|
8
|
+
-- 0041_knowledge_layer.sql.
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS "rigs" (
|
|
11
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
12
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
13
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
14
|
+
"name" text NOT NULL,
|
|
15
|
+
"description" text,
|
|
16
|
+
"created_by" text,
|
|
17
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
18
|
+
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "rigs_workspace_name_idx" ON "rigs" ("workspace_id", "name");
|
|
22
|
+
CREATE INDEX IF NOT EXISTS "rigs_workspace_created_idx" ON "rigs" ("workspace_id", "created_at");
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS "rig_versions" (
|
|
25
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
26
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
27
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
28
|
+
"rig_id" uuid NOT NULL REFERENCES "rigs"("id") ON DELETE CASCADE,
|
|
29
|
+
"version" integer NOT NULL,
|
|
30
|
+
"image" text,
|
|
31
|
+
"setup_script" text,
|
|
32
|
+
"checks" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
33
|
+
"credential_hooks" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
34
|
+
"default_variable_set_ids" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
35
|
+
"changelog" text,
|
|
36
|
+
"created_by" text,
|
|
37
|
+
"active" boolean NOT NULL DEFAULT false,
|
|
38
|
+
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
-- Monotonic version number per rig.
|
|
42
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "rig_versions_rig_version_idx" ON "rig_versions" ("rig_id", "version");
|
|
43
|
+
-- At most one active version per rig (single-active invariant enforced in DB).
|
|
44
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "rig_versions_rig_active_idx" ON "rig_versions" ("rig_id") WHERE "active";
|
|
45
|
+
CREATE INDEX IF NOT EXISTS "rig_versions_workspace_rig_idx" ON "rig_versions" ("workspace_id", "rig_id", "version");
|
|
46
|
+
|
|
47
|
+
CREATE TABLE IF NOT EXISTS "rig_changes" (
|
|
48
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
49
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
50
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
51
|
+
"rig_id" uuid NOT NULL REFERENCES "rigs"("id") ON DELETE CASCADE,
|
|
52
|
+
"base_version_id" uuid REFERENCES "rig_versions"("id") ON DELETE SET NULL,
|
|
53
|
+
"kind" text NOT NULL,
|
|
54
|
+
"payload" jsonb NOT NULL,
|
|
55
|
+
"status" text NOT NULL DEFAULT 'proposed',
|
|
56
|
+
"proposed_by" text,
|
|
57
|
+
"verification" jsonb,
|
|
58
|
+
"result_version_id" uuid REFERENCES "rig_versions"("id") ON DELETE SET NULL,
|
|
59
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
60
|
+
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
61
|
+
CONSTRAINT "rig_changes_kind_check" CHECK ("kind" IN ('setup_append', 'definition_edit')),
|
|
62
|
+
CONSTRAINT "rig_changes_status_check" CHECK ("status" IN ('proposed', 'verifying', 'merged', 'rejected', 'failed'))
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE INDEX IF NOT EXISTS "rig_changes_workspace_rig_idx" ON "rig_changes" ("workspace_id", "rig_id", "created_at");
|
|
66
|
+
CREATE INDEX IF NOT EXISTS "rig_changes_workspace_status_idx" ON "rig_changes" ("workspace_id", "status");
|
|
67
|
+
|
|
68
|
+
-- M3 wiring columns (inert until the runtime-binding milestone consumes them).
|
|
69
|
+
ALTER TABLE "sessions" ADD COLUMN IF NOT EXISTS "rig_id" uuid;
|
|
70
|
+
ALTER TABLE "sessions" ADD COLUMN IF NOT EXISTS "rig_version_id" uuid;
|
|
71
|
+
ALTER TABLE "scheduled_tasks" ADD COLUMN IF NOT EXISTS "rig_id" uuid;
|
|
72
|
+
ALTER TABLE "workspaces" ADD COLUMN IF NOT EXISTS "default_rig_id" uuid;
|
|
73
|
+
|
|
74
|
+
DO $$
|
|
75
|
+
BEGIN
|
|
76
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'sessions_rig_id_fk') THEN
|
|
77
|
+
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_rig_id_fk"
|
|
78
|
+
FOREIGN KEY ("rig_id") REFERENCES "rigs"("id") ON DELETE SET NULL;
|
|
79
|
+
END IF;
|
|
80
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'sessions_rig_version_id_fk') THEN
|
|
81
|
+
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_rig_version_id_fk"
|
|
82
|
+
FOREIGN KEY ("rig_version_id") REFERENCES "rig_versions"("id") ON DELETE SET NULL;
|
|
83
|
+
END IF;
|
|
84
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'scheduled_tasks_rig_id_fk') THEN
|
|
85
|
+
ALTER TABLE "scheduled_tasks" ADD CONSTRAINT "scheduled_tasks_rig_id_fk"
|
|
86
|
+
FOREIGN KEY ("rig_id") REFERENCES "rigs"("id") ON DELETE SET NULL;
|
|
87
|
+
END IF;
|
|
88
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspaces_default_rig_id_fk') THEN
|
|
89
|
+
ALTER TABLE "workspaces" ADD CONSTRAINT "workspaces_default_rig_id_fk"
|
|
90
|
+
FOREIGN KEY ("default_rig_id") REFERENCES "rigs"("id") ON DELETE SET NULL;
|
|
91
|
+
END IF;
|
|
92
|
+
END $$;
|
|
93
|
+
|
|
94
|
+
CREATE INDEX IF NOT EXISTS "sessions_rig_idx" ON "sessions" ("workspace_id", "rig_id");
|
|
95
|
+
CREATE INDEX IF NOT EXISTS "scheduled_tasks_rig_idx" ON "scheduled_tasks" ("workspace_id", "rig_id");
|
|
96
|
+
|
|
97
|
+
-- Row-level security: workspace isolation on every rig table.
|
|
98
|
+
ALTER TABLE "rigs" ENABLE ROW LEVEL SECURITY;
|
|
99
|
+
ALTER TABLE "rigs" FORCE ROW LEVEL SECURITY;
|
|
100
|
+
DO $$
|
|
101
|
+
BEGIN
|
|
102
|
+
IF EXISTS (
|
|
103
|
+
SELECT 1 FROM pg_policies
|
|
104
|
+
WHERE schemaname = current_schema() AND tablename = 'rigs' AND policyname = 'workspace_isolation'
|
|
105
|
+
) THEN
|
|
106
|
+
DROP POLICY workspace_isolation ON "rigs";
|
|
107
|
+
END IF;
|
|
108
|
+
END $$;
|
|
109
|
+
CREATE POLICY workspace_isolation ON "rigs"
|
|
110
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
111
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
112
|
+
|
|
113
|
+
ALTER TABLE "rig_versions" ENABLE ROW LEVEL SECURITY;
|
|
114
|
+
ALTER TABLE "rig_versions" FORCE ROW LEVEL SECURITY;
|
|
115
|
+
DO $$
|
|
116
|
+
BEGIN
|
|
117
|
+
IF EXISTS (
|
|
118
|
+
SELECT 1 FROM pg_policies
|
|
119
|
+
WHERE schemaname = current_schema() AND tablename = 'rig_versions' AND policyname = 'workspace_isolation'
|
|
120
|
+
) THEN
|
|
121
|
+
DROP POLICY workspace_isolation ON "rig_versions";
|
|
122
|
+
END IF;
|
|
123
|
+
END $$;
|
|
124
|
+
CREATE POLICY workspace_isolation ON "rig_versions"
|
|
125
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
126
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
127
|
+
|
|
128
|
+
ALTER TABLE "rig_changes" ENABLE ROW LEVEL SECURITY;
|
|
129
|
+
ALTER TABLE "rig_changes" FORCE ROW LEVEL SECURITY;
|
|
130
|
+
DO $$
|
|
131
|
+
BEGIN
|
|
132
|
+
IF EXISTS (
|
|
133
|
+
SELECT 1 FROM pg_policies
|
|
134
|
+
WHERE schemaname = current_schema() AND tablename = 'rig_changes' AND policyname = 'workspace_isolation'
|
|
135
|
+
) THEN
|
|
136
|
+
DROP POLICY workspace_isolation ON "rig_changes";
|
|
137
|
+
END IF;
|
|
138
|
+
END $$;
|
|
139
|
+
CREATE POLICY workspace_isolation ON "rig_changes"
|
|
140
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
141
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
142
|
+
|
|
143
|
+
DO $$
|
|
144
|
+
BEGIN
|
|
145
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
146
|
+
EXECUTE format(
|
|
147
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
|
|
148
|
+
current_schema()
|
|
149
|
+
);
|
|
150
|
+
END IF;
|
|
151
|
+
END $$;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- M3 runtime binding: stamp the frozen rig version onto the group lease so a
|
|
2
|
+
-- live shared box conflicts on a rig-version mismatch exactly as it does on an
|
|
3
|
+
-- image mismatch (a shared box is one filesystem; a different rig = different
|
|
4
|
+
-- setup/tooling baked in). Nullable: a legacy/cold row or a rig-less session
|
|
5
|
+
-- reads NULL = "rig unknown", which never conflicts (rig-less sessions share as
|
|
6
|
+
-- today). sandbox_leases carries NO row-level security (it is keyed by a bare
|
|
7
|
+
-- sandbox_group_id, not an FK — see schema.ts), so this is a bare ADD COLUMN
|
|
8
|
+
-- with no policy/grant changes, mirroring the existing `image` column.
|
|
9
|
+
ALTER TABLE "sandbox_leases" ADD COLUMN IF NOT EXISTS "rig_version_id" uuid;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- Immediate-offline on a CLEAN going-offline signal.
|
|
2
|
+
--
|
|
3
|
+
-- A machine that explicitly announced "I'm stopping" (a typed GoingOffline:
|
|
4
|
+
-- user-stop / self-update / host-shutdown) must read OFFLINE immediately — for the
|
|
5
|
+
-- Machines dashboard AND for anything deciding whether to route work at it —
|
|
6
|
+
-- instead of waiting out the last_seen dead-detect window. Persist that goodbye as
|
|
7
|
+
-- a nullable marker on the enrollment: went_offline_at (WHEN it said goodbye) +
|
|
8
|
+
-- went_offline_reason (the typed reason string). The liveness derivation gives an
|
|
9
|
+
-- un-cleared marker precedence over last_seen aging; ANY newer liveness signal (a
|
|
10
|
+
-- reconnect Hello or a fresher heartbeat) clears it back to NULL. Both NULL (the
|
|
11
|
+
-- default) ⇒ today's behavior exactly — every existing + new row is a
|
|
12
|
+
-- behavior-preserving no-op, no backfill.
|
|
13
|
+
ALTER TABLE "enrollments"
|
|
14
|
+
ADD COLUMN IF NOT EXISTS "went_offline_at" timestamptz,
|
|
15
|
+
ADD COLUMN IF NOT EXISTS "went_offline_reason" text;
|
|
16
|
+
|
|
17
|
+
-- Re-grant on the new columns (idempotent; schema-agnostic per the Migration
|
|
18
|
+
-- Authoring rules — grants target current_schema() via dynamic SQL so an embedded
|
|
19
|
+
-- host running under a dedicated data schema is covered, not just `public`).
|
|
20
|
+
DO $$
|
|
21
|
+
BEGIN
|
|
22
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
23
|
+
EXECUTE format(
|
|
24
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
|
|
25
|
+
current_schema()
|
|
26
|
+
);
|
|
27
|
+
END IF;
|
|
28
|
+
END $$;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "enrollments" ADD COLUMN "op_stream" boolean DEFAULT false NOT NULL;
|