@opengeni/db 3.2.2 → 3.3.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/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/{chunk-V2X2NK7T.js → chunk-5MPQZI43.js} +2675 -2540
- package/dist/{chunk-V2X2NK7T.js.map → chunk-5MPQZI43.js.map} +1 -1
- package/dist/{chunk-L3NT43Z5.js → chunk-7K5AVQUZ.js} +3 -3
- package/dist/{chunk-JYPBOJ2Y.js → chunk-AAOKWNHE.js} +4 -4
- package/dist/{chunk-D7LTIG33.js → chunk-B4RMLA2X.js} +2 -2
- package/dist/{chunk-OXO32LTK.js → chunk-CEJ3NQ3B.js} +11 -1
- package/dist/chunk-CEJ3NQ3B.js.map +1 -0
- package/dist/{chunk-AYZC65QU.js → chunk-CGSINPLW.js} +2 -2
- package/dist/{chunk-VCJAYIPH.js → chunk-FX2PSMGH.js} +2 -2
- package/dist/{chunk-HMKUJ6NI.js → chunk-HQWXEF77.js} +57 -9
- package/dist/chunk-HQWXEF77.js.map +1 -0
- package/dist/{chunk-V22DUH4C.js → chunk-HWEHZKHA.js} +5 -5
- package/dist/{chunk-6UU7OVUN.js → chunk-JTBJGSUZ.js} +37 -7
- package/dist/chunk-JTBJGSUZ.js.map +1 -0
- package/dist/{chunk-YH4GWEDM.js → chunk-KPC7I5WG.js} +2 -2
- package/dist/{chunk-XQHABE23.js → chunk-OWM6XG55.js} +4 -4
- package/dist/{chunk-YWHG45TK.js → chunk-PNEJVKKV.js} +6 -6
- package/dist/{chunk-4BAKPTPG.js → chunk-S74WULCK.js} +3 -3
- package/dist/{chunk-K6NT66NP.js → chunk-SDKFRIAN.js} +4 -4
- package/dist/{chunk-RCDMYTLP.js → chunk-UWS5DJQ6.js} +2 -2
- package/dist/{chunk-LRRGVHAQ.js → chunk-VUR5U5B3.js} +2 -2
- package/dist/{chunk-IF63BST7.js → chunk-Y4ERASEM.js} +2 -2
- package/dist/{chunk-VBG4TW5C.js → chunk-ZZOEKHGU.js} +2 -2
- package/dist/connect-attempts.js +3 -3
- package/dist/database.d.ts +9 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +93 -22
- package/dist/index.js +31 -19
- package/dist/insights-site-usage.d.ts +10 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.js +2 -2
- package/dist/migrate.js +19 -19
- package/dist/organization-integration-policy.js +3 -3
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +6 -6
- package/dist/schema.d.ts +102 -0
- package/dist/schema.js +1 -1
- package/dist/session-background-commands.js +5 -5
- package/dist/session-command-output.js +5 -5
- package/dist/session-event-slices.js +3 -3
- package/dist/session-mcp-credential-rotation.js +6 -6
- package/dist/session-tenancy.js +6 -6
- package/dist/site-publishing.d.ts +11 -0
- package/dist/{subscription-core-codex-4G4CCOJU.js → subscription-core-codex-HIAP7CY2.js} +6 -6
- package/dist/{subscription-core-codex-compat-64ACIZVP.js → subscription-core-codex-compat-IBERN7DU.js} +8 -8
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +5 -5
- package/drizzle/0793_private_site_sessions.sql +166 -0
- package/drizzle/0794_site_ai_insights.sql +175 -0
- package/drizzle/0795_site_lifecycle_cancellation.sql +121 -0
- package/package.json +9 -9
- package/src/database.ts +44 -3
- package/src/index.ts +142 -11
- package/src/insights-site-usage.ts +37 -0
- package/src/provision-roles.ts +15 -0
- package/src/runtime-posture.ts +76 -7
- package/src/schema.ts +10 -0
- package/src/site-publishing.ts +35 -0
- package/dist/chunk-6UU7OVUN.js.map +0 -1
- package/dist/chunk-HMKUJ6NI.js.map +0 -1
- package/dist/chunk-OXO32LTK.js.map +0 -1
- /package/dist/{chunk-L3NT43Z5.js.map → chunk-7K5AVQUZ.js.map} +0 -0
- /package/dist/{chunk-JYPBOJ2Y.js.map → chunk-AAOKWNHE.js.map} +0 -0
- /package/dist/{chunk-D7LTIG33.js.map → chunk-B4RMLA2X.js.map} +0 -0
- /package/dist/{chunk-AYZC65QU.js.map → chunk-CGSINPLW.js.map} +0 -0
- /package/dist/{chunk-VCJAYIPH.js.map → chunk-FX2PSMGH.js.map} +0 -0
- /package/dist/{chunk-V22DUH4C.js.map → chunk-HWEHZKHA.js.map} +0 -0
- /package/dist/{chunk-YH4GWEDM.js.map → chunk-KPC7I5WG.js.map} +0 -0
- /package/dist/{chunk-XQHABE23.js.map → chunk-OWM6XG55.js.map} +0 -0
- /package/dist/{chunk-YWHG45TK.js.map → chunk-PNEJVKKV.js.map} +0 -0
- /package/dist/{chunk-4BAKPTPG.js.map → chunk-S74WULCK.js.map} +0 -0
- /package/dist/{chunk-K6NT66NP.js.map → chunk-SDKFRIAN.js.map} +0 -0
- /package/dist/{chunk-RCDMYTLP.js.map → chunk-UWS5DJQ6.js.map} +0 -0
- /package/dist/{chunk-LRRGVHAQ.js.map → chunk-VUR5U5B3.js.map} +0 -0
- /package/dist/{chunk-IF63BST7.js.map → chunk-Y4ERASEM.js.map} +0 -0
- /package/dist/{chunk-VBG4TW5C.js.map → chunk-ZZOEKHGU.js.map} +0 -0
- /package/dist/{subscription-core-codex-4G4CCOJU.js.map → subscription-core-codex-HIAP7CY2.js.map} +0 -0
- /package/dist/{subscription-core-codex-compat-64ACIZVP.js.map → subscription-core-codex-compat-IBERN7DU.js.map} +0 -0
|
@@ -28,13 +28,13 @@ import {
|
|
|
28
28
|
settleConnectedMachineSessionBackgroundCommandWithMutation,
|
|
29
29
|
settleSessionBackgroundCommandForRetainedProcessInTransaction,
|
|
30
30
|
unobservableRetainedProcessOutcomeSql
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
33
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-HWEHZKHA.js";
|
|
32
|
+
import "./chunk-Y4ERASEM.js";
|
|
33
|
+
import "./chunk-7K5AVQUZ.js";
|
|
34
34
|
import "./chunk-4UJCXBR2.js";
|
|
35
|
-
import "./chunk-
|
|
35
|
+
import "./chunk-JTBJGSUZ.js";
|
|
36
36
|
import "./chunk-SMCA7PNJ.js";
|
|
37
|
-
import "./chunk-
|
|
37
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
38
38
|
import "./chunk-PZ5AY32C.js";
|
|
39
39
|
export {
|
|
40
40
|
COMMAND_OUTPUT_DEFAULT_BYTES,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
appendSessionCommandOutput,
|
|
3
3
|
commandOutputClientEventIdPrefix
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-HWEHZKHA.js";
|
|
5
|
+
import "./chunk-Y4ERASEM.js";
|
|
6
|
+
import "./chunk-7K5AVQUZ.js";
|
|
7
7
|
import "./chunk-4UJCXBR2.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-JTBJGSUZ.js";
|
|
9
9
|
import "./chunk-SMCA7PNJ.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
11
11
|
import "./chunk-PZ5AY32C.js";
|
|
12
12
|
export {
|
|
13
13
|
appendSessionCommandOutput,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listSessionEventSlices,
|
|
3
3
|
readSessionMessageSliceInScope
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-S74WULCK.js";
|
|
5
|
+
import "./chunk-JTBJGSUZ.js";
|
|
6
6
|
import "./chunk-SMCA7PNJ.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
8
8
|
import "./chunk-PZ5AY32C.js";
|
|
9
9
|
export {
|
|
10
10
|
listSessionEventSlices,
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
connectionMetadataMatchesBinding
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FX2PSMGH.js";
|
|
4
4
|
import {
|
|
5
5
|
lockSessionEventWriteRows,
|
|
6
6
|
organizationApiKeyAllowsWorkspace
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-HWEHZKHA.js";
|
|
8
|
+
import "./chunk-Y4ERASEM.js";
|
|
9
|
+
import "./chunk-7K5AVQUZ.js";
|
|
10
10
|
import "./chunk-4UJCXBR2.js";
|
|
11
11
|
import {
|
|
12
12
|
rawRows,
|
|
13
13
|
setSubjectRlsContext,
|
|
14
14
|
withRlsContext
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-JTBJGSUZ.js";
|
|
16
16
|
import "./chunk-SMCA7PNJ.js";
|
|
17
17
|
import {
|
|
18
18
|
apiKeys,
|
|
19
19
|
sessionCommandReceipts,
|
|
20
20
|
sessionMcpServers,
|
|
21
21
|
sessions
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-CEJ3NQ3B.js";
|
|
23
23
|
import "./chunk-PZ5AY32C.js";
|
|
24
24
|
|
|
25
25
|
// src/session-mcp-credential-rotation.ts
|
package/dist/session-tenancy.js
CHANGED
|
@@ -19,14 +19,14 @@ import {
|
|
|
19
19
|
sessionHasLiveAttempts,
|
|
20
20
|
stopSessionWorkForVisibilityChange,
|
|
21
21
|
transitionSessionVisibility
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-SDKFRIAN.js";
|
|
23
|
+
import "./chunk-HWEHZKHA.js";
|
|
24
|
+
import "./chunk-Y4ERASEM.js";
|
|
25
|
+
import "./chunk-7K5AVQUZ.js";
|
|
26
26
|
import "./chunk-4UJCXBR2.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-JTBJGSUZ.js";
|
|
28
28
|
import "./chunk-SMCA7PNJ.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
30
30
|
import "./chunk-PZ5AY32C.js";
|
|
31
31
|
export {
|
|
32
32
|
EMPTY_SESSION_VISIBILITY_WORK,
|
|
@@ -440,6 +440,17 @@ export declare function listActiveSiteRuntimeConversations(db: Database, scope:
|
|
|
440
440
|
everyoneTools: boolean;
|
|
441
441
|
};
|
|
442
442
|
}): Promise<string[]>;
|
|
443
|
+
/** Keyset-paged lifecycle inventory; no viewer transcript or age/count cutoff. */
|
|
444
|
+
export declare function listSiteRuntimeCancellationCandidates(db: Database, scope: SitePublishingScope, input: {
|
|
445
|
+
siteId: string;
|
|
446
|
+
after?: string;
|
|
447
|
+
limit: number;
|
|
448
|
+
startedBefore: Date;
|
|
449
|
+
toolsNarrowedTo?: {
|
|
450
|
+
tools: readonly string[];
|
|
451
|
+
everyoneTools: boolean;
|
|
452
|
+
};
|
|
453
|
+
}): Promise<string[]>;
|
|
443
454
|
export type SiteRuntimeUsageSnapshot = {
|
|
444
455
|
visitorDay: {
|
|
445
456
|
conversations: number;
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
subscriptionCoreReselectionPoints,
|
|
20
20
|
subscriptionCoreTurnActor,
|
|
21
21
|
touchSubscriptionCoreCodexBinding
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-AAOKWNHE.js";
|
|
23
|
+
import "./chunk-ZZOEKHGU.js";
|
|
24
24
|
import {
|
|
25
25
|
SUBSCRIPTION_CORE_CODEX_ENTITLEMENT_COOLDOWN_MS,
|
|
26
26
|
SUBSCRIPTION_CORE_CODEX_EXHAUSTED_FALLBACK_MS,
|
|
@@ -28,10 +28,10 @@ import {
|
|
|
28
28
|
SUBSCRIPTION_CORE_CODEX_RATE_LIMIT_FALLBACK_MS,
|
|
29
29
|
SubscriptionCoreCodexAccessLostError,
|
|
30
30
|
SubscriptionCoreCodexLeaseLostError
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-Y4ERASEM.js";
|
|
32
|
+
import "./chunk-JTBJGSUZ.js";
|
|
33
33
|
import "./chunk-SMCA7PNJ.js";
|
|
34
|
-
import "./chunk-
|
|
34
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
35
35
|
import "./chunk-PZ5AY32C.js";
|
|
36
36
|
export {
|
|
37
37
|
SUBSCRIPTION_CORE_CODEX_ENTITLEMENT_COOLDOWN_MS,
|
|
@@ -61,4 +61,4 @@ export {
|
|
|
61
61
|
subscriptionCoreTurnActor,
|
|
62
62
|
touchSubscriptionCoreCodexBinding
|
|
63
63
|
};
|
|
64
|
-
//# sourceMappingURL=subscription-core-codex-
|
|
64
|
+
//# sourceMappingURL=subscription-core-codex-HIAP7CY2.js.map
|
|
@@ -12,17 +12,17 @@ import {
|
|
|
12
12
|
setSubscriptionCoreCodexPrimary,
|
|
13
13
|
setSubscriptionCoreCodexRotation,
|
|
14
14
|
setSubscriptionCoreWorkspaceCodexSource
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-PNEJVKKV.js";
|
|
16
|
+
import "./chunk-ZZOEKHGU.js";
|
|
17
|
+
import "./chunk-HWEHZKHA.js";
|
|
18
18
|
import {
|
|
19
19
|
SubscriptionCoreCodexSourceRefusedError
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-Y4ERASEM.js";
|
|
21
|
+
import "./chunk-7K5AVQUZ.js";
|
|
22
22
|
import "./chunk-4UJCXBR2.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-JTBJGSUZ.js";
|
|
24
24
|
import "./chunk-SMCA7PNJ.js";
|
|
25
|
-
import "./chunk-
|
|
25
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
26
26
|
import "./chunk-PZ5AY32C.js";
|
|
27
27
|
export {
|
|
28
28
|
SubscriptionCoreCodexSourceRefusedError,
|
|
@@ -40,4 +40,4 @@ export {
|
|
|
40
40
|
setSubscriptionCoreCodexRotation,
|
|
41
41
|
setSubscriptionCoreWorkspaceCodexSource
|
|
42
42
|
};
|
|
43
|
-
//# sourceMappingURL=subscription-core-codex-compat-
|
|
43
|
+
//# sourceMappingURL=subscription-core-codex-compat-IBERN7DU.js.map
|
package/dist/video-generation.js
CHANGED
|
@@ -28,11 +28,11 @@ import {
|
|
|
28
28
|
settleVideoGenerationFailure,
|
|
29
29
|
settleVideoGenerationReady,
|
|
30
30
|
updateWorkspaceVideoGenerationPolicy
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-7K5AVQUZ.js";
|
|
32
32
|
import "./chunk-4UJCXBR2.js";
|
|
33
|
-
import "./chunk-
|
|
33
|
+
import "./chunk-JTBJGSUZ.js";
|
|
34
34
|
import "./chunk-SMCA7PNJ.js";
|
|
35
|
-
import "./chunk-
|
|
35
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
36
36
|
import "./chunk-PZ5AY32C.js";
|
|
37
37
|
export {
|
|
38
38
|
ACTIVE_VIDEO_GENERATION_STATUSES,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lockWorkspaceInferenceControl
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-HWEHZKHA.js";
|
|
4
|
+
import "./chunk-Y4ERASEM.js";
|
|
5
|
+
import "./chunk-7K5AVQUZ.js";
|
|
6
6
|
import "./chunk-4UJCXBR2.js";
|
|
7
7
|
import {
|
|
8
8
|
withWorkspaceRls
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JTBJGSUZ.js";
|
|
10
10
|
import "./chunk-SMCA7PNJ.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-CEJ3NQ3B.js";
|
|
12
12
|
import "./chunk-PZ5AY32C.js";
|
|
13
13
|
|
|
14
14
|
// src/workspace-tool-defaults.ts
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- maintenance-justification: old Site writers cannot supply the new immutable viewer audience or proof; drain them before activating the fail-closed native boundary.
|
|
3
|
+
-- Freeze an independent viewer audience on native sessions. Published legacy
|
|
4
|
+
-- mappings prove their audience; historical console provenance does not, and
|
|
5
|
+
-- is deliberately not converted into an asserted private owner.
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '5min';
|
|
8
|
+
|
|
9
|
+
ALTER TABLE sessions ADD COLUMN site_id uuid,
|
|
10
|
+
ADD COLUMN site_access_root_id uuid,
|
|
11
|
+
ADD COLUMN site_viewer_principal text,
|
|
12
|
+
ADD COLUMN site_owner_subject_id text;
|
|
13
|
+
ALTER TABLE model_call_facts ADD COLUMN site_id uuid;
|
|
14
|
+
|
|
15
|
+
-- Run the bounded source backfill under the same tenant scope as each row.
|
|
16
|
+
-- Maintenance cutover: the migration owner must enumerate all tenants even
|
|
17
|
+
-- when NOSUPERUSER/NOBYPASSRLS. These transaction-local DDL changes are restored
|
|
18
|
+
-- before commit; runtime roles never receive a cross-tenant read lane.
|
|
19
|
+
ALTER TABLE site_runtime_conversations NO FORCE ROW LEVEL SECURITY;
|
|
20
|
+
ALTER TABLE sessions NO FORCE ROW LEVEL SECURITY;
|
|
21
|
+
ALTER TABLE model_call_facts NO FORCE ROW LEVEL SECURITY;
|
|
22
|
+
-- Table locks already exclude runtime writers; do not acquire a workspace
|
|
23
|
+
-- advisory fence after those locks. Restore the exact trigger before commit.
|
|
24
|
+
ALTER TABLE sessions DISABLE TRIGGER session_tenancy_workspace_fence;
|
|
25
|
+
DO $backfill$
|
|
26
|
+
DECLARE scope record;
|
|
27
|
+
BEGIN
|
|
28
|
+
FOR scope IN SELECT DISTINCT account_id, workspace_id FROM site_runtime_conversations LOOP
|
|
29
|
+
PERFORM set_config('opengeni.account_id', scope.account_id::text, true);
|
|
30
|
+
PERFORM set_config('opengeni.workspace_id', scope.workspace_id::text, true);
|
|
31
|
+
WITH RECURSIVE bound AS (
|
|
32
|
+
SELECT s.id, s.id AS access_root, c.site_id, c.viewer_principal,
|
|
33
|
+
CASE WHEN c.viewer_principal LIKE 'user:%' THEN substr(c.viewer_principal,6) END AS owner
|
|
34
|
+
FROM sessions s JOIN site_runtime_conversations c
|
|
35
|
+
ON c.session_id=s.id AND c.account_id=s.account_id AND c.workspace_id=s.workspace_id
|
|
36
|
+
WHERE s.account_id=scope.account_id AND s.workspace_id=scope.workspace_id
|
|
37
|
+
UNION ALL
|
|
38
|
+
SELECT child.id, b.access_root,b.site_id,b.viewer_principal,b.owner
|
|
39
|
+
FROM sessions child JOIN bound b ON child.parent_session_id=b.id
|
|
40
|
+
WHERE child.account_id=scope.account_id AND child.workspace_id=scope.workspace_id
|
|
41
|
+
) UPDATE sessions s SET site_id=b.site_id,site_access_root_id=b.access_root,
|
|
42
|
+
site_viewer_principal=b.viewer_principal,site_owner_subject_id=b.owner
|
|
43
|
+
FROM bound b WHERE s.id=b.id AND s.workspace_id=scope.workspace_id;
|
|
44
|
+
UPDATE model_call_facts f SET site_id=s.site_id FROM sessions s
|
|
45
|
+
WHERE f.account_id=scope.account_id AND f.workspace_id=scope.workspace_id
|
|
46
|
+
AND s.id=f.session_id AND s.workspace_id=f.workspace_id AND s.site_id IS NOT NULL;
|
|
47
|
+
END LOOP;
|
|
48
|
+
END
|
|
49
|
+
$backfill$;
|
|
50
|
+
ALTER TABLE sessions ENABLE TRIGGER session_tenancy_workspace_fence;
|
|
51
|
+
ALTER TABLE site_runtime_conversations FORCE ROW LEVEL SECURITY;
|
|
52
|
+
ALTER TABLE sessions FORCE ROW LEVEL SECURITY;
|
|
53
|
+
ALTER TABLE model_call_facts FORCE ROW LEVEL SECURITY;
|
|
54
|
+
|
|
55
|
+
CREATE FUNCTION bind_session_site_audience() RETURNS trigger
|
|
56
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
57
|
+
DECLARE parent sessions%ROWTYPE; conversation site_runtime_conversations%ROWTYPE;
|
|
58
|
+
origin jsonb; enabled boolean;
|
|
59
|
+
BEGIN
|
|
60
|
+
IF TG_OP='UPDATE' THEN
|
|
61
|
+
IF OLD.site_access_root_id IS NOT NULL AND NEW.visibility IS DISTINCT FROM OLD.visibility THEN
|
|
62
|
+
RAISE EXCEPTION 'Site conversations keep their viewer audience; publish an output to share it' USING ERRCODE='42501';
|
|
63
|
+
END IF;
|
|
64
|
+
IF ROW(NEW.site_id,NEW.site_access_root_id,NEW.site_viewer_principal,NEW.site_owner_subject_id)
|
|
65
|
+
IS DISTINCT FROM ROW(OLD.site_id,OLD.site_access_root_id,OLD.site_viewer_principal,OLD.site_owner_subject_id)
|
|
66
|
+
THEN RAISE EXCEPTION 'Site session audience is immutable' USING ERRCODE='42501'; END IF;
|
|
67
|
+
RETURN NEW;
|
|
68
|
+
END IF;
|
|
69
|
+
IF NEW.site_id IS NOT NULL OR NEW.site_access_root_id IS NOT NULL
|
|
70
|
+
OR NEW.site_viewer_principal IS NOT NULL OR NEW.site_owner_subject_id IS NOT NULL
|
|
71
|
+
THEN RAISE EXCEPTION 'Site audience is derived by the server' USING ERRCODE='42501'; END IF;
|
|
72
|
+
IF NEW.parent_session_id IS NOT NULL THEN
|
|
73
|
+
SELECT * INTO parent FROM sessions WHERE id=NEW.parent_session_id
|
|
74
|
+
AND account_id=NEW.account_id AND workspace_id=NEW.workspace_id;
|
|
75
|
+
IF parent.site_access_root_id IS NOT NULL THEN
|
|
76
|
+
NEW.site_id:=parent.site_id; NEW.site_access_root_id:=parent.site_access_root_id;
|
|
77
|
+
NEW.site_viewer_principal:=parent.site_viewer_principal;
|
|
78
|
+
NEW.site_owner_subject_id:=parent.site_owner_subject_id;
|
|
79
|
+
RETURN NEW;
|
|
80
|
+
END IF;
|
|
81
|
+
END IF;
|
|
82
|
+
SELECT * INTO conversation FROM site_runtime_conversations
|
|
83
|
+
WHERE session_id=NEW.id AND account_id=NEW.account_id AND workspace_id=NEW.workspace_id;
|
|
84
|
+
IF FOUND THEN
|
|
85
|
+
NEW.site_id:=conversation.site_id; NEW.site_access_root_id:=NEW.id;
|
|
86
|
+
NEW.site_viewer_principal:=conversation.viewer_principal;
|
|
87
|
+
IF conversation.viewer_principal LIKE 'user:%' THEN
|
|
88
|
+
NEW.site_owner_subject_id:=substr(conversation.viewer_principal,6);
|
|
89
|
+
END IF;
|
|
90
|
+
ELSE
|
|
91
|
+
origin:=NEW.metadata->'_opengeniSiteOrigin';
|
|
92
|
+
IF jsonb_typeof(origin) IS DISTINCT FROM 'object' THEN RETURN NEW; END IF;
|
|
93
|
+
-- Native ownership must already have been derived by the authority fence.
|
|
94
|
+
-- Old console writers without private creation fail closed after migration.
|
|
95
|
+
IF NEW.owner_subject_id IS NULL OR
|
|
96
|
+
(NEW.parent_session_id IS NULL AND NEW.visibility<>'user_private') THEN
|
|
97
|
+
RAISE EXCEPTION 'Private Site sessions require verified viewer ownership' USING ERRCODE='42501';
|
|
98
|
+
END IF;
|
|
99
|
+
NEW.site_id:=nullif(origin->>'siteId','')::uuid;
|
|
100
|
+
NEW.site_access_root_id:=NEW.id; NEW.site_owner_subject_id:=NEW.owner_subject_id;
|
|
101
|
+
END IF;
|
|
102
|
+
IF current_setting('opengeni.site_session_audience_v1',true) IS DISTINCT FROM '1' THEN
|
|
103
|
+
RAISE EXCEPTION 'Site session writer upgrade required' USING ERRCODE='55000';
|
|
104
|
+
END IF;
|
|
105
|
+
-- Guest/service execution is not an exception to the organization's private
|
|
106
|
+
-- conversation policy. Native member creation already takes this lock first.
|
|
107
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('organization-membership:'||NEW.account_id,0));
|
|
108
|
+
enabled:=organization_private_sessions_enabled(NEW.account_id);
|
|
109
|
+
IF NOT enabled AND NOT EXISTS(SELECT 1 FROM organization_memberships m
|
|
110
|
+
WHERE m.account_id=NEW.account_id AND m.status='active'
|
|
111
|
+
AND m.personal_workspace_id=NEW.workspace_id AND m.subject_id=NEW.site_owner_subject_id)
|
|
112
|
+
THEN RAISE EXCEPTION 'Private Site conversations are disabled by organization policy' USING ERRCODE='P0793'; END IF;
|
|
113
|
+
RETURN NEW;
|
|
114
|
+
END
|
|
115
|
+
$body$;
|
|
116
|
+
-- Alphabetical trigger order: ownership is frozen before the Site audience.
|
|
117
|
+
CREATE TRIGGER zz_sessions_site_audience BEFORE INSERT OR UPDATE ON sessions
|
|
118
|
+
FOR EACH ROW EXECUTE FUNCTION bind_session_site_audience();
|
|
119
|
+
|
|
120
|
+
CREATE FUNCTION session_site_actor_visible(p_site uuid,p_access_root uuid,p_root uuid,p_viewer text,p_owner text)
|
|
121
|
+
RETURNS boolean LANGUAGE sql STABLE SET search_path=pg_catalog AS $body$
|
|
122
|
+
SELECT p_access_root IS NULL
|
|
123
|
+
OR nullif(current_setting('opengeni.subject_id',true),'') IS NULL
|
|
124
|
+
OR p_owner=nullif(current_setting('opengeni.subject_id',true),'')
|
|
125
|
+
OR p_owner=nullif(current_setting('opengeni.initiating_human_subject_id',true),'')
|
|
126
|
+
OR (p_root::text=nullif(current_setting('opengeni.agent_root_session_id',true),'')
|
|
127
|
+
AND (p_access_root=p_root OR p_owner=nullif(current_setting('opengeni.initiating_human_subject_id',true),'')))
|
|
128
|
+
OR (current_setting('opengeni.subject_id',true)='site:'||p_site::text
|
|
129
|
+
AND p_site::text=current_setting('opengeni.site_id',true)
|
|
130
|
+
AND p_viewer=current_setting('opengeni.site_viewer_principal',true))
|
|
131
|
+
$body$;
|
|
132
|
+
-- No inventory-owner exception: privileged Insights transcript projections
|
|
133
|
+
-- also see only the viewer's Site sessions. Their existing masked raw amount
|
|
134
|
+
-- projections retain workspace totals without exposing private session data.
|
|
135
|
+
CREATE POLICY site_viewer_isolation ON sessions AS RESTRICTIVE FOR ALL
|
|
136
|
+
USING (session_site_actor_visible(site_id,site_access_root_id,root_session_id,site_viewer_principal,site_owner_subject_id))
|
|
137
|
+
WITH CHECK (session_site_actor_visible(site_id,site_access_root_id,root_session_id,site_viewer_principal,site_owner_subject_id));
|
|
138
|
+
CREATE INDEX sessions_site_viewer_idx ON sessions(workspace_id,site_id,site_viewer_principal)
|
|
139
|
+
WHERE site_access_root_id IS NOT NULL;
|
|
140
|
+
|
|
141
|
+
CREATE FUNCTION stamp_model_call_site() RETURNS trigger
|
|
142
|
+
LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$
|
|
143
|
+
BEGIN
|
|
144
|
+
IF TG_OP='INSERT' THEN
|
|
145
|
+
SELECT s.site_id INTO NEW.site_id FROM sessions s
|
|
146
|
+
WHERE s.account_id=NEW.account_id AND s.workspace_id=NEW.workspace_id AND s.id=NEW.session_id;
|
|
147
|
+
ELSIF NEW.site_id IS DISTINCT FROM OLD.site_id THEN
|
|
148
|
+
RAISE EXCEPTION 'Model call Site attribution is immutable' USING ERRCODE='42501';
|
|
149
|
+
END IF;
|
|
150
|
+
RETURN NEW;
|
|
151
|
+
END
|
|
152
|
+
$body$;
|
|
153
|
+
CREATE TRIGGER model_call_site_attribution BEFORE INSERT OR UPDATE ON model_call_facts
|
|
154
|
+
FOR EACH ROW EXECUTE FUNCTION stamp_model_call_site();
|
|
155
|
+
CREATE INDEX model_call_facts_site_occurred_idx ON model_call_facts(workspace_id,site_id,occurred_at)
|
|
156
|
+
WHERE site_id IS NOT NULL;
|
|
157
|
+
|
|
158
|
+
REVOKE ALL ON FUNCTION bind_session_site_audience() FROM PUBLIC;
|
|
159
|
+
REVOKE ALL ON FUNCTION stamp_model_call_site() FROM PUBLIC;
|
|
160
|
+
DO $pin$
|
|
161
|
+
DECLARE data_schema text:=current_schema();
|
|
162
|
+
BEGIN
|
|
163
|
+
EXECUTE format('ALTER FUNCTION %I.bind_session_site_audience() SET search_path=pg_catalog,%I,pg_temp',data_schema,data_schema);
|
|
164
|
+
EXECUTE format('ALTER FUNCTION %I.stamp_model_call_site() SET search_path=pg_catalog,%I,pg_temp',data_schema,data_schema);
|
|
165
|
+
END
|
|
166
|
+
$pin$;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- maintenance-justification: backfill exact model debit attribution during the private Site audience cutover before old writers or analytical readers can resume.
|
|
3
|
+
-- Retain exact Site attribution on actual model debits, independently of fact
|
|
4
|
+
-- delivery and session retention. No repricing or changes to charged amounts.
|
|
5
|
+
SET LOCAL lock_timeout='5s';
|
|
6
|
+
SET LOCAL statement_timeout='5min';
|
|
7
|
+
ALTER TABLE credit_ledger_entries ADD COLUMN site_id uuid;
|
|
8
|
+
|
|
9
|
+
ALTER TABLE sessions NO FORCE ROW LEVEL SECURITY;
|
|
10
|
+
ALTER TABLE credit_ledger_entries NO FORCE ROW LEVEL SECURITY;
|
|
11
|
+
DO $backfill$
|
|
12
|
+
DECLARE scope record;
|
|
13
|
+
BEGIN
|
|
14
|
+
FOR scope IN SELECT DISTINCT account_id,workspace_id FROM sessions WHERE site_id IS NOT NULL LOOP
|
|
15
|
+
PERFORM set_config('opengeni.account_id',scope.account_id::text,true);
|
|
16
|
+
PERFORM set_config('opengeni.workspace_id',scope.workspace_id::text,true);
|
|
17
|
+
UPDATE credit_ledger_entries c SET site_id=s.site_id FROM sessions s
|
|
18
|
+
WHERE c.account_id=scope.account_id AND c.workspace_id=scope.workspace_id
|
|
19
|
+
AND c.type='model_usage_debit' AND c.source_type='model_response'
|
|
20
|
+
AND s.account_id=c.account_id AND s.workspace_id=c.workspace_id
|
|
21
|
+
AND s.id::text=c.metadata->>'sessionId' AND s.site_id IS NOT NULL;
|
|
22
|
+
END LOOP;
|
|
23
|
+
END
|
|
24
|
+
$backfill$;
|
|
25
|
+
ALTER TABLE sessions FORCE ROW LEVEL SECURITY;
|
|
26
|
+
ALTER TABLE credit_ledger_entries FORCE ROW LEVEL SECURITY;
|
|
27
|
+
|
|
28
|
+
CREATE FUNCTION stamp_model_debit_site() RETURNS trigger
|
|
29
|
+
LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$
|
|
30
|
+
BEGIN
|
|
31
|
+
IF TG_OP='INSERT' THEN
|
|
32
|
+
NEW.site_id:=NULL;
|
|
33
|
+
IF NEW.type='model_usage_debit' AND NEW.source_type='model_response' THEN
|
|
34
|
+
SELECT s.site_id INTO NEW.site_id FROM sessions s
|
|
35
|
+
WHERE s.account_id=NEW.account_id AND s.workspace_id=NEW.workspace_id
|
|
36
|
+
AND s.id::text=NEW.metadata->>'sessionId';
|
|
37
|
+
END IF;
|
|
38
|
+
ELSIF NEW.site_id IS DISTINCT FROM OLD.site_id THEN
|
|
39
|
+
RAISE EXCEPTION 'Model debit Site attribution is immutable' USING ERRCODE='42501';
|
|
40
|
+
END IF;
|
|
41
|
+
RETURN NEW;
|
|
42
|
+
END
|
|
43
|
+
$body$;
|
|
44
|
+
CREATE TRIGGER model_debit_site_attribution BEFORE INSERT OR UPDATE ON credit_ledger_entries
|
|
45
|
+
FOR EACH ROW EXECUTE FUNCTION stamp_model_debit_site();
|
|
46
|
+
REVOKE ALL ON FUNCTION stamp_model_debit_site() FROM PUBLIC;
|
|
47
|
+
CREATE INDEX credit_ledger_site_occurred_idx ON credit_ledger_entries(workspace_id,site_id,occurred_at)
|
|
48
|
+
WHERE site_id IS NOT NULL AND type='model_usage_debit' AND amount_micros<0;
|
|
49
|
+
|
|
50
|
+
DO $reader$
|
|
51
|
+
BEGIN
|
|
52
|
+
EXECUTE format($ddl$
|
|
53
|
+
CREATE FUNCTION opengeni_private.insights_site_usage(a uuid,w uuid,lo timestamptz,hi timestamptz,viewer text)
|
|
54
|
+
RETURNS TABLE(payload jsonb) LANGUAGE plpgsql VOLATILE SECURITY DEFINER
|
|
55
|
+
SET search_path=pg_catalog,%1$I,opengeni_private,pg_temp SET jit=off AS $fn$
|
|
56
|
+
DECLARE actor text:=nullif(current_setting('opengeni.subject_id',true),'');
|
|
57
|
+
human text:=nullif(current_setting('opengeni.initiating_human_subject_id',true),'');
|
|
58
|
+
BEGIN
|
|
59
|
+
IF a IS NULL OR w IS NULL OR a IS DISTINCT FROM nullif(current_setting('opengeni.account_id',true),'')::uuid
|
|
60
|
+
OR w IS DISTINCT FROM nullif(current_setting('opengeni.workspace_id',true),'')::uuid THEN
|
|
61
|
+
RAISE EXCEPTION 'Site Insights requires exact tenant scope' USING ERRCODE='42501'; END IF;
|
|
62
|
+
IF lo IS NULL OR hi IS NULL OR hi<lo OR hi-lo>interval '370 days' THEN
|
|
63
|
+
RAISE EXCEPTION 'Invalid Site Insights window' USING ERRCODE='22023'; END IF;
|
|
64
|
+
IF viewer IS NOT NULL AND viewer IS DISTINCT FROM actor AND viewer IS DISTINCT FROM human THEN
|
|
65
|
+
RAISE EXCEPTION 'Site Insights viewer differs from the verified actor' USING ERRCODE='42501'; END IF;
|
|
66
|
+
-- The existing owner+witness fact capability admits costs, not transcripts.
|
|
67
|
+
INSERT INTO opengeni_private.insights_fact_read_runtime_capabilities
|
|
68
|
+
(backend_pid,transaction_id,capability_kind,account_id,workspace_id,subject_id,initiating_human_subject_id)
|
|
69
|
+
VALUES(pg_backend_pid(),pg_current_xact_id(),'model_call_facts',a,w,actor,human);
|
|
70
|
+
BEGIN
|
|
71
|
+
RETURN QUERY WITH amounts AS MATERIALIZED (
|
|
72
|
+
SELECT f.site_id,1::bigint calls,CASE WHEN f.estimated_provider_cost_micros IS NULL THEN 0 ELSE 1 END::bigint priced,
|
|
73
|
+
0::bigint charged,coalesce(f.estimated_provider_cost_micros,0) equivalent,
|
|
74
|
+
CASE WHEN opengeni_private.insights_usage_payer(f.provider,f.billing_path)='subscription'
|
|
75
|
+
THEN coalesce(f.estimated_provider_cost_micros,0) ELSE 0 END subscription,
|
|
76
|
+
CASE WHEN opengeni_private.insights_usage_payer(f.provider,f.billing_path)='own_key'
|
|
77
|
+
THEN coalesce(f.estimated_provider_cost_micros,0) ELSE 0 END own_key
|
|
78
|
+
FROM %1$I.model_call_facts f WHERE f.account_id=a AND f.workspace_id=w
|
|
79
|
+
AND f.site_id IS NOT NULL AND f.occurred_at>=lo AND f.occurred_at<hi
|
|
80
|
+
UNION ALL
|
|
81
|
+
SELECT c.site_id,0,0,-c.amount_micros,0,0,0 FROM %1$I.credit_ledger_entries c
|
|
82
|
+
WHERE c.account_id=a AND c.workspace_id=w AND c.site_id IS NOT NULL
|
|
83
|
+
AND c.type='model_usage_debit' AND c.source_type='model_response' AND c.amount_micros<0
|
|
84
|
+
AND c.occurred_at>=lo AND c.occurred_at<hi
|
|
85
|
+
), visible AS MATERIALIZED (
|
|
86
|
+
-- Match workspace-artifacts.visibleTo: its RLS is tenant-only.
|
|
87
|
+
-- Private/unavailable Sites share one bucket; never leak their ids.
|
|
88
|
+
SELECT site.id,site.title FROM %1$I.workspace_artifacts site
|
|
89
|
+
WHERE site.account_id=a AND site.workspace_id=w
|
|
90
|
+
AND (site.visibility='workspace_shared' OR site.owner_subject_id=viewer)
|
|
91
|
+
), grouped AS (
|
|
92
|
+
SELECT v.id,coalesce(v.title,'Private or unavailable Sites') title,
|
|
93
|
+
CASE WHEN v.id IS NULL THEN 'restricted' ELSE 'site' END kind,
|
|
94
|
+
sum(calls)::bigint calls,sum(priced)::bigint priced,sum(charged)::bigint charged,
|
|
95
|
+
sum(equivalent)::bigint equivalent,sum(subscription)::bigint subscription,sum(own_key)::bigint own_key
|
|
96
|
+
FROM amounts x LEFT JOIN visible v ON v.id=x.site_id GROUP BY v.id,v.title
|
|
97
|
+
), ranked AS (
|
|
98
|
+
SELECT *,row_number() OVER(ORDER BY charged DESC,equivalent DESC,id NULLS LAST) position FROM grouped
|
|
99
|
+
), bounded AS (
|
|
100
|
+
SELECT id,title,kind,calls,priced,charged,equivalent,subscription,own_key FROM ranked WHERE position<=200
|
|
101
|
+
UNION ALL SELECT NULL,'Other Sites','other',sum(calls)::bigint,sum(priced)::bigint,sum(charged)::bigint,
|
|
102
|
+
sum(equivalent)::bigint,sum(subscription)::bigint,sum(own_key)::bigint FROM ranked WHERE position>200 HAVING count(*)>0
|
|
103
|
+
) SELECT jsonb_build_object('siteId',id,'title',title,'kind',kind,'calls',calls,'pricedCalls',priced,
|
|
104
|
+
'chargedMicros',charged,'apiEquivalentMicros',equivalent,'subscriptionApiEquivalentMicros',subscription,
|
|
105
|
+
'ownKeyApiEquivalentMicros',own_key) FROM bounded ORDER BY charged DESC,equivalent DESC,id NULLS LAST;
|
|
106
|
+
DELETE FROM opengeni_private.insights_fact_read_runtime_capabilities WHERE backend_pid=pg_backend_pid()
|
|
107
|
+
AND transaction_id=pg_current_xact_id_if_assigned() AND capability_kind='model_call_facts';
|
|
108
|
+
EXCEPTION WHEN OTHERS THEN
|
|
109
|
+
DELETE FROM opengeni_private.insights_fact_read_runtime_capabilities WHERE backend_pid=pg_backend_pid()
|
|
110
|
+
AND transaction_id=pg_current_xact_id_if_assigned() AND capability_kind='model_call_facts';
|
|
111
|
+
RAISE;
|
|
112
|
+
END;
|
|
113
|
+
END
|
|
114
|
+
$fn$;
|
|
115
|
+
$ddl$,current_schema());
|
|
116
|
+
EXECUTE format('ALTER FUNCTION %I.stamp_model_debit_site() SET search_path=pg_catalog,%I,pg_temp',current_schema(),current_schema());
|
|
117
|
+
END
|
|
118
|
+
$reader$;
|
|
119
|
+
|
|
120
|
+
REVOKE ALL ON FUNCTION opengeni_private.insights_site_usage(uuid,uuid,timestamptz,timestamptz,text) FROM PUBLIC;
|
|
121
|
+
DO $acl$
|
|
122
|
+
DECLARE role_name text;
|
|
123
|
+
BEGIN
|
|
124
|
+
FOR role_name IN SELECT r.rolname FROM jsonb_array_elements_text(current_setting('opengeni.migration_application_roles')::jsonb) configured(value)
|
|
125
|
+
JOIN pg_roles r ON r.rolname=configured.value
|
|
126
|
+
WHERE has_table_privilege(r.rolname,format('%I.model_call_facts',current_schema()),'SELECT') LOOP
|
|
127
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION opengeni_private.insights_site_usage(uuid,uuid,timestamptz,timestamptz,text) TO %I',role_name);
|
|
128
|
+
END LOOP;
|
|
129
|
+
END
|
|
130
|
+
$acl$;
|
|
131
|
+
|
|
132
|
+
-- Analytical definers also run in installations whose migration owner bypasses
|
|
133
|
+
-- RLS. Keep the independent Site audience explicit, without changing native
|
|
134
|
+
-- private-session predicates or granting inventory access to Site transcripts.
|
|
135
|
+
-- This invoker view adds only a filter; it never confers underlying table access.
|
|
136
|
+
CREATE VIEW insights_audience_sessions WITH (security_invoker=true,security_barrier=true) AS
|
|
137
|
+
SELECT * FROM sessions WHERE session_site_actor_visible(
|
|
138
|
+
site_id,site_access_root_id,root_session_id,site_viewer_principal,site_owner_subject_id);
|
|
139
|
+
REVOKE ALL ON insights_audience_sessions FROM PUBLIC;
|
|
140
|
+
|
|
141
|
+
-- Existing migrations use source-anchored function replacement to preserve
|
|
142
|
+
-- signatures, ACLs and accounting protocols. Change only the sessions relation
|
|
143
|
+
-- in the complete, enumerated family of analytical readers; refuse source drift.
|
|
144
|
+
DO $analytical_audience$
|
|
145
|
+
DECLARE routine record; original text; updated text; changed int:=0;
|
|
146
|
+
BEGIN
|
|
147
|
+
FOR routine IN SELECT p.oid,p.proname FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace
|
|
148
|
+
WHERE n.nspname='opengeni_private' AND p.proname IN (
|
|
149
|
+
'visible_workspace_insights_model_call_facts','visible_workspace_insights_usage_events',
|
|
150
|
+
'visible_workspace_insights_usage_projection','complete_workspace_insights_usage_projection',
|
|
151
|
+
'visible_workspace_insights_model_fact_rows','workspace_insights_amount_fact_rows',
|
|
152
|
+
'insights_scoped_usage_rows','insights_scoped_calls_rows') LOOP
|
|
153
|
+
original:=pg_get_functiondef(routine.oid);
|
|
154
|
+
updated:=regexp_replace(original,'\.sessions([[:space:]])','.insights_audience_sessions\1','g');
|
|
155
|
+
IF updated=original THEN
|
|
156
|
+
-- The short overloads delegate to their scoped sibling without a join.
|
|
157
|
+
IF routine.proname NOT IN('visible_workspace_insights_model_call_facts','visible_workspace_insights_usage_events') THEN
|
|
158
|
+
RAISE EXCEPTION 'Insights audience source contract changed: %',routine.proname; END IF;
|
|
159
|
+
CONTINUE;
|
|
160
|
+
END IF;
|
|
161
|
+
IF routine.proname='insights_scoped_usage_rows' THEN
|
|
162
|
+
-- A hidden Site is restricted, not a deleted conversation. This metadata
|
|
163
|
+
-- existence check cannot project identifiers and keeps totals complete.
|
|
164
|
+
updated:=replace(updated,'WHEN s.id IS NULL OR r.id IS NULL THEN ''deleted''',format(
|
|
165
|
+
'WHEN (s.id IS NULL OR r.id IS NULL) AND EXISTS(SELECT 1 FROM %I.model_call_facts sf
|
|
166
|
+
WHERE sf.account_id=a AND sf.workspace_id=w.id AND sf.session_id=f.session_id AND sf.site_id IS NOT NULL)
|
|
167
|
+
THEN ''restricted'' WHEN s.id IS NULL OR r.id IS NULL THEN ''deleted''',current_schema()));
|
|
168
|
+
IF position('sf.site_id IS NOT NULL' IN updated)=0 THEN RAISE EXCEPTION 'Insights classification source contract changed'; END IF;
|
|
169
|
+
END IF;
|
|
170
|
+
EXECUTE updated;
|
|
171
|
+
changed:=changed+1;
|
|
172
|
+
END LOOP;
|
|
173
|
+
IF changed<>9 THEN RAISE EXCEPTION 'Expected nine scoped analytical readers, updated %',changed; END IF;
|
|
174
|
+
END
|
|
175
|
+
$analytical_audience$;
|