@opengeni/db 1.3.0 → 1.4.2
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-7QWWTVYH.js → chunk-5ROHLEN6.js} +29 -4
- package/dist/chunk-5ROHLEN6.js.map +1 -0
- package/dist/{chunk-AQMH2ZEH.js → chunk-A4J7EECR.js} +2 -2
- package/dist/{chunk-VQX54TK3.js → chunk-B5C5SBSI.js} +3 -3
- package/dist/{chunk-OI6YZXTU.js → chunk-DKFEUGM4.js} +2 -2
- package/dist/{chunk-HYBQABBJ.js → chunk-HUKBIZGH.js} +2 -2
- package/dist/{chunk-LRSIODCQ.js → chunk-IT3EYY2C.js} +6 -4
- package/dist/{chunk-LRSIODCQ.js.map → chunk-IT3EYY2C.js.map} +1 -1
- package/dist/{chunk-DSNEIW5E.js → chunk-JL6TBB76.js} +2 -1
- package/dist/chunk-JL6TBB76.js.map +1 -0
- package/dist/{chunk-OEFNTGR3.js → chunk-JWBEE4CL.js} +4 -4
- package/dist/{chunk-BRVBLQAL.js → chunk-K5MTNKJU.js} +4 -4
- package/dist/{chunk-6UKNJ43T.js → chunk-NZ2SP55O.js} +2 -2
- package/dist/{chunk-3XHC5KC5.js → chunk-OKCBARX6.js} +2 -2
- package/dist/{chunk-PNWWNEDM.js → chunk-OV5AUIBA.js} +2 -2
- package/dist/{chunk-BID3CNN3.js → chunk-TODVDGMO.js} +36 -18
- package/dist/{chunk-BID3CNN3.js.map → chunk-TODVDGMO.js.map} +1 -1
- package/dist/{chunk-6S5T6FSP.js → chunk-W75DRYG3.js} +5 -5
- package/dist/{chunk-7S3O33NM.js → chunk-Y72DP3JF.js} +2 -2
- package/dist/codemode-approvals.d.ts +7 -1
- package/dist/connect-attempts.js +3 -3
- package/dist/connection-metadata.d.ts +416 -0
- package/dist/credit-balances.d.ts +25 -4
- package/dist/database.d.ts +7 -1
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +51 -8
- package/dist/index.js +10826 -10129
- package/dist/index.js.map +1 -1
- package/dist/legacy-tool-action-reviews.d.ts +75 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.js +2 -2
- package/dist/modal-native-live-origin.d.ts +75 -0
- package/dist/organization-integration-policy.js +3 -3
- package/dist/organization-slack-bots.d.ts +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +21 -21
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/sandbox-interaction-idle.d.ts +22 -0
- 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 +5 -5
- package/dist/session-realtime-billing.d.ts +48 -0
- package/dist/session-realtime.d.ts +8 -2
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/voice-transcription-settlement.d.ts +36 -0
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0623_voice_transcription_attribution.sql +128 -0
- package/drizzle/0624_azure_live_realtime_model.sql +30 -0
- package/drizzle/0625_transcription_billing_refusal_codes.sql +29 -0
- package/drizzle/0632_modal_native_live_origin.sql +239 -0
- package/package.json +7 -7
- package/src/codemode-approvals.ts +52 -22
- package/src/codemode-operations.ts +198 -181
- package/src/connection-metadata.ts +166 -0
- package/src/credit-balances.ts +58 -17
- package/src/database.ts +9 -0
- package/src/index.ts +540 -412
- package/src/insights-unified.ts +2 -1
- package/src/legacy-tool-action-reviews.ts +242 -0
- package/src/modal-native-live-origin.ts +148 -0
- package/src/organization-slack-bots.ts +1 -1
- package/src/provision-roles.ts +12 -0
- package/src/runtime-posture.ts +32 -3
- package/src/sandbox-interaction-idle.ts +91 -0
- package/src/schema.ts +1 -0
- package/src/session-realtime-billing.ts +135 -0
- package/src/session-realtime-ledger.ts +9 -4
- package/src/session-realtime.ts +20 -2
- package/src/tool-action-reviews.ts +129 -18
- package/src/transcription-recordings.ts +3 -1
- package/src/voice-transcription-settlement.ts +94 -0
- package/dist/chunk-7QWWTVYH.js.map +0 -1
- package/dist/chunk-DSNEIW5E.js.map +0 -1
- /package/dist/{chunk-AQMH2ZEH.js.map → chunk-A4J7EECR.js.map} +0 -0
- /package/dist/{chunk-VQX54TK3.js.map → chunk-B5C5SBSI.js.map} +0 -0
- /package/dist/{chunk-OI6YZXTU.js.map → chunk-DKFEUGM4.js.map} +0 -0
- /package/dist/{chunk-HYBQABBJ.js.map → chunk-HUKBIZGH.js.map} +0 -0
- /package/dist/{chunk-OEFNTGR3.js.map → chunk-JWBEE4CL.js.map} +0 -0
- /package/dist/{chunk-BRVBLQAL.js.map → chunk-K5MTNKJU.js.map} +0 -0
- /package/dist/{chunk-6UKNJ43T.js.map → chunk-NZ2SP55O.js.map} +0 -0
- /package/dist/{chunk-3XHC5KC5.js.map → chunk-OKCBARX6.js.map} +0 -0
- /package/dist/{chunk-PNWWNEDM.js.map → chunk-OV5AUIBA.js.map} +0 -0
- /package/dist/{chunk-6S5T6FSP.js.map → chunk-W75DRYG3.js.map} +0 -0
- /package/dist/{chunk-7S3O33NM.js.map → chunk-Y72DP3JF.js.map} +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Retain exact transcription payer receipts for member allowance attribution.
|
|
3
|
+
-- The API freezes the authenticated payer; ledger metadata alone is not proof.
|
|
4
|
+
DO $constraints$
|
|
5
|
+
DECLARE item record;
|
|
6
|
+
BEGIN
|
|
7
|
+
FOR item IN SELECT conname, pg_get_constraintdef(oid) AS definition FROM pg_constraint
|
|
8
|
+
WHERE conrelid='opengeni_private.usage_allowance_attribution_receipts'::regclass AND contype='c'
|
|
9
|
+
LOOP
|
|
10
|
+
IF (item.definition LIKE '%source_kind%' AND item.definition LIKE '%knowledge_query%') THEN
|
|
11
|
+
EXECUTE format('ALTER TABLE opengeni_private.usage_allowance_attribution_receipts DROP CONSTRAINT %I',item.conname);
|
|
12
|
+
END IF;
|
|
13
|
+
END LOOP;
|
|
14
|
+
END $constraints$;
|
|
15
|
+
ALTER TABLE opengeni_private.usage_allowance_attribution_receipts
|
|
16
|
+
ADD CONSTRAINT usage_attribution_source_kind CHECK (source_kind IN ('turn','schedule','knowledge_query','voice_transcription')),
|
|
17
|
+
ADD CONSTRAINT usage_attribution_source_shape CHECK (coalesce(
|
|
18
|
+
(source_kind='knowledge_query' AND session_id IS NULL AND quantity IS NOT NULL
|
|
19
|
+
AND idempotency_key='knowledge.query_cost:'||source_id)
|
|
20
|
+
OR (source_kind='voice_transcription' AND session_id IS NULL AND quantity IS NOT NULL
|
|
21
|
+
AND idempotency_key='voice.transcription_cost:'||source_id)
|
|
22
|
+
OR (source_kind IN ('turn','schedule') AND quantity IS NULL AND idempotency_key IS NULL),false));
|
|
23
|
+
|
|
24
|
+
CREATE OR REPLACE FUNCTION capture_usage_allowance_attribution()
|
|
25
|
+
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
|
|
26
|
+
DECLARE
|
|
27
|
+
v_source_kind text; v_source_id text; v_session_id uuid; v_attribution jsonb;
|
|
28
|
+
v_quantity bigint; v_idempotency_key text; opened integer;
|
|
29
|
+
BEGIN
|
|
30
|
+
IF TG_TABLE_NAME='session_turns' THEN
|
|
31
|
+
IF TG_OP='UPDATE' AND (NEW.id IS DISTINCT FROM OLD.id
|
|
32
|
+
OR NEW.account_id IS DISTINCT FROM OLD.account_id OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
33
|
+
OR NEW.session_id IS DISTINCT FROM OLD.session_id
|
|
34
|
+
OR NEW.initiating_human_subject_id IS DISTINCT FROM OLD.initiating_human_subject_id) THEN
|
|
35
|
+
RAISE EXCEPTION 'Allowance turn attribution is immutable' USING ERRCODE='23514';
|
|
36
|
+
END IF;
|
|
37
|
+
v_source_kind:='turn'; v_source_id:=NEW.id::text; v_session_id:=NEW.session_id;
|
|
38
|
+
v_attribution:=jsonb_build_object('kind','turn','turnId',NEW.id,
|
|
39
|
+
'initiatingHumanSubjectId',NEW.initiating_human_subject_id);
|
|
40
|
+
ELSIF TG_TABLE_NAME='scheduled_task_runs' THEN
|
|
41
|
+
IF TG_OP='UPDATE' AND (NEW.id IS DISTINCT FROM OLD.id
|
|
42
|
+
OR NEW.account_id IS DISTINCT FROM OLD.account_id OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
43
|
+
OR NEW.accepted_execution_snapshot->'causalHumanSubjectId' IS DISTINCT FROM
|
|
44
|
+
OLD.accepted_execution_snapshot->'causalHumanSubjectId') THEN
|
|
45
|
+
RAISE EXCEPTION 'Allowance scheduled attribution is immutable' USING ERRCODE='23514';
|
|
46
|
+
END IF;
|
|
47
|
+
v_source_kind:='schedule'; v_source_id:=NEW.id::text;
|
|
48
|
+
v_attribution:=CASE WHEN NOT coalesce(NEW.accepted_execution_snapshot ? 'causalHumanSubjectId',false)
|
|
49
|
+
THEN '{"kind":"unknown"}'::jsonb
|
|
50
|
+
WHEN NEW.accepted_execution_snapshot->>'causalHumanSubjectId' IS NULL THEN '{"kind":"service"}'::jsonb
|
|
51
|
+
ELSE jsonb_build_object('kind','human',
|
|
52
|
+
'initiatingHumanSubjectId',NEW.accepted_execution_snapshot->>'causalHumanSubjectId') END;
|
|
53
|
+
ELSIF TG_TABLE_NAME='usage_events' THEN
|
|
54
|
+
IF TG_OP='UPDATE' THEN
|
|
55
|
+
IF ((OLD.source_resource_type='knowledge_query' AND OLD.event_type='document.query_embedding_cost')
|
|
56
|
+
OR (OLD.source_resource_type='voice_transcription' AND OLD.event_type='model.cost'))
|
|
57
|
+
AND (NEW.account_id IS DISTINCT FROM OLD.account_id OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
58
|
+
OR NEW.source_resource_type IS DISTINCT FROM OLD.source_resource_type
|
|
59
|
+
OR NEW.source_resource_id IS DISTINCT FROM OLD.source_resource_id
|
|
60
|
+
OR NEW.event_type IS DISTINCT FROM OLD.event_type OR NEW.quantity IS DISTINCT FROM OLD.quantity
|
|
61
|
+
OR NEW.idempotency_key IS DISTINCT FROM OLD.idempotency_key
|
|
62
|
+
OR NEW.initiator_context->'creditDebitAttribution' IS DISTINCT FROM
|
|
63
|
+
OLD.initiator_context->'creditDebitAttribution') THEN
|
|
64
|
+
RAISE EXCEPTION 'Allowance query attribution is immutable' USING ERRCODE='23514';
|
|
65
|
+
END IF;
|
|
66
|
+
RETURN NEW;
|
|
67
|
+
END IF;
|
|
68
|
+
IF NEW.workspace_id IS NULL OR NEW.source_resource_id IS NULL THEN RETURN NEW; END IF;
|
|
69
|
+
IF NEW.source_resource_type='knowledge_query' AND NEW.event_type='document.query_embedding_cost'
|
|
70
|
+
AND NEW.idempotency_key='knowledge.query_cost:'||NEW.source_resource_id THEN
|
|
71
|
+
v_source_kind:='knowledge_query';
|
|
72
|
+
ELSIF NEW.source_resource_type='voice_transcription' AND NEW.event_type='model.cost'
|
|
73
|
+
AND NEW.idempotency_key='voice.transcription_cost:'||NEW.source_resource_id THEN
|
|
74
|
+
v_source_kind:='voice_transcription';
|
|
75
|
+
ELSE RETURN NEW;
|
|
76
|
+
END IF;
|
|
77
|
+
v_source_id:=NEW.source_resource_id;
|
|
78
|
+
v_quantity:=NEW.quantity; v_idempotency_key:=NEW.idempotency_key;
|
|
79
|
+
-- Copy only attribution fields, never the initiator envelope/query/content.
|
|
80
|
+
v_attribution:=NEW.initiator_context->'creditDebitAttribution';
|
|
81
|
+
v_attribution:=CASE v_attribution->>'kind'
|
|
82
|
+
WHEN 'human' THEN jsonb_build_object('kind','human',
|
|
83
|
+
'initiatingHumanSubjectId',v_attribution->'initiatingHumanSubjectId')
|
|
84
|
+
WHEN 'turn' THEN jsonb_build_object('kind','turn','turnId',v_attribution->'turnId',
|
|
85
|
+
'initiatingHumanSubjectId',v_attribution->'initiatingHumanSubjectId')
|
|
86
|
+
WHEN 'service' THEN '{"kind":"service"}'::jsonb
|
|
87
|
+
ELSE '{"kind":"unknown"}'::jsonb END;
|
|
88
|
+
ELSE
|
|
89
|
+
RAISE EXCEPTION 'Unknown allowance attribution source' USING ERRCODE='55000';
|
|
90
|
+
END IF;
|
|
91
|
+
IF TG_OP='UPDATE' THEN RETURN NEW; END IF;
|
|
92
|
+
INSERT INTO opengeni_private.usage_allowance_capabilities VALUES
|
|
93
|
+
(pg_backend_pid(),pg_current_xact_id(),TG_TABLE_SCHEMA,NEW.account_id,NEW.workspace_id)
|
|
94
|
+
ON CONFLICT DO NOTHING;
|
|
95
|
+
GET DIAGNOSTICS opened=ROW_COUNT;
|
|
96
|
+
INSERT INTO opengeni_private.usage_allowance_attribution_receipts
|
|
97
|
+
(account_id,workspace_id,source_kind,source_id,session_id,attribution,quantity,idempotency_key)
|
|
98
|
+
VALUES(NEW.account_id,NEW.workspace_id,v_source_kind,v_source_id,v_session_id,v_attribution,v_quantity,v_idempotency_key);
|
|
99
|
+
IF opened=1 THEN
|
|
100
|
+
DELETE FROM opengeni_private.usage_allowance_capabilities
|
|
101
|
+
WHERE backend_pid=pg_backend_pid() AND transaction_id=pg_current_xact_id_if_assigned()
|
|
102
|
+
AND data_schema=TG_TABLE_SCHEMA AND account_id=NEW.account_id AND workspace_id=NEW.workspace_id;
|
|
103
|
+
END IF;
|
|
104
|
+
RETURN NEW;
|
|
105
|
+
END $$;
|
|
106
|
+
|
|
107
|
+
DO $debit$
|
|
108
|
+
DECLARE definition text:=pg_get_functiondef('count_workspace_allowance_debit()'::regprocedure);
|
|
109
|
+
anchor text:=$anchor$IF NEW.source_type='knowledge_query' THEN$anchor$;
|
|
110
|
+
replacement text:=$replacement$IF NEW.source_type='voice_transcription' THEN
|
|
111
|
+
SELECT receipt.attribution INTO attribution FROM opengeni_private.usage_allowance_attribution_receipts receipt
|
|
112
|
+
WHERE receipt.account_id=NEW.account_id AND receipt.workspace_id=NEW.workspace_id
|
|
113
|
+
AND receipt.source_kind='voice_transcription' AND receipt.source_id=NEW.source_id
|
|
114
|
+
AND receipt.idempotency_key='voice.transcription_cost:'||NEW.source_id
|
|
115
|
+
AND receipt.quantity=amount AND NEW.type='voice_transcription_debit';
|
|
116
|
+
attribution:=coalesce(attribution,'{"kind":"unknown"}'::jsonb);
|
|
117
|
+
ELSIF NEW.source_type='knowledge_query' THEN$replacement$;
|
|
118
|
+
BEGIN
|
|
119
|
+
IF strpos(definition,anchor)=0 OR strpos(substr(definition,strpos(definition,anchor)+length(anchor)),anchor)>0 THEN
|
|
120
|
+
RAISE EXCEPTION 'Unexpected allowance debit source branch' USING ERRCODE='55000';
|
|
121
|
+
END IF;
|
|
122
|
+
EXECUTE replace(definition,anchor,replacement);
|
|
123
|
+
END $debit$;
|
|
124
|
+
|
|
125
|
+
DO $harden$
|
|
126
|
+
BEGIN
|
|
127
|
+
EXECUTE format('ALTER FUNCTION %I.capture_usage_allowance_attribution() SET search_path=pg_catalog,%I,pg_temp', current_schema(), current_schema());
|
|
128
|
+
END $harden$;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Admit configured Azure GPT Live WebRTC sessions.
|
|
3
|
+
|
|
4
|
+
SET lock_timeout = '5s';
|
|
5
|
+
SET statement_timeout = '10min';
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "session_realtime_modes"
|
|
8
|
+
DROP CONSTRAINT "session_realtime_modes_model_check";
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "session_realtime_modes"
|
|
11
|
+
ADD CONSTRAINT "session_realtime_modes_model_check"
|
|
12
|
+
CHECK (
|
|
13
|
+
"model" IN (
|
|
14
|
+
'opengeni-azure/gpt-live-1',
|
|
15
|
+
'gpt-live-1-boulder-alpha',
|
|
16
|
+
'supergrok/grok-voice-think-fast-2.0',
|
|
17
|
+
'opengeni-gateway/openai/gpt-realtime-2.1',
|
|
18
|
+
'opengeni-gateway/openai/gpt-realtime-mini',
|
|
19
|
+
'opengeni-gateway/xai/grok-voice-think-fast-2.0',
|
|
20
|
+
'workspace-gateway/openai/gpt-realtime-2.1',
|
|
21
|
+
'workspace-gateway/openai/gpt-realtime-mini',
|
|
22
|
+
'workspace-gateway/xai/grok-voice-think-fast-2.0'
|
|
23
|
+
)
|
|
24
|
+
) NOT VALID;
|
|
25
|
+
|
|
26
|
+
ALTER TABLE "session_realtime_modes"
|
|
27
|
+
VALIDATE CONSTRAINT "session_realtime_modes_model_check";
|
|
28
|
+
|
|
29
|
+
RESET statement_timeout;
|
|
30
|
+
RESET lock_timeout;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Persist the exact credit/allowance refusal on a resumable recording and its
|
|
3
|
+
-- segment, so a reloaded recording still explains why it stopped. Widening an
|
|
4
|
+
-- allowed-value CHECK only; no row is rewritten.
|
|
5
|
+
|
|
6
|
+
SET lock_timeout = '5s';
|
|
7
|
+
SET statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
ALTER TABLE "transcription_recordings"
|
|
10
|
+
DROP CONSTRAINT "transcription_recordings_error_code_check";
|
|
11
|
+
|
|
12
|
+
ALTER TABLE "transcription_recordings"
|
|
13
|
+
ADD CONSTRAINT "transcription_recordings_error_code_check"
|
|
14
|
+
CHECK ("error_code" IS NULL OR "error_code" IN (
|
|
15
|
+
'permission_denied', 'not_supported', 'network', 'provider', 'policy_blocked',
|
|
16
|
+
'timeout', 'cancelled', 'unavailable', 'too_large', 'invalid_audio', 'unknown',
|
|
17
|
+
'insufficient_credits', 'allowance_exhausted', 'monthly_model_cost_limit'
|
|
18
|
+
));
|
|
19
|
+
|
|
20
|
+
ALTER TABLE "transcription_recording_segments"
|
|
21
|
+
DROP CONSTRAINT "transcription_recording_segments_error_code_check";
|
|
22
|
+
|
|
23
|
+
ALTER TABLE "transcription_recording_segments"
|
|
24
|
+
ADD CONSTRAINT "transcription_recording_segments_error_code_check"
|
|
25
|
+
CHECK ("error_code" IS NULL OR "error_code" IN (
|
|
26
|
+
'permission_denied', 'not_supported', 'network', 'provider', 'policy_blocked',
|
|
27
|
+
'timeout', 'cancelled', 'unavailable', 'too_large', 'invalid_audio', 'unknown',
|
|
28
|
+
'insufficient_credits', 'allowance_exhausted', 'monthly_model_cost_limit'
|
|
29
|
+
));
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Inert exact-origin projection. Trusted-server SQL privilege is NOT host
|
|
3
|
+
-- authentication. No grant, lease, provider operation, wake or custody is minted.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '5min';
|
|
6
|
+
|
|
7
|
+
CREATE TABLE IF NOT EXISTS opengeni_private.modal_native_origin_read_capabilities (
|
|
8
|
+
capability_id uuid PRIMARY KEY,
|
|
9
|
+
data_schema oid NOT NULL,
|
|
10
|
+
backend_pid integer NOT NULL,
|
|
11
|
+
transaction_id xid8 NOT NULL,
|
|
12
|
+
account_id uuid NOT NULL,
|
|
13
|
+
workspace_id uuid NOT NULL,
|
|
14
|
+
session_id uuid NOT NULL,
|
|
15
|
+
turn_id uuid NOT NULL,
|
|
16
|
+
attempt_id uuid NOT NULL,
|
|
17
|
+
execution_generation integer NOT NULL CHECK (execution_generation > 0),
|
|
18
|
+
initiating_human_subject_id text NOT NULL
|
|
19
|
+
);
|
|
20
|
+
ALTER TABLE opengeni_private.modal_native_origin_read_capabilities ENABLE ROW LEVEL SECURITY;
|
|
21
|
+
ALTER TABLE opengeni_private.modal_native_origin_read_capabilities FORCE ROW LEVEL SECURITY;
|
|
22
|
+
REVOKE ALL ON TABLE opengeni_private.modal_native_origin_read_capabilities FROM PUBLIC;
|
|
23
|
+
|
|
24
|
+
DO $install$
|
|
25
|
+
DECLARE
|
|
26
|
+
target_schema text := current_schema();
|
|
27
|
+
target_oid oid := current_schema()::regnamespace;
|
|
28
|
+
owner_name text := current_user;
|
|
29
|
+
role_name text;
|
|
30
|
+
BEGIN
|
|
31
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class WHERE oid='opengeni_private.modal_native_origin_read_capabilities'::regclass
|
|
32
|
+
AND relowner=current_user::regrole) THEN
|
|
33
|
+
RAISE EXCEPTION 'native origin capability owner mismatch' USING ERRCODE='55000';
|
|
34
|
+
END IF;
|
|
35
|
+
-- Remove hostile owner defaults; no runtime or PUBLIC table/column access.
|
|
36
|
+
FOR role_name IN SELECT DISTINCT r.rolname FROM pg_class c
|
|
37
|
+
CROSS JOIN LATERAL aclexplode(coalesce(c.relacl,acldefault('r',c.relowner))) a
|
|
38
|
+
JOIN pg_roles r ON r.oid=a.grantee
|
|
39
|
+
WHERE c.oid='opengeni_private.modal_native_origin_read_capabilities'::regclass
|
|
40
|
+
AND a.grantee<>c.relowner
|
|
41
|
+
LOOP
|
|
42
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.modal_native_origin_read_capabilities FROM %I',role_name);
|
|
43
|
+
END LOOP;
|
|
44
|
+
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid='opengeni_private.modal_native_origin_read_capabilities'::regclass
|
|
45
|
+
AND polname='owner_only') THEN
|
|
46
|
+
EXECUTE format('CREATE POLICY owner_only ON opengeni_private.modal_native_origin_read_capabilities
|
|
47
|
+
USING (current_user=%L) WITH CHECK (current_user=%L)',owner_name,owner_name);
|
|
48
|
+
END IF;
|
|
49
|
+
|
|
50
|
+
EXECUTE format($ddl$
|
|
51
|
+
CREATE FUNCTION %1$I.modal_native_origin_member_read_active(p_account uuid,p_subject text)
|
|
52
|
+
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path=pg_catalog AS $body$
|
|
53
|
+
SELECT EXISTS (SELECT 1 FROM opengeni_private.modal_native_origin_read_capabilities c
|
|
54
|
+
WHERE c.data_schema=%2$s::oid AND c.backend_pid=pg_backend_pid()
|
|
55
|
+
AND c.transaction_id=pg_current_xact_id_if_assigned()
|
|
56
|
+
AND c.account_id=p_account AND c.initiating_human_subject_id=p_subject)
|
|
57
|
+
$body$;
|
|
58
|
+
REVOKE ALL ON FUNCTION %1$I.modal_native_origin_member_read_active(uuid,text) FROM PUBLIC;
|
|
59
|
+
CREATE POLICY modal_native_origin_member_read ON %1$I.organization_memberships FOR SELECT
|
|
60
|
+
USING (current_user=%3$L AND %1$I.modal_native_origin_member_read_active(account_id,subject_id));
|
|
61
|
+
$ddl$,target_schema,target_oid,owner_name);
|
|
62
|
+
|
|
63
|
+
EXECUTE format($ddl$
|
|
64
|
+
CREATE FUNCTION %1$I.lock_live_native_original_origin_v2(p_scope jsonb)
|
|
65
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER
|
|
66
|
+
SET search_path=pg_catalog,%1$I,pg_temp AS $body$
|
|
67
|
+
DECLARE
|
|
68
|
+
s sessions%%ROWTYPE; t session_turns%%ROWTYPE; a session_turn_attempts%%ROWTYPE;
|
|
69
|
+
m organization_memberships%%ROWTYPE; control workspace_inference_controls%%ROWTYPE;
|
|
70
|
+
ancestor sessions%%ROWTYPE;
|
|
71
|
+
account_value uuid; workspace_value uuid; session_value uuid; turn_value uuid; attempt_value uuid;
|
|
72
|
+
generation_value integer; route_value integer; member_key bigint; tenancy_key bigint;
|
|
73
|
+
fenced_cap uuid; member_cap uuid; parent_value uuid; visited uuid[]; override_value bigint:=0;
|
|
74
|
+
count_value integer:=0; count_json jsonb; basis text; key_name text;
|
|
75
|
+
result jsonb:='{"kind":"fenced"}'::jsonb;
|
|
76
|
+
keys constant text[]:=ARRAY['version','declarationId','planId','creatorId','accountId','workspaceId',
|
|
77
|
+
'sessionId','turnId','attemptId','executionGeneration','triggerEventId','sandboxGroupId',
|
|
78
|
+
'routeKind','routeTargetId','routeEpoch'];
|
|
79
|
+
BEGIN
|
|
80
|
+
IF jsonb_typeof(p_scope) IS DISTINCT FROM 'object' OR NOT p_scope ?& keys
|
|
81
|
+
OR p_scope-keys<>'{}'::jsonb OR p_scope->'version'<>'2'::jsonb
|
|
82
|
+
OR p_scope->'routeKind'<>'"home"'::jsonb OR p_scope->'routeTargetId'<>'null'::jsonb
|
|
83
|
+
THEN RETURN '{"kind":"unsupported","reason":"invalid_scope"}'::jsonb; END IF;
|
|
84
|
+
FOREACH key_name IN ARRAY ARRAY['declarationId','planId','creatorId','accountId','workspaceId',
|
|
85
|
+
'sessionId','turnId','attemptId','triggerEventId','sandboxGroupId'] LOOP
|
|
86
|
+
IF jsonb_typeof(p_scope->key_name)<>'string' OR (p_scope->>key_name) !~
|
|
87
|
+
'^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
88
|
+
THEN RETURN '{"kind":"unsupported","reason":"invalid_scope"}'::jsonb; END IF;
|
|
89
|
+
END LOOP;
|
|
90
|
+
FOREACH key_name IN ARRAY ARRAY['executionGeneration','routeEpoch'] LOOP
|
|
91
|
+
IF jsonb_typeof(p_scope->key_name)<>'number' OR
|
|
92
|
+
(p_scope->>key_name)::numeric<>trunc((p_scope->>key_name)::numeric) OR
|
|
93
|
+
(p_scope->>key_name)::numeric NOT BETWEEN 0 AND 2147483647
|
|
94
|
+
THEN RETURN '{"kind":"unsupported","reason":"invalid_scope"}'::jsonb; END IF;
|
|
95
|
+
END LOOP;
|
|
96
|
+
IF (p_scope->>'executionGeneration')::integer=0 OR p_scope->>'creatorId'=p_scope->>'attemptId'
|
|
97
|
+
OR p_scope->>'creatorId'=p_scope->>'planId'
|
|
98
|
+
THEN RETURN '{"kind":"unsupported","reason":"invalid_scope"}'::jsonb; END IF;
|
|
99
|
+
account_value:=(p_scope->>'accountId')::uuid; workspace_value:=(p_scope->>'workspaceId')::uuid;
|
|
100
|
+
session_value:=(p_scope->>'sessionId')::uuid; turn_value:=(p_scope->>'turnId')::uuid;
|
|
101
|
+
attempt_value:=(p_scope->>'attemptId')::uuid;
|
|
102
|
+
generation_value:=(p_scope->>'executionGeneration')::integer; route_value:=(p_scope->>'routeEpoch')::integer;
|
|
103
|
+
IF account_value IS DISTINCT FROM opengeni_private.current_account_id()
|
|
104
|
+
OR workspace_value IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
105
|
+
OR nullif(current_setting('opengeni.subject_id',true),'') IS NULL
|
|
106
|
+
THEN RETURN result; END IF;
|
|
107
|
+
member_key:=hashtextextended('organization-membership:'||account_value::text,0);
|
|
108
|
+
tenancy_key:=hashtextextended('session-tenancy:'||workspace_value::text,0);
|
|
109
|
+
IF EXISTS (SELECT 1 FROM pg_locks l WHERE l.pid=pg_backend_pid() AND l.locktype='advisory'
|
|
110
|
+
AND l.granted AND l.classid=((tenancy_key>>32)&4294967295)::oid
|
|
111
|
+
AND l.objid=(tenancy_key&4294967295)::oid AND l.objsubid=1)
|
|
112
|
+
AND NOT EXISTS (SELECT 1 FROM pg_locks l WHERE l.pid=pg_backend_pid() AND l.locktype='advisory'
|
|
113
|
+
AND l.granted AND l.mode='ExclusiveLock' AND l.classid=((member_key>>32)&4294967295)::oid
|
|
114
|
+
AND l.objid=(member_key&4294967295)::oid AND l.objsubid=1)
|
|
115
|
+
THEN RAISE EXCEPTION 'native origin requires membership-before-tenancy entry' USING ERRCODE='25001'; END IF;
|
|
116
|
+
PERFORM pg_advisory_xact_lock(member_key);
|
|
117
|
+
PERFORM acquire_session_tenancy_fence(workspace_value);
|
|
118
|
+
fenced_cap:=opengeni_private.open_session_tenancy_fenced_access(%2$s::oid);
|
|
119
|
+
<<projection>>
|
|
120
|
+
BEGIN
|
|
121
|
+
PERFORM pg_advisory_xact_lock_shared(hashtextextended('workspace-control:'||workspace_value::text,0));
|
|
122
|
+
SELECT * INTO control FROM workspace_inference_controls WHERE workspace_id=workspace_value
|
|
123
|
+
AND account_id=account_value FOR SHARE;
|
|
124
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
125
|
+
PERFORM 1 FROM workspaces WHERE id=workspace_value AND account_id=account_value FOR KEY SHARE;
|
|
126
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
127
|
+
SELECT * INTO s FROM sessions WHERE id=session_value AND workspace_id=workspace_value
|
|
128
|
+
AND account_id=account_value FOR NO KEY UPDATE;
|
|
129
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
130
|
+
PERFORM 1 FROM session_event_cursors WHERE workspace_id=workspace_value AND session_id=session_value FOR UPDATE;
|
|
131
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
132
|
+
SELECT * INTO t FROM session_turns WHERE id=turn_value AND workspace_id=workspace_value
|
|
133
|
+
AND account_id=account_value AND session_id=session_value FOR UPDATE;
|
|
134
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
135
|
+
SELECT * INTO a FROM session_turn_attempts WHERE id=attempt_value AND workspace_id=workspace_value
|
|
136
|
+
AND account_id=account_value AND session_id=session_value AND turn_id=turn_value FOR UPDATE;
|
|
137
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
138
|
+
IF s.status<>'running' OR s.admission_block IS NOT NULL OR s.active_turn_id IS DISTINCT FROM turn_value
|
|
139
|
+
OR t.status<>'running' OR t.active_attempt_id IS DISTINCT FROM attempt_value
|
|
140
|
+
OR t.execution_generation<>generation_value OR a.execution_generation<>generation_value
|
|
141
|
+
OR t.trigger_event_id<>(p_scope->>'triggerEventId')::uuid
|
|
142
|
+
OR s.sandbox_group_id<>(p_scope->>'sandboxGroupId')::uuid
|
|
143
|
+
OR s.active_sandbox_id IS NOT NULL OR s.active_epoch<>route_value
|
|
144
|
+
OR a.state NOT IN ('claimed','running') OR a.outcome IS NOT NULL
|
|
145
|
+
OR a.closed_at IS NOT NULL OR a.quiesced_at IS NOT NULL
|
|
146
|
+
OR a.authority_epoch IS NULL OR a.authority_visibility IS NULL
|
|
147
|
+
OR a.authority_epoch NOT BETWEEN s.execution_authority_epoch AND s.authority_epoch
|
|
148
|
+
OR NOT (a.authority_visibility=s.visibility OR (a.authority_visibility='user_private'
|
|
149
|
+
AND s.visibility='workspace_shared' AND a.authority_epoch<s.authority_epoch))
|
|
150
|
+
OR a.authority_owner_organization_membership_id IS DISTINCT FROM s.owner_organization_membership_id
|
|
151
|
+
OR (a.authority_visibility='user_private' AND a.authority_owner_organization_membership_id IS NULL)
|
|
152
|
+
OR EXISTS (SELECT 1 FROM session_attempt_interruptions i WHERE i.workspace_id=workspace_value
|
|
153
|
+
AND i.session_id=session_value AND i.attempt_id=attempt_value AND i.state IN ('pending','delivered','acknowledged'))
|
|
154
|
+
THEN EXIT projection; END IF;
|
|
155
|
+
-- Same override ordering as projectEffectiveControl; control SHARE
|
|
156
|
+
-- serializes all ancestry control writes, including Pause/Resume/Cancel.
|
|
157
|
+
ancestor:=s; visited:=ARRAY[]::uuid[];
|
|
158
|
+
LOOP
|
|
159
|
+
IF ancestor.id=ANY(visited) OR cardinality(visited)>10000 THEN EXIT projection; END IF;
|
|
160
|
+
visited:=array_append(visited,ancestor.id);
|
|
161
|
+
IF ancestor.direct_control_state='paused' AND
|
|
162
|
+
(ancestor.direct_pause_revision IS NULL OR override_value<=ancestor.direct_pause_revision)
|
|
163
|
+
THEN EXIT projection; END IF;
|
|
164
|
+
override_value:=greatest(override_value,coalesce(ancestor.subtree_run_override_revision,0));
|
|
165
|
+
parent_value:=ancestor.parent_session_id;
|
|
166
|
+
EXIT WHEN parent_value IS NULL;
|
|
167
|
+
SELECT * INTO ancestor FROM sessions WHERE id=parent_value AND workspace_id=workspace_value AND account_id=account_value;
|
|
168
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
169
|
+
END LOOP;
|
|
170
|
+
IF control.workspace_state='paused' AND
|
|
171
|
+
(control.workspace_pause_revision IS NULL OR override_value<=control.workspace_pause_revision)
|
|
172
|
+
THEN EXIT projection; END IF;
|
|
173
|
+
IF EXISTS (SELECT 1 FROM session_pending_tool_calls WHERE workspace_id=workspace_value AND session_id=session_value)
|
|
174
|
+
OR EXISTS (SELECT 1 FROM agent_run_states WHERE workspace_id=workspace_value AND session_id=session_value AND turn_id=turn_value)
|
|
175
|
+
-- claimTurnAttempt publishes turn.started before establishTurnSandbox.
|
|
176
|
+
-- It is lifecycle truth, not evidence that a model was dispatched.
|
|
177
|
+
OR EXISTS (SELECT 1 FROM session_events WHERE workspace_id=workspace_value AND session_id=session_value
|
|
178
|
+
AND turn_id=turn_value AND type='agent.model.request')
|
|
179
|
+
THEN result:='{"kind":"unsupported","reason":"not_premodel"}'::jsonb; EXIT projection; END IF;
|
|
180
|
+
IF t.initiating_human_subject_id IS NULL OR t.initiator_subject_id='unattributed-legacy'
|
|
181
|
+
OR t.initiating_human_subject_id NOT LIKE 'user:%%'
|
|
182
|
+
THEN result:='{"kind":"unsupported","reason":"human_unavailable"}'::jsonb; EXIT projection; END IF;
|
|
183
|
+
IF EXISTS (SELECT 1 FROM external_link_turn_authorities WHERE account_id=account_value
|
|
184
|
+
AND workspace_id=workspace_value AND turn_id=turn_value)
|
|
185
|
+
THEN result:='{"kind":"unsupported","reason":"external_authority"}'::jsonb; EXIT projection; END IF;
|
|
186
|
+
IF jsonb_typeof(t.metadata) IS DISTINCT FROM 'object' THEN EXIT projection; END IF;
|
|
187
|
+
IF t.metadata ? 'providerRecoveryCount' THEN
|
|
188
|
+
count_json:=t.metadata->'providerRecoveryCount';
|
|
189
|
+
IF jsonb_typeof(count_json) IS DISTINCT FROM 'number' THEN EXIT projection; END IF;
|
|
190
|
+
IF count_json::numeric<>trunc(count_json::numeric) OR count_json::numeric NOT BETWEEN 0 AND 5
|
|
191
|
+
THEN EXIT projection; END IF;
|
|
192
|
+
count_value:=count_json::integer;
|
|
193
|
+
END IF;
|
|
194
|
+
member_cap:=gen_random_uuid();
|
|
195
|
+
INSERT INTO opengeni_private.modal_native_origin_read_capabilities VALUES
|
|
196
|
+
(member_cap,%2$s::oid,pg_backend_pid(),pg_current_xact_id(),account_value,workspace_value,
|
|
197
|
+
session_value,turn_value,attempt_value,generation_value,t.initiating_human_subject_id);
|
|
198
|
+
SELECT * INTO m FROM organization_memberships WHERE account_id=account_value
|
|
199
|
+
AND subject_id=t.initiating_human_subject_id AND status='active' AND revoked_at IS NULL;
|
|
200
|
+
IF NOT FOUND THEN EXIT projection; END IF;
|
|
201
|
+
IF m.personal_workspace_id=workspace_value THEN basis:='personal_workspace';
|
|
202
|
+
ELSIF EXISTS (SELECT 1 FROM workspace_memberships WHERE account_id=account_value
|
|
203
|
+
AND workspace_id=workspace_value AND subject_id=t.initiating_human_subject_id)
|
|
204
|
+
THEN basis:='workspace_membership'; ELSE EXIT projection; END IF;
|
|
205
|
+
IF s.visibility='user_private' AND (s.owner_subject_id IS DISTINCT FROM t.initiating_human_subject_id
|
|
206
|
+
OR s.owner_organization_membership_id IS DISTINCT FROM m.id) THEN EXIT projection; END IF;
|
|
207
|
+
result:=jsonb_build_object('kind','live','projection',jsonb_build_object(
|
|
208
|
+
'version',1,'scope',p_scope,
|
|
209
|
+
'initiator',jsonb_build_object('kind',t.initiator_kind,'subjectId',t.initiator_subject_id,
|
|
210
|
+
'initiatingHumanSubjectId',t.initiating_human_subject_id),
|
|
211
|
+
'membership',jsonb_build_object('id',m.id,'authorizationRevision',m.authorization_revision::text,'basis',basis),
|
|
212
|
+
'acceptedAuthority',jsonb_build_object('epoch',a.authority_epoch,'visibility',a.authority_visibility,
|
|
213
|
+
'ownerOrganizationMembershipId',a.authority_owner_organization_membership_id),
|
|
214
|
+
'currentAuthority',jsonb_build_object('epoch',s.authority_epoch,'executionEpoch',s.execution_authority_epoch,
|
|
215
|
+
'visibility',s.visibility,'ownerSubjectId',s.owner_subject_id,'ownerOrganizationMembershipId',s.owner_organization_membership_id),
|
|
216
|
+
'control',jsonb_build_object('workspaceRevision',control.revision::text,'sessionVersion',s.control_version::text),
|
|
217
|
+
'execution',jsonb_build_object('workflowId',a.temporal_workflow_id,'workflowRunId',a.temporal_workflow_run_id,
|
|
218
|
+
'activityId',a.temporal_activity_id),
|
|
219
|
+
'providerRecoveryCount',count_value,'checkedAt',clock_timestamp()));
|
|
220
|
+
END projection;
|
|
221
|
+
DELETE FROM opengeni_private.modal_native_origin_read_capabilities WHERE capability_id=member_cap;
|
|
222
|
+
PERFORM opengeni_private.close_session_tenancy_fenced_access(fenced_cap);
|
|
223
|
+
RETURN result;
|
|
224
|
+
EXCEPTION WHEN OTHERS THEN
|
|
225
|
+
-- The function's exception subtransaction rolls back its own capabilities
|
|
226
|
+
-- and locks. Preserve any earlier caller capability; propagate uncertainty.
|
|
227
|
+
RAISE;
|
|
228
|
+
END
|
|
229
|
+
$body$;
|
|
230
|
+
REVOKE ALL ON FUNCTION %1$I.lock_live_native_original_origin_v2(jsonb) FROM PUBLIC;
|
|
231
|
+
$ddl$,target_schema,target_oid);
|
|
232
|
+
FOR role_name IN SELECT jsonb_array_elements_text(coalesce(nullif(
|
|
233
|
+
current_setting('opengeni.migration_application_roles',true),'')::jsonb,'[]'::jsonb)) LOOP
|
|
234
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname=role_name) THEN
|
|
235
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.lock_live_native_original_origin_v2(jsonb) TO %I',target_schema,role_name);
|
|
236
|
+
END IF;
|
|
237
|
+
END LOOP;
|
|
238
|
+
END
|
|
239
|
+
$install$;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/db",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.2",
|
|
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": {
|
|
@@ -115,12 +115,12 @@
|
|
|
115
115
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
116
116
|
},
|
|
117
117
|
"dependencies": {
|
|
118
|
-
"@opengeni/codemode": "1.
|
|
119
|
-
"@opengeni/codex": "1.
|
|
120
|
-
"@opengeni/config": "1.
|
|
121
|
-
"@opengeni/contracts": "1.
|
|
122
|
-
"@opengeni/network": "1.
|
|
123
|
-
"@opengeni/observability": "1.
|
|
118
|
+
"@opengeni/codemode": "1.4.2",
|
|
119
|
+
"@opengeni/codex": "1.4.2",
|
|
120
|
+
"@opengeni/config": "1.4.2",
|
|
121
|
+
"@opengeni/contracts": "1.4.2",
|
|
122
|
+
"@opengeni/network": "1.4.2",
|
|
123
|
+
"@opengeni/observability": "1.4.2",
|
|
124
124
|
"drizzle-orm": "^0.45.2",
|
|
125
125
|
"postgres": "^3.4.7"
|
|
126
126
|
},
|
|
@@ -277,7 +277,13 @@ export async function adoptCodemodeApproval(
|
|
|
277
277
|
);
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
/**
|
|
280
|
+
/**
|
|
281
|
+
* Read a handle with the current exact attempt and same original caller; old
|
|
282
|
+
* bearer authority is insufficient. This is a status read polled while a
|
|
283
|
+
* caller waits, so it is one plain snapshot select: it proves the exact live
|
|
284
|
+
* attempt in the same statement but takes no session/turn/attempt row locks
|
|
285
|
+
* and is not refused by a pending Pause/Steer, which owns the write fence only.
|
|
286
|
+
*/
|
|
281
287
|
export async function readTurnCodemodeOperation(
|
|
282
288
|
db: Database,
|
|
283
289
|
input: CodemodeContinuationScope & {
|
|
@@ -285,27 +291,51 @@ export async function readTurnCodemodeOperation(
|
|
|
285
291
|
callerSubjectId: string;
|
|
286
292
|
},
|
|
287
293
|
): Promise<CodemodeOperation | null> {
|
|
288
|
-
return await withRlsContext(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
.
|
|
296
|
-
|
|
297
|
-
.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
294
|
+
return await withRlsContext(db, input, async (scoped) => {
|
|
295
|
+
const [live] = await scoped
|
|
296
|
+
.select({ attemptId: schema.sessionTurnAttempts.id })
|
|
297
|
+
.from(schema.sessionTurns)
|
|
298
|
+
.innerJoin(
|
|
299
|
+
schema.sessionTurnAttempts,
|
|
300
|
+
and(
|
|
301
|
+
eq(schema.sessionTurnAttempts.id, schema.sessionTurns.activeAttemptId),
|
|
302
|
+
eq(schema.sessionTurnAttempts.turnId, schema.sessionTurns.id),
|
|
303
|
+
eq(schema.sessionTurnAttempts.sessionId, schema.sessionTurns.sessionId),
|
|
304
|
+
eq(schema.sessionTurnAttempts.accountId, schema.sessionTurns.accountId),
|
|
305
|
+
eq(
|
|
306
|
+
schema.sessionTurnAttempts.executionGeneration,
|
|
307
|
+
schema.sessionTurns.executionGeneration,
|
|
308
|
+
),
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
.where(
|
|
312
|
+
and(
|
|
313
|
+
eq(schema.sessionTurns.id, input.turnId),
|
|
314
|
+
eq(schema.sessionTurns.accountId, input.accountId),
|
|
315
|
+
eq(schema.sessionTurns.workspaceId, input.workspaceId),
|
|
316
|
+
eq(schema.sessionTurns.sessionId, input.sessionId),
|
|
317
|
+
eq(schema.sessionTurns.status, "running"),
|
|
318
|
+
eq(schema.sessionTurns.activeAttemptId, input.attemptId),
|
|
319
|
+
eq(schema.sessionTurns.executionGeneration, input.executionGeneration),
|
|
320
|
+
inArray(schema.sessionTurnAttempts.state, ["claimed", "running"]),
|
|
321
|
+
),
|
|
322
|
+
)
|
|
323
|
+
.limit(1);
|
|
324
|
+
if (!live) throw new CodemodeOperationNotExecutableError();
|
|
325
|
+
const [row] = await scoped
|
|
326
|
+
.select()
|
|
327
|
+
.from(schema.sessionAttemptCodemodeCalls)
|
|
328
|
+
.where(
|
|
329
|
+
and(
|
|
330
|
+
eq(schema.sessionAttemptCodemodeCalls.workspaceId, input.workspaceId),
|
|
331
|
+
eq(schema.sessionAttemptCodemodeCalls.sessionId, input.sessionId),
|
|
332
|
+
eq(schema.sessionAttemptCodemodeCalls.turnId, input.turnId),
|
|
333
|
+
eq(schema.sessionAttemptCodemodeCalls.operationId, input.operationId),
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
.limit(1);
|
|
337
|
+
return row && codemodeCallerCanContinue(row, input) ? mapCodemodeOperation(row) : null;
|
|
338
|
+
});
|
|
309
339
|
}
|
|
310
340
|
|
|
311
341
|
/** Signed sandbox subjects are attempt-bound; only the same owning turn can use this mapping. */
|