@opengeni/db 1.0.2 → 1.2.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.
Files changed (86) hide show
  1. package/dist/artifact-catalog.d.ts +12 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/capability-reconciliation.js +2 -2
  4. package/dist/{chunk-KH3TYDGY.js → chunk-3GAR5UN3.js} +2 -2
  5. package/dist/{chunk-UEQQ2WWU.js → chunk-3TGYFFZR.js} +3 -26
  6. package/dist/chunk-3TGYFFZR.js.map +1 -0
  7. package/dist/{chunk-F3YMVRF4.js → chunk-6EIEGHPK.js} +4 -4
  8. package/dist/{chunk-OQZOGCLG.js → chunk-CONWNUSL.js} +2 -2
  9. package/dist/{chunk-7S53WLPI.js → chunk-CYP72JTQ.js} +11 -4
  10. package/dist/{chunk-7S53WLPI.js.map → chunk-CYP72JTQ.js.map} +1 -1
  11. package/dist/{chunk-LDTQGYD2.js → chunk-FWQNHNOU.js} +54 -2
  12. package/dist/chunk-FWQNHNOU.js.map +1 -0
  13. package/dist/{chunk-KEDWSSNP.js → chunk-GDN5TSZP.js} +2 -2
  14. package/dist/{chunk-PQGTWFRF.js → chunk-JICUP7ZR.js} +2 -2
  15. package/dist/{chunk-NLSG7GGA.js → chunk-JLVWBIEM.js} +3 -3
  16. package/dist/{chunk-Y77PVZZD.js → chunk-LXPFNG4L.js} +2 -2
  17. package/dist/{chunk-56MYWKQV.js → chunk-O2Y5E5M3.js} +68 -32
  18. package/dist/chunk-O2Y5E5M3.js.map +1 -0
  19. package/dist/{chunk-7POTLB24.js → chunk-QFJNEXFL.js} +2 -2
  20. package/dist/{chunk-MACELCXG.js → chunk-UCIMHNII.js} +101 -21
  21. package/dist/chunk-UCIMHNII.js.map +1 -0
  22. package/dist/{chunk-OMHBVHX7.js → chunk-VDPCIFKL.js} +5 -5
  23. package/dist/{chunk-N5QCKHYL.js → chunk-YOYILDLY.js} +87 -19
  24. package/dist/chunk-YOYILDLY.js.map +1 -0
  25. package/dist/connect-attempts.js +3 -3
  26. package/dist/credit-balances.d.ts +18 -0
  27. package/dist/credit-promotion-policy.d.ts +10 -0
  28. package/dist/editable-artifact-durable-export.js +2 -2
  29. package/dist/editable-artifacts.js +3 -3
  30. package/dist/index.d.ts +35 -10
  31. package/dist/index.js +2432 -2255
  32. package/dist/index.js.map +1 -1
  33. package/dist/managed-auth-session-sets.js +3 -3
  34. package/dist/managed-user-setup.d.ts +1 -0
  35. package/dist/mcp-operations.js +2 -2
  36. package/dist/organization-integration-policy.js +3 -3
  37. package/dist/organization-membership-lifecycle.d.ts +1 -0
  38. package/dist/persistence-errors.d.ts +9 -6
  39. package/dist/provision-roles.js +1 -1
  40. package/dist/retained-provider-commands.js +6 -6
  41. package/dist/runtime-posture.d.ts +7 -6
  42. package/dist/schema.d.ts +214 -2
  43. package/dist/schema.js +5 -1
  44. package/dist/session-background-commands.js +4 -4
  45. package/dist/session-command-output.js +5 -5
  46. package/dist/session-event-slices.js +3 -3
  47. package/dist/session-mcp-credential-rotation.js +5 -5
  48. package/dist/session-tenancy.d.ts +6 -1
  49. package/dist/session-tenancy.js +3 -5
  50. package/dist/video-generation.js +3 -3
  51. package/dist/workspace-tool-defaults.js +4 -4
  52. package/drizzle/0610_artifact_catalog_pins.sql +165 -0
  53. package/drizzle/0611_universal_session_tenancy_activation.sql +166 -0
  54. package/drizzle/0612_scheduled_model_unavailable_refusal.sql +21 -0
  55. package/drizzle/0613_model_scoped_promotional_credits.sql +238 -0
  56. package/drizzle/0614_quiescence_command_containment.sql +7 -0
  57. package/drizzle/0615_credit_promotion_policy_validation.sql +63 -0
  58. package/package.json +7 -7
  59. package/src/artifact-catalog.ts +31 -5
  60. package/src/credit-balances.ts +109 -0
  61. package/src/credit-promotion-policy.ts +12 -0
  62. package/src/database.ts +81 -42
  63. package/src/index.ts +295 -79
  64. package/src/managed-user-setup.ts +8 -0
  65. package/src/organization-membership-lifecycle.ts +8 -0
  66. package/src/persistence-errors.ts +17 -6
  67. package/src/provision-roles.ts +25 -0
  68. package/src/runtime-posture-cli.ts +0 -2
  69. package/src/runtime-posture.ts +86 -27
  70. package/src/schema.ts +57 -1
  71. package/src/session-tenancy.ts +9 -31
  72. package/src/video-generation.ts +2 -7
  73. package/dist/chunk-56MYWKQV.js.map +0 -1
  74. package/dist/chunk-LDTQGYD2.js.map +0 -1
  75. package/dist/chunk-MACELCXG.js.map +0 -1
  76. package/dist/chunk-N5QCKHYL.js.map +0 -1
  77. package/dist/chunk-UEQQ2WWU.js.map +0 -1
  78. /package/dist/{chunk-KH3TYDGY.js.map → chunk-3GAR5UN3.js.map} +0 -0
  79. /package/dist/{chunk-F3YMVRF4.js.map → chunk-6EIEGHPK.js.map} +0 -0
  80. /package/dist/{chunk-OQZOGCLG.js.map → chunk-CONWNUSL.js.map} +0 -0
  81. /package/dist/{chunk-KEDWSSNP.js.map → chunk-GDN5TSZP.js.map} +0 -0
  82. /package/dist/{chunk-PQGTWFRF.js.map → chunk-JICUP7ZR.js.map} +0 -0
  83. /package/dist/{chunk-NLSG7GGA.js.map → chunk-JLVWBIEM.js.map} +0 -0
  84. /package/dist/{chunk-Y77PVZZD.js.map → chunk-LXPFNG4L.js.map} +0 -0
  85. /package/dist/{chunk-7POTLB24.js.map → chunk-QFJNEXFL.js.map} +0 -0
  86. /package/dist/{chunk-OMHBVHX7.js.map → chunk-VDPCIFKL.js.map} +0 -0
@@ -6,6 +6,12 @@ export declare class SessionTenancyConflictError extends Error {
6
6
  readonly name = "SessionTenancyConflictError";
7
7
  constructor(reason: "not_quiescent" | "authority_epoch" | "operation_reuse", blocker?: SessionTenancyBlocker | null, options?: ErrorOptions);
8
8
  }
9
+ /**
10
+ * The organization's owner/admin Only-me setting does not permit a new private
11
+ * session (or private transition) in this shared workspace. Activation itself is
12
+ * universal since migration 0611; the name and the SESSION_TENANCY_NOT_ACTIVATED
13
+ * wire code are kept for client compatibility.
14
+ */
9
15
  export declare class SessionTenancyNotActivatedError extends Error {
10
16
  readonly name = "SessionTenancyNotActivatedError";
11
17
  constructor(options?: ErrorOptions);
@@ -83,7 +89,6 @@ export type ForkSessionContentResult = {
83
89
  runtimeEventId: string | null;
84
90
  runtimeEventSequence: number | null;
85
91
  };
86
- export declare function sessionTenancyProductActivated(db: Database, workspaceId: string): Promise<boolean>;
87
92
  export declare function getPrivateSessionCreatePolicy(db: Database, input: {
88
93
  workspaceId: string;
89
94
  actorSubjectId: string;
@@ -12,11 +12,10 @@ import {
12
12
  openPrivateChildSessionCreateCapability,
13
13
  openPrivateSessionCreateCapability,
14
14
  replayAppliedSessionFork,
15
- sessionTenancyProductActivated,
16
15
  transitionSessionVisibility
17
- } from "./chunk-UEQQ2WWU.js";
18
- import "./chunk-56MYWKQV.js";
19
- import "./chunk-LDTQGYD2.js";
16
+ } from "./chunk-3TGYFFZR.js";
17
+ import "./chunk-O2Y5E5M3.js";
18
+ import "./chunk-FWQNHNOU.js";
20
19
  import "./chunk-P7YDM2RI.js";
21
20
  import "./chunk-PZ5AY32C.js";
22
21
  export {
@@ -33,7 +32,6 @@ export {
33
32
  openPrivateChildSessionCreateCapability,
34
33
  openPrivateSessionCreateCapability,
35
34
  replayAppliedSessionFork,
36
- sessionTenancyProductActivated,
37
35
  transitionSessionVisibility
38
36
  };
39
37
  //# sourceMappingURL=session-tenancy.js.map
@@ -29,10 +29,10 @@ import {
29
29
  settleVideoGenerationFailure,
30
30
  settleVideoGenerationReady,
31
31
  updateWorkspaceVideoGenerationPolicy
32
- } from "./chunk-MACELCXG.js";
32
+ } from "./chunk-UCIMHNII.js";
33
33
  import "./chunk-4UJCXBR2.js";
34
- import "./chunk-56MYWKQV.js";
35
- import "./chunk-LDTQGYD2.js";
34
+ import "./chunk-O2Y5E5M3.js";
35
+ import "./chunk-FWQNHNOU.js";
36
36
  import "./chunk-P7YDM2RI.js";
37
37
  import "./chunk-PZ5AY32C.js";
38
38
  export {
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  lockWorkspaceInferenceControl
3
- } from "./chunk-7S53WLPI.js";
4
- import "./chunk-MACELCXG.js";
3
+ } from "./chunk-CYP72JTQ.js";
4
+ import "./chunk-UCIMHNII.js";
5
5
  import "./chunk-4UJCXBR2.js";
6
6
  import {
7
7
  withWorkspaceRls
8
- } from "./chunk-56MYWKQV.js";
9
- import "./chunk-LDTQGYD2.js";
8
+ } from "./chunk-O2Y5E5M3.js";
9
+ import "./chunk-FWQNHNOU.js";
10
10
  import "./chunk-P7YDM2RI.js";
11
11
  import "./chunk-PZ5AY32C.js";
12
12
 
@@ -0,0 +1,165 @@
1
+ -- deployment-mode: rolling
2
+ -- Shared discovery metadata only. Existing content and authorization domains
3
+ -- remain canonical; older binaries keep their original publication capability.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '5min';
6
+
7
+ CREATE TABLE opengeni_private.artifact_catalog_pins (
8
+ account_id uuid NOT NULL,
9
+ workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
10
+ kind text NOT NULL CHECK (kind IN ('site','document','spreadsheet','presentation','image','file')),
11
+ artifact_id text NOT NULL CHECK (length(artifact_id) BETWEEN 1 AND 128),
12
+ pinned_at timestamptz NOT NULL DEFAULT now(),
13
+ PRIMARY KEY (account_id, workspace_id, kind, artifact_id)
14
+ );
15
+ ALTER TABLE opengeni_private.artifact_catalog_pins ENABLE ROW LEVEL SECURITY;
16
+ ALTER TABLE opengeni_private.artifact_catalog_pins FORCE ROW LEVEL SECURITY;
17
+ CREATE POLICY workspace_isolation ON opengeni_private.artifact_catalog_pins
18
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
19
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
20
+
21
+ CREATE FUNCTION opengeni_private.update_artifact_pin(
22
+ p_account uuid, p_workspace uuid, p_kind text, p_id text, p_pinned boolean
23
+ ) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $body$
24
+ DECLARE v_visible boolean := false;
25
+ BEGIN
26
+ IF p_account IS NULL OR p_workspace IS NULL OR p_kind IS NULL OR p_id IS NULL OR p_pinned IS NULL
27
+ OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
28
+ OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
29
+ OR p_kind NOT IN ('site','document','spreadsheet','presentation','image','file')
30
+ OR length(p_id) NOT BETWEEN 1 AND 128
31
+ THEN RAISE EXCEPTION 'artifact pin scope mismatch' USING ERRCODE = '42501'; END IF;
32
+ -- Serialize opposite writes even before the first row exists.
33
+ PERFORM pg_advisory_xact_lock(hashtextextended('artifact-pin:' || p_account || ':' || p_workspace || ':' || p_kind || ':' || p_id, 0));
34
+ IF p_kind = 'site' THEN
35
+ SELECT EXISTS (SELECT 1 FROM workspace_artifacts a WHERE a.account_id=p_account
36
+ AND a.workspace_id=p_workspace AND a.id::text=p_id) INTO v_visible;
37
+ ELSIF p_kind IN ('document','spreadsheet','presentation') THEN
38
+ SELECT EXISTS (SELECT 1 FROM editable_artifacts a WHERE a.account_id=p_account
39
+ AND a.workspace_id=p_workspace AND a.id=p_id AND a.modality=p_kind) INTO v_visible;
40
+ ELSE
41
+ SELECT EXISTS (SELECT 1 FROM files f WHERE f.account_id=p_account AND f.workspace_id=p_workspace
42
+ AND f.id::text=p_id AND f.status='ready'
43
+ AND (f.private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(f.private_owner_subject_ids))
44
+ AND ((p_kind='image' AND EXISTS (SELECT 1 FROM generated_image_artifacts g
45
+ WHERE g.account_id=p_account AND g.workspace_id=p_workspace AND g.artifact_id=f.id AND g.status='ready'))
46
+ OR (EXISTS (SELECT 1 FROM opengeni_private.sandbox_file_publications p
47
+ WHERE p.account_id=p_account AND p.workspace_id=p_workspace AND p.file_id=f.id)
48
+ AND p_kind=CASE WHEN f.content_type IN ('image/png','image/jpeg','image/gif','image/webp','image/avif','image/svg+xml')
49
+ THEN 'image' ELSE 'file' END))) INTO v_visible;
50
+ END IF;
51
+ -- Also validate unpin: no probe or write for missing, foreign or hidden targets.
52
+ IF NOT v_visible THEN RAISE EXCEPTION 'artifact pin target unavailable' USING ERRCODE = '42501'; END IF;
53
+ IF p_pinned THEN
54
+ INSERT INTO opengeni_private.artifact_catalog_pins(account_id,workspace_id,kind,artifact_id)
55
+ VALUES(p_account,p_workspace,p_kind,p_id) ON CONFLICT DO NOTHING;
56
+ ELSE
57
+ DELETE FROM opengeni_private.artifact_catalog_pins p WHERE p.account_id=p_account
58
+ AND p.workspace_id=p_workspace AND p.kind=p_kind AND p.artifact_id=p_id;
59
+ END IF;
60
+ END $body$;
61
+
62
+ CREATE FUNCTION opengeni_private.list_artifact_pins(p_account uuid, p_workspace uuid)
63
+ RETURNS TABLE(kind text, artifact_id text)
64
+ LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $body$
65
+ BEGIN
66
+ IF p_account IS NULL OR p_workspace IS NULL
67
+ OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
68
+ OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
69
+ THEN RAISE EXCEPTION 'artifact pin scope mismatch' USING ERRCODE = '42501'; END IF;
70
+ -- Only joined to independently authorized candidates. Private files remain
71
+ -- hidden even when the migration owner is a superuser rather than FORCE-RLS-bound.
72
+ RETURN QUERY SELECT p.kind,p.artifact_id FROM opengeni_private.artifact_catalog_pins p
73
+ WHERE p.account_id=p_account AND p.workspace_id=p_workspace
74
+ AND (p.kind NOT IN ('file','image') OR EXISTS (SELECT 1 FROM files f
75
+ WHERE f.account_id=p_account AND f.workspace_id=p_workspace AND f.id::text=p.artifact_id AND f.status='ready'
76
+ AND (f.private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(f.private_owner_subject_ids))));
77
+ END $body$;
78
+
79
+ -- A page-local re-sort is incorrect: this bounded branch must apply the same
80
+ -- leading pin key and frontier as the complete union before taking its LIMIT.
81
+ CREATE FUNCTION opengeni_private.list_sandbox_file_publications_pinned(
82
+ p_account uuid, p_workspace uuid, p_query jsonb
83
+ ) RETURNS TABLE(file_id uuid, title text, kind text, source_session_id uuid, published_at timestamptz, pinned boolean)
84
+ LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $body$
85
+ DECLARE
86
+ v_limit integer := (p_query->>'limit')::integer;
87
+ v_sort text := p_query->>'sort';
88
+ v_after text := p_query#>>'{after,key}';
89
+ v_after_pinned boolean := coalesce((p_query#>>'{after,pinned}')::boolean,false);
90
+ BEGIN
91
+ IF p_account IS NULL OR p_workspace IS NULL
92
+ OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
93
+ OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
94
+ OR v_limit IS NULL OR v_limit NOT BETWEEN 1 AND 201
95
+ OR v_sort IS NULL OR v_sort NOT IN ('updated','newest','title')
96
+ OR jsonb_typeof(p_query) IS DISTINCT FROM 'object'
97
+ OR jsonb_typeof(p_query->'kinds') IS DISTINCT FROM 'array'
98
+ OR jsonb_array_length(p_query->'kinds') NOT BETWEEN 1 AND 2
99
+ OR length(coalesce(p_query->>'q',''))>200
100
+ THEN RAISE EXCEPTION 'publication list scope mismatch' USING ERRCODE = '42501'; END IF;
101
+ RETURN QUERY WITH candidates AS (
102
+ SELECT p.file_id, f.filename AS title,
103
+ CASE WHEN f.content_type IN ('image/png','image/jpeg','image/gif','image/webp','image/avif','image/svg+xml')
104
+ THEN 'image' ELSE 'file' END AS kind,
105
+ p.source_session_id, p.published_at,
106
+ CASE WHEN v_sort='title' THEN lower(f.filename)
107
+ ELSE to_char(p.published_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS.US') END AS sort_key
108
+ FROM opengeni_private.sandbox_file_publications p JOIN files f
109
+ ON f.account_id=p.account_id AND f.workspace_id=p.workspace_id AND f.id=p.file_id
110
+ WHERE p.account_id=p_account AND p.workspace_id=p_workspace AND f.status='ready'
111
+ AND (f.private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(f.private_owner_subject_ids))
112
+ AND (p_query->>'sourceSessionId' IS NULL OR p.source_session_id=(p_query->>'sourceSessionId')::uuid)
113
+ AND p.published_at<=(p_query->>'snapshotAt')::timestamptz
114
+ AND strpos(lower(f.filename),lower(coalesce(p_query->>'q','')))>0
115
+ ), ordered AS (
116
+ SELECT c.*, (pins.artifact_id IS NOT NULL) AS pinned FROM candidates c
117
+ LEFT JOIN opengeni_private.list_artifact_pins(p_account,p_workspace) pins
118
+ ON pins.kind=c.kind AND pins.artifact_id=c.file_id::text
119
+ ) SELECT c.file_id,c.title,c.kind,c.source_session_id,c.published_at,c.pinned FROM ordered c
120
+ WHERE (p_query->'kinds') ? c.kind AND (v_after IS NULL OR c.pinned<v_after_pinned OR
121
+ (c.pinned=v_after_pinned AND (
122
+ (v_sort='title' AND c.sort_key COLLATE "C">v_after COLLATE "C") OR
123
+ (v_sort<>'title' AND c.sort_key COLLATE "C"<v_after COLLATE "C") OR
124
+ (c.sort_key=v_after AND (c.kind COLLATE "C",c.file_id::text COLLATE "C")>
125
+ ((p_query#>>'{after,kind}') COLLATE "C",(p_query#>>'{after,id}') COLLATE "C")))))
126
+ ORDER BY c.pinned DESC, CASE WHEN v_sort='title' THEN c.sort_key END COLLATE "C" ASC,
127
+ CASE WHEN v_sort<>'title' THEN c.sort_key END COLLATE "C" DESC,c.kind COLLATE "C",c.file_id::text COLLATE "C"
128
+ LIMIT v_limit;
129
+ END $body$;
130
+ REVOKE ALL ON TABLE opengeni_private.artifact_catalog_pins FROM PUBLIC;
131
+ REVOKE ALL ON FUNCTION opengeni_private.update_artifact_pin(uuid,uuid,text,text,boolean),
132
+ opengeni_private.list_artifact_pins(uuid,uuid),
133
+ opengeni_private.list_sandbox_file_publications_pinned(uuid,uuid,jsonb) FROM PUBLIC;
134
+
135
+ DO $grants$
136
+ DECLARE target_schema text := current_schema(); recipient record; routine text;
137
+ BEGIN
138
+ FOREACH routine IN ARRAY ARRAY['update_artifact_pin(uuid,uuid,text,text,boolean)',
139
+ 'list_artifact_pins(uuid,uuid)','list_sandbox_file_publications_pinned(uuid,uuid,jsonb)']
140
+ LOOP
141
+ EXECUTE format('ALTER FUNCTION opengeni_private.%s SET search_path = pg_catalog, %I, pg_temp', routine, target_schema);
142
+ -- Strip inherited default function ACLs, then grant only existing runtimes.
143
+ FOR recipient IN SELECT DISTINCT acl.grantee,r.rolname FROM pg_proc p
144
+ CROSS JOIN LATERAL aclexplode(p.proacl) acl LEFT JOIN pg_roles r ON r.oid=acl.grantee
145
+ WHERE p.oid=to_regprocedure('opengeni_private.' || routine) AND acl.grantee<>p.proowner
146
+ LOOP
147
+ EXECUTE format('REVOKE ALL ON FUNCTION opengeni_private.%s FROM %s', routine,
148
+ CASE WHEN recipient.grantee=0 THEN 'PUBLIC' ELSE quote_ident(recipient.rolname) END);
149
+ END LOOP;
150
+ END LOOP;
151
+ FOR recipient IN SELECT DISTINCT acl.grantee,r.rolname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
152
+ CROSS JOIN LATERAL aclexplode(c.relacl) acl LEFT JOIN pg_roles r ON r.oid=acl.grantee
153
+ WHERE n.nspname='opengeni_private' AND c.relname='artifact_catalog_pins' AND acl.grantee<>c.relowner
154
+ LOOP
155
+ EXECUTE format('REVOKE ALL ON TABLE opengeni_private.artifact_catalog_pins FROM %s',
156
+ CASE WHEN recipient.grantee=0 THEN 'PUBLIC' ELSE quote_ident(recipient.rolname) END);
157
+ END LOOP;
158
+ FOR recipient IN SELECT DISTINCT r.rolname FROM pg_proc p
159
+ CROSS JOIN LATERAL aclexplode(p.proacl) acl JOIN pg_roles r ON r.oid=acl.grantee
160
+ WHERE p.oid=to_regprocedure('opengeni_private.record_sandbox_file_publication(uuid,uuid,uuid,uuid)')
161
+ AND acl.privilege_type='EXECUTE' AND acl.grantee<>p.proowner
162
+ LOOP
163
+ EXECUTE format('GRANT EXECUTE ON FUNCTION opengeni_private.update_artifact_pin(uuid,uuid,text,text,boolean), opengeni_private.list_artifact_pins(uuid,uuid), opengeni_private.list_sandbox_file_publications_pinned(uuid,uuid,jsonb) TO %I', recipient.rolname);
164
+ END LOOP;
165
+ END $grants$;
@@ -0,0 +1,166 @@
1
+ -- deployment-mode: rolling
2
+ -- Retire per-organization session-tenancy activation. Every organization is
3
+ -- activated: private ("Only me") sessions, visibility transitions, forks and
4
+ -- personal-resource grants no longer require a version-1
5
+ -- session_tenancy_activations receipt, and the owner/admin Only-me setting
6
+ -- (organization_private_session_settings) now defaults to ENABLED when an
7
+ -- organization has no row. Owners/admins may still disable it; an explicit
8
+ -- disabled row keeps gating new Only-me chats in shared workspaces.
9
+ --
10
+ -- No receipts are written and no data is backfilled. The receipt keeps exactly
11
+ -- one meaning: opengeni_private.session_tenancy_account_activated (0340) still
12
+ -- retires the legacy_user connection and unattributed-writer compatibility
13
+ -- lanes only for organizations that already hold one. Organizations without a
14
+ -- receipt keep those lanes exactly as before this migration, so no stored
15
+ -- authority becomes invisible and no ownership is inferred.
16
+ --
17
+ -- Rolling: this only widens product predicates and flips a missing-row default.
18
+ -- A pre-0611 binary keeps working against the rewritten routines: its own
19
+ -- receipt probe (session_tenancy_product_activated) now answers true, its
20
+ -- startup interlock probe (session_tenancy_any_product_activation) answers
21
+ -- false regardless of the retired environment switch, and every routine keeps
22
+ -- its signature, owner, ACL, SECURITY DEFINER and search_path. No table is
23
+ -- locked or rewritten.
24
+ SET LOCAL lock_timeout = '5s';
25
+ SET LOCAL statement_timeout = '5min';
26
+
27
+ -- Remove the receipt prerequisite from every runtime predicate and default the
28
+ -- Only-me setting to enabled. Each rewrite replaces one exact, reviewed
29
+ -- fragment of the live definition; a changed shape aborts instead of silently
30
+ -- leaving a receipt gate behind. Owner, grants, SECURITY DEFINER and
31
+ -- search_path are preserved by CREATE OR REPLACE of the catalog's own
32
+ -- definition.
33
+ DO $universal_session_tenancy_predicates$
34
+ DECLARE
35
+ data_schema text := pg_catalog.current_schema();
36
+ target regprocedure;
37
+ definition text;
38
+ rewrite record;
39
+ occurrences integer;
40
+ -- Fragments below are routine SOURCE text matched against
41
+ -- pg_get_functiondef, never queries: this block reads only the catalog.
42
+ -- The table names are spliced in so the source is not mistaken for a
43
+ -- migration-time row read of a FORCE-RLS table.
44
+ receipts constant text := 'session_tenancy_activations';
45
+ settings constant text := 'organization_private_session_settings';
46
+ receipt_gate constant text :=
47
+ E' IF NOT EXISTS (\n'
48
+ || E' SELECT 1 FROM ' || receipts || E' activation\n'
49
+ || E' WHERE activation.account_id = p_account_id AND activation.activation_version = 1\n'
50
+ || E' ) THEN\n'
51
+ || E' RAISE EXCEPTION ''session tenancy product is not activated'' USING ERRCODE = ''42501'';\n'
52
+ || E' END IF;\n';
53
+ receipt_gate_removed constant text :=
54
+ E' -- 0611: every organization is session-tenancy activated.\n';
55
+ setting_receipt_gate constant text :=
56
+ E' IF p_enabled AND NOT session_tenancy_product_activated(p_account_id, 1) THEN\n'
57
+ || E' RAISE EXCEPTION ''session tenancy product surface is not available for this organization'' USING ERRCODE = ''55000'';\n'
58
+ || E' END IF;\n';
59
+ BEGIN
60
+ FOR rewrite IN
61
+ SELECT * FROM (VALUES
62
+ (1, pg_catalog.format('%I.session_tenancy_product_activated(uuid,integer)', data_schema),
63
+ E' AND EXISTS (\n'
64
+ || E' SELECT 1 FROM ' || receipts || E' activation\n'
65
+ || E' WHERE activation.account_id = p_account_id\n'
66
+ || E' AND activation.activation_version = p_activation_version\n'
67
+ || E' )',
68
+ E' AND p_account_id IS NOT NULL\n'
69
+ || E' AND p_activation_version IS NOT DISTINCT FROM 1'),
70
+ -- The value-free startup interlock is retired with the deployment switch.
71
+ -- Answering false keeps every earlier binary startable regardless of the
72
+ -- removed OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED value.
73
+ (3, pg_catalog.format('%I.session_tenancy_any_product_activation()', data_schema),
74
+ E' SELECT EXISTS (\n'
75
+ || E' SELECT 1 FROM ' || receipts || E'\n'
76
+ || E' ) INTO activated;',
77
+ E' activated := false;'),
78
+ (4, pg_catalog.format(
79
+ '%I.list_self_user_resource_authorities(uuid,uuid,text,uuid,integer)', data_schema),
80
+ receipt_gate, receipt_gate_removed),
81
+ (5, pg_catalog.format(
82
+ '%I.issue_self_user_resource_grant(uuid,uuid,uuid,text,text,text,uuid,integer,boolean)',
83
+ data_schema),
84
+ receipt_gate, receipt_gate_removed),
85
+ (6, pg_catalog.format('%I.revoke_self_user_resource_grant(uuid,uuid,uuid)', data_schema),
86
+ receipt_gate, receipt_gate_removed),
87
+ (7, pg_catalog.format(
88
+ '%I.accept_turn_personal_resource_attachment(uuid,uuid,uuid,uuid,text,integer,boolean,integer)',
89
+ data_schema),
90
+ receipt_gate, receipt_gate_removed),
91
+ -- Only-me chats default to enabled: only an explicit disabled row gates.
92
+ (8, pg_catalog.format('%I.organization_private_sessions_enabled(uuid)', data_schema),
93
+ E' AND session_tenancy_product_activated(p_account_id, 1)\n'
94
+ || E' AND EXISTS (\n'
95
+ || E' SELECT 1 FROM ' || settings || E' setting\n'
96
+ || E' WHERE setting.account_id = p_account_id AND setting.enabled\n'
97
+ || E' )',
98
+ E' AND NOT EXISTS (\n'
99
+ || E' SELECT 1 FROM ' || settings || E' setting\n'
100
+ || E' WHERE setting.account_id = p_account_id AND NOT setting.enabled\n'
101
+ || E' )'),
102
+ (9, pg_catalog.format(
103
+ '%I.get_organization_private_session_settings(uuid,text)', data_schema),
104
+ E' ''enabled'', coalesce(setting.enabled, false),\n',
105
+ E' ''enabled'', coalesce(setting.enabled, true),\n'),
106
+ (10, pg_catalog.format(
107
+ '%I.update_organization_private_session_settings(uuid,text,boolean,bigint,uuid)',
108
+ data_schema),
109
+ setting_receipt_gate, ''),
110
+ (11, pg_catalog.format(
111
+ '%I.update_organization_private_session_settings(uuid,text,boolean,bigint,uuid)',
112
+ data_schema),
113
+ E' did_change := coalesce(setting.enabled, false) IS DISTINCT FROM p_enabled;\n',
114
+ E' did_change := coalesce(setting.enabled, true) IS DISTINCT FROM p_enabled;\n')
115
+ ) AS rewrites(ordinal, signature, old_fragment, new_fragment)
116
+ ORDER BY ordinal
117
+ LOOP
118
+ target := pg_catalog.to_regprocedure(rewrite.signature);
119
+ IF target IS NULL THEN
120
+ RAISE EXCEPTION 'universal session tenancy predicate is missing: %', rewrite.signature
121
+ USING ERRCODE = '55000';
122
+ END IF;
123
+ definition := pg_catalog.pg_get_functiondef(target);
124
+ occurrences := (
125
+ pg_catalog.length(definition)
126
+ - pg_catalog.length(pg_catalog.replace(definition, rewrite.old_fragment, ''))
127
+ ) / pg_catalog.length(rewrite.old_fragment);
128
+ IF occurrences <> 1 THEN
129
+ RAISE EXCEPTION 'universal session tenancy predicate shape changed: % (% matches)',
130
+ rewrite.signature, occurrences USING ERRCODE = '55000';
131
+ END IF;
132
+ EXECUTE pg_catalog.replace(definition, rewrite.old_fragment, rewrite.new_fragment);
133
+ END LOOP;
134
+
135
+ -- Fail closed if any other routine still consults the receipt table. Only
136
+ -- the retained, now-dormant receipt writers (operator, greenfield and
137
+ -- additional-organization activation, plus the operator preference
138
+ -- enabler's lock) and the private legacy-lane retirement predicate may.
139
+ -- That predicate is not a product prerequisite: it closes the legacy_user
140
+ -- connection and unattributed-writer compatibility lanes only for an
141
+ -- organization that already holds a receipt.
142
+ IF EXISTS (
143
+ SELECT 1 FROM pg_catalog.pg_proc routine
144
+ JOIN pg_catalog.pg_namespace namespace ON namespace.oid = routine.pronamespace
145
+ WHERE namespace.nspname IN (data_schema, 'opengeni_private')
146
+ AND routine.prosrc LIKE '%session_tenancy_activations%'
147
+ AND routine.proname NOT IN (
148
+ 'activate_session_tenancy_product',
149
+ 'activate_greenfield_session_tenancy_from_setup',
150
+ 'activate_session_tenancy_from_additional_organization',
151
+ 'enable_organization_private_sessions_from_activation',
152
+ 'session_tenancy_account_activated'
153
+ )
154
+ ) THEN
155
+ RAISE EXCEPTION 'a runtime routine still requires a session tenancy activation receipt'
156
+ USING ERRCODE = '55000';
157
+ END IF;
158
+ END
159
+ $universal_session_tenancy_predicates$;
160
+
161
+ COMMENT ON FUNCTION session_tenancy_product_activated(uuid, integer) IS
162
+ 'Universal since 0611: true for the transaction''s own organization at activation version 1. No per-organization receipt is required.';
163
+ COMMENT ON FUNCTION opengeni_private.session_tenancy_account_activated(uuid) IS
164
+ 'Legacy-lane retirement predicate (0340): true when the transaction''s own organization holds a receipt. Since 0611 it gates no product surface; it only closes the legacy_user connection and unattributed-writer lanes.';
165
+ COMMENT ON FUNCTION session_tenancy_any_product_activation() IS
166
+ 'Retired by 0611 with OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED. Always false so no binary enforces the removed startup interlock.';
@@ -0,0 +1,21 @@
1
+ -- deployment-mode: rolling
2
+ -- A scheduled occurrence whose model is retired or no longer in the catalog is
3
+ -- refused as a visible terminal run (`scheduled_model_unavailable`) instead of
4
+ -- exhausting activity retries and leaving no run. Preserve the immutable 0539
5
+ -- refusal lifecycle (as extended by 0552), adding only this reason. Exact
6
+ -- anchoring refuses an unexpected deployed guard definition. Old binaries never
7
+ -- write the new reason, so this is rolling-safe.
8
+ SET LOCAL lock_timeout = '5s';
9
+ SET LOCAL statement_timeout = '10min';
10
+
11
+ DO $scheduled_model_refusal$
12
+ DECLARE
13
+ definition text:=pg_get_functiondef('opengeni_private.guard_scheduled_admission_refusal()'::regprocedure);
14
+ anchor text:='''monthly_agent_run_limit'', ''allowance_exhausted'')';
15
+ BEGIN
16
+ IF strpos(definition,anchor)=0 OR strpos(substr(definition,strpos(definition,anchor)+length(anchor)),anchor)>0 THEN
17
+ RAISE EXCEPTION '0612 unexpected scheduled refusal guard definition' USING ERRCODE='55000';
18
+ END IF;
19
+ EXECUTE replace(definition,anchor,
20
+ '''monthly_agent_run_limit'', ''allowance_exhausted'', ''scheduled_model_unavailable'')');
21
+ END $scheduled_model_refusal$;