@opengeni/db 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/browser-sessions.d.ts +26 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/capability-reconciliation.js +2 -2
  4. package/dist/{chunk-4RDUZ5UV.js → chunk-4ZQFT5II.js} +2 -2
  5. package/dist/{chunk-6ORJPCYW.js → chunk-5UOWNKPP.js} +2 -2
  6. package/dist/{chunk-4CCFFLDZ.js → chunk-BG46J5HF.js} +13 -2
  7. package/dist/chunk-BG46J5HF.js.map +1 -0
  8. package/dist/{chunk-CSVHCF3R.js → chunk-IB3SW3JW.js} +11 -4
  9. package/dist/{chunk-CSVHCF3R.js.map → chunk-IB3SW3JW.js.map} +1 -1
  10. package/dist/{chunk-GSB4ISHE.js → chunk-IIPWNNVR.js} +4 -1
  11. package/dist/chunk-IIPWNNVR.js.map +1 -0
  12. package/dist/{chunk-MAMBI2HR.js → chunk-JPYN5XKV.js} +39 -3
  13. package/dist/chunk-JPYN5XKV.js.map +1 -0
  14. package/dist/{chunk-LSLNMENV.js → chunk-KZXWUXWV.js} +4 -4
  15. package/dist/{chunk-LO6BVXXH.js → chunk-LD52IFS3.js} +2 -2
  16. package/dist/{chunk-JILAWE26.js → chunk-M5Z6II2P.js} +2 -2
  17. package/dist/{chunk-PGIFHEBA.js → chunk-RVEOGRMF.js} +2 -2
  18. package/dist/{chunk-H7BC444C.js → chunk-SRAMNVHT.js} +17 -1
  19. package/dist/chunk-SRAMNVHT.js.map +1 -0
  20. package/dist/{chunk-XK6HXON2.js → chunk-UXFBXCHM.js} +2 -2
  21. package/dist/{chunk-RXH5CFG3.js → chunk-VE2XK2KD.js} +12 -8
  22. package/dist/{chunk-RXH5CFG3.js.map → chunk-VE2XK2KD.js.map} +1 -1
  23. package/dist/{chunk-FULLE5CF.js → chunk-WT7HG43Y.js} +3 -3
  24. package/dist/{chunk-I7HQL6FK.js → chunk-ZM5BA3TD.js} +3 -3
  25. package/dist/connect-attempts.js +3 -3
  26. package/dist/database.d.ts +8 -0
  27. package/dist/editable-artifact-durable-export.js +2 -2
  28. package/dist/editable-artifacts.js +3 -3
  29. package/dist/index.d.ts +69 -20
  30. package/dist/index.js +633 -202
  31. package/dist/index.js.map +1 -1
  32. package/dist/knowledge-entries.d.ts +2 -0
  33. package/dist/knowledge-indexing.d.ts +17 -0
  34. package/dist/managed-auth-session-sets.js +3 -3
  35. package/dist/managed-user-setup.d.ts +1 -0
  36. package/dist/mcp-operations.js +2 -2
  37. package/dist/migrate.d.ts +10 -1
  38. package/dist/migrate.js +3 -1
  39. package/dist/organization-integration-policy.js +3 -3
  40. package/dist/organization-membership-lifecycle.d.ts +1 -0
  41. package/dist/provision-roles.js +1 -1
  42. package/dist/retained-provider-commands.d.ts +4 -2
  43. package/dist/retained-provider-commands.js +6 -6
  44. package/dist/runtime-posture.d.ts +1 -1
  45. package/dist/schema.d.ts +25 -8
  46. package/dist/schema.js +1 -1
  47. package/dist/session-background-commands.js +4 -4
  48. package/dist/session-command-output.js +5 -5
  49. package/dist/session-event-slices.js +3 -3
  50. package/dist/session-mcp-credential-rotation.js +4 -4
  51. package/dist/session-tenancy.js +3 -3
  52. package/dist/video-generation.js +3 -3
  53. package/dist/workspace-tool-defaults.js +4 -4
  54. package/drizzle/0508_deadline_command_workspace_capture.sql +75 -0
  55. package/drizzle/0509_verified_signup_trial_credits.sql +76 -0
  56. package/drizzle/0510_knowledge_index_funding_wait.sql +174 -0
  57. package/drizzle/0511_knowledge_visible_index_status.sql +56 -0
  58. package/drizzle/0512_insights_fact_reads_hash_join.sql +31 -0
  59. package/drizzle/0513_message_fork_prefix_compaction.sql +85 -0
  60. package/drizzle/0514_failed_session_variable_set_attach.sql +25 -0
  61. package/package.json +5 -5
  62. package/src/browser-sessions.ts +71 -1
  63. package/src/database.ts +18 -0
  64. package/src/index.ts +706 -252
  65. package/src/insights.ts +44 -36
  66. package/src/knowledge-entries.ts +43 -2
  67. package/src/knowledge-indexing.ts +79 -1
  68. package/src/managed-user-setup.ts +18 -1
  69. package/src/migrate.ts +58 -1
  70. package/src/organization-membership-lifecycle.ts +11 -0
  71. package/src/provision-roles.ts +12 -0
  72. package/src/retained-provider-commands.ts +16 -6
  73. package/src/runtime-posture.ts +4 -0
  74. package/src/schema.ts +3 -0
  75. package/dist/chunk-4CCFFLDZ.js.map +0 -1
  76. package/dist/chunk-GSB4ISHE.js.map +0 -1
  77. package/dist/chunk-H7BC444C.js.map +0 -1
  78. package/dist/chunk-MAMBI2HR.js.map +0 -1
  79. /package/dist/{chunk-4RDUZ5UV.js.map → chunk-4ZQFT5II.js.map} +0 -0
  80. /package/dist/{chunk-6ORJPCYW.js.map → chunk-5UOWNKPP.js.map} +0 -0
  81. /package/dist/{chunk-LSLNMENV.js.map → chunk-KZXWUXWV.js.map} +0 -0
  82. /package/dist/{chunk-LO6BVXXH.js.map → chunk-LD52IFS3.js.map} +0 -0
  83. /package/dist/{chunk-JILAWE26.js.map → chunk-M5Z6II2P.js.map} +0 -0
  84. /package/dist/{chunk-PGIFHEBA.js.map → chunk-RVEOGRMF.js.map} +0 -0
  85. /package/dist/{chunk-XK6HXON2.js.map → chunk-UXFBXCHM.js.map} +0 -0
  86. /package/dist/{chunk-FULLE5CF.js.map → chunk-WT7HG43Y.js.map} +0 -0
  87. /package/dist/{chunk-I7HQL6FK.js.map → chunk-ZM5BA3TD.js.map} +0 -0
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  appendSessionCommandOutput
3
- } from "./chunk-LSLNMENV.js";
4
- import "./chunk-CSVHCF3R.js";
5
- import "./chunk-I7HQL6FK.js";
3
+ } from "./chunk-KZXWUXWV.js";
4
+ import "./chunk-IB3SW3JW.js";
5
+ import "./chunk-ZM5BA3TD.js";
6
6
  import "./chunk-4UJCXBR2.js";
7
- import "./chunk-4CCFFLDZ.js";
8
- import "./chunk-GSB4ISHE.js";
7
+ import "./chunk-BG46J5HF.js";
8
+ import "./chunk-IIPWNNVR.js";
9
9
  import "./chunk-S2TZ3F4L.js";
10
10
  import "./chunk-PZ5AY32C.js";
11
11
  export {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  listSessionEventSlices,
3
3
  readSessionMessageSliceInScope
4
- } from "./chunk-FULLE5CF.js";
5
- import "./chunk-4CCFFLDZ.js";
6
- import "./chunk-GSB4ISHE.js";
4
+ } from "./chunk-WT7HG43Y.js";
5
+ import "./chunk-BG46J5HF.js";
6
+ import "./chunk-IIPWNNVR.js";
7
7
  import "./chunk-S2TZ3F4L.js";
8
8
  import "./chunk-PZ5AY32C.js";
9
9
  export {
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  lockSessionEventWriteRows
3
- } from "./chunk-CSVHCF3R.js";
4
- import "./chunk-I7HQL6FK.js";
3
+ } from "./chunk-IB3SW3JW.js";
4
+ import "./chunk-ZM5BA3TD.js";
5
5
  import "./chunk-4UJCXBR2.js";
6
6
  import {
7
7
  rawRows,
8
8
  setSubjectRlsContext,
9
9
  withRlsContext
10
- } from "./chunk-4CCFFLDZ.js";
10
+ } from "./chunk-BG46J5HF.js";
11
11
  import {
12
12
  apiKeys,
13
13
  sessionCommandReceipts,
14
14
  sessionMcpServers,
15
15
  sessions
16
- } from "./chunk-GSB4ISHE.js";
16
+ } from "./chunk-IIPWNNVR.js";
17
17
  import "./chunk-S2TZ3F4L.js";
18
18
  import "./chunk-PZ5AY32C.js";
19
19
 
@@ -14,9 +14,9 @@ import {
14
14
  replayAppliedSessionFork,
15
15
  sessionTenancyProductActivated,
16
16
  transitionSessionVisibility
17
- } from "./chunk-JILAWE26.js";
18
- import "./chunk-4CCFFLDZ.js";
19
- import "./chunk-GSB4ISHE.js";
17
+ } from "./chunk-M5Z6II2P.js";
18
+ import "./chunk-BG46J5HF.js";
19
+ import "./chunk-IIPWNNVR.js";
20
20
  import "./chunk-S2TZ3F4L.js";
21
21
  import "./chunk-PZ5AY32C.js";
22
22
  export {
@@ -29,9 +29,9 @@ import {
29
29
  settleVideoGenerationFailure,
30
30
  settleVideoGenerationReady,
31
31
  updateWorkspaceVideoGenerationPolicy
32
- } from "./chunk-I7HQL6FK.js";
33
- import "./chunk-4CCFFLDZ.js";
34
- import "./chunk-GSB4ISHE.js";
32
+ } from "./chunk-ZM5BA3TD.js";
33
+ import "./chunk-BG46J5HF.js";
34
+ import "./chunk-IIPWNNVR.js";
35
35
  import "./chunk-S2TZ3F4L.js";
36
36
  import "./chunk-PZ5AY32C.js";
37
37
  export {
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  lockWorkspaceInferenceControl
3
- } from "./chunk-CSVHCF3R.js";
4
- import "./chunk-I7HQL6FK.js";
3
+ } from "./chunk-IB3SW3JW.js";
4
+ import "./chunk-ZM5BA3TD.js";
5
5
  import "./chunk-4UJCXBR2.js";
6
6
  import {
7
7
  withWorkspaceRls
8
- } from "./chunk-4CCFFLDZ.js";
9
- import "./chunk-GSB4ISHE.js";
8
+ } from "./chunk-BG46J5HF.js";
9
+ import "./chunk-IIPWNNVR.js";
10
10
  import "./chunk-S2TZ3F4L.js";
11
11
  import "./chunk-PZ5AY32C.js";
12
12
 
@@ -0,0 +1,75 @@
1
+ -- deployment-mode: rolling
2
+ -- A scheduled Modal rotation may capture current files after a bounded
3
+ -- cancellation grace, even if a legacy command remains active. Exact enrollment
4
+ -- still requires a quiesced owner and no other holders/admissions.
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '10min';
7
+
8
+ -- Keep the original cancellation intent immutable. A deadline can arrive after
9
+ -- an explicit stop, so it needs its own clock for the bounded capture grace.
10
+ ALTER TABLE sandbox_retained_processes
11
+ ADD COLUMN deadline_cancellation_requested_at timestamptz;
12
+
13
+ DO $install$
14
+ DECLARE target_schema text := current_schema();
15
+ BEGIN
16
+ EXECUTE format($definition$
17
+ CREATE OR REPLACE FUNCTION opengeni_private.list_unobservable_command_drain_candidates(p_limit integer)
18
+ RETURNS TABLE(account_id uuid, workspace_id uuid, sandbox_group_id uuid)
19
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, %1$I, pg_temp
20
+ AS $body$
21
+ DECLARE inventory_id uuid;
22
+ BEGIN
23
+ IF p_limit < 1 OR p_limit > 100 THEN RAISE EXCEPTION 'invalid drain batch'; END IF;
24
+ inventory_id := opengeni_private.open_session_tenancy_fence_inventory(%1$I.session_tenancy_fence_target_schema());
25
+ RETURN QUERY SELECT lease.account_id, lease.workspace_id, lease.sandbox_group_id
26
+ FROM %1$I.sandbox_leases lease
27
+ WHERE lease.backend = 'modal' AND lease.liveness IN ('warm', 'draining')
28
+ AND (lease.unobservable_command_drain_ids IS NOT NULL OR EXISTS (
29
+ SELECT 1 FROM %1$I.sandbox_retained_processes process
30
+ WHERE process.lease_id = lease.id AND process.state = 'active'
31
+ AND (
32
+ process.last_reconcile_outcome IN (
33
+ 'process_observation_unavailable', 'quarantined_process_observation_unavailable',
34
+ 'provider_binding_missing', 'quarantined_provider_binding_missing',
35
+ 'provider_binding_mismatch', 'quarantined_provider_binding_mismatch'
36
+ )
37
+ OR (
38
+ process.last_reconcile_outcome = 'provider_error'
39
+ AND process.reconcile_attempts >= 5
40
+ AND EXISTS (
41
+ SELECT 1 FROM %1$I.session_background_commands command
42
+ WHERE command.retained_process_id = process.id
43
+ AND command.workspace_id = process.workspace_id
44
+ AND command.session_id = process.session_id
45
+ AND command.provider = 'managed' AND command.state = 'stopping'
46
+ AND command.cancel_requested_at IS NOT NULL
47
+ )
48
+ )
49
+ OR (
50
+ lease.rotation_reason = 'provider_deadline'
51
+ AND lease.rotation_requested_at IS NOT NULL
52
+ AND process.deadline_cancellation_requested_at < now() - interval '2 minutes'
53
+ AND process.reconcile_attempts >= 1
54
+ )
55
+ )
56
+ ))
57
+ ORDER BY
58
+ CASE WHEN lease.rotation_reason = 'provider_deadline'
59
+ AND lease.rotation_requested_at IS NOT NULL THEN 0 ELSE 1 END,
60
+ CASE WHEN lease.rotation_reason = 'provider_deadline'
61
+ AND lease.rotation_requested_at IS NOT NULL
62
+ THEN lease.provider_deadline_at END NULLS LAST,
63
+ lease.unobservable_command_checked_at NULLS FIRST,
64
+ lease.id LIMIT p_limit;
65
+ PERFORM opengeni_private.close_session_tenancy_fence_inventory(inventory_id);
66
+ EXCEPTION WHEN OTHERS THEN
67
+ PERFORM opengeni_private.close_session_tenancy_fence_inventory(inventory_id);
68
+ RAISE;
69
+ END $body$;
70
+ $definition$, target_schema);
71
+ REVOKE ALL ON FUNCTION opengeni_private.list_unobservable_command_drain_candidates(integer) FROM PUBLIC;
72
+ END $install$;
73
+
74
+ RESET statement_timeout;
75
+ RESET lock_timeout;
@@ -0,0 +1,76 @@
1
+ -- deployment-mode: rolling
2
+ -- Only a newly completed, verified self-service organization setup can receive
3
+ -- this campaign's $10 account credit. The API explicitly opts in per request;
4
+ -- deploying this migration alone does not activate the offer. Older receipts,
5
+ -- invited users, and additional organizations are never backfilled.
6
+
7
+ CREATE FUNCTION opengeni_private.grant_verified_signup_trial_credit()
8
+ RETURNS trigger
9
+ LANGUAGE plpgsql
10
+ SECURITY DEFINER
11
+ SET search_path FROM CURRENT
12
+ AS $body$
13
+ DECLARE
14
+ verified_owner boolean;
15
+ account_owner boolean;
16
+ inserted_count integer;
17
+ BEGIN
18
+ IF pg_catalog.current_setting('opengeni.verified_signup_trial_enabled', true)
19
+ IS DISTINCT FROM 'on' THEN
20
+ RETURN NEW;
21
+ END IF;
22
+
23
+ -- This is a defense in depth check: the sole receipt writer already checks
24
+ -- the canonical verified human and the absence of prior memberships.
25
+ SELECT candidate.email_verified IS TRUE
26
+ INTO verified_owner
27
+ FROM auth_users candidate
28
+ WHERE candidate.id = NEW.auth_user_id;
29
+ SELECT account.external_source = 'better-auth:user'
30
+ AND account.external_id = NEW.auth_user_id
31
+ INTO account_owner
32
+ FROM managed_accounts account
33
+ WHERE account.id = NEW.account_id;
34
+ IF verified_owner IS DISTINCT FROM TRUE
35
+ OR account_owner IS DISTINCT FROM TRUE
36
+ OR opengeni_private.current_subject_id() IS DISTINCT FROM 'user:' || NEW.auth_user_id
37
+ OR opengeni_private.current_account_id() IS DISTINCT FROM NEW.account_id
38
+ OR NEW.result ->> 'organizationId' IS DISTINCT FROM NEW.account_id::text
39
+ THEN
40
+ RAISE EXCEPTION 'verified signup trial requires an exact new owner'
41
+ USING ERRCODE = '42501';
42
+ END IF;
43
+
44
+ INSERT INTO credit_ledger_entries (
45
+ account_id, workspace_id, type, amount_micros, source_type, source_id,
46
+ idempotency_key, metadata
47
+ ) VALUES (
48
+ NEW.account_id, NULL, 'grant', 10000000, 'verified_signup_trial',
49
+ NEW.auth_user_id, 'verified-signup-trial:v1:' || NEW.auth_user_id,
50
+ pg_catalog.jsonb_build_object('campaign', 'verified_signup_trial_v1')
51
+ ) ON CONFLICT (idempotency_key) DO NOTHING;
52
+ GET DIAGNOSTICS inserted_count = ROW_COUNT;
53
+ IF inserted_count <> 1 THEN
54
+ RAISE EXCEPTION 'verified signup trial credit idempotency collision'
55
+ USING ERRCODE = '23505';
56
+ END IF;
57
+ RETURN NEW;
58
+ END
59
+ $body$;
60
+
61
+ REVOKE ALL ON FUNCTION opengeni_private.grant_verified_signup_trial_credit() FROM PUBLIC;
62
+ DO $posture$
63
+ DECLARE
64
+ data_schema text := pg_catalog.current_schema();
65
+ BEGIN
66
+ EXECUTE pg_catalog.format(
67
+ 'ALTER FUNCTION opengeni_private.grant_verified_signup_trial_credit() '
68
+ || 'SET search_path = pg_catalog, %I, opengeni_private, pg_temp',
69
+ data_schema
70
+ );
71
+ END
72
+ $posture$;
73
+
74
+ CREATE TRIGGER self_service_setup_trial_credit
75
+ AFTER INSERT ON self_service_organization_setup_receipts
76
+ FOR EACH ROW EXECUTE FUNCTION opengeni_private.grant_verified_signup_trial_credit();
@@ -0,0 +1,174 @@
1
+ -- deployment-mode: rolling
2
+ -- Preserve an unfinished projection while its account is unfunded. The same
3
+ -- leased capability used by knowledge_index_work is required; no human or
4
+ -- public request may put a different tenant's revision into this state.
5
+ ALTER TABLE knowledge_index_jobs ADD COLUMN billing_mode text
6
+ CHECK (billing_mode IN ('usage_only','shadow','credits'));
7
+ ALTER TABLE knowledge_index_jobs ADD COLUMN billed_generation integer;
8
+ ALTER TABLE knowledge_index_jobs ADD COLUMN billing_rate_micros_per_million_bytes bigint
9
+ CHECK (billing_rate_micros_per_million_bytes >= 0);
10
+
11
+ -- Freeze the mode at first work for a generation. Rows queued before the
12
+ -- explicitly configured paid cutover cannot be retroactively charged, even
13
+ -- if a worker restarts. Already-started generations keep their chosen mode.
14
+ CREATE FUNCTION knowledge_index_billing_policy(
15
+ p_account uuid, p_revision uuid, p_lease uuid, p_requested text,
16
+ p_activation_at timestamptz, p_rate bigint
17
+ )
18
+ RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
19
+ DECLARE
20
+ job knowledge_index_jobs%ROWTYPE;
21
+ decided text;
22
+ frozen_rate bigint;
23
+ decision_outcome text;
24
+ previous text := current_setting('opengeni.knowledge_index_dispatcher',true);
25
+ BEGIN
26
+ IF p_account IS DISTINCT FROM opengeni_private.current_account_id() OR
27
+ p_requested IS NULL OR p_requested NOT IN ('usage_only','shadow','credits') OR
28
+ p_activation_at IS NULL OR p_rate IS NULL OR p_rate < 0 OR
29
+ (p_requested='credits' AND p_rate=0) THEN
30
+ RAISE EXCEPTION 'Knowledge index billing policy unavailable' USING ERRCODE='42501';
31
+ END IF;
32
+ PERFORM set_config('opengeni.knowledge_index_dispatcher','1',true);
33
+ SELECT * INTO job FROM knowledge_index_jobs j WHERE j.account_id=p_account
34
+ AND j.revision_id=p_revision AND j.lease_id=p_lease AND j.state='running'
35
+ AND j.lease_until>clock_timestamp() FOR UPDATE;
36
+ IF NOT FOUND THEN
37
+ RAISE EXCEPTION 'Knowledge index lease unavailable' USING ERRCODE='40001';
38
+ END IF;
39
+ IF job.billed_generation IS DISTINCT FROM job.generation THEN
40
+ decided:=CASE WHEN job.created_at>=p_activation_at THEN p_requested ELSE 'usage_only' END;
41
+ frozen_rate:=CASE WHEN decided='usage_only' THEN 0 ELSE p_rate END;
42
+ ELSE
43
+ decided:=job.billing_mode;
44
+ frozen_rate:=job.billing_rate_micros_per_million_bytes;
45
+ END IF;
46
+ IF decided='credits' THEN
47
+ SELECT d.outcome INTO decision_outcome FROM knowledge_entry_decisions d
48
+ WHERE d.account_id=p_account AND d.entry_id=job.entry_id AND d.revision_id=p_revision
49
+ ORDER BY d.version DESC LIMIT 1;
50
+ IF decision_outcome='rejected' THEN
51
+ UPDATE knowledge_index_jobs SET state='obsolete',lease_id=NULL,lease_until=NULL
52
+ WHERE revision_id=p_revision;
53
+ DELETE FROM knowledge_entry_vectors WHERE account_id=p_account AND revision_id=p_revision;
54
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
55
+ RETURN jsonb_build_object('mode','obsolete','rateMicrosPerMillionBytes',0);
56
+ END IF;
57
+ END IF;
58
+ -- A review-first draft is not a paid purchase. Keep its lease checkpoint
59
+ -- intact and poll until this exact revision is published; a rejected latest
60
+ -- revision may remain visible for review but must never incur an embedding
61
+ -- charge. Recheck on every batch so a publication change fences work.
62
+ IF decided='credits' AND NOT EXISTS (
63
+ SELECT 1 FROM knowledge_entries e WHERE e.account_id=p_account
64
+ AND e.id=job.entry_id AND e.published_revision_id=p_revision AND NOT e.archived
65
+ ) THEN
66
+ UPDATE knowledge_index_jobs SET state='pending',lease_id=NULL,lease_until=NULL,
67
+ last_failure='waiting_for_review',next_attempt_at=clock_timestamp()+interval '1 minute',
68
+ attempts=0 WHERE revision_id=p_revision;
69
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
70
+ RETURN jsonb_build_object('mode','awaiting_review','rateMicrosPerMillionBytes',0);
71
+ END IF;
72
+ IF job.billed_generation IS DISTINCT FROM job.generation THEN
73
+ UPDATE knowledge_index_jobs SET billing_mode=decided,billed_generation=job.generation,
74
+ billing_rate_micros_per_million_bytes=frozen_rate
75
+ WHERE revision_id=p_revision;
76
+ END IF;
77
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
78
+ RETURN jsonb_build_object('mode',decided,
79
+ 'rateMicrosPerMillionBytes',coalesce(frozen_rate,0));
80
+ EXCEPTION WHEN OTHERS THEN
81
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
82
+ RAISE;
83
+ END $$;
84
+ REVOKE ALL ON FUNCTION knowledge_index_billing_policy(uuid,uuid,uuid,text,timestamptz,bigint) FROM PUBLIC;
85
+
86
+ CREATE FUNCTION knowledge_index_wait_for_funding(p_account uuid, p_revision uuid, p_lease uuid)
87
+ RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
88
+ DECLARE
89
+ job knowledge_index_jobs%ROWTYPE;
90
+ previous text := current_setting('opengeni.knowledge_index_dispatcher',true);
91
+ BEGIN
92
+ IF p_account IS DISTINCT FROM opengeni_private.current_account_id() THEN
93
+ RAISE EXCEPTION 'Knowledge index tenant mismatch' USING ERRCODE='42501';
94
+ END IF;
95
+ -- knowledge_index_jobs is FORCE-RLS and only the owner-definer dispatcher
96
+ -- policy may see the leased job. Restore the transaction-local capability
97
+ -- even if the lease fence fails.
98
+ PERFORM set_config('opengeni.knowledge_index_dispatcher','1',true);
99
+ SELECT * INTO job FROM knowledge_index_jobs j WHERE j.account_id=p_account
100
+ AND j.revision_id=p_revision AND j.lease_id=p_lease AND j.state='running'
101
+ AND j.lease_until>clock_timestamp() FOR UPDATE;
102
+ IF NOT FOUND THEN
103
+ RAISE EXCEPTION 'Knowledge index lease unavailable' USING ERRCODE='40001';
104
+ END IF;
105
+ UPDATE knowledge_index_jobs SET state='pending',lease_id=NULL,lease_until=NULL,
106
+ last_failure='waiting_for_funding',next_attempt_at=clock_timestamp()+interval '1 minute',
107
+ attempts=0 WHERE revision_id=p_revision;
108
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
109
+ RETURN jsonb_build_object('status','pending');
110
+ EXCEPTION WHEN OTHERS THEN
111
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
112
+ RAISE;
113
+ END $$;
114
+ REVOKE ALL ON FUNCTION knowledge_index_wait_for_funding(uuid,uuid,uuid) FROM PUBLIC;
115
+
116
+ -- An approval/rejection can commit while a paid embedding provider is running.
117
+ -- Recheck publication after the provider returns, then hold a share lock on
118
+ -- that entry until the append, usage and debit transaction commits. A concurrent
119
+ -- review writer takes FOR UPDATE and cannot change the publication in between.
120
+ CREATE FUNCTION knowledge_index_paid_publication_guard(p_account uuid, p_revision uuid, p_lease uuid)
121
+ RETURNS text LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
122
+ DECLARE
123
+ job knowledge_index_jobs%ROWTYPE;
124
+ entry knowledge_entries%ROWTYPE;
125
+ decision_outcome text;
126
+ previous text := current_setting('opengeni.knowledge_index_dispatcher',true);
127
+ BEGIN
128
+ IF p_account IS DISTINCT FROM opengeni_private.current_account_id() THEN
129
+ RAISE EXCEPTION 'Knowledge index tenant mismatch' USING ERRCODE='42501';
130
+ END IF;
131
+ PERFORM set_config('opengeni.knowledge_index_dispatcher','1',true);
132
+ SELECT * INTO job FROM knowledge_index_jobs j WHERE j.account_id=p_account
133
+ AND j.revision_id=p_revision AND j.lease_id=p_lease AND j.state='running'
134
+ AND j.lease_until>clock_timestamp() AND j.billing_mode='credits' FOR UPDATE;
135
+ IF NOT FOUND THEN
136
+ RAISE EXCEPTION 'Paid Knowledge index lease unavailable' USING ERRCODE='40001';
137
+ END IF;
138
+ SELECT * INTO entry FROM knowledge_entries e WHERE e.account_id=p_account
139
+ AND e.id=job.entry_id FOR SHARE;
140
+ SELECT d.outcome INTO decision_outcome FROM knowledge_entry_decisions d
141
+ WHERE d.account_id=p_account AND d.entry_id=job.entry_id AND d.revision_id=p_revision
142
+ ORDER BY d.version DESC LIMIT 1;
143
+ IF decision_outcome='rejected' THEN
144
+ UPDATE knowledge_index_jobs SET state='obsolete',lease_id=NULL,lease_until=NULL
145
+ WHERE revision_id=p_revision;
146
+ DELETE FROM knowledge_entry_vectors WHERE account_id=p_account AND revision_id=p_revision;
147
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
148
+ RETURN 'obsolete';
149
+ END IF;
150
+ IF entry.id IS NULL OR entry.archived OR entry.published_revision_id IS DISTINCT FROM p_revision THEN
151
+ UPDATE knowledge_index_jobs SET state='pending',lease_id=NULL,lease_until=NULL,
152
+ last_failure='waiting_for_review',next_attempt_at=clock_timestamp()+interval '1 minute',
153
+ attempts=0 WHERE revision_id=p_revision;
154
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
155
+ RETURN 'awaiting_review';
156
+ END IF;
157
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
158
+ RETURN 'published';
159
+ EXCEPTION WHEN OTHERS THEN
160
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
161
+ RAISE;
162
+ END $$;
163
+ REVOKE ALL ON FUNCTION knowledge_index_paid_publication_guard(uuid,uuid,uuid) FROM PUBLIC;
164
+ DO $roles$
165
+ DECLARE runtime_role text;
166
+ BEGIN
167
+ FOR runtime_role IN SELECT jsonb_array_elements_text(current_setting('opengeni.migration_application_roles')::jsonb) LOOP
168
+ IF runtime_role <> current_user AND EXISTS(SELECT 1 FROM pg_roles WHERE rolname=runtime_role) THEN
169
+ EXECUTE format('GRANT EXECUTE ON FUNCTION knowledge_index_billing_policy(uuid,uuid,uuid,text,timestamptz,bigint) TO %I',runtime_role);
170
+ EXECUTE format('GRANT EXECUTE ON FUNCTION knowledge_index_wait_for_funding(uuid,uuid,uuid) TO %I',runtime_role);
171
+ EXECUTE format('GRANT EXECUTE ON FUNCTION knowledge_index_paid_publication_guard(uuid,uuid,uuid) TO %I',runtime_role);
172
+ END IF;
173
+ END LOOP;
174
+ END $roles$;
@@ -0,0 +1,56 @@
1
+ -- deployment-mode: rolling
2
+ -- The dispatcher table is not readable by the app role. Recheck every exact
3
+ -- visible revision through the canonical Knowledge reader before projecting a
4
+ -- content-free status; an entry ID supplied by a caller is not an access grant.
5
+ CREATE FUNCTION knowledge_visible_index_status(p_account uuid,p_workspace uuid,p_actor jsonb,p_items jsonb,p_view text)
6
+ RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
7
+ DECLARE item jsonb; visible jsonb; result jsonb := '[]'::jsonb;
8
+ job knowledge_index_jobs%ROWTYPE;
9
+ previous text := current_setting('opengeni.knowledge_index_dispatcher',true);
10
+ status text;
11
+ BEGIN
12
+ IF jsonb_typeof(p_items) IS DISTINCT FROM 'array' OR jsonb_array_length(p_items)>50
13
+ OR p_view IS NULL OR p_view NOT IN ('published','needs_review','archived','rejected') THEN
14
+ RAISE EXCEPTION 'Invalid Knowledge index status request' USING ERRCODE='22023';
15
+ END IF;
16
+ FOR item IN SELECT value FROM jsonb_array_elements(p_items) LOOP
17
+ IF jsonb_typeof(item) IS DISTINCT FROM 'object' OR (item->>'entryId') IS NULL
18
+ OR (item->>'revisionId') IS NULL THEN
19
+ RAISE EXCEPTION 'Invalid Knowledge index status item' USING ERRCODE='22023';
20
+ END IF;
21
+ visible := knowledge_entry_read(p_account,p_workspace,p_actor,
22
+ jsonb_build_object('operation','get','entryId',item->>'entryId','view',p_view,'limit',1));
23
+ IF visible#>>'{0,revision,id}' IS DISTINCT FROM item->>'revisionId'
24
+ OR visible#>>'{0,revision,kind}' IS DISTINCT FROM 'source' THEN
25
+ CONTINUE;
26
+ END IF;
27
+ PERFORM set_config('opengeni.knowledge_index_dispatcher','1',true);
28
+ SELECT * INTO job FROM knowledge_index_jobs j WHERE j.account_id=p_account
29
+ AND j.entry_id=(item->>'entryId')::uuid AND j.revision_id=(item->>'revisionId')::uuid;
30
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
31
+ status := CASE
32
+ WHEN job.revision_id IS NULL THEN 'saved'
33
+ WHEN job.state='ready' AND job.completed_generation=job.generation THEN 'indexed'
34
+ WHEN job.state='running' THEN 'indexing'
35
+ WHEN job.state='pending' AND job.last_failure='waiting_for_funding' THEN 'awaiting_funding'
36
+ WHEN job.state='pending' AND job.last_failure='source_unavailable' THEN 'source_unavailable'
37
+ WHEN job.state='pending' AND job.last_failure='embedding_unavailable' THEN 'provider_failed'
38
+ ELSE 'queued' END;
39
+ result := result || jsonb_build_array(jsonb_build_object(
40
+ 'entryId',item->>'entryId','revisionId',item->>'revisionId','status',status));
41
+ END LOOP;
42
+ RETURN result;
43
+ EXCEPTION WHEN OTHERS THEN
44
+ PERFORM set_config('opengeni.knowledge_index_dispatcher',coalesce(previous,''),true);
45
+ RAISE;
46
+ END $$;
47
+ REVOKE ALL ON FUNCTION knowledge_visible_index_status(uuid,uuid,jsonb,jsonb,text) FROM PUBLIC;
48
+ DO $roles$
49
+ DECLARE runtime_role text;
50
+ BEGIN
51
+ FOR runtime_role IN SELECT jsonb_array_elements_text(current_setting('opengeni.migration_application_roles')::jsonb) LOOP
52
+ IF runtime_role <> current_user AND EXISTS(SELECT 1 FROM pg_roles WHERE rolname=runtime_role) THEN
53
+ EXECUTE format('GRANT EXECUTE ON FUNCTION knowledge_visible_index_status(uuid,uuid,jsonb,jsonb,text) TO %I',runtime_role);
54
+ END IF;
55
+ END LOOP;
56
+ END $roles$;
@@ -0,0 +1,31 @@
1
+ -- deployment-mode: rolling
2
+ -- The five insights fact authority functions (0359, 0484) join the exact fact window
3
+ -- against a MATERIALIZED visible-session CTE so private-session checks run once
4
+ -- per session rather than once per fact. The planner estimates the fact window
5
+ -- from occurred_at statistics, and a time-series window newer than the last
6
+ -- ANALYZE (always true for "today", and for a week whenever autovacuum lags)
7
+ -- is estimated as ~1 row. That misestimate turns the CTE join into a Nested
8
+ -- Loop that rescans the whole CTE for every fact row: with 11k sessions and a
9
+ -- 175k-row usage week the join costs ~2 billion comparisons (observed 5-14
10
+ -- minutes per request on a staging workspace) while a hash join over the same
11
+ -- inputs completes in well under a second.
12
+ --
13
+ -- Pin the hash-join-safe planner setting on the functions themselves. The
14
+ -- setting is scoped to each function's own statements, the authorization,
15
+ -- window, filter, and capability protocol is byte-for-byte unchanged, and
16
+ -- ordinary statements planned outside these functions are unaffected.
17
+ ALTER FUNCTION opengeni_private.visible_workspace_insights_usage_projection(
18
+ uuid, timestamp with time zone, timestamp with time zone, text[]
19
+ ) SET enable_nestloop = off;
20
+ ALTER FUNCTION opengeni_private.visible_workspace_insights_usage_events(
21
+ uuid, timestamp with time zone, timestamp with time zone, text[]
22
+ ) SET enable_nestloop = off;
23
+ ALTER FUNCTION opengeni_private.visible_workspace_insights_usage_events(
24
+ uuid, timestamp with time zone, timestamp with time zone
25
+ ) SET enable_nestloop = off;
26
+ ALTER FUNCTION opengeni_private.visible_workspace_insights_model_call_facts(
27
+ uuid, timestamp with time zone, timestamp with time zone, text, text
28
+ ) SET enable_nestloop = off;
29
+ ALTER FUNCTION opengeni_private.visible_workspace_insights_model_call_facts(
30
+ uuid, timestamp with time zone, timestamp with time zone
31
+ ) SET enable_nestloop = off;
@@ -0,0 +1,85 @@
1
+ -- deployment-mode: rolling
2
+ -- A message fork copies the retained model-facing prefix through its selected
3
+ -- boundary. After real compaction, old rows are inactive and the replacement
4
+ -- (including the summary) is active: copy only that active prefix. Never
5
+ -- reinterpret a superseded message as a valid boundary. Opaque compaction
6
+ -- items need the exact earlier durable compaction receipt to enter a fork.
7
+ -- Inactive rows within a selected prefix require an earlier durable active
8
+ -- checkpoint; a stray inactive row cannot silently disappear from the fork.
9
+ -- The two blocks share the migration's implicit transaction: first verify
10
+ -- every catalog-source anchor, then install the validated replacement. The
11
+ -- preflight itself reads no tenant tables (the replacement function will).
12
+ DO $preflight$
13
+ DECLARE
14
+ definition text := pg_get_functiondef('fork_session_content(uuid,uuid,uuid,text,uuid,text,boolean,text,text,integer,uuid)'::regprocedure);
15
+ anchor text;
16
+ BEGIN
17
+ FOR anchor IN SELECT * FROM (VALUES
18
+ (E'AND history.turn_id = selected_turn_id\n AND history.item ->> ''role'' = ''user'''),
19
+ (E'AND history.turn_id = selected_event.turn_id\n AND history.item ->> ''role'' = ''assistant'''),
20
+ (E'AND history.session_id = p_source_session_id\n AND (NOT history.active OR (history.position <> trunc(history.position)'),
21
+ (E'AND (NOT history.active OR (history.position <> trunc(history.position)'),
22
+ ('OR history.item ->> ''type'' = ''compaction'''),
23
+ (E'AND history.session_id = p_source_session_id AND history.position <= boundary_position'),
24
+ (E'AND source_item.session_id = p_source_session_id AND source_item.position <= boundary_position')
25
+ ) AS anchors(value) LOOP
26
+ IF (length(definition) - length(replace(definition, anchor, ''))) / length(anchor) <> 1 THEN
27
+ RAISE EXCEPTION 'message fork prefix rewrite did not match exactly once at %', left(anchor, 96);
28
+ END IF;
29
+ END LOOP;
30
+ END
31
+ $preflight$;
32
+
33
+ DO $migration$
34
+ DECLARE
35
+ target regprocedure := 'fork_session_content(uuid,uuid,uuid,text,uuid,text,boolean,text,text,integer,uuid)'::regprocedure;
36
+ definition text;
37
+ updated text;
38
+ change record;
39
+ BEGIN
40
+ definition := pg_get_functiondef(target);
41
+ updated := definition;
42
+ FOR change IN SELECT * FROM (VALUES
43
+ (E'AND history.turn_id = selected_turn_id\n AND history.item ->> ''role'' = ''user''',
44
+ E'AND history.turn_id = selected_turn_id\n AND history.active\n AND history.item ->> ''role'' = ''user'''),
45
+ (E'AND history.turn_id = selected_event.turn_id\n AND history.item ->> ''role'' = ''assistant''',
46
+ E'AND history.turn_id = selected_event.turn_id\n AND history.active\n AND history.item ->> ''role'' = ''assistant'''),
47
+ (E'AND history.session_id = p_source_session_id\n AND (NOT history.active OR (history.position <> trunc(history.position)',
48
+ E'AND history.session_id = p_source_session_id\n AND history.position <= boundary_position\n AND (NOT history.active OR (history.position <> trunc(history.position)'),
49
+ (E'AND (NOT history.active OR (history.position <> trunc(history.position)',
50
+ $replacement$AND ((NOT history.active AND NOT EXISTS (
51
+ SELECT 1 FROM session_events compaction_event
52
+ JOIN session_history_items summary_history
53
+ ON summary_history.account_id = compaction_event.account_id
54
+ AND summary_history.workspace_id = compaction_event.workspace_id
55
+ AND summary_history.session_id = compaction_event.session_id
56
+ AND summary_history.position::text = compaction_event.payload ->> 'summaryPosition'
57
+ AND summary_history.active
58
+ WHERE compaction_event.account_id = p_account_id
59
+ AND compaction_event.workspace_id = p_source_workspace_id
60
+ AND compaction_event.session_id = p_source_session_id
61
+ AND compaction_event.type = 'session.context.compacted'
62
+ AND compaction_event.sequence < selected_event.sequence
63
+ AND history.position < summary_history.position
64
+ AND summary_history.position <= boundary_position
65
+ )) OR (history.active AND history.position <> trunc(history.position)$replacement$),
66
+ ('OR history.item ->> ''type'' = ''compaction''',
67
+ $replacement$OR (history.active AND history.item ->> 'type' = 'compaction' AND NOT EXISTS (
68
+ SELECT 1 FROM session_events compaction_event
69
+ WHERE compaction_event.account_id = p_account_id
70
+ AND compaction_event.workspace_id = p_source_workspace_id
71
+ AND compaction_event.session_id = p_source_session_id
72
+ AND compaction_event.type = 'session.context.compacted'
73
+ AND compaction_event.payload ->> 'summaryPosition' = history.position::text
74
+ AND compaction_event.sequence < selected_event.sequence
75
+ ))$replacement$),
76
+ (E'AND history.session_id = p_source_session_id AND history.position <= boundary_position',
77
+ E'AND history.session_id = p_source_session_id AND history.active\n AND history.position <= boundary_position'),
78
+ (E'AND source_item.session_id = p_source_session_id AND source_item.position <= boundary_position',
79
+ E'AND source_item.session_id = p_source_session_id AND source_item.active\n AND source_item.position <= boundary_position')
80
+ ) AS rewrites(anchor, replacement) LOOP
81
+ updated := replace(updated, change.anchor, change.replacement);
82
+ END LOOP;
83
+ EXECUTE updated;
84
+ END
85
+ $migration$;
@@ -0,0 +1,25 @@
1
+ -- deployment-mode: rolling
2
+ -- A failed session can still attach its managed sandbox for an explicitly
3
+ -- consented checkpoint restore. Keep its selected variable sets available to
4
+ -- that attach; all existing scope, ownership, and revocation checks still run.
5
+ DO $migration$
6
+ DECLARE
7
+ target regprocedure := 'materialize_scoped_variable_set_for_session(uuid,uuid,uuid,uuid)'::regprocedure;
8
+ definition text := pg_get_functiondef(target);
9
+ before_status text := $before$AND session_value.status IN (
10
+ 'queued', 'running', 'idle', 'requires_action', 'recovering', 'waiting_capacity'
11
+ )$before$;
12
+ after_status text := $after$AND session_value.status IN (
13
+ 'queued', 'running', 'idle', 'requires_action', 'recovering', 'waiting_capacity', 'failed'
14
+ )$after$;
15
+ BEGIN
16
+ IF (length(definition) - length(replace(definition, after_status, ''))) / length(after_status) = 1
17
+ AND (length(definition) - length(replace(definition, before_status, ''))) / length(before_status) = 0 THEN
18
+ RETURN;
19
+ END IF;
20
+ IF (length(definition) - length(replace(definition, before_status, ''))) / length(before_status) <> 1 THEN
21
+ RAISE EXCEPTION 'failed-session variable-set attach source contract changed';
22
+ END IF;
23
+ EXECUTE replace(definition, before_status, after_status);
24
+ END
25
+ $migration$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
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": {
@@ -114,10 +114,10 @@
114
114
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
115
115
  },
116
116
  "dependencies": {
117
- "@opengeni/codemode": "^0.6.1",
118
- "@opengeni/codex": "^0.2.25",
119
- "@opengeni/config": "^2.1.0",
120
- "@opengeni/contracts": "^5.1.0",
117
+ "@opengeni/codemode": "^0.6.2",
118
+ "@opengeni/codex": "^0.2.26",
119
+ "@opengeni/config": "^2.1.1",
120
+ "@opengeni/contracts": "^5.1.1",
121
121
  "@opengeni/network": "^0.3.1",
122
122
  "drizzle-orm": "^0.45.2",
123
123
  "postgres": "^3.4.7"