@opengeni/db 3.6.0 → 3.7.4-canary.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/automations.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-UGDVSQ6U.js → chunk-3ZMN4EXC.js} +2 -2
- package/dist/{chunk-744DFC65.js → chunk-6FIVOJ3V.js} +96 -6
- package/dist/chunk-6FIVOJ3V.js.map +1 -0
- package/dist/{chunk-UJ2EW3QI.js → chunk-DXJ2WY6W.js} +427 -85
- package/dist/chunk-DXJ2WY6W.js.map +1 -0
- package/dist/{chunk-CDMMSUWJ.js → chunk-JP6IYGYI.js} +3 -3
- package/dist/{chunk-Q5YUGQVK.js → chunk-JS3AWDKZ.js} +3 -3
- package/dist/{chunk-G3732M3T.js → chunk-LHKB62GC.js} +2 -2
- package/dist/{chunk-2NT476HD.js → chunk-QL4AHBYD.js} +2 -2
- package/dist/{chunk-IW6O6G7R.js → chunk-VDXHLVDE.js} +2 -2
- package/dist/{chunk-WW7AJ5IC.js → chunk-ZTSJPFDH.js} +5 -7
- package/dist/chunk-ZTSJPFDH.js.map +1 -0
- package/dist/codex-token-resolver.d.ts +12 -1
- package/dist/company-profile-agent-admin.d.ts +4 -2
- package/dist/company-profile-agent-policy.d.ts +20 -0
- package/dist/company-profile-schema.d.ts +573 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +487 -20
- package/dist/index.js +9489 -6041
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-human-provisioning.d.ts +3 -3
- package/dist/memory-domain.d.ts +5 -3
- package/dist/model-catalog.d.ts +153 -0
- package/dist/pr-review.d.ts +9 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +7 -7
- package/dist/schema.d.ts +1069 -6
- package/dist/schema.js +17 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +75 -5
- package/dist/session-queue-commands.d.ts +19 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/user-resource-authority.d.ts +8 -0
- package/dist/video-generation.js +3 -3
- package/dist/xai-subscription.d.ts +8 -0
- package/drizzle/0369_local_human_personal_authority.sql +350 -0
- package/drizzle/0370_workspace_member_management_scope.sql +98 -0
- package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
- package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
- package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
- package/drizzle/0374_session_event_cursors.sql +315 -0
- package/drizzle/0375_session_recovery_observability.sql +73 -0
- package/drizzle/0376_organization_workspace_inventory.sql +88 -0
- package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
- package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
- package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
- package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
- package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
- package/drizzle/0382_organization_api_key_provenance.sql +91 -0
- package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
- package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
- package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
- package/drizzle/0386_local_organization_administration.sql +157 -0
- package/drizzle/0387_bound_host_export_session_payloads.sql +165 -0
- package/drizzle/0388_sandbox_provider_deadline_interactions.sql +491 -0
- package/drizzle/0389_model_catalog_and_gateway_custom_models.sql +241 -0
- package/package.json +6 -6
- package/src/automations.ts +9 -1
- package/src/codex-token-resolver.ts +26 -1
- package/src/company-brain-governed-writes.ts +30 -1
- package/src/company-profile-agent-admin.ts +68 -1
- package/src/company-profile-agent-policy.ts +126 -0
- package/src/company-profile-schema.ts +115 -1
- package/src/database.ts +3 -5
- package/src/index.ts +4473 -570
- package/src/insights.ts +21 -7
- package/src/managed-human-provisioning.ts +3 -3
- package/src/memory-domain.ts +9 -4
- package/src/model-catalog.ts +682 -0
- package/src/organization-membership-lifecycle.ts +0 -1
- package/src/pr-review.ts +19 -0
- package/src/provision-roles.ts +83 -4
- package/src/runtime-posture.ts +26 -0
- package/src/schema.ts +259 -10
- package/src/session-control.ts +347 -59
- package/src/session-queue-commands.ts +303 -122
- package/src/session-tool-call-settlement.ts +34 -7
- package/src/user-resource-authority.ts +56 -0
- package/src/xai-subscription.ts +66 -14
- package/dist/chunk-744DFC65.js.map +0 -1
- package/dist/chunk-UJ2EW3QI.js.map +0 -1
- package/dist/chunk-WW7AJ5IC.js.map +0 -1
- /package/dist/{chunk-UGDVSQ6U.js.map → chunk-3ZMN4EXC.js.map} +0 -0
- /package/dist/{chunk-CDMMSUWJ.js.map → chunk-JP6IYGYI.js.map} +0 -0
- /package/dist/{chunk-Q5YUGQVK.js.map → chunk-JS3AWDKZ.js.map} +0 -0
- /package/dist/{chunk-G3732M3T.js.map → chunk-LHKB62GC.js.map} +0 -0
- /package/dist/{chunk-2NT476HD.js.map → chunk-QL4AHBYD.js.map} +0 -0
- /package/dist/{chunk-IW6O6G7R.js.map → chunk-VDXHLVDE.js.map} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Organization-wide workspace authority must come from an explicitly issued
|
|
3
|
+
-- organization key, never from the historical workspace_id IS NULL shape.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
|
|
8
|
+
ALTER TABLE api_keys
|
|
9
|
+
ADD COLUMN credential_kind text;
|
|
10
|
+
|
|
11
|
+
ALTER TABLE api_keys NO FORCE ROW LEVEL SECURITY;
|
|
12
|
+
|
|
13
|
+
UPDATE api_keys
|
|
14
|
+
SET
|
|
15
|
+
credential_kind = CASE
|
|
16
|
+
WHEN workspace_id IS NOT NULL THEN 'workspace'
|
|
17
|
+
ELSE 'legacy_account'
|
|
18
|
+
END,
|
|
19
|
+
revoked_at = CASE
|
|
20
|
+
WHEN workspace_id IS NULL THEN COALESCE(revoked_at, CURRENT_TIMESTAMP)
|
|
21
|
+
ELSE revoked_at
|
|
22
|
+
END,
|
|
23
|
+
updated_at = CASE
|
|
24
|
+
WHEN workspace_id IS NULL AND revoked_at IS NULL THEN CURRENT_TIMESTAMP
|
|
25
|
+
ELSE updated_at
|
|
26
|
+
END;
|
|
27
|
+
|
|
28
|
+
ALTER TABLE api_keys FORCE ROW LEVEL SECURITY;
|
|
29
|
+
|
|
30
|
+
CREATE FUNCTION opengeni_private.normalize_api_key_credential_kind()
|
|
31
|
+
RETURNS trigger
|
|
32
|
+
LANGUAGE plpgsql
|
|
33
|
+
SECURITY INVOKER
|
|
34
|
+
SET search_path = pg_catalog, public
|
|
35
|
+
AS $function$
|
|
36
|
+
BEGIN
|
|
37
|
+
IF NEW.workspace_id IS NOT NULL THEN
|
|
38
|
+
NEW.credential_kind := 'workspace';
|
|
39
|
+
ELSIF NEW.credential_kind IS NULL THEN
|
|
40
|
+
-- The previous application version omitted this column. Its organization-key
|
|
41
|
+
-- route is the only null-workspace insert path with this exact fixed grant.
|
|
42
|
+
IF NEW.permissions = '["account:read","workspace:create","workspace:read","workspace:admin","api_keys:manage"]'::jsonb THEN
|
|
43
|
+
NEW.credential_kind := 'organization';
|
|
44
|
+
ELSE
|
|
45
|
+
NEW.credential_kind := 'legacy_account';
|
|
46
|
+
END IF;
|
|
47
|
+
END IF;
|
|
48
|
+
|
|
49
|
+
-- Old application versions authorize every active null-workspace row by
|
|
50
|
+
-- shape alone. Keep ambiguous legacy rows unusable through that old lookup
|
|
51
|
+
-- path for the entire rolling window; only the exact previous organization
|
|
52
|
+
-- writer above may create an active null-workspace credential.
|
|
53
|
+
IF NEW.credential_kind = 'legacy_account' AND NEW.revoked_at IS NULL THEN
|
|
54
|
+
NEW.revoked_at := CURRENT_TIMESTAMP;
|
|
55
|
+
NEW.updated_at := CURRENT_TIMESTAMP;
|
|
56
|
+
END IF;
|
|
57
|
+
|
|
58
|
+
RETURN NEW;
|
|
59
|
+
END;
|
|
60
|
+
$function$;
|
|
61
|
+
|
|
62
|
+
REVOKE ALL ON FUNCTION opengeni_private.normalize_api_key_credential_kind() FROM PUBLIC;
|
|
63
|
+
|
|
64
|
+
CREATE TRIGGER api_keys_00_normalize_credential_kind
|
|
65
|
+
BEFORE INSERT OR UPDATE OF workspace_id, credential_kind, permissions, revoked_at
|
|
66
|
+
ON api_keys
|
|
67
|
+
FOR EACH ROW
|
|
68
|
+
EXECUTE FUNCTION opengeni_private.normalize_api_key_credential_kind();
|
|
69
|
+
|
|
70
|
+
ALTER TABLE api_keys
|
|
71
|
+
ALTER COLUMN credential_kind SET NOT NULL;
|
|
72
|
+
|
|
73
|
+
ALTER TABLE api_keys
|
|
74
|
+
ADD CONSTRAINT api_keys_credential_kind_check
|
|
75
|
+
CHECK (
|
|
76
|
+
(workspace_id IS NOT NULL AND credential_kind = 'workspace')
|
|
77
|
+
OR
|
|
78
|
+
(workspace_id IS NULL AND credential_kind = 'organization')
|
|
79
|
+
OR
|
|
80
|
+
(workspace_id IS NULL AND credential_kind = 'legacy_account' AND revoked_at IS NOT NULL)
|
|
81
|
+
) NOT VALID;
|
|
82
|
+
|
|
83
|
+
ALTER TABLE api_keys
|
|
84
|
+
VALIDATE CONSTRAINT api_keys_credential_kind_check;
|
|
85
|
+
|
|
86
|
+
CREATE INDEX api_keys_organization_account_idx
|
|
87
|
+
ON api_keys (account_id, created_at DESC)
|
|
88
|
+
WHERE credential_kind = 'organization';
|
|
89
|
+
|
|
90
|
+
COMMENT ON COLUMN api_keys.credential_kind IS
|
|
91
|
+
'Persisted API-key provenance. Only organization keys receive organization-wide shared-workspace authority; ambiguous legacy account keys are revoked.';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
--
|
|
3
|
+
-- Give Codex cooldowns typed provenance plus an independent revision. A fresh
|
|
4
|
+
-- provider usage read may then clear only the exact quota cooldown it observed
|
|
5
|
+
-- before the request; generic backpressure and a concurrently newer refusal
|
|
6
|
+
-- remain authoritative. Legacy writers do not know these columns, so the
|
|
7
|
+
-- trigger advances the revision and removes typed auto-clear authority whenever
|
|
8
|
+
-- they change exhausted_until alone during a rolling deployment.
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "codex_subscription_credentials"
|
|
11
|
+
ADD COLUMN "exhausted_kind" text,
|
|
12
|
+
ADD COLUMN "exhausted_revision" bigint NOT NULL DEFAULT 0,
|
|
13
|
+
ADD CONSTRAINT "codex_credentials_exhausted_kind_chk" CHECK (
|
|
14
|
+
("exhausted_until" IS NULL AND "exhausted_kind" IS NULL)
|
|
15
|
+
OR (
|
|
16
|
+
"exhausted_until" IS NOT NULL
|
|
17
|
+
AND ("exhausted_kind" IS NULL OR "exhausted_kind" IN ('quota', 'rate_limit'))
|
|
18
|
+
)
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
CREATE OR REPLACE FUNCTION opengeni_private.codex_cooldown_revision_guard()
|
|
22
|
+
RETURNS trigger
|
|
23
|
+
LANGUAGE plpgsql
|
|
24
|
+
SET search_path = pg_catalog
|
|
25
|
+
AS $function$
|
|
26
|
+
BEGIN
|
|
27
|
+
IF NEW.exhausted_until IS DISTINCT FROM OLD.exhausted_until THEN
|
|
28
|
+
IF NEW.exhausted_revision = OLD.exhausted_revision THEN
|
|
29
|
+
-- Compatibility path for a pre-0382 writer: retain the cooldown but do
|
|
30
|
+
-- not let a new usage reader mistake its unknown cause for quota.
|
|
31
|
+
NEW.exhausted_kind := NULL;
|
|
32
|
+
NEW.exhausted_revision := OLD.exhausted_revision + 1;
|
|
33
|
+
ELSIF NEW.exhausted_revision <> OLD.exhausted_revision + 1 THEN
|
|
34
|
+
RAISE EXCEPTION 'Codex cooldown revision must advance exactly once';
|
|
35
|
+
END IF;
|
|
36
|
+
ELSIF NEW.exhausted_kind IS DISTINCT FROM OLD.exhausted_kind
|
|
37
|
+
OR NEW.exhausted_revision IS DISTINCT FROM OLD.exhausted_revision
|
|
38
|
+
THEN
|
|
39
|
+
IF NEW.exhausted_revision <> OLD.exhausted_revision + 1 THEN
|
|
40
|
+
RAISE EXCEPTION 'Codex cooldown metadata revision must advance exactly once';
|
|
41
|
+
END IF;
|
|
42
|
+
END IF;
|
|
43
|
+
RETURN NEW;
|
|
44
|
+
END
|
|
45
|
+
$function$;
|
|
46
|
+
|
|
47
|
+
CREATE TRIGGER codex_cooldown_revision_guard
|
|
48
|
+
BEFORE UPDATE OF exhausted_until, exhausted_kind, exhausted_revision
|
|
49
|
+
ON "codex_subscription_credentials"
|
|
50
|
+
FOR EACH ROW
|
|
51
|
+
EXECUTE FUNCTION opengeni_private.codex_cooldown_revision_guard();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Trigger helpers are owner-internal implementation details. PostgreSQL grants
|
|
3
|
+
-- PUBLIC execute on new functions by default, so remove the inherited privilege
|
|
4
|
+
-- that exposed this guard to unrelated runtime roles.
|
|
5
|
+
|
|
6
|
+
REVOKE ALL ON FUNCTION opengeni_private.codex_cooldown_revision_guard() FROM PUBLIC;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Native Connected Machine workspace roots made tilde paths invalid. Repair
|
|
3
|
+
-- only legacy sessions whose active machine has reported an exact root; leave
|
|
4
|
+
-- ambiguous/offline legacy rows untouched rather than guessing a user home.
|
|
5
|
+
|
|
6
|
+
-- Migrations run as the NOSUPERUSER/NOBYPASSRLS table owner. Temporarily let
|
|
7
|
+
-- that owner see the joined rows; application roles remain policy-bound.
|
|
8
|
+
ALTER TABLE "sessions" NO FORCE ROW LEVEL SECURITY;
|
|
9
|
+
ALTER TABLE "sandboxes" NO FORCE ROW LEVEL SECURITY;
|
|
10
|
+
ALTER TABLE "enrollments" NO FORCE ROW LEVEL SECURITY;
|
|
11
|
+
|
|
12
|
+
UPDATE "sessions" AS session
|
|
13
|
+
SET "working_dir" =
|
|
14
|
+
rtrim(enrollment."workspace_root", '/') || '/' || substring(session."working_dir" FROM 3)
|
|
15
|
+
FROM "sandboxes" AS sandbox
|
|
16
|
+
JOIN "enrollments" AS enrollment ON enrollment."id" = sandbox."enrollment_id"
|
|
17
|
+
WHERE session."active_sandbox_id" = sandbox."id"
|
|
18
|
+
AND session."working_dir" LIKE '~/%'
|
|
19
|
+
AND enrollment."status" = 'active'
|
|
20
|
+
AND enrollment."workspace_root" IS NOT NULL
|
|
21
|
+
AND enrollment."workspace_root" <> '';
|
|
22
|
+
|
|
23
|
+
ALTER TABLE "enrollments" FORCE ROW LEVEL SECURITY;
|
|
24
|
+
ALTER TABLE "sandboxes" FORCE ROW LEVEL SECURITY;
|
|
25
|
+
ALTER TABLE "sessions" FORCE ROW LEVEL SECURITY;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
--
|
|
3
|
+
-- The canonical single-user local bootstrap already grants `dev` account-owner
|
|
4
|
+
-- authority in the application, but its durable organization membership was
|
|
5
|
+
-- left at the default member role. Converge that exact built-in identity and
|
|
6
|
+
-- admit it to organization Codex administration without broadening configured,
|
|
7
|
+
-- delegated, API-key, service, or agent principals.
|
|
8
|
+
|
|
9
|
+
SET LOCAL lock_timeout = '5s';
|
|
10
|
+
SET LOCAL statement_timeout = '10min';
|
|
11
|
+
|
|
12
|
+
CREATE OR REPLACE FUNCTION opengeni_private.assign_managed_self_organization_owner()
|
|
13
|
+
RETURNS trigger
|
|
14
|
+
LANGUAGE plpgsql
|
|
15
|
+
SECURITY DEFINER
|
|
16
|
+
SET search_path FROM CURRENT
|
|
17
|
+
AS $body$
|
|
18
|
+
BEGIN
|
|
19
|
+
IF EXISTS (
|
|
20
|
+
SELECT 1 FROM managed_accounts account
|
|
21
|
+
WHERE account.id = NEW.account_id
|
|
22
|
+
AND (
|
|
23
|
+
(
|
|
24
|
+
account.external_source = 'better-auth:user'
|
|
25
|
+
AND NEW.subject_id = 'user:' || account.external_id
|
|
26
|
+
) OR (
|
|
27
|
+
account.external_source = 'opengeni:local'
|
|
28
|
+
AND account.external_id = 'default'
|
|
29
|
+
AND NEW.subject_id = 'dev'
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
) THEN
|
|
33
|
+
NEW.role := 'owner';
|
|
34
|
+
END IF;
|
|
35
|
+
RETURN NEW;
|
|
36
|
+
END
|
|
37
|
+
$body$;
|
|
38
|
+
|
|
39
|
+
DO $pin_local_owner_trigger$
|
|
40
|
+
DECLARE data_schema text := current_schema();
|
|
41
|
+
BEGIN
|
|
42
|
+
EXECUTE format(
|
|
43
|
+
'ALTER FUNCTION opengeni_private.assign_managed_self_organization_owner() SET search_path = pg_catalog, %I, pg_temp',
|
|
44
|
+
data_schema
|
|
45
|
+
);
|
|
46
|
+
END
|
|
47
|
+
$pin_local_owner_trigger$;
|
|
48
|
+
|
|
49
|
+
ALTER TABLE organization_memberships NO FORCE ROW LEVEL SECURITY;
|
|
50
|
+
UPDATE organization_memberships membership
|
|
51
|
+
SET role = 'owner',
|
|
52
|
+
authorization_revision = membership.authorization_revision + 1,
|
|
53
|
+
updated_at = clock_timestamp()
|
|
54
|
+
FROM managed_accounts account
|
|
55
|
+
WHERE account.id = membership.account_id
|
|
56
|
+
AND account.external_source = 'opengeni:local'
|
|
57
|
+
AND account.external_id = 'default'
|
|
58
|
+
AND membership.subject_id = 'dev'
|
|
59
|
+
AND membership.status = 'active'
|
|
60
|
+
AND membership.role <> 'owner';
|
|
61
|
+
ALTER TABLE organization_memberships FORCE ROW LEVEL SECURITY;
|
|
62
|
+
|
|
63
|
+
DO $codex_scope_visibility_schema$
|
|
64
|
+
DECLARE data_schema text := current_schema();
|
|
65
|
+
BEGIN
|
|
66
|
+
EXECUTE format($ddl$
|
|
67
|
+
CREATE OR REPLACE FUNCTION opengeni_private.codex_organization_scope_visible(
|
|
68
|
+
p_account_id uuid
|
|
69
|
+
) RETURNS boolean
|
|
70
|
+
LANGUAGE plpgsql
|
|
71
|
+
SECURITY DEFINER
|
|
72
|
+
SET search_path = pg_catalog
|
|
73
|
+
AS $function$
|
|
74
|
+
DECLARE
|
|
75
|
+
workspace_value uuid := opengeni_private.current_workspace_id();
|
|
76
|
+
subject_value text := opengeni_private.current_subject_id();
|
|
77
|
+
visible boolean := false;
|
|
78
|
+
previous_lifecycle text := pg_catalog.current_setting(
|
|
79
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
80
|
+
);
|
|
81
|
+
BEGIN
|
|
82
|
+
IF p_account_id IS NULL
|
|
83
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
84
|
+
THEN
|
|
85
|
+
RETURN false;
|
|
86
|
+
END IF;
|
|
87
|
+
IF workspace_value IS NOT NULL THEN
|
|
88
|
+
PERFORM pg_catalog.set_config(
|
|
89
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
90
|
+
'organization_membership_lifecycle', true
|
|
91
|
+
);
|
|
92
|
+
SELECT EXISTS (
|
|
93
|
+
SELECT 1 FROM %1$I.workspaces workspace
|
|
94
|
+
WHERE workspace.account_id = p_account_id AND workspace.id = workspace_value
|
|
95
|
+
) AND NOT EXISTS (
|
|
96
|
+
SELECT 1 FROM %1$I.organization_memberships membership
|
|
97
|
+
WHERE membership.account_id = p_account_id
|
|
98
|
+
AND membership.personal_workspace_id = workspace_value
|
|
99
|
+
) INTO visible;
|
|
100
|
+
PERFORM pg_catalog.set_config(
|
|
101
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
102
|
+
);
|
|
103
|
+
RETURN coalesce(visible, false);
|
|
104
|
+
END IF;
|
|
105
|
+
IF subject_value IS NULL OR NOT (
|
|
106
|
+
subject_value LIKE 'user:%%'
|
|
107
|
+
OR (
|
|
108
|
+
subject_value = 'dev'
|
|
109
|
+
AND EXISTS (
|
|
110
|
+
SELECT 1 FROM %1$I.managed_accounts account
|
|
111
|
+
WHERE account.id = p_account_id
|
|
112
|
+
AND account.external_source = 'opengeni:local'
|
|
113
|
+
AND account.external_id = 'default'
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
) THEN
|
|
117
|
+
RETURN false;
|
|
118
|
+
END IF;
|
|
119
|
+
PERFORM pg_catalog.set_config(
|
|
120
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
121
|
+
'organization_membership_lifecycle', true
|
|
122
|
+
);
|
|
123
|
+
SELECT EXISTS (
|
|
124
|
+
SELECT 1 FROM %1$I.organization_memberships membership
|
|
125
|
+
WHERE membership.account_id = p_account_id
|
|
126
|
+
AND membership.subject_id = subject_value
|
|
127
|
+
AND membership.status = 'active'
|
|
128
|
+
AND membership.role IN ('owner', 'admin')
|
|
129
|
+
) INTO visible;
|
|
130
|
+
PERFORM pg_catalog.set_config(
|
|
131
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
132
|
+
);
|
|
133
|
+
RETURN coalesce(visible, false);
|
|
134
|
+
EXCEPTION WHEN OTHERS THEN
|
|
135
|
+
PERFORM pg_catalog.set_config(
|
|
136
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
137
|
+
);
|
|
138
|
+
RAISE;
|
|
139
|
+
END
|
|
140
|
+
$function$
|
|
141
|
+
$ddl$, data_schema);
|
|
142
|
+
END
|
|
143
|
+
$codex_scope_visibility_schema$;
|
|
144
|
+
|
|
145
|
+
REVOKE ALL ON FUNCTION opengeni_private.assign_managed_self_organization_owner() FROM PUBLIC;
|
|
146
|
+
REVOKE ALL ON FUNCTION opengeni_private.codex_organization_scope_visible(uuid) FROM PUBLIC;
|
|
147
|
+
|
|
148
|
+
DO $grants$
|
|
149
|
+
BEGIN
|
|
150
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
151
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.assign_managed_self_organization_owner()
|
|
152
|
+
TO opengeni_app;
|
|
153
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.codex_organization_scope_visible(uuid)
|
|
154
|
+
TO opengeni_app;
|
|
155
|
+
END IF;
|
|
156
|
+
END
|
|
157
|
+
$grants$;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Host export is an optional bounded projection. Canonical session_events keeps
|
|
3
|
+
-- the complete lossless payload; an oversized projection must never roll back
|
|
4
|
+
-- the source event or a later lifecycle transaction that closes pending work.
|
|
5
|
+
|
|
6
|
+
SET lock_timeout = '5s';
|
|
7
|
+
SET statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
DO $migration$
|
|
10
|
+
DECLARE target_schema text := current_schema();
|
|
11
|
+
BEGIN
|
|
12
|
+
EXECUTE format($create$
|
|
13
|
+
CREATE OR REPLACE FUNCTION opengeni_private.enqueue_host_session_event_export()
|
|
14
|
+
RETURNS trigger
|
|
15
|
+
LANGUAGE plpgsql
|
|
16
|
+
SECURITY DEFINER
|
|
17
|
+
SET search_path = pg_catalog
|
|
18
|
+
AS $function$
|
|
19
|
+
DECLARE
|
|
20
|
+
v_enabled boolean;
|
|
21
|
+
v_initiator jsonb;
|
|
22
|
+
v_context jsonb := '{}'::jsonb;
|
|
23
|
+
v_origin text;
|
|
24
|
+
v_source_payload_bytes integer;
|
|
25
|
+
v_export_payload jsonb;
|
|
26
|
+
v_export_payload_codec_version smallint;
|
|
27
|
+
v_payload_bytes integer;
|
|
28
|
+
BEGIN
|
|
29
|
+
IF NEW.type IN (
|
|
30
|
+
'agent.message.delta', 'agent.reasoning.delta',
|
|
31
|
+
'sandbox.command.output.delta', 'terminal.pty.output.delta'
|
|
32
|
+
) THEN
|
|
33
|
+
RETURN NEW;
|
|
34
|
+
END IF;
|
|
35
|
+
|
|
36
|
+
SELECT c.session_events_enabled INTO v_enabled
|
|
37
|
+
FROM %1$I.host_export_config c WHERE c.id = 1
|
|
38
|
+
FOR SHARE;
|
|
39
|
+
IF coalesce(v_enabled, false) = false THEN
|
|
40
|
+
RETURN NEW;
|
|
41
|
+
END IF;
|
|
42
|
+
|
|
43
|
+
IF NEW.turn_id IS NOT NULL THEN
|
|
44
|
+
SELECT
|
|
45
|
+
CASE WHEN octet_length(t.initiator_subject_id) <= 1024 THEN
|
|
46
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
47
|
+
'kind', t.initiator_kind,
|
|
48
|
+
'subjectId', t.initiator_subject_id,
|
|
49
|
+
'label', CASE
|
|
50
|
+
WHEN jsonb_typeof(t.initiator_context -> 'label') = 'string'
|
|
51
|
+
THEN left(t.initiator_context ->> 'label', 256)
|
|
52
|
+
ELSE NULL
|
|
53
|
+
END
|
|
54
|
+
))
|
|
55
|
+
ELSE NULL END,
|
|
56
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
57
|
+
'label', CASE
|
|
58
|
+
WHEN jsonb_typeof(t.initiator_context -> 'label') = 'string'
|
|
59
|
+
THEN left(t.initiator_context ->> 'label', 256)
|
|
60
|
+
ELSE NULL
|
|
61
|
+
END,
|
|
62
|
+
'backfill', CASE
|
|
63
|
+
WHEN jsonb_typeof(t.initiator_context -> 'backfill') = 'boolean'
|
|
64
|
+
THEN t.initiator_context -> 'backfill'
|
|
65
|
+
ELSE NULL
|
|
66
|
+
END,
|
|
67
|
+
'attributionOmitted', CASE
|
|
68
|
+
WHEN octet_length(t.initiator_subject_id) > 1024 THEN 'subject_id_too_large'
|
|
69
|
+
ELSE NULL
|
|
70
|
+
END
|
|
71
|
+
)),
|
|
72
|
+
t.source
|
|
73
|
+
INTO v_initiator, v_context, v_origin
|
|
74
|
+
FROM %1$I.session_turns t
|
|
75
|
+
WHERE t.workspace_id = NEW.workspace_id AND t.id = NEW.turn_id;
|
|
76
|
+
ELSIF NEW.type = 'session.created' THEN
|
|
77
|
+
SELECT
|
|
78
|
+
CASE WHEN octet_length(s.created_by_subject_id) <= 1024 THEN
|
|
79
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
80
|
+
'kind', s.created_by_kind,
|
|
81
|
+
'subjectId', s.created_by_subject_id,
|
|
82
|
+
'label', CASE
|
|
83
|
+
WHEN jsonb_typeof(s.created_by_context -> 'label') = 'string'
|
|
84
|
+
THEN left(s.created_by_context ->> 'label', 256)
|
|
85
|
+
ELSE NULL
|
|
86
|
+
END
|
|
87
|
+
))
|
|
88
|
+
ELSE NULL END,
|
|
89
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
90
|
+
'label', CASE
|
|
91
|
+
WHEN jsonb_typeof(s.created_by_context -> 'label') = 'string'
|
|
92
|
+
THEN left(s.created_by_context ->> 'label', 256)
|
|
93
|
+
ELSE NULL
|
|
94
|
+
END,
|
|
95
|
+
'backfill', CASE
|
|
96
|
+
WHEN jsonb_typeof(s.created_by_context -> 'backfill') = 'boolean'
|
|
97
|
+
THEN s.created_by_context -> 'backfill'
|
|
98
|
+
ELSE NULL
|
|
99
|
+
END,
|
|
100
|
+
'attributionOmitted', CASE
|
|
101
|
+
WHEN octet_length(s.created_by_subject_id) > 1024 THEN 'subject_id_too_large'
|
|
102
|
+
ELSE NULL
|
|
103
|
+
END
|
|
104
|
+
)),
|
|
105
|
+
NULL
|
|
106
|
+
INTO v_initiator, v_context, v_origin
|
|
107
|
+
FROM %1$I.sessions s
|
|
108
|
+
WHERE s.workspace_id = NEW.workspace_id AND s.id = NEW.session_id;
|
|
109
|
+
END IF;
|
|
110
|
+
|
|
111
|
+
v_source_payload_bytes := octet_length(NEW.payload::text);
|
|
112
|
+
v_export_payload := NEW.payload;
|
|
113
|
+
v_export_payload_codec_version := NEW.payload_codec_version;
|
|
114
|
+
IF v_source_payload_bytes > 65536 THEN
|
|
115
|
+
v_export_payload := jsonb_build_object(
|
|
116
|
+
'_hostExport', jsonb_build_object(
|
|
117
|
+
'payloadMode', 'summary',
|
|
118
|
+
'payloadTruncated', true,
|
|
119
|
+
'originalBytes', v_source_payload_bytes,
|
|
120
|
+
'sourceEventId', NEW.id,
|
|
121
|
+
'fullPayload', 'retained in canonical session event'
|
|
122
|
+
),
|
|
123
|
+
'preview', '[host-export payload omitted at bounded projection boundary]'
|
|
124
|
+
);
|
|
125
|
+
-- The projection is literal PostgreSQL JSON, not an application-codec
|
|
126
|
+
-- envelope. Preserve codec truth only when the original payload is kept.
|
|
127
|
+
v_export_payload_codec_version := NULL;
|
|
128
|
+
END IF;
|
|
129
|
+
|
|
130
|
+
v_payload_bytes := octet_length(v_export_payload::text)
|
|
131
|
+
+ octet_length(NEW.type)
|
|
132
|
+
+ coalesce(octet_length(NEW.client_event_id), 0)
|
|
133
|
+
+ coalesce(octet_length(NEW.turn_association), 0)
|
|
134
|
+
+ coalesce(octet_length(NEW.duplicate_reason), 0)
|
|
135
|
+
+ octet_length(coalesce(v_initiator, 'null'::jsonb)::text)
|
|
136
|
+
+ octet_length(v_context::text)
|
|
137
|
+
+ 768;
|
|
138
|
+
INSERT INTO %1$I.host_export_outbox (
|
|
139
|
+
export_kind, source_id, account_id, workspace_id, session_id,
|
|
140
|
+
turn_id, turn_generation, turn_attempt_id, session_sequence,
|
|
141
|
+
client_event_id, turn_association, duplicate_of_event_id, duplicate_reason,
|
|
142
|
+
event_type, idempotency_key, initiator, initiator_context, origin,
|
|
143
|
+
payload, payload_codec_version, envelope_bytes, occurred_at,
|
|
144
|
+
source_recorded_at, enqueued_at
|
|
145
|
+
) VALUES (
|
|
146
|
+
'session_event', NEW.id, NEW.account_id, NEW.workspace_id, NEW.session_id,
|
|
147
|
+
NEW.turn_id, NEW.turn_generation, NEW.turn_attempt_id, NEW.sequence,
|
|
148
|
+
NEW.client_event_id, NEW.turn_association, NEW.duplicate_of_event_id,
|
|
149
|
+
NEW.duplicate_reason,
|
|
150
|
+
NEW.type, 'session_event:' || NEW.id::text, v_initiator,
|
|
151
|
+
coalesce(v_context, '{}'::jsonb), v_origin, v_export_payload,
|
|
152
|
+
v_export_payload_codec_version, greatest(1, v_payload_bytes), NEW.occurred_at,
|
|
153
|
+
NEW.created_at, clock_timestamp()
|
|
154
|
+
)
|
|
155
|
+
ON CONFLICT (export_kind, source_id) DO NOTHING;
|
|
156
|
+
RETURN NEW;
|
|
157
|
+
END $function$;
|
|
158
|
+
$create$, target_schema);
|
|
159
|
+
END
|
|
160
|
+
$migration$;
|
|
161
|
+
|
|
162
|
+
COMMENT ON COLUMN host_export_outbox.payload IS
|
|
163
|
+
'Immutable bounded host projection. Canonical session_events retains the complete payload; oversized host projections carry an explicit content-free truncation receipt.';
|
|
164
|
+
COMMENT ON COLUMN host_export_outbox.payload_codec_version IS
|
|
165
|
+
'Codec truth for the stored host projection. NULL means literal SQL JSON, including oversized-payload projections; 1 means the original application-coded payload was retained exactly.';
|