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