@opengeni/db 0.4.0 → 0.6.0

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/schema.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import 'drizzle-orm';
2
2
  import 'drizzle-orm/pg-core';
3
- export { i as agentRunStates, j as apiKeys, k as auditEvents, l as billingCustomers, m as capabilityCatalogItems, n as capabilityInstallations, o as codexRotationSettings, p as codexSubscriptionCredentials, q as creditLedgerEntries, r as deviceEnrollmentRequests, d as deviceEnrollmentStatusValues, t as documentBases, u as documentChunks, v as documents, a as enrollmentExposureValues, e as enrollmentOsValues, b as enrollmentStatusValues, w as enrollments, x as fileUploads, y as files, z as githubInstallations, A as machineMetricsLatest, B as machineMetricsSeries, C as managedAccounts, D as packInstallations, c as sandboxKindValues, E as sandboxLeaseHolders, F as sandboxLeaseLivenessValues, G as sandboxLeases, H as sandboxPtySessions, I as sandboxSessionEnvelopes, J as sandboxes, K as scheduledTaskRuns, L as scheduledTasks, M as sessionEvents, N as sessionGoals, O as sessionHistoryItems, P as sessionMcpServers, f as sessionRecordingCodecValues, g as sessionRecordingModeValues, h as sessionRecordingStateValues, Q as sessionRecordings, R as sessionTurns, S as sessions, T as socialConnections, U as socialPosts, V as stripeWebhookEvents, W as usageEvents, X as workspaceEnvironmentVariables, Y as workspaceEnvironments, Z as workspaceMemberships, _ as workspacePacks, $ as workspaces } from './schema-DuRsrmzD.js';
3
+ export { i as agentRunStates, j as apiKeys, k as auditEvents, l as billingCustomers, m as capabilityCatalogItems, n as capabilityInstallations, o as codexRotationSettings, p as codexSubscriptionCredentials, q as connections, r as creditLedgerEntries, t as deviceEnrollmentRequests, d as deviceEnrollmentStatusValues, u as documentBases, v as documentChunks, w as documents, a as enrollmentExposureValues, e as enrollmentOsValues, b as enrollmentStatusValues, x as enrollments, y as fileUploads, z as files, A as githubInstallations, B as importBatches, C as integrationOauthClients, D as integrationOauthStateNonces, E as knowledgeMemories, F as machineMetricsLatest, G as machineMetricsSeries, H as managedAccounts, I as packInstallations, c as sandboxKindValues, J as sandboxLeaseHolders, K as sandboxLeaseLivenessValues, L as sandboxLeases, M as sandboxPtySessions, N as sandboxSessionEnvelopes, O as sandboxes, P as scheduledTaskRuns, Q as scheduledTasks, R as sessionEvents, S as sessionGoals, T as sessionHistoryItems, U as sessionMcpServers, f as sessionRecordingCodecValues, g as sessionRecordingModeValues, h as sessionRecordingStateValues, V as sessionRecordings, W as sessionTurns, X as sessions, Y as socialConnections, Z as socialPosts, _ as stripeWebhookEvents, $ as usageEvents, a0 as workspaceEnvironmentVariables, a1 as workspaceEnvironments, a2 as workspaceMemberships, a3 as workspacePacks, a4 as workspaces } from './schema-Dsz6UHNv.js';
package/dist/schema.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  capabilityInstallations,
8
8
  codexRotationSettings,
9
9
  codexSubscriptionCredentials,
10
+ connections,
10
11
  creditLedgerEntries,
11
12
  deviceEnrollmentRequests,
12
13
  deviceEnrollmentStatusValues,
@@ -20,6 +21,10 @@ import {
20
21
  fileUploads,
21
22
  files,
22
23
  githubInstallations,
24
+ importBatches,
25
+ integrationOauthClients,
26
+ integrationOauthStateNonces,
27
+ knowledgeMemories,
23
28
  machineMetricsLatest,
24
29
  machineMetricsSeries,
25
30
  managedAccounts,
@@ -52,7 +57,7 @@ import {
52
57
  workspaceMemberships,
53
58
  workspacePacks,
54
59
  workspaces
55
- } from "./chunk-T2U4H4Z2.js";
60
+ } from "./chunk-ZIUCA2IO.js";
56
61
  import "./chunk-PZ5AY32C.js";
57
62
  export {
58
63
  agentRunStates,
@@ -63,6 +68,7 @@ export {
63
68
  capabilityInstallations,
64
69
  codexRotationSettings,
65
70
  codexSubscriptionCredentials,
71
+ connections,
66
72
  creditLedgerEntries,
67
73
  deviceEnrollmentRequests,
68
74
  deviceEnrollmentStatusValues,
@@ -76,6 +82,10 @@ export {
76
82
  fileUploads,
77
83
  files,
78
84
  githubInstallations,
85
+ importBatches,
86
+ integrationOauthClients,
87
+ integrationOauthStateNonces,
88
+ knowledgeMemories,
79
89
  machineMetricsLatest,
80
90
  machineMetricsSeries,
81
91
  managedAccounts,
@@ -0,0 +1,18 @@
1
+ -- Server-visible "display present but capture not granted" state.
2
+ --
3
+ -- A machine can have a display it cannot CAPTURE (macOS Screen Recording / TCC not
4
+ -- granted). The agent's connect Hello now reports has_display=false AND a human,
5
+ -- actionable reason for that case; persist the reason alongside has_display so the
6
+ -- Machines dashboard / VM picker can surface "display: capture not granted" instead
7
+ -- of a bare display_unavailable — the state must be VISIBLE server-side, not just in
8
+ -- a local agent log. NULL means capture is permitted OR the machine is genuinely
9
+ -- headless, so every existing row keeps its historical semantics with no backfill.
10
+ ALTER TABLE "enrollments"
11
+ ADD COLUMN IF NOT EXISTS "desktop_unavailable_reason" text;
12
+
13
+ DO $$
14
+ BEGIN
15
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
16
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO opengeni_app;
17
+ END IF;
18
+ END $$;
@@ -0,0 +1,32 @@
1
+ -- 0038_observability_counts.sql
2
+ -- Cross-workspace aggregate reads for Prometheus gauges. These mirror the
3
+ -- sandbox reaper's SECURITY DEFINER pattern so workers can refresh global
4
+ -- process metrics without disabling FORCE RLS on workspace-scoped tables.
5
+
6
+ CREATE OR REPLACE FUNCTION opengeni_private.count_queued_turns()
7
+ RETURNS bigint
8
+ LANGUAGE sql
9
+ SECURITY DEFINER
10
+ AS $$
11
+ SELECT count(*)::bigint
12
+ FROM session_turns
13
+ WHERE status = 'queued';
14
+ $$;
15
+
16
+ CREATE OR REPLACE FUNCTION opengeni_private.count_sandbox_leases_by_liveness()
17
+ RETURNS TABLE (liveness text, count bigint)
18
+ LANGUAGE sql
19
+ SECURITY DEFINER
20
+ AS $$
21
+ SELECT L.liveness, count(*)::bigint
22
+ FROM sandbox_leases L
23
+ GROUP BY L.liveness;
24
+ $$;
25
+
26
+ DO $$
27
+ BEGIN
28
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
29
+ GRANT EXECUTE ON FUNCTION opengeni_private.count_queued_turns() TO opengeni_app;
30
+ GRANT EXECUTE ON FUNCTION opengeni_private.count_sandbox_leases_by_liveness() TO opengeni_app;
31
+ END IF;
32
+ END $$;
@@ -0,0 +1,62 @@
1
+ CREATE TABLE IF NOT EXISTS "connections" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
4
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
5
+ "subject_id" text,
6
+ "provider_domain" text NOT NULL,
7
+ "kind" text NOT NULL,
8
+ "status" text NOT NULL DEFAULT 'active',
9
+ "credential_encrypted" text NOT NULL,
10
+ "granted_scopes" jsonb NOT NULL DEFAULT '[]'::jsonb,
11
+ "expires_at" timestamptz,
12
+ "last_refresh_at" timestamptz,
13
+ "last_used_at" timestamptz,
14
+ "last_error" text,
15
+ "version" integer NOT NULL DEFAULT 1,
16
+ "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
17
+ "created_by_subject_id" text,
18
+ "updated_by_subject_id" text,
19
+ "created_at" timestamptz NOT NULL DEFAULT now(),
20
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
21
+ CONSTRAINT "connections_kind_chk"
22
+ CHECK ("kind" IN ('oauth2', 'api_key', 'app_install', 'delegated')),
23
+ CONSTRAINT "connections_status_chk"
24
+ CHECK ("status" IN ('active', 'needs_reauth', 'revoked', 'error')),
25
+ CONSTRAINT "connections_granted_scopes_array_chk"
26
+ CHECK (jsonb_typeof("granted_scopes") = 'array'),
27
+ CONSTRAINT "connections_metadata_object_chk"
28
+ CHECK (jsonb_typeof("metadata") = 'object'),
29
+ CONSTRAINT "connections_version_positive_chk"
30
+ CHECK ("version" > 0)
31
+ );
32
+
33
+ CREATE INDEX IF NOT EXISTS "connections_workspace_provider_status_idx"
34
+ ON "connections" ("workspace_id", "provider_domain", "status");
35
+ CREATE INDEX IF NOT EXISTS "connections_workspace_subject_provider_idx"
36
+ ON "connections" ("workspace_id", "subject_id", "provider_domain");
37
+ CREATE INDEX IF NOT EXISTS "connections_workspace_kind_idx"
38
+ ON "connections" ("workspace_id", "kind");
39
+ CREATE INDEX IF NOT EXISTS "connections_workspace_expires_idx"
40
+ ON "connections" ("workspace_id", "expires_at");
41
+
42
+ ALTER TABLE "connections" ENABLE ROW LEVEL SECURITY;
43
+ ALTER TABLE "connections" FORCE ROW LEVEL SECURITY;
44
+ DO $$
45
+ BEGIN
46
+ IF EXISTS (
47
+ SELECT 1 FROM pg_policies
48
+ WHERE schemaname = current_schema() AND tablename = 'connections' AND policyname = 'workspace_isolation'
49
+ ) THEN
50
+ DROP POLICY workspace_isolation ON "connections";
51
+ END IF;
52
+ END $$;
53
+ CREATE POLICY workspace_isolation ON "connections"
54
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
55
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
56
+
57
+ DO $$
58
+ BEGIN
59
+ IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'opengeni_app') THEN
60
+ GRANT SELECT, INSERT, UPDATE, DELETE ON "connections" TO opengeni_app;
61
+ END IF;
62
+ END $$;
@@ -0,0 +1,7 @@
1
+ -- 0039_session_mcp_require_approval.sql
2
+ -- Per-session-MCP-server human-approval policy. jsonb holds either `true`
3
+ -- (every tool of the server requires approval) or a JSON array of UNPREFIXED
4
+ -- tool names (only those require it). NULL / absent = auto-run (the historical
5
+ -- default), so this is additive and backfill-free.
6
+
7
+ ALTER TABLE "session_mcp_servers" ADD COLUMN IF NOT EXISTS "require_approval" jsonb;
@@ -0,0 +1,36 @@
1
+ -- Correct historical opengeni_app grant blocks that hardcoded `public`.
2
+ -- Embedded hosts run migrations with search_path pointed at their dedicated
3
+ -- data schema, so grants must target current_schema().
4
+
5
+ DO $$
6
+ DECLARE
7
+ target_schema text := current_schema();
8
+ owner_role text := current_user;
9
+ app_role text := 'opengeni_app';
10
+ BEGIN
11
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = app_role) THEN
12
+ EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', target_schema, app_role);
13
+ EXECUTE format(
14
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO %I',
15
+ target_schema,
16
+ app_role
17
+ );
18
+ EXECUTE format(
19
+ 'GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %I TO %I',
20
+ target_schema,
21
+ app_role
22
+ );
23
+ EXECUTE format(
24
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO %I',
25
+ owner_role,
26
+ target_schema,
27
+ app_role
28
+ );
29
+ EXECUTE format(
30
+ 'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I GRANT USAGE, SELECT ON SEQUENCES TO %I',
31
+ owner_role,
32
+ target_schema,
33
+ app_role
34
+ );
35
+ END IF;
36
+ END $$;
@@ -0,0 +1,73 @@
1
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_kind" text NOT NULL DEFAULT 'manual_upload';
2
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_uri" text;
3
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_external_id" text;
4
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_title" text;
5
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_author" text;
6
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_created_at" timestamptz;
7
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_updated_at" timestamptz;
8
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "source_version" text;
9
+ ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "acl_tags" jsonb NOT NULL DEFAULT '[]'::jsonb;
10
+
11
+ CREATE INDEX IF NOT EXISTS "documents_workspace_source_kind_idx" ON "documents" ("workspace_id", "source_kind");
12
+ CREATE INDEX IF NOT EXISTS "documents_workspace_source_external_id_idx" ON "documents" ("workspace_id", "source_external_id");
13
+ CREATE INDEX IF NOT EXISTS "documents_acl_tags_idx" ON "documents" USING gin ("acl_tags");
14
+ CREATE INDEX IF NOT EXISTS "document_chunks_text_fts_idx" ON "document_chunks" USING gin (to_tsvector('simple', "text"));
15
+
16
+ CREATE TABLE IF NOT EXISTS "knowledge_memories" (
17
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
18
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
19
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
20
+ "status" text NOT NULL DEFAULT 'proposed',
21
+ "kind" text NOT NULL DEFAULT 'semantic',
22
+ "scope" text NOT NULL DEFAULT 'workspace',
23
+ "text" text NOT NULL,
24
+ "source_refs" jsonb NOT NULL DEFAULT '[]'::jsonb,
25
+ "confidence" integer NOT NULL DEFAULT 50,
26
+ "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
27
+ "created_by_session_id" uuid REFERENCES "sessions"("id") ON DELETE SET NULL,
28
+ "reviewed_by" text,
29
+ "reviewed_at" timestamptz,
30
+ "created_at" timestamptz NOT NULL DEFAULT now(),
31
+ "updated_at" timestamptz NOT NULL DEFAULT now()
32
+ );
33
+
34
+ DO $$
35
+ BEGIN
36
+ IF NOT EXISTS (
37
+ SELECT 1 FROM pg_constraint WHERE conname = 'knowledge_memories_workspace_account_fk'
38
+ ) THEN
39
+ ALTER TABLE "knowledge_memories" ADD CONSTRAINT "knowledge_memories_workspace_account_fk"
40
+ FOREIGN KEY ("workspace_id", "account_id") REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
41
+ END IF;
42
+ END $$;
43
+
44
+ CREATE INDEX IF NOT EXISTS "knowledge_memories_workspace_status_idx" ON "knowledge_memories" ("workspace_id", "status", "updated_at");
45
+ CREATE INDEX IF NOT EXISTS "knowledge_memories_workspace_kind_idx" ON "knowledge_memories" ("workspace_id", "kind");
46
+ CREATE INDEX IF NOT EXISTS "knowledge_memories_workspace_scope_idx" ON "knowledge_memories" ("workspace_id", "scope");
47
+ CREATE INDEX IF NOT EXISTS "knowledge_memories_workspace_created_by_session_idx" ON "knowledge_memories" ("workspace_id", "created_by_session_id");
48
+ CREATE INDEX IF NOT EXISTS "knowledge_memories_text_fts_idx" ON "knowledge_memories" USING gin (to_tsvector('simple', "text"));
49
+
50
+ ALTER TABLE "knowledge_memories" ENABLE ROW LEVEL SECURITY;
51
+ ALTER TABLE "knowledge_memories" FORCE ROW LEVEL SECURITY;
52
+ DO $$
53
+ BEGIN
54
+ IF EXISTS (
55
+ SELECT 1 FROM pg_policies
56
+ WHERE schemaname = current_schema() AND tablename = 'knowledge_memories' AND policyname = 'workspace_isolation'
57
+ ) THEN
58
+ DROP POLICY workspace_isolation ON "knowledge_memories";
59
+ END IF;
60
+ END $$;
61
+ CREATE POLICY workspace_isolation ON "knowledge_memories"
62
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
63
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
64
+
65
+ DO $$
66
+ BEGIN
67
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
68
+ EXECUTE format(
69
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
70
+ current_schema()
71
+ );
72
+ END IF;
73
+ END $$;
@@ -0,0 +1,56 @@
1
+ CREATE TABLE IF NOT EXISTS "integration_oauth_clients" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "issuer" text NOT NULL,
4
+ "authorization_server" text NOT NULL,
5
+ "client_id" text NOT NULL,
6
+ "client_secret_encrypted" text,
7
+ "token_endpoint_auth_method" text NOT NULL DEFAULT 'none',
8
+ "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
9
+ "created_at" timestamptz NOT NULL DEFAULT now(),
10
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
11
+ CONSTRAINT "integration_oauth_clients_metadata_object_chk"
12
+ CHECK (jsonb_typeof("metadata") = 'object')
13
+ );
14
+
15
+ CREATE UNIQUE INDEX IF NOT EXISTS "integration_oauth_clients_issuer_idx"
16
+ ON "integration_oauth_clients" ("issuer");
17
+ CREATE INDEX IF NOT EXISTS "integration_oauth_clients_as_idx"
18
+ ON "integration_oauth_clients" ("authorization_server");
19
+
20
+ CREATE TABLE IF NOT EXISTS "integration_oauth_state_nonces" (
21
+ "nonce" text PRIMARY KEY NOT NULL,
22
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
23
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
24
+ "subject_id" text NOT NULL,
25
+ "expires_at" timestamptz NOT NULL,
26
+ "used_at" timestamptz NOT NULL DEFAULT now(),
27
+ "created_at" timestamptz NOT NULL DEFAULT now()
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS "integration_oauth_state_nonces_workspace_idx"
31
+ ON "integration_oauth_state_nonces" ("workspace_id");
32
+ CREATE INDEX IF NOT EXISTS "integration_oauth_state_nonces_expires_idx"
33
+ ON "integration_oauth_state_nonces" ("expires_at");
34
+
35
+ ALTER TABLE "integration_oauth_state_nonces" ENABLE ROW LEVEL SECURITY;
36
+ ALTER TABLE "integration_oauth_state_nonces" FORCE ROW LEVEL SECURITY;
37
+ DO $$
38
+ BEGIN
39
+ IF EXISTS (
40
+ SELECT 1 FROM pg_policies
41
+ WHERE schemaname = current_schema() AND tablename = 'integration_oauth_state_nonces' AND policyname = 'workspace_isolation'
42
+ ) THEN
43
+ DROP POLICY workspace_isolation ON "integration_oauth_state_nonces";
44
+ END IF;
45
+ END $$;
46
+ CREATE POLICY workspace_isolation ON "integration_oauth_state_nonces"
47
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
48
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
49
+
50
+ DO $$
51
+ BEGIN
52
+ IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'opengeni_app') THEN
53
+ GRANT SELECT, INSERT, UPDATE, DELETE ON "integration_oauth_clients" TO opengeni_app;
54
+ GRANT SELECT, INSERT, UPDATE, DELETE ON "integration_oauth_state_nonces" TO opengeni_app;
55
+ END IF;
56
+ END $$;
@@ -0,0 +1,104 @@
1
+ CREATE TABLE IF NOT EXISTS "import_batches" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "source" text NOT NULL,
4
+ "snapshot_date" timestamptz NOT NULL,
5
+ "snapshot_ref" text,
6
+ "attribution_note" text NOT NULL,
7
+ "imported_count" integer NOT NULL DEFAULT 0,
8
+ "skipped_count" integer NOT NULL DEFAULT 0,
9
+ "quarantined_count" integer NOT NULL DEFAULT 0,
10
+ "logo_failure_count" integer NOT NULL DEFAULT 0,
11
+ "stale_count" integer NOT NULL DEFAULT 0,
12
+ "details" jsonb NOT NULL DEFAULT '{}'::jsonb,
13
+ "created_at" timestamptz NOT NULL DEFAULT now(),
14
+ "updated_at" timestamptz NOT NULL DEFAULT now()
15
+ );
16
+
17
+ CREATE INDEX IF NOT EXISTS "import_batches_source_snapshot_idx" ON "import_batches" ("source", "snapshot_date");
18
+ CREATE INDEX IF NOT EXISTS "import_batches_created_at_idx" ON "import_batches" ("created_at");
19
+
20
+ ALTER TABLE "import_batches" ENABLE ROW LEVEL SECURITY;
21
+ ALTER TABLE "import_batches" FORCE ROW LEVEL SECURITY;
22
+ DO $$
23
+ BEGIN
24
+ IF EXISTS (
25
+ SELECT 1 FROM pg_policies
26
+ WHERE schemaname = current_schema() AND tablename = 'import_batches' AND policyname = 'global_catalog_import_batches'
27
+ ) THEN
28
+ DROP POLICY global_catalog_import_batches ON "import_batches";
29
+ END IF;
30
+ END $$;
31
+ CREATE POLICY global_catalog_import_batches ON "import_batches"
32
+ USING (true)
33
+ WITH CHECK (true);
34
+
35
+ ALTER TABLE "capability_catalog_items" ALTER COLUMN "account_id" DROP NOT NULL;
36
+ ALTER TABLE "capability_catalog_items" ALTER COLUMN "workspace_id" DROP NOT NULL;
37
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "provider_domain" text;
38
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "surface_type" text;
39
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "transport" text;
40
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "mcp_url" text;
41
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "auth_kind" text;
42
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "credential_facts" jsonb NOT NULL DEFAULT '[]'::jsonb;
43
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "tier" text;
44
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "provenance" text;
45
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "logo_asset_path" text;
46
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "import_batch_id" uuid REFERENCES "import_batches"("id") ON DELETE SET NULL;
47
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "stale" boolean NOT NULL DEFAULT false;
48
+ ALTER TABLE "capability_catalog_items" ADD COLUMN IF NOT EXISTS "stale_at" timestamptz;
49
+
50
+ DO $$
51
+ BEGIN
52
+ IF NOT EXISTS (
53
+ SELECT 1 FROM pg_constraint WHERE conname = 'capability_catalog_items_registry_global_ck'
54
+ ) THEN
55
+ ALTER TABLE "capability_catalog_items" ADD CONSTRAINT "capability_catalog_items_registry_global_ck"
56
+ CHECK (
57
+ "source" <> 'registry'
58
+ OR (
59
+ "account_id" IS NULL
60
+ AND "workspace_id" IS NULL
61
+ AND "kind" = 'mcp'
62
+ AND "provider_domain" IS NOT NULL
63
+ AND "mcp_url" IS NOT NULL
64
+ )
65
+ );
66
+ END IF;
67
+ END $$;
68
+
69
+ CREATE UNIQUE INDEX IF NOT EXISTS "capability_catalog_items_registry_surface_idx"
70
+ ON "capability_catalog_items" ("source", "provider_domain", "mcp_url");
71
+ CREATE UNIQUE INDEX IF NOT EXISTS "capability_catalog_items_global_capability_idx"
72
+ ON "capability_catalog_items" ("id") WHERE "workspace_id" IS NULL;
73
+ CREATE INDEX IF NOT EXISTS "capability_catalog_items_provider_domain_idx" ON "capability_catalog_items" ("provider_domain");
74
+ CREATE INDEX IF NOT EXISTS "capability_catalog_items_import_batch_idx" ON "capability_catalog_items" ("import_batch_id");
75
+ CREATE INDEX IF NOT EXISTS "capability_catalog_items_source_stale_idx" ON "capability_catalog_items" ("source", "stale");
76
+
77
+ DO $$
78
+ BEGIN
79
+ IF EXISTS (
80
+ SELECT 1 FROM pg_policies
81
+ WHERE schemaname = current_schema() AND tablename = 'capability_catalog_items' AND policyname = 'workspace_isolation'
82
+ ) THEN
83
+ DROP POLICY workspace_isolation ON "capability_catalog_items";
84
+ END IF;
85
+ END $$;
86
+ CREATE POLICY workspace_isolation ON "capability_catalog_items"
87
+ USING (
88
+ ("account_id" IS NULL AND "workspace_id" IS NULL)
89
+ OR opengeni_private.workspace_rls_visible(account_id, workspace_id)
90
+ )
91
+ WITH CHECK (
92
+ ("account_id" IS NULL AND "workspace_id" IS NULL)
93
+ OR opengeni_private.workspace_rls_visible(account_id, workspace_id)
94
+ );
95
+
96
+ DO $$
97
+ BEGIN
98
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
99
+ EXECUTE format(
100
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
101
+ current_schema()
102
+ );
103
+ END IF;
104
+ END $$;
@@ -0,0 +1,14 @@
1
+ -- Per-turn toolspace call budget counter. The previous enforcement read the
2
+ -- count of `agent.toolCall.created` toolspace events then compared against the
3
+ -- limit before appending the next event (read-then-append TOCTOU): concurrent
4
+ -- tools/call requests all observed a stale count and all passed. This column
5
+ -- makes the budget a single atomic reservation:
6
+ -- UPDATE session_turns SET toolspace_call_count = toolspace_call_count + 1
7
+ -- WHERE id = $turn AND toolspace_call_count < $limit RETURNING toolspace_call_count
8
+ -- Concurrent reservations serialize on the row lock, so exactly `limit` succeed.
9
+ --
10
+ -- opengeni_app already holds table-level DML on session_turns (migration 0040
11
+ -- grants ON ALL TABLES + default privileges); a new column inherits those
12
+ -- grants, so no additional GRANT block is required.
13
+ ALTER TABLE "session_turns"
14
+ ADD COLUMN IF NOT EXISTS "toolspace_call_count" integer NOT NULL DEFAULT 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -48,19 +48,14 @@
48
48
  "migrate": "bun src/migrate.ts",
49
49
  "provision-roles": "bun src/provision-roles.ts",
50
50
  "typecheck": "tsc --noEmit",
51
- "build": "tsup"
51
+ "build": "tsup",
52
+ "prepublishOnly": "bash ../../scripts/prepublish-guard"
52
53
  },
53
54
  "dependencies": {
54
55
  "@opengeni/codex": "^0.2.1",
55
- "@opengeni/config": "^0.2.4",
56
- "@opengeni/contracts": "^0.6.0",
56
+ "@opengeni/config": "^0.3.0",
57
+ "@opengeni/contracts": "^0.9.0",
57
58
  "drizzle-orm": "^0.45.2",
58
59
  "postgres": "^3.4.7"
59
- },
60
- "devDependencies": {
61
- "@opengeni/testing": "workspace:*",
62
- "drizzle-kit": "^0.31.8",
63
- "tsup": "^8.5.0",
64
- "typescript": "^6.0.3"
65
60
  }
66
61
  }